mirror of
https://github.com/Sarjuuk/aowow.git
synced 2025-11-29 15:58:16 +08:00
Compare commits
1 Commits
master-dep
...
v1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7d49befdf |
14
.gitattributes
vendored
14
.gitattributes
vendored
@@ -1,14 +0,0 @@
|
|||||||
* text=input
|
|
||||||
|
|
||||||
*.php text eol=lf
|
|
||||||
*.js text eol=lf
|
|
||||||
*.css text eol=lf
|
|
||||||
*.sql text eol=lf
|
|
||||||
aowow text eol=lf
|
|
||||||
prQueue text eol=lf
|
|
||||||
|
|
||||||
*.png binary
|
|
||||||
*.jpg binary
|
|
||||||
*.gif binary
|
|
||||||
*.ttf binary
|
|
||||||
*.swf binary
|
|
||||||
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: issue template
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug and how to reproduce it**
|
|
||||||
additionally paste relevant lines from db table `aowow_errors`
|
|
||||||
or your browsers console here.
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
**System:**
|
|
||||||
- OS: [e.g. Win10]
|
|
||||||
- PHP version:
|
|
||||||
- revision used:
|
|
||||||
- Browser (in case of JavaScript / display errors):
|
|
||||||
- AzerothCore: yes/no
|
|
||||||
20
.gitignore
vendored
20
.gitignore
vendored
@@ -1,44 +1,30 @@
|
|||||||
# Git
|
|
||||||
*.orig
|
|
||||||
|
|
||||||
# cache
|
# cache
|
||||||
/cache/template/*
|
/cache/template/*
|
||||||
/cache/alphaMaps/*
|
/setup/generated/alphaMaps/*.png
|
||||||
/cache/setup/*
|
|
||||||
|
|
||||||
# extract from MPQ
|
|
||||||
/setup/mpqdata/*
|
|
||||||
|
|
||||||
# generated files
|
# generated files
|
||||||
/static/js/profile_all.js
|
/static/js/profile_all.js
|
||||||
/static/js/locale.js
|
/static/js/locale.js
|
||||||
/static/js/Markup.js
|
|
||||||
/static/widgets/power.js
|
/static/widgets/power.js
|
||||||
/static/widgets/power/demo.html
|
/static/widgets/power/demo.html
|
||||||
/static/widgets/searchbox.js
|
/static/widgets/searchbox.js
|
||||||
/static/widgets/searchbox/searchbox.html
|
/static/widgets/searchbox/searchbox.html
|
||||||
/static/download/searchplugins/aowow.xml
|
/static/download/searchplugins/aowow.xml
|
||||||
/config/config.php
|
/config/config.php
|
||||||
/datasets/*
|
|
||||||
!/datasets/zones
|
|
||||||
# /datasets/item-scaling
|
# /datasets/item-scaling
|
||||||
|
|
||||||
# extracted sounds
|
|
||||||
/static/wowsounds/*
|
|
||||||
|
|
||||||
# extracted images
|
# extracted images
|
||||||
/static/images/wow/icons/large/*
|
/static/images/wow/icons/large/*
|
||||||
/static/images/wow/icons/medium/*
|
/static/images/wow/icons/medium/*
|
||||||
/static/images/wow/icons/small/*
|
/static/images/wow/icons/small/*
|
||||||
/static/images/wow/icons/tiny/*
|
/static/images/wow/icons/tiny/*
|
||||||
!/static/images/wow/icons/tiny/quest_*
|
!/static/images/wow/icons/tiny/quest_*
|
||||||
/static/images/wow/hunterpettalents/*
|
/static/images/wow/hunterpettalents/icons*
|
||||||
/static/images/wow/Interface/*
|
/static/images/wow/interface/*
|
||||||
/static/images/wow/loadingscreens/*
|
/static/images/wow/loadingscreens/*
|
||||||
/static/images/wow/maps/*
|
/static/images/wow/maps/*
|
||||||
!/static/images/wow/maps/overlay*
|
!/static/images/wow/maps/overlay*
|
||||||
/static/images/wow/talents/icons/*
|
/static/images/wow/talents/icons/*
|
||||||
/static/images/wow/talents/backgrounds/*
|
|
||||||
|
|
||||||
# modelviewer (~7GB data)
|
# modelviewer (~7GB data)
|
||||||
/static/modelviewer/models/*
|
/static/modelviewer/models/*
|
||||||
|
|||||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[submodule "includes/tools/MPQExtractor"]
|
||||||
|
path = includes/tools/MPQExtractor
|
||||||
|
url = https://github.com/iamcal/MPQExtractor.git
|
||||||
|
[submodule "includes/tools/BLPConverter"]
|
||||||
|
path = includes/tools/BLPConverter
|
||||||
|
url = https://github.com/Kanma/BLPConverter.git
|
||||||
24
.htaccess
24
.htaccess
@@ -1,19 +1,11 @@
|
|||||||
Order Deny,Allow
|
Order Deny,Allow
|
||||||
<FilesMatch "\.(conf|php|in)$">
|
<FilesMatch "\.(conf|php|in)|aowow$">
|
||||||
Deny from all
|
Deny from all
|
||||||
</FilesMatch>
|
</FilesMatch>
|
||||||
<FilesMatch "^(index)\.php$">
|
<FilesMatch "^(index)\.php$">
|
||||||
Allow from all
|
Allow from all
|
||||||
</FilesMatch>
|
</FilesMatch>
|
||||||
|
|
||||||
<Files "aowow">
|
|
||||||
ForceType application/x-httpd-php
|
|
||||||
</Files>
|
|
||||||
|
|
||||||
<Files "prQueue">
|
|
||||||
ForceType application/x-httpd-php
|
|
||||||
</Files>
|
|
||||||
|
|
||||||
# Block view of some folders
|
# Block view of some folders
|
||||||
Options -Indexes
|
Options -Indexes
|
||||||
DirectoryIndex index.php
|
DirectoryIndex index.php
|
||||||
@@ -21,13 +13,13 @@ DirectoryIndex index.php
|
|||||||
# Support for UTF8
|
# Support for UTF8
|
||||||
AddDefaultCharset utf8
|
AddDefaultCharset utf8
|
||||||
<IfModule mod_charset.c>
|
<IfModule mod_charset.c>
|
||||||
CharsetDisable on
|
CharsetDisable on
|
||||||
CharsetRecodeMultipartForms Off
|
CharsetRecodeMultipartForms Off
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
# UHD screenshots can get pretty large (cannot be set in config)
|
# 5MB should be enough for the largest screenshots in the land
|
||||||
php_value upload_max_filesize 20M
|
php_value default_charset UTF-8
|
||||||
php_value post_max_size 25M
|
php_value upload_max_filesize 5M
|
||||||
|
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
# RewriteBase /~user/localPath/ # enable if the rules do not work, when they should
|
# RewriteBase /~user/localPath/ # enable if the rules do not work, when they should
|
||||||
|
|||||||
140
README.md
140
README.md
@@ -1,140 +0,0 @@
|
|||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Build Status
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
AoWoW is a Database tool for World of Warcraft v3.3.5 (build 12340)
|
|
||||||
It is based upon the other famous Database tool for WoW, featuring the red smiling rocket.
|
|
||||||
While the first releases can be found as early as 2008, today it is impossible to say who created this project.
|
|
||||||
This is a complete rewrite of the serverside php code and update to the clientside javascripts from 2008 to something 2013ish.
|
|
||||||
|
|
||||||
I myself take no credit for the clientside scripting, design and layout that these php-scripts cater to.
|
|
||||||
Also, this project is not meant to be used for commercial purposes of any kind!
|
|
||||||
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
+ Webserver running PHP ≥ 8.2 including extensions:
|
|
||||||
+ [SimpleXML](https://www.php.net/manual/en/book.simplexml.php)
|
|
||||||
+ [GD](https://www.php.net/manual/en/book.image)
|
|
||||||
+ [MySQL Improved](https://www.php.net/manual/en/book.mysqli.php)
|
|
||||||
+ [Multibyte String](https://www.php.net/manual/en/book.mbstring.php)
|
|
||||||
+ [File Information](https://www.php.net/manual/en/book.fileinfo.php)
|
|
||||||
+ [GNU Multiple Precision](https://www.php.net/manual/en/book.gmp.php) (When using TrinityCore as auth source)
|
|
||||||
+ MySQL ≥ 5.7.0 OR MariaDB ≥ 10.6.4 OR similar
|
|
||||||
+ [TDB 335.21101](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.21101) (no other other providers are supported at this time)
|
|
||||||
+ WIN: php.exe needs to be added to the `PATH` system variable, if it isn't already.
|
|
||||||
+ Tools require cmake: Please refer to the individual repositories for detailed information
|
|
||||||
+ [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / (optional: [BLPConverter](https://github.com/Sarjuuk/BLPConverter))
|
|
||||||
+ WIN users may find it easier to use these alternatives
|
|
||||||
+ [MPQEditor](http://www.zezula.net/en/mpq/download.html) / [FFmpeg](http://ffmpeg.zeranoe.com/builds/) / (optional: [BLPConverter](https://github.com/PatrickCyr/BLPConverter))
|
|
||||||
|
|
||||||
audio processing may require [lame](https://sourceforge.net/projects/lame/files/lame/3.99/) or [vorbis-tools](https://www.xiph.org/downloads/) (which may require libvorbis (which may require libogg))
|
|
||||||
|
|
||||||
|
|
||||||
#### Highly Recommended
|
|
||||||
+ setting the following configuration values on your TrinityCore server will greatly increase the accuracy of spawn points
|
|
||||||
> Calculate.Creature.Zone.Area.Data = 1
|
|
||||||
> Calculate.Gameobject.Zone.Area.Data = 1
|
|
||||||
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
#### 1. Acquire the required repositories
|
|
||||||
`git clone git@github.com:Sarjuuk/aowow.git aowow`
|
|
||||||
`git clone git@github.com:Sarjuuk/MPQExtractor.git MPQExtractor`
|
|
||||||
|
|
||||||
#### 2. Prepare the database
|
|
||||||
Ensure that the account you are going to use has **full** access on the database AoWoW is going to occupy and ideally only **read** access on the world database you are going to reference.
|
|
||||||
Import `setup/db_structure.sql` into the AoWoW database `mysql -p {your-db-here} < setup/db_structure.sql`
|
|
||||||
|
|
||||||
#### 3. Server created files
|
|
||||||
See to it, that the web server is able to write the following directories and their children. If they are missing, the setup will create them with appropriate permissions
|
|
||||||
* `cache/`
|
|
||||||
* `config/`
|
|
||||||
* `static/download/`
|
|
||||||
* `static/widgets/`
|
|
||||||
* `static/js/`
|
|
||||||
* `static/uploads/`
|
|
||||||
* `static/images/wow/`
|
|
||||||
* `datasets/`
|
|
||||||
|
|
||||||
#### 4. Extract the client archives (MPQs)
|
|
||||||
Extract the following directories from the client archives into `setup/mpqdata/`, while maintaining patch order (base mpq -> patch-mpq: 1 -> 9 -> A -> Z). The required paths are scattered across the archives. Overwrite older files if asked to.
|
|
||||||
.. for every locale you are going to use:
|
|
||||||
> \<localeCode>/DBFilesClient/
|
|
||||||
> \<localeCode>/Interface/WorldMap/
|
|
||||||
> \<localeCode>/Interface/FrameXML/GlobalStrings.lua
|
|
||||||
|
|
||||||
.. once is enough (still apply the localeCode though):
|
|
||||||
> \<localeCode>/Interface/TalentFrame/
|
|
||||||
> \<localeCode>/Interface/Icons/
|
|
||||||
> \<localeCode>/Interface/Spellbook/
|
|
||||||
> \<localeCode>/Interface/PaperDoll/
|
|
||||||
> \<localeCode>/Interface/Glues/CharacterCreate/
|
|
||||||
> \<localeCode>/Interface/Pictures
|
|
||||||
> \<localeCode>/Interface/PvPRankBadges
|
|
||||||
> \<localeCode>/Interface/FlavorImages
|
|
||||||
> \<localeCode>/Interface/Calendar/Holidays/
|
|
||||||
> \<localeCode>/Sound/
|
|
||||||
|
|
||||||
.. optionally (not used in AoWoW):
|
|
||||||
> \<localeCode>/Interface/Glues/Loadingscreens/
|
|
||||||
> \<localeCode>/Interface/Glues/Credits/
|
|
||||||
|
|
||||||
#### 5. Reencode the audio files
|
|
||||||
WAV-files need to be reencoded as `ogg/vorbis` and some MP3s may identify themselves as `application/octet-stream` instead of `audio/mpeg`.
|
|
||||||
* [example for WIN](https://gist.github.com/Sarjuuk/d77b203f7b71d191509afddabad5fc9f)
|
|
||||||
* [example for \*nix](https://gist.github.com/Sarjuuk/1f05ef2affe49a7e7ca0fad7b01c081d)
|
|
||||||
|
|
||||||
#### 6. Run the initial setup from the CLI
|
|
||||||
`php aowow --setup`.
|
|
||||||
This should guide you through with minimal input required from your end, but will take some time though, especially compiling the zone-images. Use it to familiarize yourself with the other functions this setup has. Yes, I'm dead serious: *Go read the code!* It will help you understand how to configure AoWoW and keep it in sync with your world database.
|
|
||||||
When you've created your admin account you are done.
|
|
||||||
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
Q: The Page appears white, without any styles.
|
|
||||||
A: The static content is not being displayed. You are either using SSL and AoWoW is unable to detect it or STATIC_HOST is not defined properly. Either way this can be fixed via config `php aowow --siteconfig`
|
|
||||||
|
|
||||||
Q: Fatal error: Can't inherit abstract function \<functionName> (previously declared abstract in \<className>) in \<path>
|
|
||||||
A: You are using cache optimization modules for php, that are in conflict with each other. (Zend OPcache, XCache, ..) Disable all but one.
|
|
||||||
|
|
||||||
Q: Some generated images appear distorted or have alpha-channel issues.
|
|
||||||
A: Image compression is beyond my understanding, so i am unable to fix these issues within the blpReader.
|
|
||||||
BUT you can convert the affected blp file into a png file in the same directory, using the provided BLPConverter.
|
|
||||||
AoWoW will prioritize png files over blp files.
|
|
||||||
|
|
||||||
Q: How can i get the modelviewer to work?
|
|
||||||
A: You can't anymore. Wowhead switched from Flash to WebGL (as they should) and moved or deleted the old files in the process.
|
|
||||||
|
|
||||||
Q: I'm getting random javascript errors!
|
|
||||||
A: Some server configurations or external services (like Cloudflare) come with modules, that automatically minify js and css files. Sometimes they break in the process. Disable the module in this case.
|
|
||||||
|
|
||||||
Q: Some search results within the profiler act rather strange. How does it work?
|
|
||||||
A: Whenever you try to view a new character, AoWoW needs to fetch it first. Since the data is structured for the needs of TrinityCore and not for easy viewing, AoWoW needs to save and restructure it locally. To this end, every char request is placed in a queue. While the queue is not empty, a single instance of `prQueue` is run in the background as not to overwhelm the characters database with requests. This also means, some more exotic search queries can't be run against the characters database and have to use the incomplete/outdated cached profiles of AoWoW.
|
|
||||||
|
|
||||||
Q: Screenshot upload fails, because the file size is too large and/or the subdirectories are visible from the web!
|
|
||||||
A: That's a web server configuration issue. If you are using Apache you may need to [enable the use of .htaccess](http://httpd.apache.org/docs/2.4/de/mod/core.html#allowoverride). Other servers require individual configuration.
|
|
||||||
|
|
||||||
Q: An Item, Quest or NPC i added or edited can't be searched. Why?
|
|
||||||
A: A search is only conducted against the currently used locale. You may have only edited the name field in the base table instead of adding multiple strings into the appropriate \*_locale tables. In this case searches in a non-english locale are run against an empty name field.
|
|
||||||
|
|
||||||
## Thanks
|
|
||||||
|
|
||||||
@mix: for providing the php-script to parse .blp and .dbc into usable images and tables
|
|
||||||
@LordJZ: the wrapper-class for DBSimple; the basic idea for the user-class
|
|
||||||
@kliver: basic implementation of screenshot uploads
|
|
||||||
@Sarjuuk: maintainer of the project
|
|
||||||
|
|
||||||
|
|
||||||
## Special Thanks
|
|
||||||
Said website with the red smiling rocket, for providing this beautiful website!
|
|
||||||
Please do not regard this project as blatant rip-off, rather as "We do really liked your presentation, but since time and content progresses, you are sadly no longer supplying the data we need".
|
|
||||||
|
|
||||||

|
|
||||||
17
aowow
Executable file → Normal file
17
aowow
Executable file → Normal file
@@ -1,15 +1,12 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
if (PHP_SAPI !== 'cli')
|
require 'includes/shared.php';
|
||||||
|
|
||||||
|
if (!CLI)
|
||||||
die("this script must be run from CLI\n");
|
die("this script must be run from CLI\n");
|
||||||
if (PHP_SAPI === 'cli' && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
|
if (CLI && getcwd().DIRECTORY_SEPARATOR.'aowow' != __FILE__)
|
||||||
die("this script must be run from the aowow root directory\n");
|
die("this script must be run from root directory\n");
|
||||||
|
else
|
||||||
require_once 'includes/kernel.php';
|
require 'setup/setup.php';
|
||||||
require_once 'includes/setup/cli.class.php';
|
|
||||||
require_once 'includes/setup/timer.class.php';
|
|
||||||
|
|
||||||
require_once 'setup/setup.php';
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
49
config/config.php.in
Normal file
49
config/config.php.in
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('AOWOW_REVISION'))
|
||||||
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
|
// use as example. File is generated from setup.
|
||||||
|
|
||||||
|
// -- Aowow Database --
|
||||||
|
// contains world-data, user-data and logs
|
||||||
|
$AoWoWconf['aowow'] = array(
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'user' => '<user>',
|
||||||
|
'pass' => '<pass>',
|
||||||
|
'db' => 'world',
|
||||||
|
'prefix' => 'aowow_'
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- World Database --
|
||||||
|
// used to generate data-tables
|
||||||
|
$AoWoWconf['world'] = array(
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'user' => '<user>',
|
||||||
|
'pass' => '<pass>',
|
||||||
|
'db' => 'world',
|
||||||
|
'prefix' => ''
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- Auth Database --
|
||||||
|
// used to generate user-tables
|
||||||
|
$AoWoWconf['auth'] = array(
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'user' => '<user>',
|
||||||
|
'pass' => '<pass>',
|
||||||
|
'db' => 'auth',
|
||||||
|
'prefix' => ''
|
||||||
|
);
|
||||||
|
|
||||||
|
// -- Characters Database --
|
||||||
|
// used to display profiles
|
||||||
|
$AoWoWconf['characters'][<realmId>] = array(
|
||||||
|
'host' => '127.0.0.1',
|
||||||
|
'user' => '<user>',
|
||||||
|
'pass' => '<pass>',
|
||||||
|
'db' => 'characters',
|
||||||
|
'prefix' => ''
|
||||||
|
);
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -4,16 +4,13 @@ if (!defined('AOWOW_REVISION'))
|
|||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
function extAuth(string &$usernameOrEmail, string $password, int &$userId = 0, int &$userGroup = -1) : int
|
function extAuth($user, $pass, &$userId = 0)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
insert some auth mechanism here
|
insert some auth mechanism here
|
||||||
|
|
||||||
set usernameOrEmail to a valid username, do not pass back the email if used for login
|
see defines for usable return values
|
||||||
set userId to uid from external auth provider for identification
|
set userId for identification
|
||||||
(optional) set userGroup to a valid userGroup (see U_GROUP_* defines)
|
|
||||||
|
|
||||||
return an AUTH_* result (see defines)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return AUTH_INTERNAL_ERR;
|
return AUTH_INTERNAL_ERR;
|
||||||
|
|||||||
74
datasets/weight-presets
Normal file
74
datasets/weight-presets
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
var wt_presets = {
|
||||||
|
1: {
|
||||||
|
pve: {
|
||||||
|
arms: {__icon:'ability_rogue_eviscerate'},
|
||||||
|
fury: {__icon:'ability_warrior_innerrage',exprtng:100,str:82,critstrkrtng:66,agi:53,armorpenrtng:52,hitrtng:48,hastertng:36,atkpwr:31,armor:5},
|
||||||
|
prot: {__icon:'ability_warrior_defensivestance',sta:100,dodgertng:90,defrtng:86,block:81,agi:67,parryrtng:67,blockrtng:48,str:48,exprtng:19,hitrtng:10,armorpenrtng:10,critstrkrtng:7,armor:6,hastertng:1,atkpwr:1}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
pve: {
|
||||||
|
holy: {__icon:'spell_holy_holybolt',int:100,manargn:88,splpwr:58,critstrkrtng:46,hastertng:35},
|
||||||
|
prot: {__icon:'ability_paladin_shieldofthetemplar',sta:100,dodgertng:94,block:86,defrtng:86,exprtng:79,agi:76,parryrtng:76,hitrtng:58,blockrtng:52,str:50,armor:6,atkpwr:6,splpwr:4,critstrkrtng:3},
|
||||||
|
retrib: {__icon:'spell_holy_auraoflight',mledps:470,hitrtng:100,str:80,exprtng:66,critstrkrtng:40,atkpwr:34,agi:32,hastertng:30,armorpenrtng:22,splpwr:9}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
pve: {
|
||||||
|
beast: {__icon:'ability_hunter_beasttaming',rgddps:213,hitrtng:100,agi:58,critstrkrtng:40,int:37,atkpwr:30,armorpenrtng:28,hastertng:21},
|
||||||
|
marks: {__icon:'ability_marksmanship',rgddps:379,hitrtng:100,agi:74,critstrkrtng:57,armorpenrtng:40,int:39,atkpwr:32,hastertng:24},
|
||||||
|
surv: {__icon:'ability_hunter_swiftstrike',rgddps:181,hitrtng:100,agi:76,critstrkrtng:42,int:35,hastertng:31,atkpwr:29,armorpenrtng:26}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
pve: {
|
||||||
|
assas: {__icon:'ability_rogue_eviscerate',mledps:170,agi:100,exprtng:87,hitrtng:83,critstrkrtng:81,atkpwr:65,armorpenrtng:65,hastertng:64,str:55},
|
||||||
|
combat: {__icon:'ability_backstab',mledps:220,armorpenrtng:100,agi:100,exprtng:82,hitrtng:80,critstrkrtng:75,hastertng:73,str:55,atkpwr:50},
|
||||||
|
subtle: {__icon:'ability_stealth',mledps:228,exprtng:100,agi:100,hitrtng:80,armorpenrtng:75,critstrkrtng:75,hastertng:75,str:55,atkpwr:50}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
pve: {
|
||||||
|
disc: {__icon:'spell_holy_wordfortitude',splpwr:100,manargn:67,int:65,hastertng:59,critstrkrtng:48,spi:22},
|
||||||
|
holy: {__icon:'spell_holy_guardianspirit',manargn:100,int:69,splpwr:60,spi:52,critstrkrtng:38,hastertng:31},
|
||||||
|
shadow: {__icon:'spell_shadow_shadowwordpain',hitrtng:100,shasplpwr:76,splpwr:76,critstrkrtng:54,hastertng:50,spi:16,int:16}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
6: {
|
||||||
|
pve: {
|
||||||
|
blooddps: {__icon:'spell_deathknight_bloodpresence',mledps:360,armorpenrtng:100,str:99,hitrtng:91,exprtng:90,critstrkrtng:57,hastertng:55,atkpwr:36,armor:1},
|
||||||
|
frostdps: {__icon:'spell_deathknight_frostpresence',mledps:337,hitrtng:100,str:97,exprtng:81,armorpenrtng:61,critstrkrtng:45,atkpwr:35,hastertng:28,armor:1},
|
||||||
|
frosttank: {__icon:'spell_deathknight_frostpresence',mledps:419,parryrtng:100,hitrtng:97,str:96,defrtng:85,exprtng:69,dodgertng:61,agi:61,sta:61,critstrkrtng:49,atkpwr:41,armorpenrtng:31,armor:5},
|
||||||
|
unholydps: {__icon:'spell_deathknight_unholypresence',mledps:209,str:100,hitrtng:66,exprtng:51,hastertng:48,critstrkrtng:45,atkpwr:34,armorpenrtng:32,armor:1}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
7: {
|
||||||
|
pve: {
|
||||||
|
elem: {__icon:'spell_nature_lightning',hitrtng:100,splpwr:60,hastertng:56,critstrkrtng:40,int:11},
|
||||||
|
enhance: {__icon:'spell_nature_lightningshield',mledps:135,hitrtng:100,exprtng:84,agi:55,int:55,critstrkrtng:55,hastertng:42,str:35,atkpwr:32,splpwr:29,armorpenrtng:26},
|
||||||
|
resto: {__icon:'spell_nature_magicimmunity',manargn:100,int:85,splpwr:77,critstrkrtng:62,hastertng:35}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
8: {
|
||||||
|
pve: {
|
||||||
|
arcane: {__icon:'spell_holy_magicalsentry',hitrtng:100,hastertng:54,arcsplpwr:49,splpwr:49,critstrkrtng:37,int:34,frosplpwr:24,firsplpwr:24,spi:14},
|
||||||
|
fire: {__icon:'spell_fire_firebolt02',hitrtng:100,hastertng:53,firsplpwr:46,splpwr:46,critstrkrtng:43,frosplpwr:23,arcsplpwr:23,int:13},
|
||||||
|
frost: {__icon:'spell_frost_frostbolt02',hitrtng:100,hastertng:42,frosplpwr:39,splpwr:39,arcsplpwr:19,firsplpwr:19,critstrkrtng:19,int:6}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
9: {
|
||||||
|
pve: {
|
||||||
|
afflic: {__icon:'spell_shadow_deathcoil',hitrtng:100,shasplpwr:72,splpwr:72,hastertng:61,critstrkrtng:38,firsplpwr:36,spi:34,int:15},
|
||||||
|
demo: {__icon:'spell_shadow_metamorphosis',hitrtng:100,hastertng:50,firsplpwr:45,shasplpwr:45,splpwr:45,critstrkrtng:31,spi:29,int:13},
|
||||||
|
destro: {__icon:'spell_shadow_rainoffire',hitrtng:100,firsplpwr:47,splpwr:47,hastertng:46,spi:26,shasplpwr:23,critstrkrtng:16,int:13}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
11: {
|
||||||
|
pve: {
|
||||||
|
balance: {__icon:'spell_nature_starfall',hitrtng:100,splpwr:66,hastertng:54,critstrkrtng:43,spi:22,int:22},
|
||||||
|
feraltank: {__icon:'ability_racial_bearform',agi:100,sta:75,dodgertng:65,defrtng:60,exprtng:16,str:10,armor:10,hitrtng:8,hastertng:5,atkpwr:4,feratkpwr:4,critstrkrtng:3},
|
||||||
|
resto: {__icon:'spell_nature_healingtouch',splpwr:100,manargn:73,hastertng:57,int:51,spi:32,critstrkrtng:11},
|
||||||
|
feraldps: {__icon:'ability_druid_catform',agi:100,armorpenrtng:90,str:80,critstrkrtng:55,exprtng:50,hitrtng:50,feratkpwr:40,atkpwr:40,hastertng:35}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
46
datasets/zones
Normal file
46
datasets/zones
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
Mapper.multiLevelZones = {
|
||||||
|
206: ['206-1', '206-2', '206-3'],
|
||||||
|
209: ['209-1', '209-2', '209-3', '209-4', '209-5', '209-6', '209-7'],
|
||||||
|
616: ['616-1', '616_1', '616_2'],
|
||||||
|
719: ['719-1', '719-2', '719-3'],
|
||||||
|
721: ['721-1', '721-2', '721-3', '721-4'],
|
||||||
|
796: ['796-1', '796-2', '796-3', '796-4'],
|
||||||
|
1196: ['1196-1', '1196-2'],
|
||||||
|
1337: ['1337-1', '1337-2'],
|
||||||
|
1581: ['1581-1', '1581-2'],
|
||||||
|
1583: ['1583-1', '1583-2', '1583-3', '1583-4', '1583-5', '1583-6', '1583-7'],
|
||||||
|
1584: ['1584-1', '1584-2'],
|
||||||
|
2017: ['2017-1', '2017-2'],
|
||||||
|
2057: ['2057-1', '2057-2', '2057-3', '2057-4'],
|
||||||
|
2100: ['2100-1', '2100-2'],
|
||||||
|
2557: ['2557-1', '2557-2', '2557-3', '2557-4', '2557-5', '2557-6'],
|
||||||
|
2677: ['2677-1', '2677-2', '2677-3', '2677-4'],
|
||||||
|
3959: ['3959', '3959-1', '3959-2', '3959-3', '3959-4', '3959-5', '3959-6', '3959-7'],
|
||||||
|
3428: ['3428-1', '3428-2', '3428-3'],
|
||||||
|
3456: ['3456-1', '3456-2', '3456-3', '3456-4', '3456-5', '3456-6'],
|
||||||
|
3457: ['3457-1', '3457-2', '3457-3', '3457-4', '3457-5', '3457-6', '3457-7', '3457-8', '3457-9', '3457-10', '3457-11', '3457-12', '3457-13', '3457-14', '3457-15', '3457-16', '3457-17'],
|
||||||
|
3477: ['3477-1', '3477-2', '3477-3'],
|
||||||
|
3715: ['3715-1', '3715-2'],
|
||||||
|
3790: ['3790-1', '3790-2'],
|
||||||
|
3791: ['3791-1', '3791-2'],
|
||||||
|
3848: ['3848-1', '3848-2', '3848-3'],
|
||||||
|
3849: ['3849-1', '3849-2'],
|
||||||
|
4075: ['4075', '4075-1'],
|
||||||
|
4100: ['4100-1', '4100-2'],
|
||||||
|
4131: ['4131-1', '4131-2'],
|
||||||
|
4196: ['4196-1', '4196-2'],
|
||||||
|
4228: ['4228-1', '4228-2', '4228-3', '4228-4'],
|
||||||
|
4272: ['4272-1', '4272-2'],
|
||||||
|
4273: ['4273-0', '4273-1', '4273-2', '4273-3', '4273-4', '4273-5'],
|
||||||
|
4277: ['4277-1', '4277-2', '4277-3'],
|
||||||
|
4395: ['4395-1', '4395-2'],
|
||||||
|
4494: ['4494-1', '4494-2'],
|
||||||
|
4714: ['4714-1', '4714_1', '4714_2', '4714_3'],
|
||||||
|
4722: ['4722-1', '4722-2'],
|
||||||
|
4812: ['4812-1', '4812-2', '4812-3', '4812-4', '4812-5', '4812-6', '4812-7', '4812-8'],
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
var g_zone_areas = {};
|
||||||
|
in locale files
|
||||||
|
*/
|
||||||
1304
includes/ajaxHandler.class.php
Normal file
1304
includes/ajaxHandler.class.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,173 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxAccount extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $validParams = ['exclude', 'weightscales', 'favorites'];
|
|
||||||
protected $_post = array(
|
|
||||||
'groups' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'save' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'delete' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdList'],
|
|
||||||
'name' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxAccount::checkName' ],
|
|
||||||
'scale' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxAccount::checkScale' ],
|
|
||||||
'reset' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'mode' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'type' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'add' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'remove' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
// 'sessionKey' => ['filter' => FILTER_SANITIZE_NUMBER_INT]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params || !User::isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// select handler
|
|
||||||
if ($this->params[0] == 'exclude')
|
|
||||||
$this->handler = 'handleExclude';
|
|
||||||
else if ($this->params[0] == 'weightscales')
|
|
||||||
$this->handler = 'handleWeightscales';
|
|
||||||
else if ($this->params[0] == 'favorites')
|
|
||||||
$this->handler = 'handleFavorites';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleExclude() : void
|
|
||||||
{
|
|
||||||
if ($this->_post['mode'] == 1) // directly set exludes
|
|
||||||
{
|
|
||||||
$type = $this->_post['type'];
|
|
||||||
$ids = $this->_post['id'];
|
|
||||||
|
|
||||||
if ($validIds = Type::validateIds($this->_post['type'], $this->_post['id']))
|
|
||||||
{
|
|
||||||
// ready for some bullshit? here it comes!
|
|
||||||
// we don't get signaled whether an id should be added to or removed from either includes or excludes
|
|
||||||
// so we throw everything into one table and toggle the mode if its already in here
|
|
||||||
|
|
||||||
$includes = DB::Aowow()->selectCol('SELECT `typeId` FROM ?_profiler_excludes WHERE `type` = ?d AND `typeId` IN (?a)', $this->_post['type'], $validIds);
|
|
||||||
|
|
||||||
foreach ($validIds as $typeId)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE `mode` = (`mode` ^ 0x3)',
|
|
||||||
[User::$id, $this->_post['type'], $typeId, in_array($typeId, $includes) ? 2 : 1]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxAccount::handleExclude - invalid type #'.$type.(empty($ids) ? ' or id-list empty' : ''), E_USER_ERROR);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if ($this->_post['reset'] == 1) // defaults to unavailable
|
|
||||||
{
|
|
||||||
$mask = PR_EXCLUDE_GROUP_UNAVAILABLE;
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_account_excludes WHERE userId = ?d', User::$id);
|
|
||||||
}
|
|
||||||
else // clamp to real groups
|
|
||||||
$mask = $this->_post['groups'] & PR_EXCLUDE_GROUP_ANY;
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_account SET excludeGroups = ?d WHERE id = ?d', $mask, User::$id);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleWeightscales() : string
|
|
||||||
{
|
|
||||||
if ($this->_post['save'])
|
|
||||||
{
|
|
||||||
if (!$this->_post['scale'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAccount::handleWeightscales - scaleId empty', E_USER_ERROR);
|
|
||||||
return '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = 0;
|
|
||||||
|
|
||||||
if ($this->_post['id'] && ($id = $this->_post['id'][0]))
|
|
||||||
{
|
|
||||||
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_account_weightscales WHERE `userId` = ?d AND `id` = ?d', User::$id, $id))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAccount::handleWeightscales - scale #'.$id.' not in db or owned by user #'.User::$id, E_USER_ERROR);
|
|
||||||
return '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_account_weightscales SET `name` = ? WHERE `id` = ?d', $this->_post['name'], $id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$nScales = DB::Aowow()->selectCell('SELECT COUNT(`id`) FROM ?_account_weightscales WHERE `userId` = ?d', User::$id);
|
|
||||||
if ($nScales >= 5) // more or less hard-defined in LANG.message_weightscalesaveerror
|
|
||||||
return '0';
|
|
||||||
|
|
||||||
$id = DB::Aowow()->query('INSERT INTO ?_account_weightscales (`userId`, `name`) VALUES (?d, ?)', User::$id, $this->_post['name']);
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE `id` = ?d', $id);
|
|
||||||
|
|
||||||
foreach (explode(',', $this->_post['scale']) as $s)
|
|
||||||
{
|
|
||||||
[$k, $v] = explode(':', $s);
|
|
||||||
if (!in_array($k, Util::$weightScales) || $v < 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $id, $k, $v);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string)$id;
|
|
||||||
}
|
|
||||||
else if ($this->_post['delete'] && $this->_post['id'] && $this->_post['id'][0])
|
|
||||||
{
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_account_weightscales WHERE `id` = ?d AND `userId` = ?d', $this->_post['id'][0], User::$id);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('AjaxAccount::handleWeightscales - malformed request received', E_USER_ERROR);
|
|
||||||
return '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleFavorites() : void
|
|
||||||
{
|
|
||||||
// omit usage of sessionKey
|
|
||||||
if (count($this->_post['id']) != 1 || empty($this->_post['id'][0]))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAccount::handleFavorites - malformed request received', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$typeId = $this->_post['id'][0];
|
|
||||||
|
|
||||||
if ($type = $this->_post['add'])
|
|
||||||
{
|
|
||||||
if (!Type::validateIds($this->_post['add'], $this->_post['id']))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAccount::handleFavorites - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_account_favorites (`userId`, `type`, `typeId`) VALUES (?d, ?d, ?d)', User::$id, $type, $typeId);
|
|
||||||
}
|
|
||||||
else if ($type = $this->_post['remove'])
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_account_favorites WHERE `userId` = ?d AND `type` = ?d AND `typeId` = ?d', User::$id, $type, $typeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkScale(string $val) : string
|
|
||||||
{
|
|
||||||
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
|
|
||||||
return $val;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkName(string $val) : string
|
|
||||||
{
|
|
||||||
$var = trim(urldecode($val));
|
|
||||||
|
|
||||||
return filter_var($var, FILTER_SANITIZE_SPECIAL_CHARS, FILTER_FLAG_STRIP_LOW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,557 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxAdmin extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $validParams = ['screenshots', 'siteconfig', 'weight-presets', 'spawn-override', 'guide', 'comment'];
|
|
||||||
protected $_get = array(
|
|
||||||
'action' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine' ],
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdListUnsigned'],
|
|
||||||
'key' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxAdmin::checkKey' ],
|
|
||||||
'all' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkEmptySet' ],
|
|
||||||
'type' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ],
|
|
||||||
'typeid' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ],
|
|
||||||
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxAdmin::checkUser' ],
|
|
||||||
'val' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob' ],
|
|
||||||
'guid' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ],
|
|
||||||
'area' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ],
|
|
||||||
'floor' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ]
|
|
||||||
);
|
|
||||||
protected $_post = array(
|
|
||||||
'alt' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob'],
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ],
|
|
||||||
'scale' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxAdmin::checkScale' ],
|
|
||||||
'__icon' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxAdmin::checkKey' ],
|
|
||||||
'status' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt' ],
|
|
||||||
'msg' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($this->params[0] == 'screenshots' && $this->_get['action'])
|
|
||||||
{
|
|
||||||
if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($this->_get['action'] == 'list')
|
|
||||||
$this->handler = 'ssList';
|
|
||||||
else if ($this->_get['action'] == 'manage')
|
|
||||||
$this->handler = 'ssManage';
|
|
||||||
else if ($this->_get['action'] == 'editalt')
|
|
||||||
$this->handler = 'ssEditAlt';
|
|
||||||
else if ($this->_get['action'] == 'approve')
|
|
||||||
$this->handler = 'ssApprove';
|
|
||||||
else if ($this->_get['action'] == 'sticky')
|
|
||||||
$this->handler = 'ssSticky';
|
|
||||||
else if ($this->_get['action'] == 'delete')
|
|
||||||
$this->handler = 'ssDelete';
|
|
||||||
else if ($this->_get['action'] == 'relocate')
|
|
||||||
$this->handler = 'ssRelocate';
|
|
||||||
}
|
|
||||||
else if ($this->params[0] == 'siteconfig' && $this->_get['action'])
|
|
||||||
{
|
|
||||||
if (!User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($this->_get['action'] == 'add')
|
|
||||||
$this->handler = 'confAdd';
|
|
||||||
else if ($this->_get['action'] == 'remove')
|
|
||||||
$this->handler = 'confRemove';
|
|
||||||
else if ($this->_get['action'] == 'update')
|
|
||||||
$this->handler = 'confUpdate';
|
|
||||||
}
|
|
||||||
else if ($this->params[0] == 'weight-presets' && $this->_get['action'])
|
|
||||||
{
|
|
||||||
if (!User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_BUREAU))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($this->_get['action'] == 'save')
|
|
||||||
$this->handler = 'wtSave';
|
|
||||||
}
|
|
||||||
else if ($this->params[0] == 'spawn-override')
|
|
||||||
{
|
|
||||||
if (!User::isInGroup(U_GROUP_MODERATOR))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->handler = 'spawnPosFix';
|
|
||||||
}
|
|
||||||
else if ($this->params[0] == 'guide')
|
|
||||||
{
|
|
||||||
if (!User::isInGroup(U_GROUP_STAFF))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->handler = 'guideManage';
|
|
||||||
}
|
|
||||||
else if ($this->params[0] == 'comment')
|
|
||||||
{
|
|
||||||
if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->handler = 'commentOutOfDate';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get all => null (optional)
|
|
||||||
// evaled response .. UNK
|
|
||||||
protected function ssList() : string
|
|
||||||
{
|
|
||||||
// ssm_screenshotPages
|
|
||||||
// ssm_numPagesFound
|
|
||||||
|
|
||||||
$pages = CommunityContent::getScreenshotPagesForManager($this->_get['all'], $nPages);
|
|
||||||
$buff = 'ssm_screenshotPages = '.Util::toJSON($pages).";\n";
|
|
||||||
$buff .= 'ssm_numPagesFound = '.$nPages.';';
|
|
||||||
|
|
||||||
return $buff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get: [type => type, typeId => typeId] || [user => username]
|
|
||||||
// evaled response .. UNK
|
|
||||||
protected function ssManage() : string
|
|
||||||
{
|
|
||||||
$res = [];
|
|
||||||
|
|
||||||
if ($this->_get['type'] && $this->_get['type'] && $this->_get['typeid'] && $this->_get['typeid'])
|
|
||||||
$res = CommunityContent::getScreenshotsForManager($this->_get['type'], $this->_get['typeid']);
|
|
||||||
else if ($this->_get['user'])
|
|
||||||
if ($uId = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user']))
|
|
||||||
$res = CommunityContent::getScreenshotsForManager(0, 0, $uId);
|
|
||||||
|
|
||||||
return 'ssm_screenshotData = '.Util::toJSON($res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get: id => SSid
|
|
||||||
// resp: ''
|
|
||||||
protected function ssEditAlt() : void
|
|
||||||
{
|
|
||||||
// doesn't need to be htmlEscaped, ths javascript does that
|
|
||||||
if ($this->_get['id'] && $this->_post['alt'] !== null)
|
|
||||||
DB::Aowow()->query('UPDATE ?_screenshots SET caption = ? WHERE id = ?d', trim($this->_post['alt']), $this->_get['id'][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get: id => comma-separated SSids
|
|
||||||
// resp: ''
|
|
||||||
protected function ssApprove() : void
|
|
||||||
{
|
|
||||||
if (!$this->reqGET('id'))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAdmin::ssApprove - screenshotId empty', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create resized and thumb version of screenshot
|
|
||||||
$resized = [772, 618];
|
|
||||||
$thumb = [150, 150];
|
|
||||||
$path = 'static/uploads/screenshots/%s/%d.jpg';
|
|
||||||
|
|
||||||
foreach ($this->_get['id'] as $id)
|
|
||||||
{
|
|
||||||
// must not be already approved
|
|
||||||
if ($ssEntry = DB::Aowow()->selectRow('SELECT userIdOwner, date, type, typeId FROM ?_screenshots WHERE (status & ?d) = 0 AND id = ?d', CC_FLAG_APPROVED, $id))
|
|
||||||
{
|
|
||||||
// should also error-log
|
|
||||||
if (!file_exists(sprintf($path, 'pending', $id)))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAdmin::ssApprove - screenshot #'.$id.' exists in db but not as file', E_USER_ERROR);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$srcImg = imagecreatefromjpeg(sprintf($path, 'pending', $id));
|
|
||||||
$srcW = imagesx($srcImg);
|
|
||||||
$srcH = imagesy($srcImg);
|
|
||||||
|
|
||||||
// write thumb
|
|
||||||
$scale = min(1.0, min($thumb[0] / $srcW, $thumb[1] / $srcH));
|
|
||||||
$destW = $srcW * $scale;
|
|
||||||
$destH = $srcH * $scale;
|
|
||||||
$destImg = imagecreatetruecolor($destW, $destH);
|
|
||||||
|
|
||||||
imagefill($destImg, 0, 0, imagecolorallocate($destImg, 255, 255, 255));
|
|
||||||
imagecopyresampled($destImg, $srcImg, 0, 0, 0, 0, $destW, $destH, $srcW, $srcH);
|
|
||||||
|
|
||||||
imagejpeg($destImg, sprintf($path, 'thumb', $id), 100);
|
|
||||||
|
|
||||||
// write resized (only if required)
|
|
||||||
if ($srcW > $resized[0] || $srcH > $resized[1])
|
|
||||||
{
|
|
||||||
$scale = min(1.0, min($resized[0] / $srcW, $resized[1] / $srcH));
|
|
||||||
$destW = $srcW * $scale;
|
|
||||||
$destH = $srcH * $scale;
|
|
||||||
$destImg = imagecreatetruecolor($destW, $destH);
|
|
||||||
|
|
||||||
imagefill($destImg, 0, 0, imagecolorallocate($destImg, 255, 255, 255));
|
|
||||||
imagecopyresampled($destImg, $srcImg, 0, 0, 0, 0, $destW, $destH, $srcW, $srcH);
|
|
||||||
|
|
||||||
imagejpeg($destImg, sprintf($path, 'resized', $id), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
imagedestroy($srcImg);
|
|
||||||
|
|
||||||
// move screenshot from pending to normal
|
|
||||||
rename(sprintf($path, 'pending', $id), sprintf($path, 'normal', $id));
|
|
||||||
|
|
||||||
// set as approved in DB and gain rep (once!)
|
|
||||||
DB::Aowow()->query('UPDATE ?_screenshots SET status = ?d, userIdApprove = ?d WHERE id = ?d', CC_FLAG_APPROVED, User::$id, $id);
|
|
||||||
Util::gainSiteReputation($ssEntry['userIdOwner'], SITEREP_ACTION_UPLOAD, ['id' => $id, 'what' => 1, 'date' => $ssEntry['date']]);
|
|
||||||
// flag DB entry as having screenshots
|
|
||||||
if ($tbl = Type::getClassAttrib($ssEntry['type'], 'dataTable'))
|
|
||||||
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $ssEntry['typeId']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxAdmin::ssApprove - screenshot #'.$id.' not in db or already approved', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get: id => comma-separated SSids
|
|
||||||
// resp: ''
|
|
||||||
protected function ssSticky() : void
|
|
||||||
{
|
|
||||||
if (!$this->reqGET('id'))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAdmin::ssSticky - screenshotId empty', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// approve soon to be sticky screenshots
|
|
||||||
$this->ssApprove();
|
|
||||||
|
|
||||||
// this one is a bit strange: as far as i've seen, the only thing a 'sticky' screenshot does is show up in the infobox
|
|
||||||
// this also means, that only one screenshot per page should be sticky
|
|
||||||
// so, handle it one by one and the last one affecting one particular type/typId-key gets the cake
|
|
||||||
foreach ($this->_get['id'] as $id)
|
|
||||||
{
|
|
||||||
// reset all others
|
|
||||||
DB::Aowow()->query('UPDATE ?_screenshots a, ?_screenshots b SET a.status = a.status & ~?d WHERE a.type = b.type AND a.typeId = b.typeId AND a.id <> b.id AND b.id = ?d', CC_FLAG_STICKY, $id);
|
|
||||||
|
|
||||||
// toggle sticky status
|
|
||||||
DB::Aowow()->query('UPDATE ?_screenshots SET `status` = IF(`status` & ?d, `status` & ~?d, `status` | ?d) WHERE id = ?d AND `status` & ?d', CC_FLAG_STICKY, CC_FLAG_STICKY, CC_FLAG_STICKY, $id, CC_FLAG_APPROVED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get: id => comma-separated SSids
|
|
||||||
// resp: ''
|
|
||||||
// 2 steps: 1) remove from sight, 2) remove from disk
|
|
||||||
protected function ssDelete() : void
|
|
||||||
{
|
|
||||||
if (!$this->reqGET('id'))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAdmin::ssDelete - screenshotId empty', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$path = 'static/uploads/screenshots/%s/%d.jpg';
|
|
||||||
|
|
||||||
foreach ($this->_get['id'] as $id)
|
|
||||||
{
|
|
||||||
// irrevocably remove already deleted files
|
|
||||||
if (User::isInGroup(U_GROUP_ADMIN) && DB::Aowow()->selectCell('SELECT 1 FROM ?_screenshots WHERE status & ?d AND id = ?d', CC_FLAG_DELETED, $id))
|
|
||||||
{
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_screenshots WHERE id = ?d', $id);
|
|
||||||
if (file_exists(sprintf($path, 'pending', $id)))
|
|
||||||
unlink(sprintf($path, 'pending', $id));
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// move pending or normal to pending
|
|
||||||
if (file_exists(sprintf($path, 'normal', $id)))
|
|
||||||
rename(sprintf($path, 'normal', $id), sprintf($path, 'pending', $id));
|
|
||||||
|
|
||||||
// remove resized and thumb
|
|
||||||
if (file_exists(sprintf($path, 'thumb', $id)))
|
|
||||||
unlink(sprintf($path, 'thumb', $id));
|
|
||||||
|
|
||||||
if (file_exists(sprintf($path, 'resized', $id)))
|
|
||||||
unlink(sprintf($path, 'resized', $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// flag as deleted if not aready
|
|
||||||
$oldEntries = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, GROUP_CONCAT(typeId) FROM ?_screenshots WHERE id IN (?a) GROUP BY `type`', $this->_get['id']);
|
|
||||||
DB::Aowow()->query('UPDATE ?_screenshots SET status = ?d, userIdDelete = ?d WHERE id IN (?a)', CC_FLAG_DELETED, User::$id, $this->_get['id']);
|
|
||||||
// deflag db entry as having screenshots
|
|
||||||
foreach ($oldEntries as $type => $typeIds)
|
|
||||||
{
|
|
||||||
$typeIds = explode(',', $typeIds);
|
|
||||||
$toUnflag = DB::Aowow()->selectCol('SELECT typeId AS ARRAY_KEY, IF(BIT_OR(`status`) & ?d, 1, 0) AS hasMore FROM ?_screenshots WHERE `type` = ?d AND typeId IN (?a) GROUP BY typeId HAVING hasMore = 0', CC_FLAG_APPROVED, $type, $typeIds);
|
|
||||||
if ($toUnflag && ($tbl = Type::getClassAttrib($type, 'dataTable')))
|
|
||||||
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get: id => ssId, typeid => typeId (but not type..?)
|
|
||||||
// resp: ''
|
|
||||||
protected function ssRelocate() : void
|
|
||||||
{
|
|
||||||
if (!$this->reqGET('id', 'typeid'))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxAdmin::ssRelocate - screenshotId or typeId empty', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $this->_get['id'][0];
|
|
||||||
[$type, $oldTypeId] = array_values(DB::Aowow()->selectRow('SELECT type, typeId FROM ?_screenshots WHERE id = ?d', $id));
|
|
||||||
$typeId = (int)$this->_get['typeid'];
|
|
||||||
|
|
||||||
$tc = Type::newList($type, [['id', $typeId]]);
|
|
||||||
if ($tc && !$tc->error)
|
|
||||||
{
|
|
||||||
// move screenshot
|
|
||||||
DB::Aowow()->query('UPDATE ?_screenshots SET typeId = ?d WHERE id = ?d', $typeId, $id);
|
|
||||||
|
|
||||||
// flag target as having screenshot
|
|
||||||
DB::Aowow()->query('UPDATE '.$tc::$dataTable.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $typeId);
|
|
||||||
|
|
||||||
// deflag source for having had screenshots (maybe)
|
|
||||||
$ssInfo = DB::Aowow()->selectRow('SELECT IF(BIT_OR(~status) & ?d, 1, 0) AS hasMore FROM ?_screenshots WHERE `status`& ?d AND `type` = ?d AND typeId = ?d', CC_FLAG_DELETED, CC_FLAG_APPROVED, $type, $oldTypeId);
|
|
||||||
if($ssInfo || !$ssInfo['hasMore'])
|
|
||||||
DB::Aowow()->query('UPDATE '.$tc::$dataTable.' SET cuFlags = cuFlags & ~?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $oldTypeId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxAdmin::ssRelocate - invalid typeId #'.$typeId.' for type #'.$type, E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function confAdd() : string
|
|
||||||
{
|
|
||||||
$key = trim($this->_get['key']);
|
|
||||||
$val = trim(urldecode($this->_get['val']));
|
|
||||||
|
|
||||||
return Cfg::add($key, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function confRemove() : string
|
|
||||||
{
|
|
||||||
if (!$this->reqGET('key'))
|
|
||||||
return 'invalid configuration option given';
|
|
||||||
|
|
||||||
return Cfg::delete($this->_get['key']);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function confUpdate() : string
|
|
||||||
{
|
|
||||||
$key = trim($this->_get['key']);
|
|
||||||
$val = trim(urldecode($this->_get['val']));
|
|
||||||
|
|
||||||
return Cfg::set($key, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function wtSave() : string
|
|
||||||
{
|
|
||||||
if (!$this->reqPOST('id', '__icon'))
|
|
||||||
return '3';
|
|
||||||
|
|
||||||
// save to db
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_account_weightscale_data WHERE id = ?d', $this->_post['id']);
|
|
||||||
DB::Aowow()->query('UPDATE ?_account_weightscales SET `icon`= ? WHERE `id` = ?d', $this->_post['__icon'], $this->_post['id']);
|
|
||||||
|
|
||||||
foreach (explode(',', $this->_post['scale']) as $s)
|
|
||||||
{
|
|
||||||
[$k, $v] = explode(':', $s);
|
|
||||||
|
|
||||||
if (!in_array($k, Util::$weightScales) || $v < 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (DB::Aowow()->query('INSERT INTO ?_account_weightscale_data VALUES (?d, ?, ?d)', $this->_post['id'], $k, $v) === null)
|
|
||||||
return '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
// write dataset
|
|
||||||
exec('php aowow --build=weightPresets', $out);
|
|
||||||
foreach ($out as $o)
|
|
||||||
if (strstr($o, 'ERR'))
|
|
||||||
return '2';
|
|
||||||
|
|
||||||
// all done
|
|
||||||
return '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function spawnPosFix() : string
|
|
||||||
{
|
|
||||||
if (!$this->reqGET('type', 'guid', 'area', 'floor'))
|
|
||||||
return '-4';
|
|
||||||
|
|
||||||
$guid = $this->_get['guid'];
|
|
||||||
$type = $this->_get['type'];
|
|
||||||
$area = $this->_get['area'];
|
|
||||||
$floor = $this->_get['floor'];
|
|
||||||
|
|
||||||
if (!in_array($type, [Type::NPC, Type::OBJECT, Type::SOUND, Type::AREATRIGGER, Type::ZONE]))
|
|
||||||
return '-3';
|
|
||||||
|
|
||||||
DB::Aowow()->query('REPLACE INTO ?_spawns_override VALUES (?d, ?d, ?d, ?d, ?d)', $type, $guid, $area, $floor, AOWOW_REVISION);
|
|
||||||
|
|
||||||
if ($wPos = WorldPosition::getForGUID($type, $guid))
|
|
||||||
{
|
|
||||||
if ($point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $wPos[$guid]['posX'], $wPos[$guid]['posY'], $area, $floor))
|
|
||||||
{
|
|
||||||
$updGUIDs = [$guid];
|
|
||||||
$newPos = array(
|
|
||||||
'posX' => $point[0]['posX'],
|
|
||||||
'posY' => $point[0]['posY'],
|
|
||||||
'areaId' => $point[0]['areaId'],
|
|
||||||
'floor' => $point[0]['floor']
|
|
||||||
);
|
|
||||||
|
|
||||||
// if creature try for waypoints
|
|
||||||
if ($type == Type::NPC)
|
|
||||||
{
|
|
||||||
$jobs = array(
|
|
||||||
'SELECT -w.id AS `entry`, w.point AS `pointId`, w.position_x AS `posX`, w.position_y AS `posY` FROM creature_addon ca JOIN waypoint_data w ON w.id = ca.path_id WHERE ca.guid = ?d AND ca.path_id <> 0',
|
|
||||||
'SELECT `entry`, `pointId`, `location_x` AS `posX`, `location_y` AS `posY` FROM `script_waypoint` WHERE `entry` = ?d',
|
|
||||||
'SELECT `entry`, `pointId`, `position_x` AS `posX`, `position_y` AS `posY` FROM `waypoints` WHERE `entry` = ?d'
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($jobs as $idx => $job)
|
|
||||||
{
|
|
||||||
if ($swp = DB::World()->select($job, $idx ? $wPos[$guid]['id'] : $guid))
|
|
||||||
{
|
|
||||||
foreach ($swp as $w)
|
|
||||||
{
|
|
||||||
if ($point = WorldPosition::toZonePos($wPos[$guid]['mapId'], $w['posX'], $w['posY'], $area, $floor))
|
|
||||||
{
|
|
||||||
$p = array(
|
|
||||||
'posX' => $point[0]['posX'],
|
|
||||||
'posY' => $point[0]['posY'],
|
|
||||||
'areaId' => $point[0]['areaId'],
|
|
||||||
'floor' => $point[0]['floor']
|
|
||||||
);
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_creature_waypoints SET ?a WHERE `creatureOrPath` = ?d AND `point` = ?d', $p, $w['entry'], $w['pointId']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// also move linked vehicle accessories (on the very same position)
|
|
||||||
$updGUIDs = array_merge($updGUIDs, DB::Aowow()->selectCol('SELECT s2.guid FROM ?_spawns s1 JOIN ?_spawns s2 ON s1.posX = s2.posX AND s1.posY = s2.posY AND
|
|
||||||
s1.areaId = s2.areaId AND s1.floor = s2.floor AND s2.guid < 0 WHERE s1.guid = ?d', $guid));
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_spawns SET ?a WHERE `type` = ?d AND `guid` IN (?a)', $newPos, $type, $updGUIDs);
|
|
||||||
|
|
||||||
return '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '-2';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '-1';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function guideManage() : string
|
|
||||||
{
|
|
||||||
$update = function (int $id, int $status, ?string $msg = null) : bool
|
|
||||||
{
|
|
||||||
if (!DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', $status, $id))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// set display rev to latest
|
|
||||||
if ($status == GUIDE_STATUS_APPROVED)
|
|
||||||
DB::Aowow()->query('UPDATE ?_guides SET `rev` = (SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1), `approveUserId` = ?d, `approveDate` = ?d WHERE `id` = ?d', Type::GUIDE, $id, User::$id, time(), $id);
|
|
||||||
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $id, time(), User::$id, $status);
|
|
||||||
if ($msg)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?)', $id, time(), User::$id, $msg);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!$this->_post['id'])
|
|
||||||
trigger_error('AjaxHander::guideManage - malformed request: id: '.$this->_post['id'].', status: '.$this->_post['status']);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$guide = DB::Aowow()->selectRow('SELECT `userId`, `status` FROM ?_guides WHERE `id` = ?d', $this->_post['id']);
|
|
||||||
if (!$guide)
|
|
||||||
trigger_error('AjaxHander::guideManage - guide #'.$this->_post['id'].' not found');
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($this->_post['status'] == $guide['status'])
|
|
||||||
trigger_error('AjaxHander::guideManage - guide #'.$this->_post['id'].' already has status #'.$this->_post['status']);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($this->_post['status'] == GUIDE_STATUS_APPROVED)
|
|
||||||
{
|
|
||||||
if ($update($this->_post['id'], GUIDE_STATUS_APPROVED, $this->_post['msg']))
|
|
||||||
{
|
|
||||||
Util::gainSiteReputation($guide['userId'], SITEREP_ACTION_ARTICLE, ['id' => $this->_post['id']]);
|
|
||||||
return '1';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return '-2';
|
|
||||||
}
|
|
||||||
else if ($this->_post['status'] == GUIDE_STATUS_REJECTED)
|
|
||||||
return $update($this->_post['id'], GUIDE_STATUS_REJECTED, $this->_post['msg']) ? '1' : '-2';
|
|
||||||
else
|
|
||||||
trigger_error('AjaxHander::guideManage - unhandled status change request');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '-1';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function commentOutOfDate() : string
|
|
||||||
{
|
|
||||||
$ok = false;
|
|
||||||
switch ($this->_post['status'])
|
|
||||||
{
|
|
||||||
case 0: // up to date
|
|
||||||
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` = ?d', CC_FLAG_OUTDATED, $this->_post['id']))
|
|
||||||
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
|
|
||||||
$rep->close(Report::STATUS_CLOSED_WONTFIX);
|
|
||||||
break;
|
|
||||||
case 1: // outdated, mark as deleted and clear other flags (sticky + outdated)
|
|
||||||
if ($ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = ?d, `deleteUserId` = ?d, `deleteDate` = ?d WHERE `id` = ?d', CC_FLAG_DELETED, User::$id, time(), $this->_post['id']))
|
|
||||||
if ($rep = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id']))
|
|
||||||
$rep->close(Report::STATUS_CLOSED_SOLVED);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
trigger_error('AjaxHandler::comentOutOfDate - called with invalid status');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ok ? '1' : '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***************************/
|
|
||||||
/* additional input filter */
|
|
||||||
/***************************/
|
|
||||||
|
|
||||||
protected static function checkKey(string $val) : string
|
|
||||||
{
|
|
||||||
// expecting string
|
|
||||||
if (preg_match(Cfg::PATTERN_INV_CONF_KEY, $val))
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return strtolower($val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkUser($val) : string
|
|
||||||
{
|
|
||||||
$n = Util::lower(trim(urldecode($val)));
|
|
||||||
|
|
||||||
if (User::isValidName($n))
|
|
||||||
return $n;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkScale($val) : string
|
|
||||||
{
|
|
||||||
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
|
|
||||||
return $val;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class AjaxHandler
|
|
||||||
{
|
|
||||||
use TrRequestData;
|
|
||||||
|
|
||||||
protected $validParams = [];
|
|
||||||
protected $params = [];
|
|
||||||
protected $handler;
|
|
||||||
|
|
||||||
protected $contentType = MIME_TYPE_JSON;
|
|
||||||
|
|
||||||
public $doRedirect = false;
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
$this->params = $params;
|
|
||||||
|
|
||||||
$this->initRequestData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(string &$out) : bool
|
|
||||||
{
|
|
||||||
if (!$this->handler)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ($this->validParams)
|
|
||||||
{
|
|
||||||
if (count($this->params) != 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!in_array($this->params[0], $this->validParams))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$out = $this->{$this->handler}() ?? '';
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContentType() : string
|
|
||||||
{
|
|
||||||
return $this->contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function reqPOST(string ...$keys) : bool
|
|
||||||
{
|
|
||||||
foreach ($keys as $k)
|
|
||||||
if (!isset($this->_post[$k]) || $this->_post[$k] === null || $this->_post[$k] === '')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function reqGET(string ...$keys) : bool
|
|
||||||
{
|
|
||||||
foreach ($keys as $k)
|
|
||||||
if (!isset($this->_get[$k]) || $this->_get[$k] === null || $this->_get[$k] === '')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxArenaTeam extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $validParams = ['resync', 'status'];
|
|
||||||
protected $_get = array(
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdList' ],
|
|
||||||
'profile' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkEmptySet'],
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch ($this->params[0])
|
|
||||||
{
|
|
||||||
case 'resync':
|
|
||||||
$this->handler = 'handleResync';
|
|
||||||
break;
|
|
||||||
case 'status':
|
|
||||||
$this->handler = 'handleStatus';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
user: <string> [optional, not used]
|
|
||||||
profile: <empty> [optional, also get related chars]
|
|
||||||
return: 1
|
|
||||||
*/
|
|
||||||
protected function handleResync() : string
|
|
||||||
{
|
|
||||||
if ($teams = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_arena_team WHERE id IN (?a)', $this->_get['id']))
|
|
||||||
foreach ($teams as $t)
|
|
||||||
Profiler::scheduleResync(Type::ARENA_TEAM, $t['realm'], $t['realmGUID']);
|
|
||||||
|
|
||||||
if ($this->_get['profile'])
|
|
||||||
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles p JOIN ?_profiler_arena_team_member atm ON atm.profileId = p.id WHERE atm.arenaTeamId IN (?a)', $this->_get['id']))
|
|
||||||
foreach ($chars as $c)
|
|
||||||
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
|
|
||||||
|
|
||||||
return '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
return
|
|
||||||
<status object>
|
|
||||||
[
|
|
||||||
nQueueProcesses,
|
|
||||||
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
|
|
||||||
[<anotherStatus>]
|
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
not all fields are required, if zero they are omitted
|
|
||||||
statusCode:
|
|
||||||
0: end the request
|
|
||||||
1: waiting
|
|
||||||
2: working...
|
|
||||||
3: ready; click to view
|
|
||||||
4: error / retry
|
|
||||||
errorCode:
|
|
||||||
0: unk error
|
|
||||||
1: char does not exist
|
|
||||||
2: armory gone
|
|
||||||
*/
|
|
||||||
protected function handleStatus() : string
|
|
||||||
{
|
|
||||||
return Profiler::resyncStatus(Type::ARENA_TEAM, $this->_get['id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,485 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxComment extends AjaxHandler
|
|
||||||
{
|
|
||||||
const COMMENT_LENGTH_MIN = 10;
|
|
||||||
const COMMENT_LENGTH_MAX = 7500;
|
|
||||||
const REPLY_LENGTH_MIN = 15;
|
|
||||||
const REPLY_LENGTH_MAX = 600;
|
|
||||||
|
|
||||||
protected $_post = array(
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdListUnsigned'],
|
|
||||||
'body' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob' ],
|
|
||||||
'commentbody' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob' ],
|
|
||||||
'response' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob' ],
|
|
||||||
'reason' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob' ],
|
|
||||||
'remove' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'commentId' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'replyId' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'sticky' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
// 'username' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine' ]
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $_get = array(
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt'],
|
|
||||||
'type' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt'],
|
|
||||||
'typeid' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt'],
|
|
||||||
'rating' => ['filter' => FILTER_SANITIZE_NUMBER_INT ]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params || count($this->params) != 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// note: return values must be formated as STRICT json!
|
|
||||||
|
|
||||||
// select handler
|
|
||||||
if ($this->params[0] == 'add')
|
|
||||||
$this->handler = 'handleCommentAdd';
|
|
||||||
else if ($this->params[0] == 'edit')
|
|
||||||
$this->handler = 'handleCommentEdit';
|
|
||||||
else if ($this->params[0] == 'delete')
|
|
||||||
$this->handler = 'handleCommentDelete';
|
|
||||||
else if ($this->params[0] == 'undelete')
|
|
||||||
$this->handler = 'handleCommentUndelete';
|
|
||||||
else if ($this->params[0] == 'rating') // up/down - distribution
|
|
||||||
$this->handler = 'handleCommentRating';
|
|
||||||
else if ($this->params[0] == 'vote') // up, down and remove
|
|
||||||
$this->handler = 'handleCommentVote';
|
|
||||||
else if ($this->params[0] == 'sticky') // toggle flag
|
|
||||||
$this->handler = 'handleCommentSticky';
|
|
||||||
else if ($this->params[0] == 'out-of-date') // toggle flag
|
|
||||||
$this->handler = 'handleCommentOutOfDate';
|
|
||||||
else if ($this->params[0] == 'show-replies')
|
|
||||||
$this->handler = 'handleCommentShowReplies';
|
|
||||||
else if ($this->params[0] == 'add-reply') // also returns all replies on success
|
|
||||||
$this->handler = 'handleReplyAdd';
|
|
||||||
else if ($this->params[0] == 'edit-reply') // also returns all replies on success
|
|
||||||
$this->handler = 'handleReplyEdit';
|
|
||||||
else if ($this->params[0] == 'detach-reply')
|
|
||||||
$this->handler = 'handleReplyDetach';
|
|
||||||
else if ($this->params[0] == 'delete-reply')
|
|
||||||
$this->handler = 'handleReplyDelete';
|
|
||||||
else if ($this->params[0] == 'flag-reply')
|
|
||||||
$this->handler = 'handleReplyFlag';
|
|
||||||
else if ($this->params[0] == 'upvote-reply')
|
|
||||||
$this->handler = 'handleReplyUpvote';
|
|
||||||
else if ($this->params[0] == 'downvote-reply')
|
|
||||||
$this->handler = 'handleReplyDownvote';
|
|
||||||
}
|
|
||||||
|
|
||||||
// i .. have problems believing, that everything uses nifty ajax while adding comments requires a brutal header(Loacation: <wherever>), yet, thats how it is
|
|
||||||
protected function handleCommentAdd() : string
|
|
||||||
{
|
|
||||||
if (!$this->_get['typeid'] || !$this->_get['type'] || !Type::exists($this->_get['type']))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentAdd - malforemd request received', E_USER_ERROR);
|
|
||||||
return ''; // whatever, we cant even send him back
|
|
||||||
}
|
|
||||||
|
|
||||||
// this type cannot be commented on
|
|
||||||
if (!Type::checkClassAttrib($this->_get['type'], 'contribute', CONTRIBUTE_CO))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentAdd - tried to comment on unsupported type #'.$this->_get['type'], E_USER_ERROR);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// trim to max length
|
|
||||||
if (!User::isInGroup(U_GROUP_MODERATOR) && mb_strlen($this->_post['commentbody']) > (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)))
|
|
||||||
$this->_post['commentbody'] = mb_substr($this->_post['commentbody'], 0, (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)));
|
|
||||||
|
|
||||||
if (User::canComment())
|
|
||||||
{
|
|
||||||
if (!empty($this->_post['commentbody']) && mb_strlen($this->_post['commentbody']) >= self::COMMENT_LENGTH_MIN)
|
|
||||||
{
|
|
||||||
if ($postIdx = DB::Aowow()->query('INSERT INTO ?_comments (type, typeId, userId, roles, body, date) VALUES (?d, ?d, ?d, ?d, ?, UNIX_TIMESTAMP())', $this->_get['type'], $this->_get['typeid'], User::$id, User::$groups, $this->_post['commentbody']))
|
|
||||||
{
|
|
||||||
Util::gainSiteReputation(User::$id, SITEREP_ACTION_COMMENT, ['id' => $postIdx]);
|
|
||||||
|
|
||||||
// every comment starts with a rating of +1 and i guess the simplest thing to do is create a db-entry with the system as owner
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, 0, 1)', RATING_COMMENT, $postIdx);
|
|
||||||
|
|
||||||
// flag target with hasComment
|
|
||||||
if ($tbl = Type::getClassAttrib($this->_get['type'], 'dataTable'))
|
|
||||||
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $this->_get['typeid']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$_SESSION['error']['co'] = Lang::main('intError');
|
|
||||||
trigger_error('AjaxComment::handleCommentAdd - write to db failed', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$_SESSION['error']['co'] = Lang::main('textLength', [mb_strlen($this->_post['commentbody']), self::COMMENT_LENGTH_MIN, self::COMMENT_LENGTH_MAX]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$_SESSION['error']['co'] = Lang::main('cannotComment');
|
|
||||||
|
|
||||||
$this->doRedirect = true;
|
|
||||||
|
|
||||||
$idOrUrl = $this->_get['typeid'];
|
|
||||||
if ($this->_get['type'] == Type::GUIDE)
|
|
||||||
if ($_ = DB::Aowow()->selectCell('SELECT `url` FROM ?_guides WHERE `id` = ?d', $this->_get['typeid']))
|
|
||||||
$idOrUrl = $_;
|
|
||||||
|
|
||||||
return '?'.Type::getFileString($this->_get['type']).'='.$idOrUrl.'#comments';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentEdit() : void
|
|
||||||
{
|
|
||||||
if (!User::canComment() && !User::isInGroup(U_GROUP_MODERATOR))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentEdit - user #'.User::$id.' not allowed to edit', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_get['id'] || !$this->_post['body'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentEdit - malforemd request received', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mb_strlen($this->_post['body']) < self::COMMENT_LENGTH_MIN)
|
|
||||||
return; // no point in reporting this trifle
|
|
||||||
|
|
||||||
// trim to max length
|
|
||||||
if (!User::isInGroup(U_GROUP_MODERATOR) && mb_strlen($this->_post['body']) > (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)))
|
|
||||||
$this->_post['body'] = mb_substr($this->_post['body'], 0, (self::COMMENT_LENGTH_MAX * (User::isPremium() ? 3 : 1)));
|
|
||||||
|
|
||||||
$update = array(
|
|
||||||
'body' => $this->_post['body'],
|
|
||||||
'editUserId' => User::$id,
|
|
||||||
'editDate' => time()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (User::isInGroup(U_GROUP_MODERATOR))
|
|
||||||
{
|
|
||||||
$update['responseBody'] = !$this->_post['response'] ? '' : $this->_post['response'];
|
|
||||||
$update['responseUserId'] = !$this->_post['response'] ? 0 : User::$id;
|
|
||||||
$update['responseRoles'] = !$this->_post['response'] ? 0 : User::$groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_comments SET editCount = editCount + 1, ?a WHERE id = ?d', $update, $this->_get['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentDelete() : void
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || !User::isLoggedIn())
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentDelete - commentId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// in theory, there is a username passed alongside... lets just use the current user (see user.js)
|
|
||||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` | ?d, `deleteUserId` = ?d, `deleteDate` = UNIX_TIMESTAMP() WHERE `id` IN (?a){ AND `userId` = ?d}',
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
User::$id,
|
|
||||||
$this->_post['id'],
|
|
||||||
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
|
|
||||||
);
|
|
||||||
|
|
||||||
// deflag hasComment
|
|
||||||
if ($ok)
|
|
||||||
{
|
|
||||||
$coInfo = DB::Aowow()->select('SELECT IF(BIT_OR(~b.`flags`) & ?d, 1, 0) AS hasMore, b.`type`, b.`typeId` FROM ?_comments a JOIN ?_comments b ON a.`type` = b.`type` AND a.`typeId` = b.`typeId` WHERE a.`id` IN (?a) GROUP BY b.`type`, b.`typeId`',
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
$this->_post['id']
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($coInfo as $co)
|
|
||||||
if (!$co['hasMore'] && ($tbl = Type::getClassAttrib($co['type'], 'dataTable')))
|
|
||||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` & ~?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $co['typeId']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxComment::handleCommentDelete - user #'.User::$id.' could not flag comment #'.$this->_post['id'].' as deleted', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentUndelete() : void
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || !User::isLoggedIn())
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentUndelete - commentId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// in theory, there is a username passed alongside... lets just use the current user (see user.js)
|
|
||||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET `flags` = `flags` & ~?d WHERE `id` IN (?a){ AND `userId` = `deleteUserId` AND `deleteUserId` = ?d}',
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
$this->_post['id'],
|
|
||||||
User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id
|
|
||||||
);
|
|
||||||
|
|
||||||
// reflag hasComment
|
|
||||||
if ($ok)
|
|
||||||
{
|
|
||||||
$coInfo = DB::Aowow()->select('SELECT `type`, `typeId` FROM ?_comments WHERE `id` IN (?a) GROUP BY `type`, `typeId`', $this->_post['id']);
|
|
||||||
foreach ($coInfo as $co)
|
|
||||||
if ($tbl = Type::getClassAttrib($co['type'], 'dataTable'))
|
|
||||||
DB::Aowow()->query('UPDATE ?# SET `cuFlags` = `cuFlags` | ?d WHERE `id` = ?d', $tbl, CUSTOM_HAS_COMMENT, $co['typeId']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxComment::handleCommentUndelete - user #'.User::$id.' could not unflag comment #'.$this->_post['id'].' as deleted', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentRating() : string
|
|
||||||
{
|
|
||||||
if (!$this->_get['id'])
|
|
||||||
return Util::toJSON(['success' => 0]);
|
|
||||||
|
|
||||||
if ($votes = DB::Aowow()->selectRow('SELECT 1 AS success, SUM(IF(`value` > 0, `value`, 0)) AS up, SUM(IF(`value` < 0, -`value`, 0)) AS down FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND userId <> 0 GROUP BY `entry`', RATING_COMMENT, $this->_get['id']))
|
|
||||||
return Util::toJSON($votes);
|
|
||||||
else
|
|
||||||
return Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentVote() : string
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || !$this->_get['id'] || !$this->_get['rating'])
|
|
||||||
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
|
|
||||||
|
|
||||||
$target = DB::Aowow()->selectRow('SELECT c.`userId` AS owner, ur.`value` FROM ?_comments c LEFT JOIN ?_user_ratings ur ON ur.`type` = ?d AND ur.`entry` = c.id AND ur.`userId` = ?d WHERE c.id = ?d', RATING_COMMENT, User::$id, $this->_get['id']);
|
|
||||||
$val = User::canSupervote() ? 2 : 1;
|
|
||||||
if ($this->_get['rating'] < 0)
|
|
||||||
$val *= -1;
|
|
||||||
|
|
||||||
if (User::getCurrentDailyVotes() <= 0)
|
|
||||||
return Util::toJSON(['error' => 1, 'message' => Lang::main('tooManyVotes')]);
|
|
||||||
else if (!$target || $val != $this->_get['rating'])
|
|
||||||
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
|
|
||||||
else if (($val > 0 && !User::canUpvote()) || ($val < 0 && !User::canDownvote()))
|
|
||||||
return Util::toJSON(['error' => 1, 'message' => Lang::main('bannedRating')]);
|
|
||||||
|
|
||||||
$ok = false;
|
|
||||||
// old and new have same sign; undo vote (user may have gained/lost access to superVote in the meantime)
|
|
||||||
if ($target['value'] && ($target['value'] < 0) == ($val < 0))
|
|
||||||
$ok = DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_COMMENT, $this->_get['id'], User::$id);
|
|
||||||
else // replace, because we may be overwriting an old, opposing vote
|
|
||||||
if ($ok = DB::Aowow()->query('REPLACE INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)', RATING_COMMENT, (int)$this->_get['id'], User::$id, $val))
|
|
||||||
User::decrementDailyVotes(); // do not refund retracted votes!
|
|
||||||
|
|
||||||
if (!$ok)
|
|
||||||
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
|
|
||||||
|
|
||||||
if ($val > 0) // gain rep
|
|
||||||
Util::gainSiteReputation($target['owner'], SITEREP_ACTION_UPVOTED, ['id' => $this->_get['id'], 'voterId' => User::$id]);
|
|
||||||
else if ($val < 0)
|
|
||||||
Util::gainSiteReputation($target['owner'], SITEREP_ACTION_DOWNVOTED, ['id' => $this->_get['id'], 'voterId' => User::$id]);
|
|
||||||
|
|
||||||
return Util::toJSON(['error' => 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentSticky() : void
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || !User::isInGroup(U_GROUP_MODERATOR))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentSticky - commentId empty or user #'.User::$id.' not moderator', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->_post['sticky'])
|
|
||||||
DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d WHERE id = ?d', CC_FLAG_STICKY, $this->_post['id'][0]);
|
|
||||||
else
|
|
||||||
DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~?d WHERE id = ?d', CC_FLAG_STICKY, $this->_post['id'][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentOutOfDate() : string
|
|
||||||
{
|
|
||||||
$this->contentType = MIME_TYPE_TEXT;
|
|
||||||
|
|
||||||
if (!$this->_post['id'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleCommentOutOfDate - commentId empty', E_USER_ERROR);
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
|
|
||||||
$ok = false;
|
|
||||||
if (User::isInGroup(U_GROUP_MODERATOR)) // directly mark as outdated
|
|
||||||
{
|
|
||||||
if (!$this->_post['remove'])
|
|
||||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d WHERE id = ?d', CC_FLAG_OUTDATED, $this->_post['id'][0]);
|
|
||||||
else
|
|
||||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~?d WHERE id = ?d', CC_FLAG_OUTDATED, $this->_post['id'][0]);
|
|
||||||
}
|
|
||||||
else // try to report as outdated
|
|
||||||
{
|
|
||||||
$report = new Report(Report::MODE_COMMENT, Report::CO_OUT_OF_DATE, $this->_post['id'][0]);
|
|
||||||
if ($report->create($this->_post['reason']))
|
|
||||||
$ok = true; // the script expects the actual characters 'ok' not some json string like "ok"
|
|
||||||
else
|
|
||||||
return Lang::main('intError');
|
|
||||||
|
|
||||||
if (count($report->getSimilar()) >= 5) // 5 or more reports on the same comment: trigger flag
|
|
||||||
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags | ?d WHERE id = ?d', CC_FLAG_OUTDATED, $this->_post['id'][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($ok)
|
|
||||||
return 'ok';
|
|
||||||
else
|
|
||||||
trigger_error('AjaxComment::handleCommentOutOfDate - failed to update comment in db', E_USER_ERROR);
|
|
||||||
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleCommentShowReplies() : string
|
|
||||||
{
|
|
||||||
return Util::toJSON(!$this->_get['id'] ? [] : CommunityContent::getCommentReplies($this->_get['id']));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyAdd() : string
|
|
||||||
{
|
|
||||||
$this->contentType = MIME_TYPE_TEXT;
|
|
||||||
|
|
||||||
if (!User::canComment())
|
|
||||||
return Lang::main('cannotComment');
|
|
||||||
|
|
||||||
if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ?_comments WHERE id = ?d', $this->_post['commentId']))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyAdd - comment #'.$this->_post['commentId'].' does not exist', E_USER_ERROR);
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_post['body'] || mb_strlen($this->_post['body']) < self::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > self::REPLY_LENGTH_MAX)
|
|
||||||
return Lang::main('textLength', [mb_strlen($this->_post['body']), self::REPLY_LENGTH_MIN, self::REPLY_LENGTH_MAX]);
|
|
||||||
|
|
||||||
if (DB::Aowow()->query('INSERT INTO ?_comments (`userId`, `roles`, `body`, `date`, `replyTo`) VALUES (?d, ?d, ?, UNIX_TIMESTAMP(), ?d)', User::$id, User::$groups, $this->_post['body'], $this->_post['commentId']))
|
|
||||||
return Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
|
|
||||||
|
|
||||||
trigger_error('AjaxComment::handleReplyAdd - write to db failed', E_USER_ERROR);
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyEdit() : string
|
|
||||||
{
|
|
||||||
$this->contentType = MIME_TYPE_TEXT;
|
|
||||||
|
|
||||||
if (!User::canComment())
|
|
||||||
return Lang::main('cannotComment');
|
|
||||||
|
|
||||||
if ((!$this->_post['replyId'] || !$this->_post['commentId']) && DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_comments WHERE id IN (?a)', [$this->_post['replyId'], $this->_post['commentId']]))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyEdit - comment #'.$this->_post['commentId'].' or reply #'.$this->_post['replyId'].' does not exist', E_USER_ERROR);
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_post['body'] || mb_strlen($this->_post['body']) < self::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > self::REPLY_LENGTH_MAX)
|
|
||||||
return Lang::main('textLength', [mb_strlen($this->_post['body']), self::REPLY_LENGTH_MIN, self::REPLY_LENGTH_MAX]);
|
|
||||||
|
|
||||||
if (DB::Aowow()->query('UPDATE ?_comments SET body = ?, editUserId = ?d, editDate = UNIX_TIMESTAMP(), editCount = editCount + 1 WHERE id = ?d AND replyTo = ?d{ AND userId = ?d}',
|
|
||||||
$this->_post['body'], User::$id, $this->_post['replyId'], $this->_post['commentId'], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id))
|
|
||||||
return Util::toJSON(CommunityContent::getCommentReplies($this->_post['commentId']));
|
|
||||||
|
|
||||||
trigger_error('AjaxComment::handleReplyEdit - write to db failed', E_USER_ERROR);
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyDetach() : void
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || !User::isInGroup(U_GROUP_MODERATOR))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyDetach - commentId empty or user #'.User::$id.' not moderator', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_comments c1, ?_comments c2 SET c1.replyTo = 0, c1.type = c2.type, c1.typeId = c2.typeId WHERE c1.replyTo = c2.id AND c1.id = ?d', $this->_post['id'][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyDelete() : void
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || !$this->_post['id'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyDelete - commentId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DB::Aowow()->query('DELETE FROM ?_comments WHERE id = ?d{ AND userId = ?d}', $this->_post['id'][0], User::isInGroup(U_GROUP_MODERATOR) ? DBSIMPLE_SKIP : User::$id))
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_COMMENT, $this->_post['id'][0]);
|
|
||||||
else
|
|
||||||
trigger_error('AjaxComment::handleReplyDelete - deleting comment #'.$this->_post['id'][0].' by user #'.User::$id.' from db failed', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyFlag() : void
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || !$this->_post['id'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyFlag - commentId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$report = new Report(Report::MODE_COMMENT, Report::CO_INAPPROPRIATE, $this->_post['id'][0]);
|
|
||||||
$report->create('Report Reply Button Click');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyUpvote() : void
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || !User::canUpvote())
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyUpvote - commentId empty or user not allowed to vote', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$owner = DB::Aowow()->selectCell('SELECT userId FROM ?_comments WHERE id = ?d', $this->_post['id'][0]);
|
|
||||||
if (!$owner)
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyUpvote - comment #'.$this->_post['id'][0].' not found in db', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ok = DB::Aowow()->query(
|
|
||||||
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
|
|
||||||
RATING_COMMENT,
|
|
||||||
$this->_post['id'][0],
|
|
||||||
User::$id,
|
|
||||||
User::canSupervote() ? 2 : 1
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($ok)
|
|
||||||
{
|
|
||||||
Util::gainSiteReputation($owner, SITEREP_ACTION_UPVOTED, ['id' => $this->_post['id'][0], 'voterId' => User::$id]);
|
|
||||||
User::decrementDailyVotes();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxComment::handleReplyUpvote - write to db failed', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleReplyDownvote() : void
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || !User::canDownvote())
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyDownvote - commentId empty or user not allowed to vote', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$owner = DB::Aowow()->selectCell('SELECT userId FROM ?_comments WHERE id = ?d', $this->_post['id'][0]);
|
|
||||||
if (!$owner)
|
|
||||||
{
|
|
||||||
trigger_error('AjaxComment::handleReplyDownvote - comment #'.$this->_post['id'][0].' not found in db', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ok = DB::Aowow()->query(
|
|
||||||
'INSERT INTO ?_user_ratings (`type`, `entry`, `userId`, `value`) VALUES (?d, ?d, ?d, ?d)',
|
|
||||||
RATING_COMMENT,
|
|
||||||
$this->_post['id'][0],
|
|
||||||
User::$id,
|
|
||||||
User::canSupervote() ? -2 : -1
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($ok)
|
|
||||||
{
|
|
||||||
Util::gainSiteReputation($owner, SITEREP_ACTION_DOWNVOTED, ['id' => $this->_post['id'][0], 'voterId' => User::$id]);
|
|
||||||
User::decrementDailyVotes();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxComment::handleReplyDownvote - write to db failed', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxContactus extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_post = array(
|
|
||||||
'mode' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'reason' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'ua' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
'appname' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
'page' => ['filter' => FILTER_SANITIZE_URL ],
|
|
||||||
'desc' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob'],
|
|
||||||
'id' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'relatedurl' => ['filter' => FILTER_SANITIZE_URL ],
|
|
||||||
'email' => ['filter' => FILTER_SANITIZE_EMAIL ]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
// always this one
|
|
||||||
$this->handler = 'handleContactUs';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* responses
|
|
||||||
0: success
|
|
||||||
1: captcha invalid
|
|
||||||
2: description too long
|
|
||||||
3: reason missing
|
|
||||||
7: already reported
|
|
||||||
$: prints response
|
|
||||||
*/
|
|
||||||
protected function handleContactUs() : string
|
|
||||||
{
|
|
||||||
$report = new Report($this->_post['mode'], $this->_post['reason'], $this->_post['id']);
|
|
||||||
if ($report->create($this->_post['desc'], $this->_post['ua'], $this->_post['appname'], $this->_post['page'], $this->_post['relatedurl'], $this->_post['email']))
|
|
||||||
return 0;
|
|
||||||
else if (($e = $report->getError()) > 0)
|
|
||||||
return $e;
|
|
||||||
else
|
|
||||||
return Lang::main('intError');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxCookie extends AjaxHandler
|
|
||||||
{
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
// note that parent::__construct has to come after this
|
|
||||||
if (!$params || !User::isLoggedIn())
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->_get = array(
|
|
||||||
$params[0] => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
);
|
|
||||||
|
|
||||||
// NOW we know, what to expect and sanitize
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
// always this one
|
|
||||||
$this->handler = 'handleCookie';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* responses
|
|
||||||
0: success
|
|
||||||
$: silent error
|
|
||||||
*/
|
|
||||||
protected function handleCookie() : string
|
|
||||||
{
|
|
||||||
if (User::isLoggedIn() && $this->params && $this->_get[$this->params[0]])
|
|
||||||
{
|
|
||||||
if (DB::Aowow()->query('REPLACE INTO ?_account_cookies VALUES (?d, ?, ?)', User::$id, $this->params[0], $this->_get[$this->params[0]]))
|
|
||||||
return '0';
|
|
||||||
else
|
|
||||||
trigger_error('AjaxCookie::handleCookie - write to db failed', E_USER_ERROR);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxCookie::handleCookie - malformed request received', E_USER_ERROR);
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxData extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_get = array(
|
|
||||||
'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\Locale::tryFrom' ],
|
|
||||||
't' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
'catg' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'skill' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxData::checkSkill' ],
|
|
||||||
'class' => ['filter' => FILTER_SANITIZE_NUMBER_INT ],
|
|
||||||
'callback' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxData::checkCallback' ]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if ($this->_get['locale']?->validate())
|
|
||||||
Lang::load($this->_get['locale']);
|
|
||||||
|
|
||||||
// always this one
|
|
||||||
$this->handler = 'handleData';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* responses
|
|
||||||
<string>
|
|
||||||
*/
|
|
||||||
protected function handleData() : string
|
|
||||||
{
|
|
||||||
$result = '';
|
|
||||||
|
|
||||||
// different data can be strung together
|
|
||||||
foreach ($this->params as $set)
|
|
||||||
{
|
|
||||||
// requires valid token to hinder automated access
|
|
||||||
if ($set != 'item-scaling' && (!$this->_get['t'] || empty($_SESSION['dataKey']) || $this->_get['t'] != $_SESSION['dataKey']))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxData::handleData - session data key empty or expired', E_USER_ERROR);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($set)
|
|
||||||
{
|
|
||||||
/* issue on no initial data:
|
|
||||||
when we loadOnDemand, the jScript tries to generate the catg-tree before it is initialized
|
|
||||||
it cant be initialized, without loading the data as empty catg are omitted
|
|
||||||
loading the data triggers the generation of the catg-tree
|
|
||||||
*/
|
|
||||||
case 'factions':
|
|
||||||
$result .= $this->loadProfilerData($set);
|
|
||||||
break;
|
|
||||||
case 'companions':
|
|
||||||
$result .= $this->loadProfilerData($set, SKILL_COMPANIONS);
|
|
||||||
break;
|
|
||||||
case 'mounts':
|
|
||||||
$result .= $this->loadProfilerData($set, SKILL_MOUNTS);
|
|
||||||
break;
|
|
||||||
case 'quests':
|
|
||||||
$catg = isset($this->_get['catg']) ? $this->_get['catg'] : 'null';
|
|
||||||
if ($catg == 'null')
|
|
||||||
Util::loadStaticFile('p-'.$set, $result, false);
|
|
||||||
else
|
|
||||||
Util::loadStaticFile('p-'.$set.'-'.$catg, $result, true);
|
|
||||||
|
|
||||||
$result .= "\n\$WowheadProfiler.loadOnDemand('".$set."', ".$catg.");\n";
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'recipes':
|
|
||||||
if (!$this->_get['callback'] || !$this->_get['skill'])
|
|
||||||
break;
|
|
||||||
|
|
||||||
foreach ($this->_get['skill'] as $s)
|
|
||||||
Util::loadStaticFile('p-recipes-'.$s, $result, true);
|
|
||||||
|
|
||||||
Util::loadStaticFile('p-recipes-sec', $result, true);
|
|
||||||
$result .= "\n\$WowheadProfiler.loadOnDemand('recipes', null);\n";
|
|
||||||
|
|
||||||
break;
|
|
||||||
// locale independent
|
|
||||||
case 'quick-excludes':
|
|
||||||
case 'weight-presets':
|
|
||||||
case 'item-scaling':
|
|
||||||
case 'realms':
|
|
||||||
case 'statistics':
|
|
||||||
if (!Util::loadStaticFile($set, $result) && Cfg::get('DEBUG'))
|
|
||||||
$result .= "alert('could not fetch static data: ".$set."');";
|
|
||||||
|
|
||||||
$result .= "\n\n";
|
|
||||||
break;
|
|
||||||
// localized
|
|
||||||
case 'talents':
|
|
||||||
if ($_ = $this->_get['class'])
|
|
||||||
$set .= "-".$_;
|
|
||||||
case 'achievements':
|
|
||||||
case 'pet-talents':
|
|
||||||
case 'glyphs':
|
|
||||||
case 'gems':
|
|
||||||
case 'enchants':
|
|
||||||
case 'itemsets':
|
|
||||||
case 'pets':
|
|
||||||
case 'zones':
|
|
||||||
if (!Util::loadStaticFile($set, $result, true) && Cfg::get('DEBUG'))
|
|
||||||
$result .= "alert('could not fetch static data: ".$set." for locale: ".Lang::getLocale()->json()."');";
|
|
||||||
|
|
||||||
$result .= "\n\n";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
trigger_error('AjaxData::handleData - invalid file "'.$set.'" in request', E_USER_ERROR);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkSkill(string $val) : array
|
|
||||||
{
|
|
||||||
return array_intersect(array_merge(SKILLS_TRADE_PRIMARY, [SKILL_FIRST_AID, SKILL_COOKING, SKILL_FISHING]), explode(',', $val));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkCallback(string $val) : bool
|
|
||||||
{
|
|
||||||
return substr($val, 0, 29) === '$WowheadProfiler.loadOnDemand';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function loadProfilerData(string $file, string $catg = 'null') : string
|
|
||||||
{
|
|
||||||
$result = '';
|
|
||||||
if ($this->_get['callback'])
|
|
||||||
if (Util::loadStaticFile('p-'.$file, $result, true))
|
|
||||||
$result .= "\n\$WowheadProfiler.loadOnDemand('".$file."', ".$catg.");\n";
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxEdit extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_get = array(
|
|
||||||
'qqfile' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
'guide' => ['filter' => FILTER_SANITIZE_NUMBER_INT ]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($params[0] == 'image')
|
|
||||||
$this->handler = 'handleUpload';
|
|
||||||
else if ($params[0] == 'article') // has it's own editor page
|
|
||||||
$this->handler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
success: bool
|
|
||||||
id: image enumerator
|
|
||||||
type: 3 ? png : jpg
|
|
||||||
name: old filename
|
|
||||||
error: errString
|
|
||||||
*/
|
|
||||||
protected function handleUpload() : string
|
|
||||||
{
|
|
||||||
if (!User::canWriteGuide() || $this->_get['guide'] != 1)
|
|
||||||
return Util::toJSON(['success' => false, 'error' => '']);
|
|
||||||
|
|
||||||
require_once('includes/libs/qqFileUploader.class.php');
|
|
||||||
|
|
||||||
$targetPath = 'static/uploads/guide/images/';
|
|
||||||
$tmpPath = 'static/uploads/temp/';
|
|
||||||
$tmpFile = User::$username.'-'.Type::GUIDE.'-0-'.Util::createHash(16);
|
|
||||||
|
|
||||||
$uploader = new \qqFileUploader(['jpg', 'jpeg', 'png'], 10 * 1024 * 1024);
|
|
||||||
$result = $uploader->handleUpload($tmpPath, $tmpFile, true);
|
|
||||||
|
|
||||||
if (isset($result['success']))
|
|
||||||
{
|
|
||||||
$finfo = new \finfo(FILEINFO_MIME);
|
|
||||||
$mime = $finfo->file($tmpPath.$result['newFilename']);
|
|
||||||
if (preg_match('/^image\/(png|jpe?g)/i', $mime, $m))
|
|
||||||
{
|
|
||||||
$i = 1; // image index
|
|
||||||
if ($files = scandir($targetPath, SCANDIR_SORT_DESCENDING))
|
|
||||||
if (rsort($files, SORT_NATURAL) && $files[0] != '.' && $files[0] != '..')
|
|
||||||
$i = explode('.', $files[0])[0] + 1;
|
|
||||||
|
|
||||||
$targetFile = $i . ($m[1] == 'png' ? '.png' : '.jpg');
|
|
||||||
|
|
||||||
// move to final location
|
|
||||||
if (!rename($tmpPath.$result['newFilename'], $targetPath.$targetFile))
|
|
||||||
return Util::toJSON(['error' => Lang::main('intError')]);
|
|
||||||
|
|
||||||
// send success
|
|
||||||
return Util::toJSON(array(
|
|
||||||
'success' => true,
|
|
||||||
'id' => $i,
|
|
||||||
'type' => $m[1] == 'png' ? 3 : 2,
|
|
||||||
'name' => $this->_get['qqfile']
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Util::toJSON(['error' => Lang::screenshot('error', 'unkFormat')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Util::toJSON($result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class AjaxFilter extends AjaxHandler
|
|
||||||
{
|
|
||||||
public $doRedirect = true;
|
|
||||||
|
|
||||||
private $cat = [];
|
|
||||||
private $page = '';
|
|
||||||
private $filter = null;
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
if (!$params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
$p = explode('=', $params[0]);
|
|
||||||
|
|
||||||
$this->page = $p[0];
|
|
||||||
|
|
||||||
if (isset($p[1]))
|
|
||||||
$this->cat[] = $p[1];
|
|
||||||
|
|
||||||
if (count($params) > 1)
|
|
||||||
for ($i = 1; $i < count($params); $i++)
|
|
||||||
$this->cat[] = $params[$i];
|
|
||||||
|
|
||||||
$opts = ['parentCats' => $this->cat];
|
|
||||||
|
|
||||||
// so usually the page call is just the DBTypes file string with a plural 's' .. but then there are currencies
|
|
||||||
$fileStr = match ($this->page)
|
|
||||||
{
|
|
||||||
'currencies' => 'currency',
|
|
||||||
default => substr($this->page, 0, -1)
|
|
||||||
};
|
|
||||||
|
|
||||||
// yes, the whole _POST! .. should the input fields be exposed and static so they can be evaluated via BaseResponse::initRequestData() ?
|
|
||||||
$this->filter = Type::newFilter($fileStr, $_POST, $opts);
|
|
||||||
|
|
||||||
// always this one
|
|
||||||
$this->handler = 'handleFilter';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleFilter() : string
|
|
||||||
{
|
|
||||||
$url = '?'.$this->page;
|
|
||||||
|
|
||||||
$this->filter?->mergeCat($this->cat);
|
|
||||||
|
|
||||||
if ($this->cat)
|
|
||||||
$url .= '='.implode('.', $this->cat);
|
|
||||||
|
|
||||||
if ($x = $this->filter?->buildGETParam())
|
|
||||||
$url .= '&filter='.$x;
|
|
||||||
|
|
||||||
if ($this->filter?->error)
|
|
||||||
$_SESSION['error']['fi'] = get_class($this->filter);
|
|
||||||
|
|
||||||
// do get request
|
|
||||||
return $url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxGetdescription extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_post = array(
|
|
||||||
'description' => [FILTER_CALLBACK, ['options' => 'Aowow\AjaxHandler::checkTextBlob']]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$params || $params[0]) // should be empty
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->handler = 'handleDescription';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handleDescription() : string
|
|
||||||
{
|
|
||||||
$this->contentType = MIME_TYPE_TEXT;
|
|
||||||
|
|
||||||
if (!User::canWriteGuide())
|
|
||||||
return '';
|
|
||||||
|
|
||||||
$desc = Markup::stripTags($this->_post['description']);
|
|
||||||
|
|
||||||
return Lang::trimTextClean($desc, 120);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxGotocomment extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_get = array(
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkInt']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
// always this one
|
|
||||||
$this->handler = 'handleGoToComment';
|
|
||||||
$this->doRedirect = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* responses
|
|
||||||
header()
|
|
||||||
*/
|
|
||||||
protected function handleGoToComment() : string
|
|
||||||
{
|
|
||||||
if (!$this->_get['id'])
|
|
||||||
return '.'; // go home
|
|
||||||
|
|
||||||
if ($_ = DB::Aowow()->selectRow('SELECT IFNULL(c2.`id`, c1.`id`) AS "id", IFNULL(c2.`type`, c1.`type`) AS "type", IFNULL(c2.`typeId`, c1.`typeId`) AS "typeId" FROM ?_comments c1 LEFT JOIN ?_comments c2 ON c1.`replyTo` = c2.`id` WHERE c1.`id` = ?d', $this->_get['id']))
|
|
||||||
return '?'.Type::getFileString(intVal($_['type'])).'='.$_['typeId'].'#comments:id='.$_['id'].($_['id'] != $this->_get['id'] ? ':reply='.$this->_get['id'] : null);
|
|
||||||
else
|
|
||||||
trigger_error('AjaxGotocomment::handleGoToComment - could not find comment #'.$this->_get['id'], E_USER_ERROR);
|
|
||||||
|
|
||||||
return '.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxGuide extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_post = array(
|
|
||||||
'id' => [FILTER_SANITIZE_NUMBER_INT, null],
|
|
||||||
'rating' => [FILTER_SANITIZE_NUMBER_INT, null]
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params || count($this->params) != 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$this->contentType = MIME_TYPE_TEXT;
|
|
||||||
|
|
||||||
// select handler
|
|
||||||
if ($this->params[0] == 'vote')
|
|
||||||
$this->handler = 'voteGuide';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function voteGuide() : string
|
|
||||||
{
|
|
||||||
if (!$this->_post['id'] || $this->_post['rating'] < 0 || $this->_post['rating'] > 5)
|
|
||||||
{
|
|
||||||
header('HTTP/1.0 404 Not Found', true, 404);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
else if (!User::canUpvote() || !User::canDownvote()) // same logic as comments?
|
|
||||||
{
|
|
||||||
header('HTTP/1.0 403 Forbidden', true, 403);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
// by id, not own, published
|
|
||||||
if ($g = DB::Aowow()->selectRow('SELECT `userId`, `cuFlags` FROM ?_guides WHERE `id` = ?d AND (`status` = ?d OR `rev` > 0)', $this->_post['id'], GUIDE_STATUS_APPROVED))
|
|
||||||
{
|
|
||||||
if ($g['cuFlags'] & GUIDE_CU_NO_RATING || $g['userId'] == User::$id)
|
|
||||||
{
|
|
||||||
header('HTTP/1.0 403 Forbidden', true, 403);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->_post['rating'])
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d AND `userId` = ?d', RATING_GUIDE, $this->_post['id'], User::$id);
|
|
||||||
else
|
|
||||||
DB::Aowow()->query('REPLACE INTO ?_user_ratings VALUES (?d, ?d, ?d, ?d)', RATING_GUIDE, $this->_post['id'], User::$id, $this->_post['rating']);
|
|
||||||
|
|
||||||
$res = DB::Aowow()->selectRow('SELECT IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n` FROM ?_user_ratings WHERE `type` = ?d AND `entry` = ?d', RATING_GUIDE, $this->_post['id']);
|
|
||||||
return Util::toJSON($res['n'] ? ['rating' => $res['t'] / $res['n'], 'nvotes' => $res['n']] : ['rating' => 0, 'nvotes' => 0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Util::toJSON(['rating' => 0, 'nvotes' => 0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxGuild extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $validParams = ['resync', 'status'];
|
|
||||||
protected $_get = array(
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdList' ],
|
|
||||||
'profile' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkEmptySet'],
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch ($this->params[0])
|
|
||||||
{
|
|
||||||
case 'resync':
|
|
||||||
$this->handler = 'handleResync';
|
|
||||||
break;
|
|
||||||
case 'status':
|
|
||||||
$this->handler = 'handleStatus';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
user: <string> [optional, not used]
|
|
||||||
profile: <empty> [optional, also get related chars]
|
|
||||||
return: 1
|
|
||||||
*/
|
|
||||||
protected function handleResync() : string
|
|
||||||
{
|
|
||||||
if ($guilds = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_guild WHERE id IN (?a)', $this->_get['id']))
|
|
||||||
foreach ($guilds as $g)
|
|
||||||
Profiler::scheduleResync(Type::GUILD, $g['realm'], $g['realmGUID']);
|
|
||||||
|
|
||||||
if ($this->_get['profile'])
|
|
||||||
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles WHERE guild IN (?a)', $this->_get['id']))
|
|
||||||
foreach ($chars as $c)
|
|
||||||
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
|
|
||||||
|
|
||||||
return '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
return
|
|
||||||
<status object>
|
|
||||||
[
|
|
||||||
nQueueProcesses,
|
|
||||||
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
|
|
||||||
[<anotherStatus>]
|
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
not all fields are required, if zero they are omitted
|
|
||||||
statusCode:
|
|
||||||
0: end the request
|
|
||||||
1: waiting
|
|
||||||
2: working...
|
|
||||||
3: ready; click to view
|
|
||||||
4: error / retry
|
|
||||||
errorCode:
|
|
||||||
0: unk error
|
|
||||||
1: char does not exist
|
|
||||||
2: armory gone
|
|
||||||
*/
|
|
||||||
protected function handleStatus() : string
|
|
||||||
{
|
|
||||||
return Profiler::resyncStatus(Type::GUILD, $this->_get['id']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxLocale extends AjaxHandler
|
|
||||||
{
|
|
||||||
protected $_get = array(
|
|
||||||
'locale' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\Locale::tryFrom']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
// always this one
|
|
||||||
$this->handler = 'handleLocale';
|
|
||||||
$this->doRedirect = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* responses
|
|
||||||
header()
|
|
||||||
*/
|
|
||||||
protected function handleLocale() : string
|
|
||||||
{
|
|
||||||
if ($this->_get['locale']?->validate())
|
|
||||||
{
|
|
||||||
User::$preferedLoc = $this->_get['locale'];
|
|
||||||
User::save(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,780 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class AjaxProfile extends AjaxHandler
|
|
||||||
{
|
|
||||||
private $undo = false;
|
|
||||||
|
|
||||||
protected $validParams = ['link', 'unlink', 'pin', 'unpin', 'public', 'private', 'avatar', 'resync', 'status', 'save', 'delete', 'purge', 'summary', 'load'];
|
|
||||||
protected $_get = array(
|
|
||||||
'id' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdList' ],
|
|
||||||
'items' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxProfile::checkItemList'],
|
|
||||||
'size' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
'guild' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkEmptySet'],
|
|
||||||
'arena-team' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkEmptySet'],
|
|
||||||
'user' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxProfile::checkUser' ]
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $_post = array(
|
|
||||||
'name' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine'],
|
|
||||||
'level' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'class' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'race' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'gender' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'nomodel' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'talenttree1' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'talenttree2' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'talenttree3' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'activespec' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'talentbuild1' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxProfile::checkTalentString'],
|
|
||||||
'glyphs1' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxProfile::checkGlyphString' ],
|
|
||||||
'talentbuild2' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxProfile::checkTalentString'],
|
|
||||||
'glyphs2' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxProfile::checkGlyphString' ],
|
|
||||||
'icon' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextLine' ],
|
|
||||||
'description' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkTextBlob' ],
|
|
||||||
'source' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'copy' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'public' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'gearscore' => ['filter' => FILTER_SANITIZE_NUMBER_INT],
|
|
||||||
'inv' => ['filter' => FILTER_CALLBACK, 'options' => 'Aowow\AjaxHandler::checkIdListUnsigned', 'flags' => FILTER_REQUIRE_ARRAY],
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $params)
|
|
||||||
{
|
|
||||||
parent::__construct($params);
|
|
||||||
|
|
||||||
if (!$this->params)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!Cfg::get('PROFILER_ENABLE'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch ($this->params[0])
|
|
||||||
{
|
|
||||||
case 'unlink':
|
|
||||||
$this->undo = true;
|
|
||||||
case 'link':
|
|
||||||
$this->handler = 'handleLink'; // always returns null
|
|
||||||
break;
|
|
||||||
case 'unpin':
|
|
||||||
$this->undo = true;
|
|
||||||
case 'pin':
|
|
||||||
$this->handler = 'handlePin'; // always returns null
|
|
||||||
break;
|
|
||||||
case 'private':
|
|
||||||
$this->undo = true;
|
|
||||||
case 'public':
|
|
||||||
$this->handler = 'handlePrivacy'; // always returns null
|
|
||||||
break;
|
|
||||||
case 'avatar':
|
|
||||||
$this->handler = 'handleAvatar'; // sets an image header
|
|
||||||
break; // so it has to die here or another header will be set
|
|
||||||
case 'resync':
|
|
||||||
$this->handler = 'handleResync'; // always returns "1"
|
|
||||||
break;
|
|
||||||
case 'status':
|
|
||||||
$this->handler = 'handleStatus'; // returns status object
|
|
||||||
break;
|
|
||||||
case 'save':
|
|
||||||
$this->handler = 'handleSave';
|
|
||||||
break;
|
|
||||||
case 'delete':
|
|
||||||
$this->handler = 'handleDelete';
|
|
||||||
break;
|
|
||||||
case 'purge':
|
|
||||||
$this->handler = 'handlePurge';
|
|
||||||
break;
|
|
||||||
case 'summary': // page is generated by jScript
|
|
||||||
die(); // just be empty
|
|
||||||
case 'load':
|
|
||||||
$this->handler = 'handleLoad';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
user: <string> [optional]
|
|
||||||
return: null
|
|
||||||
*/
|
|
||||||
protected function handleLink() : void // links char with account
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || empty($this->_get['id']))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleLink - profileId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uid = User::$id;
|
|
||||||
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
|
|
||||||
{
|
|
||||||
if (!($uid = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleLink - user "'.$this->_get['user'].'" does not exist', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->undo)
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_account_profiles WHERE `accountId` = ?d AND `profileId` IN (?a)', $uid, $this->_get['id']);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach ($this->_get['id'] as $prId) // only link characters, not custom profiles
|
|
||||||
{
|
|
||||||
if ($prId = DB::Aowow()->selectCell('SELECT `id` FROM ?_profiler_profiles WHERE `id` = ?d AND `realm` IS NOT NULL', $prId))
|
|
||||||
DB::Aowow()->query('INSERT IGNORE INTO ?_account_profiles VALUES (?d, ?d, 0)', $uid, $prId);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleLink - profile #'.$prId.' is custom or does not exist', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
user: <string> [optional]
|
|
||||||
return: null
|
|
||||||
*/
|
|
||||||
protected function handlePin() : void // (un)favorite
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || empty($this->_get['id'][0]))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handlePin - profileId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uid = User::$id;
|
|
||||||
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
|
|
||||||
{
|
|
||||||
if (!($uid = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handlePin - user "'.$this->_get['user'].'" does not exist', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// since only one character can be pinned at a time we can reset everything
|
|
||||||
DB::Aowow()->query('UPDATE ?_account_profiles SET `extraFlags` = `extraFlags` & ?d WHERE `accountId` = ?d', ~PROFILER_CU_PINNED, $uid);
|
|
||||||
// and set a single char if necessary
|
|
||||||
if (!$this->undo)
|
|
||||||
DB::Aowow()->query('UPDATE ?_account_profiles SET `extraFlags` = `extraFlags` | ?d WHERE `profileId` = ?d AND `accountId` = ?d', PROFILER_CU_PINNED, $this->_get['id'][0], $uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
user: <string> [optional]
|
|
||||||
return: null
|
|
||||||
*/
|
|
||||||
protected function handlePrivacy() : void // public visibility
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || empty($this->_get['id'][0]))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handlePrivacy - profileId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$uid = User::$id;
|
|
||||||
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
|
|
||||||
{
|
|
||||||
if (!($uid = DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE LOWER(`username`) = LOWER(?)', $this->_get['user'])))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handlePrivacy - user "'.$this->_get['user'].'" does not exist', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->undo)
|
|
||||||
{
|
|
||||||
DB::Aowow()->query('UPDATE ?_account_profiles SET `extraFlags` = `extraFlags` & ?d WHERE `profileId` IN (?a) AND `accountId` = ?d', ~PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
|
|
||||||
DB::Aowow()->query('UPDATE ?_profiler_profiles SET `cuFlags` = `cuFlags` & ?d WHERE `id` IN (?a) AND `user` = ?d', ~PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DB::Aowow()->query('UPDATE ?_account_profiles SET `extraFlags` = `extraFlags` | ?d WHERE `profileId` IN (?a) AND `accountId` = ?d', PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
|
|
||||||
DB::Aowow()->query('UPDATE ?_profiler_profiles SET `cuFlags` = `cuFlags` | ?d WHERE `id` IN (?a) AND `user` = ?d', PROFILER_CU_PUBLISHED, $this->_get['id'], $uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId>
|
|
||||||
size: <string> [optional]
|
|
||||||
return: image-header
|
|
||||||
*/
|
|
||||||
protected function handleAvatar() : void // image
|
|
||||||
{
|
|
||||||
// something happened in the last years: those textures do not include tiny icons
|
|
||||||
$sizes = [/* 'tiny' => 15, */'small' => 18, 'medium' => 36, 'large' => 56];
|
|
||||||
$aPath = 'uploads/avatars/%d.jpg';
|
|
||||||
$s = $this->_get['size'] ?: 'medium';
|
|
||||||
|
|
||||||
if (!$this->_get['id'] || !preg_match('/^([0-9]+)\.(jpg|gif)$/', $this->_get['id'][0], $matches) || !in_array($s, array_keys($sizes)))
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleAvatar - malformed request received', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->contentType = $matches[2] == 'png' ? MIME_TYPE_PNG : MIME_TYPE_JPEG;
|
|
||||||
|
|
||||||
$id = $matches[1];
|
|
||||||
$dest = imageCreateTruecolor($sizes[$s], $sizes[$s]);
|
|
||||||
|
|
||||||
if (file_exists(sprintf($aPath, $id)))
|
|
||||||
{
|
|
||||||
$offsetX = $offsetY = 0;
|
|
||||||
|
|
||||||
switch ($s)
|
|
||||||
{
|
|
||||||
case 'tiny':
|
|
||||||
$offsetX += $sizes['small'];
|
|
||||||
case 'small':
|
|
||||||
$offsetY += $sizes['medium'];
|
|
||||||
case 'medium':
|
|
||||||
$offsetX += $sizes['large'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$src = imageCreateFromJpeg(printf($aPath, $id));
|
|
||||||
imagecopymerge($dest, $src, 0, 0, $offsetX, $offsetY, $sizes[$s], $sizes[$s], 100);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxProfile::handleAvatar - avatar file #'.$id.' not found', E_USER_ERROR);
|
|
||||||
|
|
||||||
if ($matches[2] == 'gif')
|
|
||||||
imageGif($dest);
|
|
||||||
else
|
|
||||||
imageJpeg($dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
user: <string> [optional, not used]
|
|
||||||
return: 1
|
|
||||||
*/
|
|
||||||
protected function handleResync() : string
|
|
||||||
{
|
|
||||||
if ($chars = DB::Aowow()->select('SELECT realm, realmGUID FROM ?_profiler_profiles WHERE id IN (?a)', $this->_get['id']))
|
|
||||||
{
|
|
||||||
foreach ($chars as $c)
|
|
||||||
Profiler::scheduleResync(Type::PROFILE, $c['realm'], $c['realmGUID']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('AjaxProfile::handleResync - profiles '.implode(', ', $this->_get['id']).' not found in db', E_USER_ERROR);
|
|
||||||
|
|
||||||
return '1';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
return
|
|
||||||
<status object>
|
|
||||||
[
|
|
||||||
nQueueProcesses,
|
|
||||||
[statusCode, timeToRefresh, curQueuePos, errorCode, nResyncTries],
|
|
||||||
[<anotherStatus>]
|
|
||||||
...
|
|
||||||
]
|
|
||||||
|
|
||||||
not all fields are required, if zero they are omitted
|
|
||||||
statusCode:
|
|
||||||
0: end the request
|
|
||||||
1: waiting
|
|
||||||
2: working...
|
|
||||||
3: ready; click to view
|
|
||||||
4: error / retry
|
|
||||||
errorCode:
|
|
||||||
0: unk error
|
|
||||||
1: char does not exist
|
|
||||||
2: armory gone
|
|
||||||
*/
|
|
||||||
protected function handleStatus() : string
|
|
||||||
{
|
|
||||||
// roster resync for this guild was requested -> get char list
|
|
||||||
if ($this->_get['guild'])
|
|
||||||
$ids = DB::Aowow()->selectCol('SELECT id FROM ?_profiler_profiles WHERE guild IN (?a)', $this->_get['id']);
|
|
||||||
else if ($this->_get['arena-team'])
|
|
||||||
$ids = DB::Aowow()->selectCol('SELECT profileId FROM ?_profiler_arena_team_member WHERE arenaTeamId IN (?a)', $this->_get['id']);
|
|
||||||
else
|
|
||||||
$ids = $this->_get['id'];
|
|
||||||
|
|
||||||
if (!$ids)
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleStatus - no profileIds to resync'.($this->_get['guild'] ? ' for guild #'.$this->_get['guild'] : ($this->_get['arena-team'] ? ' for areana team #'.$this->_get['arena-team'] : '')), E_USER_ERROR);
|
|
||||||
return Util::toJSON([1, [PR_QUEUE_STATUS_ERROR, 0, 0, PR_QUEUE_ERROR_CHAR]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Profiler::resyncStatus(Type::PROFILE, $ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params (get))
|
|
||||||
id: <prId1,0> [0: new profile]
|
|
||||||
params (post)
|
|
||||||
<various char data> [see below]
|
|
||||||
return:
|
|
||||||
proileId [onSuccess]
|
|
||||||
-1 [onError]
|
|
||||||
*/
|
|
||||||
protected function handleSave() : string // unKill a profile
|
|
||||||
{
|
|
||||||
// todo (med): detail check this post-data
|
|
||||||
$cuProfile = array(
|
|
||||||
'user' => User::$id,
|
|
||||||
// 'userName' => User::$username,
|
|
||||||
'name' => $this->_post['name'],
|
|
||||||
'level' => $this->_post['level'],
|
|
||||||
'class' => $this->_post['class'],
|
|
||||||
'race' => $this->_post['race'],
|
|
||||||
'gender' => $this->_post['gender'],
|
|
||||||
'nomodelMask' => $this->_post['nomodel'],
|
|
||||||
'talenttree1' => $this->_post['talenttree1'],
|
|
||||||
'talenttree2' => $this->_post['talenttree2'],
|
|
||||||
'talenttree3' => $this->_post['talenttree3'],
|
|
||||||
'talentbuild1' => $this->_post['talentbuild1'],
|
|
||||||
'talentbuild2' => $this->_post['talentbuild2'],
|
|
||||||
'activespec' => $this->_post['activespec'],
|
|
||||||
'glyphs1' => $this->_post['glyphs1'],
|
|
||||||
'glyphs2' => $this->_post['glyphs2'],
|
|
||||||
'gearscore' => $this->_post['gearscore'],
|
|
||||||
'icon' => $this->_post['icon'],
|
|
||||||
'cuFlags' => PROFILER_CU_PROFILE | ($this->_post['public'] ? PROFILER_CU_PUBLISHED : 0)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (strstr($cuProfile['icon'], 'profile=avatar')) // how the profiler is supposed to handle icons is beyond me
|
|
||||||
$cuProfile['icon'] = '';
|
|
||||||
|
|
||||||
if ($_ = $this->_post['description'])
|
|
||||||
$cuProfile['description'] = $_;
|
|
||||||
|
|
||||||
if ($_ = $this->_post['source']) // should i also set sourcename?
|
|
||||||
$cuProfile['sourceId'] = $_;
|
|
||||||
|
|
||||||
if ($_ = $this->_post['copy']) // gets set to source profileId when "save as" is clicked. Whats the difference to 'source' though?
|
|
||||||
{
|
|
||||||
// get character origin info if possible
|
|
||||||
if ($r = DB::Aowow()->selectCell('SELECT realm FROM ?_profiler_profiles WHERE id = ?d AND realm IS NOT NULL', $_))
|
|
||||||
$cuProfile['realm'] = $r;
|
|
||||||
|
|
||||||
$cuProfile['sourceId'] = $_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($cuProfile['sourceId']))
|
|
||||||
$cuProfile['sourceName'] = DB::Aowow()->selectCell('SELECT name FROM ?_profiler_profiles WHERE id = ?d', $cuProfile['sourceId']);
|
|
||||||
|
|
||||||
$charId = -1;
|
|
||||||
if ($id = $this->_get['id'][0]) // update
|
|
||||||
{
|
|
||||||
if ($charId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE id = ?d', $id))
|
|
||||||
DB::Aowow()->query('UPDATE ?_profiler_profiles SET ?a WHERE id = ?d', $cuProfile, $id);
|
|
||||||
}
|
|
||||||
else // new
|
|
||||||
{
|
|
||||||
$nProfiles = DB::Aowow()->selectCell('SELECT COUNT(*) FROM ?_profiler_profiles WHERE user = ?d AND (cuFlags & ?d) = 0 AND realmGUID IS NULL', User::$id, PROFILER_CU_DELETED);
|
|
||||||
if ($nProfiles < 10 || User::isPremium())
|
|
||||||
if ($newId = DB::Aowow()->query('INSERT INTO ?_profiler_profiles (?#) VALUES (?a)', array_keys($cuProfile), array_values($cuProfile)))
|
|
||||||
$charId = $newId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update items
|
|
||||||
if ($charId != -1)
|
|
||||||
{
|
|
||||||
// ok, 'funny' thing: wether an item has en extra prismatic sockel is determined contextual
|
|
||||||
// either the socket is -1 or it has an itemId in a socket where there shouldn't be one
|
|
||||||
$keys = ['id', 'slot', 'item', 'subitem', 'permEnchant', 'tempEnchant', 'gem1', 'gem2', 'gem3', 'gem4'];
|
|
||||||
|
|
||||||
// validate Enchantments
|
|
||||||
$enchIds = array_merge(
|
|
||||||
array_column($this->_post['inv'], 3), // perm enchantments
|
|
||||||
array_column($this->_post['inv'], 4) // temp enchantments (not used..?)
|
|
||||||
);
|
|
||||||
$enchs = new EnchantmentList(array(['id', $enchIds]));
|
|
||||||
|
|
||||||
// validate items
|
|
||||||
$itemIds = array_merge(
|
|
||||||
array_column($this->_post['inv'], 1), // base item
|
|
||||||
array_column($this->_post['inv'], 5), // gem slot 1
|
|
||||||
array_column($this->_post['inv'], 6), // gem slot 2
|
|
||||||
array_column($this->_post['inv'], 7), // gem slot 3
|
|
||||||
array_column($this->_post['inv'], 8) // gem slot 4
|
|
||||||
);
|
|
||||||
|
|
||||||
$items = new ItemList(array(['id', $itemIds]));
|
|
||||||
if (!$items->error)
|
|
||||||
{
|
|
||||||
foreach ($this->_post['inv'] as $slot => $itemData)
|
|
||||||
{
|
|
||||||
if ($slot + 1 == array_sum($itemData)) // only slot definition set => empty slot
|
|
||||||
{
|
|
||||||
DB::Aowow()->query('DELETE FROM ?_profiler_items WHERE id = ?d AND slot = ?d', $charId, $itemData[0]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// item does not exist
|
|
||||||
if (!$items->getEntry($itemData[1]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// sub-item check
|
|
||||||
if (!$items->getRandEnchantForItem($itemData[1]))
|
|
||||||
$itemData[2] = 0;
|
|
||||||
|
|
||||||
// item sockets are fubar
|
|
||||||
$nSockets = $items->json[$itemData[1]]['nsockets'] ?? 0;
|
|
||||||
$nSockets += in_array($slot, [SLOT_WAIST, SLOT_WRISTS, SLOT_HANDS]) ? 1 : 0;
|
|
||||||
for ($i = 5; $i < 9; $i++)
|
|
||||||
if ($itemData[$i] > 0 && (!$items->getEntry($itemData[$i]) || $i >= (5 + $nSockets)))
|
|
||||||
$itemData[$i] = 0;
|
|
||||||
|
|
||||||
// item enchantments are borked
|
|
||||||
if ($itemData[3] && !$enchs->getEntry($itemData[3]))
|
|
||||||
$itemData[3] = 0;
|
|
||||||
|
|
||||||
if ($itemData[4] && !$enchs->getEntry($itemData[4]))
|
|
||||||
$itemData[4] = 0;
|
|
||||||
|
|
||||||
// looks good
|
|
||||||
array_unshift($itemData, $charId);
|
|
||||||
DB::Aowow()->query('REPLACE INTO ?_profiler_items (?#) VALUES (?a)', $keys, $itemData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (string)$charId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId1,prId2,..,prIdN>
|
|
||||||
return
|
|
||||||
null
|
|
||||||
*/
|
|
||||||
protected function handleDelete() : void // kill a profile
|
|
||||||
{
|
|
||||||
if (!User::isLoggedIn() || !$this->_get['id'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleDelete - profileId empty or user not logged in', E_USER_ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only flag as deleted; only custom profiles
|
|
||||||
DB::Aowow()->query(
|
|
||||||
'UPDATE ?_profiler_profiles SET cuFlags = cuFlags | ?d WHERE id IN (?a) AND cuFlags & ?d {AND user = ?d}',
|
|
||||||
PROFILER_CU_DELETED,
|
|
||||||
$this->_get['id'],
|
|
||||||
PROFILER_CU_PROFILE,
|
|
||||||
User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU) ? DBSIMPLE_SKIP : User::$id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: profileId
|
|
||||||
items: string [itemIds.join(':')]
|
|
||||||
unnamed: unixtime [only to force the browser to reload instead of cache]
|
|
||||||
return
|
|
||||||
lots...
|
|
||||||
*/
|
|
||||||
protected function handleLoad() : string
|
|
||||||
{
|
|
||||||
// titles, achievements, characterData, talents, pets
|
|
||||||
// and some onLoad-hook to .. load it registerProfile($data)
|
|
||||||
// everything else goes through data.php .. strangely enough
|
|
||||||
|
|
||||||
if (!$this->_get['id'])
|
|
||||||
{
|
|
||||||
trigger_error('AjaxProfile::handleLoad - profileId empty', E_USER_ERROR);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$pBase = DB::Aowow()->selectRow('SELECT pg.name AS guildname, p.* FROM ?_profiler_profiles p LEFT JOIN ?_profiler_guild pg ON pg.id = p.guild WHERE p.id = ?d', $this->_get['id'][0]);
|
|
||||||
if (!$pBase)
|
|
||||||
{
|
|
||||||
trigger_error('Profiler::handleLoad - called with invalid profileId #'.$this->_get['id'][0], E_USER_WARNING);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (($pBase['cuFlags'] & PROFILER_CU_DELETED) && !User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
|
|
||||||
return '';
|
|
||||||
|
|
||||||
|
|
||||||
$rData = [];
|
|
||||||
foreach (Profiler::getRealms() as $rId => $rData)
|
|
||||||
if ($rId == $pBase['realm'])
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (!$rData) // realm doesn't exist or access is restricted
|
|
||||||
return '';
|
|
||||||
|
|
||||||
$profile = array(
|
|
||||||
'id' => $pBase['id'],
|
|
||||||
'source' => $pBase['id'],
|
|
||||||
'level' => $pBase['level'],
|
|
||||||
'classs' => $pBase['class'],
|
|
||||||
'race' => $pBase['race'],
|
|
||||||
'faction' => ChrRace::tryFrom($pBase['race'])?->getTeam() ?? TEAM_NEUTRAL,
|
|
||||||
'gender' => $pBase['gender'],
|
|
||||||
'skincolor' => $pBase['skincolor'],
|
|
||||||
'hairstyle' => $pBase['hairstyle'],
|
|
||||||
'haircolor' => $pBase['haircolor'],
|
|
||||||
'facetype' => $pBase['facetype'],
|
|
||||||
'features' => $pBase['features'],
|
|
||||||
'title' => $pBase['title'],
|
|
||||||
'name' => $pBase['name'],
|
|
||||||
'guild' => "$'".$pBase['guildname']."'",
|
|
||||||
'published' => !!($pBase['cuFlags'] & PROFILER_CU_PUBLISHED),
|
|
||||||
'pinned' => !!($pBase['cuFlags'] & PROFILER_CU_PINNED),
|
|
||||||
'nomodel' => $pBase['nomodelMask'],
|
|
||||||
'playedtime' => $pBase['playedtime'],
|
|
||||||
'lastupdated' => $pBase['lastupdated'] * 1000,
|
|
||||||
'talents' => array(
|
|
||||||
'builds' => array( // notice the bullshit to prevent the talent-string from becoming a float! NOTICE IT!!
|
|
||||||
['talents' => '$"'.$pBase['talentbuild1'].'"', 'glyphs' => $pBase['glyphs1']],
|
|
||||||
['talents' => '$"'.$pBase['talentbuild2'].'"', 'glyphs' => $pBase['glyphs2']]
|
|
||||||
),
|
|
||||||
'active' => $pBase['activespec']
|
|
||||||
),
|
|
||||||
// set later
|
|
||||||
'inventory' => [],
|
|
||||||
'bookmarks' => [], // list of userIds who claimed this profile (claiming and owning are two different things)
|
|
||||||
|
|
||||||
// completion lists: [subjectId => amount/timestamp/1]
|
|
||||||
'skills' => [], // skillId => [curVal, maxVal]
|
|
||||||
'reputation' => [], // factionId => curVal
|
|
||||||
'titles' => [], // titleId => 1
|
|
||||||
'spells' => [], // spellId => 1; recipes, vanity pets, mounts
|
|
||||||
'achievements' => [], // achievementId => timestamp
|
|
||||||
'quests' => [], // questId => 1
|
|
||||||
'achievementpoints' => 0, // max you have
|
|
||||||
'statistics' => [], // all raid activity [achievementId => killCount]
|
|
||||||
'activity' => [], // recent raid activity [achievementId => 1] (is a subset of statistics)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($pBase['cuFlags'] & PROFILER_CU_PROFILE)
|
|
||||||
{
|
|
||||||
// this parameter is _really_ strange .. probably still not doing this right
|
|
||||||
$profile['source'] = $pBase['realm'] ? $pBase['sourceId'] : 0;
|
|
||||||
|
|
||||||
$profile['sourcename'] = $pBase['sourceName'];
|
|
||||||
$profile['description'] = $pBase['description'];
|
|
||||||
$profile['user'] = $pBase['user'];
|
|
||||||
$profile['username'] = DB::Aowow()->selectCell('SELECT `username` FROM ?_account WHERE `id` = ?d', $pBase['user']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom profiles inherit this when copied from real char :(
|
|
||||||
if ($pBase['realm'])
|
|
||||||
{
|
|
||||||
$profile['region'] = [$rData['region'], Lang::profiler('regions', $rData['region'])];
|
|
||||||
$profile['battlegroup'] = [Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP')];
|
|
||||||
$profile['realm'] = [Profiler::urlize($rData['name'], true), $rData['name']];
|
|
||||||
}
|
|
||||||
|
|
||||||
// bookmarks
|
|
||||||
if ($_ = DB::Aowow()->selectCol('SELECT accountId FROM ?_account_profiles WHERE profileId = ?d', $pBase['id']))
|
|
||||||
$profile['bookmarks'] = $_;
|
|
||||||
|
|
||||||
// arena teams - [size(2|3|5) => name]; name gets urlized to use as link
|
|
||||||
if ($at = DB::Aowow()->selectCol('SELECT type AS ARRAY_KEY, name FROM ?_profiler_arena_team at JOIN ?_profiler_arena_team_member atm ON atm.arenaTeamId = at.id WHERE atm.profileId = ?d', $pBase['id']))
|
|
||||||
$profile['arenateams'] = $at;
|
|
||||||
|
|
||||||
// pets if hunter fields: [name:name, family:petFamily, npc:npcId, displayId:modelId, talents:talentString]
|
|
||||||
if ($pets = DB::Aowow()->select('SELECT `name`, `family`, `npc`, `displayId`, CONCAT("$\"", `talents`, "\"") AS "talents" FROM ?_profiler_pets WHERE `owner` = ?d', $pBase['id']))
|
|
||||||
$profile['pets'] = $pets;
|
|
||||||
|
|
||||||
// source for custom profiles; profileId => [name, ownerId, iconString(optional)]
|
|
||||||
if ($customs = DB::Aowow()->select('SELECT id AS ARRAY_KEY, name, user, icon FROM ?_profiler_profiles WHERE sourceId = ?d AND sourceId <> id {AND (cuFlags & ?d) = 0}', $pBase['id'], User::isInGroup(U_GROUP_STAFF) ? DBSIMPLE_SKIP : PROFILER_CU_DELETED))
|
|
||||||
{
|
|
||||||
foreach ($customs as $id => $cu)
|
|
||||||
{
|
|
||||||
if (!$cu['icon'])
|
|
||||||
unset($cu['icon']);
|
|
||||||
|
|
||||||
$profile['customs'][$id] = array_values($cu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* $profile[]
|
|
||||||
// CUSTOM
|
|
||||||
'auras' => [], // custom list of buffs, debuffs [spellId]
|
|
||||||
|
|
||||||
// UNUSED
|
|
||||||
'glyphs' => [], // provided list of already known glyphs (post cataclysm feature)
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// questId => [cat1, cat2]
|
|
||||||
$profile['quests'] = [];
|
|
||||||
if ($quests = DB::Aowow()->selectCol('SELECT `questId` FROM ?_profiler_completion_quests WHERE `id` = ?d', $pBase['id']))
|
|
||||||
{
|
|
||||||
$qList = new QuestList(array(['id', $quests], Cfg::get('SQL_LIMIT_NONE')));
|
|
||||||
if (!$qList->error)
|
|
||||||
foreach ($qList->iterate() as $id => $__)
|
|
||||||
$profile['quests'][$id] = [$qList->getField('cat1'), $qList->getField('cat2')];
|
|
||||||
}
|
|
||||||
|
|
||||||
// skillId => [value, max]
|
|
||||||
$profile['skills'] = DB::Aowow()->select('SELECT `skillId` AS ARRAY_KEY, `value` AS "0", `max` AS "1" FROM ?_profiler_completion_skills WHERE `id` = ?d', $pBase['id']);
|
|
||||||
|
|
||||||
// factionId => amount
|
|
||||||
$profile['reputation'] = DB::Aowow()->selectCol('SELECT `factionId` AS ARRAY_KEY, `standing` FROM ?_profiler_completion_reputation WHERE `id` = ?d', $pBase['id']);
|
|
||||||
|
|
||||||
// titleId => 1
|
|
||||||
$profile['titles'] = DB::Aowow()->selectCol('SELECT `titleId` AS ARRAY_KEY, 1 FROM ?_profiler_completion_titles WHERE `id` = ?d', $pBase['id']);
|
|
||||||
|
|
||||||
// achievementId => js date object
|
|
||||||
$profile['achievements'] = DB::Aowow()->selectCol('SELECT `achievementId` AS ARRAY_KEY, CONCAT("$new Date(", `date` * 1000, ")") FROM ?_profiler_completion_achievements WHERE `id` = ?d', $pBase['id']);
|
|
||||||
|
|
||||||
// just points
|
|
||||||
$profile['achievementpoints'] = $profile['achievements'] ? DB::Aowow()->selectCell('SELECT SUM(`points`) FROM ?_achievement WHERE `id` IN (?a)', array_keys($profile['achievements'])) : 0;
|
|
||||||
|
|
||||||
// achievementId => counter
|
|
||||||
$profile['statistics'] = DB::Aowow()->selectCol('SELECT `achievementId` AS ARRAY_KEY, `counter` FROM ?_profiler_completion_statistics WHERE `id` = ?d', $pBase['id']);
|
|
||||||
|
|
||||||
// achievementId => 1
|
|
||||||
$profile['activity'] = DB::Aowow()->selectCol('SELECT `achievementId` AS ARRAY_KEY, 1 FROM ?_profiler_completion_statistics WHERE `id` = ?d AND `date` > ?d', $pBase['id'], time() - MONTH);
|
|
||||||
|
|
||||||
// spellId => 1
|
|
||||||
$profile['spells'] = DB::Aowow()->selectCol('SELECT `spellId` AS ARRAY_KEY, 1 FROM ?_profiler_completion_spells WHERE `id` = ?d', $pBase['id']);
|
|
||||||
|
|
||||||
|
|
||||||
$gItems = [];
|
|
||||||
|
|
||||||
$usedSlots = [];
|
|
||||||
if ($this->_get['items'])
|
|
||||||
{
|
|
||||||
$phItems = new ItemList(array(['id', $this->_get['items']], ['slot', INVTYPE_NON_EQUIP, '!']));
|
|
||||||
if (!$phItems->error)
|
|
||||||
{
|
|
||||||
$data = $phItems->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS);
|
|
||||||
foreach ($phItems->iterate() as $iId => $__)
|
|
||||||
{
|
|
||||||
$sl = $phItems->getField('slot');
|
|
||||||
foreach (Profiler::$slot2InvType as $slot => $invTypes)
|
|
||||||
{
|
|
||||||
if (in_array($sl, $invTypes) && !in_array($slot, $usedSlots))
|
|
||||||
{
|
|
||||||
// get and apply inventory
|
|
||||||
$gItems[$iId] = array(
|
|
||||||
'name_'.Lang::getLocale()->json() => $phItems->getField('name', true),
|
|
||||||
'quality' => $phItems->getField('quality'),
|
|
||||||
'icon' => $phItems->getField('iconString'),
|
|
||||||
'jsonequip' => $data[$iId]
|
|
||||||
);
|
|
||||||
$profile['inventory'][$slot] = [$iId, 0, 0, 0, 0, 0, 0, 0];
|
|
||||||
|
|
||||||
$usedSlots[] = $slot;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($items = DB::Aowow()->select('SELECT * FROM ?_profiler_items WHERE id = ?d', $pBase['id']))
|
|
||||||
{
|
|
||||||
$itemz = new ItemList(array(['id', array_column($items, 'item')], Cfg::get('SQL_LIMIT_NONE')));
|
|
||||||
if (!$itemz->error)
|
|
||||||
{
|
|
||||||
$data = $itemz->getListviewData(ITEMINFO_JSON | ITEMINFO_SUBITEMS);
|
|
||||||
|
|
||||||
foreach ($items as $i)
|
|
||||||
{
|
|
||||||
if ($itemz->getEntry($i['item']) && !in_array($i['slot'], $usedSlots))
|
|
||||||
{
|
|
||||||
// get and apply inventory
|
|
||||||
$gItems[$i['item']] = array(
|
|
||||||
'name_'.Lang::getLocale()->json() => $itemz->getField('name', true),
|
|
||||||
'quality' => $itemz->getField('quality'),
|
|
||||||
'icon' => $itemz->getField('iconString'),
|
|
||||||
'jsonequip' => $data[$i['item']]
|
|
||||||
);
|
|
||||||
$profile['inventory'][$i['slot']] = [$i['item'], $i['subItem'], $i['permEnchant'], $i['tempEnchant'], $i['gem1'], $i['gem2'], $i['gem3'], $i['gem4']];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$buff = '';
|
|
||||||
foreach ($gItems as $id => $item)
|
|
||||||
$buff .= 'g_items.add('.$id.', '.Util::toJSON($item, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).");\n";
|
|
||||||
|
|
||||||
|
|
||||||
// if ($au = $char->getField('auras'))
|
|
||||||
// {
|
|
||||||
// $auraz = new SpellList(array(['id', $char->getField('auras')], Cfg::get('SQL_LIMIT_NONE')));
|
|
||||||
// $dataz = $auraz->getListviewData();
|
|
||||||
// $modz = $auraz->getProfilerMods();
|
|
||||||
|
|
||||||
// // get and apply aura-mods
|
|
||||||
// foreach ($dataz as $id => $data)
|
|
||||||
// {
|
|
||||||
// $mods = [];
|
|
||||||
// if (!empty($modz[$id]))
|
|
||||||
// {
|
|
||||||
// foreach ($modz[$id] as $k => $v)
|
|
||||||
// {
|
|
||||||
// if (is_array($v))
|
|
||||||
// $mods[] = $v;
|
|
||||||
// else if ($str = @Game::$itemMods[$k])
|
|
||||||
// $mods[$str] = $v;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// $buff .= 'g_spells.add('.$id.", {id:".$id.", name:'".Util::jsEscape(mb_substr($data['name'], 1))."', icon:'".$data['icon']."', callback:".Util::toJSON($mods)."});\n";
|
|
||||||
// }
|
|
||||||
// $buff .= "\n";
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// load available titles
|
|
||||||
Util::loadStaticFile('p-titles-'.$pBase['gender'], $buff, true);
|
|
||||||
|
|
||||||
// add profile to buffer
|
|
||||||
$buff .= "\n\n\$WowheadProfiler.registerProfile(".Util::toJSON($profile).");";
|
|
||||||
|
|
||||||
return $buff."\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* params
|
|
||||||
id: <prId>
|
|
||||||
data: <mode> [string, tabName]
|
|
||||||
return
|
|
||||||
null
|
|
||||||
*/
|
|
||||||
protected function handlePurge() : void { } // removes completion data (as uploaded by the wowhead client) Just fail silently if someone triggers this manually
|
|
||||||
|
|
||||||
protected static function checkItemList($val) : array
|
|
||||||
{
|
|
||||||
// expecting item-list
|
|
||||||
if (preg_match('/\d+(:\d+)*/', $val))
|
|
||||||
return array_map('intVal', explode(':', $val));
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkUser(string $val) : string
|
|
||||||
{
|
|
||||||
if (User::isValidName($val))
|
|
||||||
return $val;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkTalentString(string $val) : string
|
|
||||||
{
|
|
||||||
if (preg_match('/^\d+$/', $val))
|
|
||||||
return $val;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static function checkGlyphString(string $val) : string
|
|
||||||
{
|
|
||||||
if (preg_match('/^\d+(:\d+)*$/', $val))
|
|
||||||
return $val;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,451 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
class Cfg
|
|
||||||
{
|
|
||||||
public const PATTERN_CONF_KEY = '/[a-z0-9_\.\-]/i';
|
|
||||||
public const PATTERN_INV_CONF_KEY = '/[^a-z0-9_\.\-]/i';
|
|
||||||
public const PATTERN_INVALID_CHARS = '/\p{C}/ui';
|
|
||||||
|
|
||||||
// config flags
|
|
||||||
public const FLAG_TYPE_INT = 0x001; // validate with intVal()
|
|
||||||
public const FLAG_TYPE_FLOAT = 0x002; // validate with floatVal()
|
|
||||||
public const FLAG_TYPE_BOOL = 0x004; // 0 || 1
|
|
||||||
public const FLAG_TYPE_STRING = 0x008; //
|
|
||||||
public const FLAG_OPT_LIST = 0x010; // single option
|
|
||||||
public const FLAG_BITMASK = 0x020; // multiple options
|
|
||||||
public const FLAG_PHP = 0x040; // applied with ini_set() [restrictions apply!]
|
|
||||||
public const FLAG_PERSISTENT = 0x080; // can not be deleted
|
|
||||||
public const FLAG_REQUIRED = 0x100; // required to have non-empty value
|
|
||||||
public const FLAG_ON_LOAD_FN = 0x200; // run static function of the same name after load
|
|
||||||
public const FLAG_ON_SET_FN = 0x400; // run static function of the same name as validator
|
|
||||||
public const FLAG_INTERNAL = 0x800; // can not be configures, automaticly calculated, skip on lists
|
|
||||||
|
|
||||||
public const CAT_MISCELLANEOUS = 0;
|
|
||||||
public const CAT_SITE = 1;
|
|
||||||
public const CAT_CACHE = 2;
|
|
||||||
public const CAT_ACCOUNT = 3;
|
|
||||||
public const CAT_SESSION = 4;
|
|
||||||
public const CAT_SITE_REPUTATION = 5;
|
|
||||||
public const CAT_ANALYTICS = 6;
|
|
||||||
public const CAT_PROFILER = 7;
|
|
||||||
|
|
||||||
public static $categories = array( // don't mind the ordering ... please?
|
|
||||||
1 => 'Site', 'Caching', 'Account', 'Session', 'Site Reputation', 'Google Analytics', 'Profiler', 0 => 'Other'
|
|
||||||
);
|
|
||||||
|
|
||||||
private const IDX_VALUE = 0;
|
|
||||||
private const IDX_FLAGS = 1;
|
|
||||||
private const IDX_CATEGORY = 2;
|
|
||||||
private const IDX_DEFAULT = 3;
|
|
||||||
private const IDX_COMMENT = 4;
|
|
||||||
|
|
||||||
private static $store = []; // name => [value, flags, cat, default, comment]
|
|
||||||
private static $isLoaded = false;
|
|
||||||
|
|
||||||
private static $rebuildScripts = array(
|
|
||||||
// 'rep_req_border_unco' => ['global'], // currently not a template or buildScript
|
|
||||||
// 'rep_req_border_rare' => ['global'],
|
|
||||||
// 'rep_req_border_epic' => ['global'],
|
|
||||||
// 'rep_req_border_lege' => ['global'],
|
|
||||||
'profiler_enable' => ['realms', 'realmMenu'],
|
|
||||||
'battlegroup' => ['realms', 'realmMenu'],
|
|
||||||
'name_short' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo'],
|
|
||||||
'site_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'demo', 'power'],
|
|
||||||
'static_host' => ['searchplugin', 'searchboxBody', 'searchboxScript', 'power'],
|
|
||||||
'contact_email' => ['markup'],
|
|
||||||
'locales' => ['locales']
|
|
||||||
);
|
|
||||||
|
|
||||||
public static function load() : void
|
|
||||||
{
|
|
||||||
if (!DB::isConnectable(DB_AOWOW))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$sets = DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value` AS "0", `flags` AS "1", `cat` AS "2", `default` AS "3", `comment` AS "4" FROM ?_config ORDER BY `key` ASC');
|
|
||||||
foreach ($sets as $key => [$value, $flags, $catg, $default, $comment])
|
|
||||||
{
|
|
||||||
$php = $flags & self::FLAG_PHP;
|
|
||||||
|
|
||||||
if ($err = self::validate($value, $flags, $comment))
|
|
||||||
{
|
|
||||||
self::throwError('Aowow config '.strtoupper($key).' failed validation and was skipped: '.$err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_INTERNAL)
|
|
||||||
{
|
|
||||||
self::throwError('Aowow config '.strtoupper($key).' is flagged as internaly generated and should not have been set in DB.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_ON_LOAD_FN)
|
|
||||||
{
|
|
||||||
if (!method_exists(__CLASS__, $key))
|
|
||||||
self::throwError('Aowow config '.strtoupper($key).' flagged for onLoadFN handling, but no handler was set');
|
|
||||||
else
|
|
||||||
self::{$key}($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($php)
|
|
||||||
ini_set(strtolower($key), $value);
|
|
||||||
|
|
||||||
self::$store[strtolower($key)] = [$value, $flags, $catg, $default, $comment];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CLI && !count(self::$store))
|
|
||||||
{
|
|
||||||
CLI::write('Cfg::load - aowow_config unexpectedly empty.', CLI::LOG_WARN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::$isLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function add(string $key, /*int|string*/ $value) : string
|
|
||||||
{
|
|
||||||
if (!self::$isLoaded)
|
|
||||||
return 'used add() on uninitialized config';
|
|
||||||
|
|
||||||
if (!$key)
|
|
||||||
return 'empty option name given';
|
|
||||||
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
if (preg_match(self::PATTERN_INV_CONF_KEY, $key))
|
|
||||||
return 'invalid chars in option name: [a-z 0-9 _ . -] are allowed';
|
|
||||||
|
|
||||||
if (isset(self::$store[$key]))
|
|
||||||
return 'this configuration option is already in use';
|
|
||||||
|
|
||||||
if ($errStr = self::validate($value))
|
|
||||||
return $errStr;
|
|
||||||
|
|
||||||
if (ini_get($key) === false || ini_set($key, $value) === false)
|
|
||||||
return 'this configuration option cannot be set';
|
|
||||||
|
|
||||||
$flags = self::FLAG_TYPE_STRING | self::FLAG_PHP;
|
|
||||||
if (!DB::Aowow()->query('INSERT IGNORE INTO ?_config (`key`, `value`, `cat`, `flags`) VALUES (?, ?, ?d, ?d)', $key, $value, self::CAT_MISCELLANEOUS, $flags))
|
|
||||||
return 'internal error';
|
|
||||||
|
|
||||||
self::$store[$key] = [$value, $flags, self::CAT_MISCELLANEOUS, null, null];
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function delete(string $key) : string
|
|
||||||
{
|
|
||||||
if (!self::$isLoaded)
|
|
||||||
return 'used delete() on uninitialized config';
|
|
||||||
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
if (!isset(self::$store[$key]))
|
|
||||||
return 'configuration option not found';
|
|
||||||
|
|
||||||
if (self::$store[$key][self::IDX_FLAGS] & self::FLAG_PERSISTENT)
|
|
||||||
return 'can\'t delete persistent option';
|
|
||||||
|
|
||||||
if (!(self::$store[$key][self::IDX_FLAGS] & self::FLAG_PHP))
|
|
||||||
return 'can\'t delete non-php option';
|
|
||||||
|
|
||||||
if (self::$store[$key][self::IDX_FLAGS] & self::FLAG_INTERNAL)
|
|
||||||
return 'can\'t delete internal option';
|
|
||||||
|
|
||||||
if (!DB::Aowow()->query('DELETE FROM ?_config WHERE `key` = ? AND (`flags` & ?d) = 0 AND (`flags` & ?d) > 0', $key, self::FLAG_PERSISTENT, self::FLAG_PHP))
|
|
||||||
return 'internal error';
|
|
||||||
|
|
||||||
unset(self::$store[$key]);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function get(string $key, bool $fromDB = false, bool $fullInfo = false) // : int|float|string
|
|
||||||
{
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
if (!isset(self::$store[$key]))
|
|
||||||
{
|
|
||||||
if (self::$isLoaded)
|
|
||||||
self::throwError('cfg not defined: '.strtoupper($key));
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($fromDB && $fullInfo)
|
|
||||||
return array_values(DB::Aowow()->selectRow('SELECT `value`, `flags`, `cat`, `default`, `comment` FROM ?_config WHERE `key` = ?', $key));
|
|
||||||
if ($fromDB)
|
|
||||||
return DB::Aowow()->selectCell('SELECT `value` FROM ?_config WHERE `key` = ?', $key);
|
|
||||||
if ($fullInfo)
|
|
||||||
return self::$store[$key];
|
|
||||||
|
|
||||||
return self::$store[$key][self::IDX_VALUE];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function set(string $key, /*int|string*/ $value, ?array &$rebuildFiles = []) : string
|
|
||||||
{
|
|
||||||
if (!self::$isLoaded)
|
|
||||||
return 'used set() on uninitialized config';
|
|
||||||
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
if (!isset(self::$store[$key]))
|
|
||||||
return 'configuration option not found';
|
|
||||||
|
|
||||||
[$oldValue, $flags, , , $comment] = self::$store[$key];
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_INTERNAL)
|
|
||||||
return 'can\'t set an internal option directly';
|
|
||||||
|
|
||||||
if ($err = self::validate($value, $flags, $comment))
|
|
||||||
return $err;
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_REQUIRED && !strlen($value))
|
|
||||||
return 'empty value given for required config';
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $value, $key);
|
|
||||||
|
|
||||||
self::$store[$key][self::IDX_VALUE] = $value;
|
|
||||||
|
|
||||||
// validate change
|
|
||||||
if ($flags & self::FLAG_ON_SET_FN)
|
|
||||||
{
|
|
||||||
$errMsg = '';
|
|
||||||
if (!method_exists(__CLASS__, $key))
|
|
||||||
$errMsg = 'Aowow config '.strtoupper($key).' flagged for onSetFN validation, but no handler was set';
|
|
||||||
else
|
|
||||||
self::{$key}($value, $errMsg);
|
|
||||||
|
|
||||||
if ($errMsg)
|
|
||||||
{
|
|
||||||
// rollback change
|
|
||||||
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $oldValue, $key);
|
|
||||||
self::$store[$key][self::IDX_VALUE] = $oldValue;
|
|
||||||
|
|
||||||
return $errMsg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_ON_LOAD_FN)
|
|
||||||
{
|
|
||||||
if (!method_exists(__CLASS__, $key))
|
|
||||||
return 'Aowow config '.strtoupper($key).' flagged for onLoadFN handling, but no handler was set';
|
|
||||||
else
|
|
||||||
self::{$key}($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger setup build
|
|
||||||
return self::handleFileBuild($key, $rebuildFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function reset(string $key, ?array &$rebuildFiles = []) : string
|
|
||||||
{
|
|
||||||
if (!self::$isLoaded)
|
|
||||||
return 'used reset() on uninitialized config';
|
|
||||||
|
|
||||||
$key = strtolower($key);
|
|
||||||
|
|
||||||
if (!isset(self::$store[$key]))
|
|
||||||
return 'configuration option not found';
|
|
||||||
|
|
||||||
[$oldValue, $flags, , $default, ] = self::$store[$key];
|
|
||||||
if ($flags & self::FLAG_INTERNAL)
|
|
||||||
return 'can\'t set an internal option directly';
|
|
||||||
|
|
||||||
if (!$default)
|
|
||||||
return 'config option has no default value';
|
|
||||||
|
|
||||||
// @eval .. some dafault values are supplied as bitmask or the likes
|
|
||||||
if (!($flags & Cfg::FLAG_TYPE_STRING))
|
|
||||||
$default = @eval('return ('.$default.');');
|
|
||||||
|
|
||||||
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $default, $key);
|
|
||||||
self::$store[$key][self::IDX_VALUE] = $default;
|
|
||||||
|
|
||||||
// validate change
|
|
||||||
if ($flags & self::FLAG_ON_SET_FN)
|
|
||||||
{
|
|
||||||
$errMsg = '';
|
|
||||||
if (!method_exists(__CLASS__, $key))
|
|
||||||
$errMsg = 'required onSetFN validator not set';
|
|
||||||
else
|
|
||||||
self::{$key}($default, $errMsg);
|
|
||||||
|
|
||||||
if ($errMsg)
|
|
||||||
{
|
|
||||||
// rollback change
|
|
||||||
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $oldValue, $key);
|
|
||||||
self::$store[$key][self::IDX_VALUE] = $oldValue;
|
|
||||||
|
|
||||||
return $errMsg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// trigger setup build
|
|
||||||
return self::handleFileBuild($key, $rebuildFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function forCategory(int $category) : \Generator
|
|
||||||
{
|
|
||||||
foreach (self::$store as $k => [, $flags, $catg, , ])
|
|
||||||
if ($catg == $category && !($flags & self::FLAG_INTERNAL))
|
|
||||||
yield $k => self::$store[$k];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function applyToString(string $string) : string
|
|
||||||
{
|
|
||||||
return preg_replace_callback(
|
|
||||||
['/CFG_([A-Z_]+)/', '/((HOST|STATIC)_URL)/'],
|
|
||||||
function ($m) {
|
|
||||||
if (!isset(self::$store[strtolower($m[1])]))
|
|
||||||
return $m[1];
|
|
||||||
|
|
||||||
[$val, $flags, , , ] = self::$store[strtolower($m[1])];
|
|
||||||
return $flags & (self::FLAG_TYPE_FLOAT | self::FLAG_TYPE_INT) ? Lang::nf($val) : $val;
|
|
||||||
},
|
|
||||||
$string
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************/
|
|
||||||
/* internal */
|
|
||||||
/************/
|
|
||||||
|
|
||||||
private static function validate(&$value, int $flags = self::FLAG_TYPE_STRING | self::FLAG_PHP, string $comment = ' - ') : string
|
|
||||||
{
|
|
||||||
$value = preg_replace(self::PATTERN_INVALID_CHARS, '', $value);
|
|
||||||
|
|
||||||
if (!($flags & (self::FLAG_TYPE_BOOL | self::FLAG_TYPE_FLOAT | self::FLAG_TYPE_INT | self::FLAG_TYPE_STRING)))
|
|
||||||
return 'no type set for value';
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_TYPE_INT && !Util::checkNumeric($value, NUM_CAST_INT))
|
|
||||||
return 'value must be integer';
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_TYPE_FLOAT && !Util::checkNumeric($value, NUM_CAST_FLOAT))
|
|
||||||
return 'value must be float';
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_OPT_LIST)
|
|
||||||
{
|
|
||||||
$info = explode(' - ', $comment)[1];
|
|
||||||
foreach (explode(', ', $info) as $option)
|
|
||||||
if (explode(':', $option)[0] == $value)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return 'value not in range';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_BITMASK)
|
|
||||||
{
|
|
||||||
$mask = 0x0;
|
|
||||||
$info = explode(' - ', $comment)[1];
|
|
||||||
foreach (explode(', ', $info) as $option)
|
|
||||||
$mask |= (1 << explode(':', $option)[0]);
|
|
||||||
|
|
||||||
if (!($value &= $mask) && ($flags & self::FLAG_REQUIRED))
|
|
||||||
return 'value not in range';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($flags & self::FLAG_TYPE_BOOL)
|
|
||||||
$value = (bool)$value;
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function handleFileBuild(string $key, array &$rebuildFiles) : string
|
|
||||||
{
|
|
||||||
if (!isset(self::$rebuildScripts[$key]))
|
|
||||||
return '';
|
|
||||||
|
|
||||||
$msg = '';
|
|
||||||
|
|
||||||
if (CLI)
|
|
||||||
{
|
|
||||||
$rebuildFiles = array_merge($rebuildFiles, self::$rebuildScripts[$key]);
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// not in CLI mode and build() can only be run from CLI. .. todo: other options..?
|
|
||||||
exec('php aowow --build='.implode(',', self::$rebuildScripts[$key]), $out);
|
|
||||||
foreach ($out as $o)
|
|
||||||
if (strstr($o, 'ERR'))
|
|
||||||
$msg .= explode('0m]', $o)[1]."<br />\n";
|
|
||||||
|
|
||||||
return $msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function throwError($msg) : void
|
|
||||||
{
|
|
||||||
if (CLI)
|
|
||||||
CLI::write($msg, CLI::LOG_ERROR);
|
|
||||||
else
|
|
||||||
trigger_error($msg, E_USER_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function locales(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
||||||
{
|
|
||||||
if (!CLI)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// note: Change is written to db and storage at this point, but can be rolled back.
|
|
||||||
if (CLISetup::setLocales())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
$msg .= 'no valid locales set';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function acc_auth_mode(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
||||||
{
|
|
||||||
if ($value == 1 && !extension_loaded('gmp'))
|
|
||||||
{
|
|
||||||
$msg .= 'PHP extension GMP is required to use TrinityCore as auth source, but is not currently enabled.';
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function profiler_enable(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
||||||
{
|
|
||||||
if ($value != 1)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return Profiler::queueStart($msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function static_host(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
||||||
{
|
|
||||||
self::$store['static_url'] = array( // points js to images & scripts
|
|
||||||
(self::useSSL() ? 'https://' : 'http://').$value,
|
|
||||||
self::FLAG_PERSISTENT | self::FLAG_TYPE_STRING | self::FLAG_INTERNAL,
|
|
||||||
self::CAT_SITE,
|
|
||||||
null, // no default value
|
|
||||||
null, // no comment/info
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function site_host(/*int|string*/ $value, ?string &$msg = '') : bool
|
|
||||||
{
|
|
||||||
self::$store['host_url'] = array( // points js to executable files
|
|
||||||
(self::useSSL() ? 'https://' : 'http://').$value,
|
|
||||||
self::FLAG_PERSISTENT | self::FLAG_TYPE_STRING | self::FLAG_INTERNAL,
|
|
||||||
self::CAT_SITE,
|
|
||||||
null, // no default value
|
|
||||||
null, // no comment/info
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function useSSL() : bool
|
|
||||||
{
|
|
||||||
return (($_SERVER['HTTPS'] ?? 'off') != 'off') || (self::$store['force_ssl'][self::IDX_VALUE] ?? 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
526
includes/community.class.php
Normal file
526
includes/community.class.php
Normal file
@@ -0,0 +1,526 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('AOWOW_REVISION'))
|
||||||
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
|
/************
|
||||||
|
* get Community Content
|
||||||
|
************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
{id:115,user:'Ciderhelm',date:'2010/05/10 19:14:18',caption:'TankSpot\'s Guide to the Fury Warrior (Part 1)',videoType:1,videoId:'VUvxFvVmttg',type:13,typeId:1},
|
||||||
|
{id:116,user:'Ciderhelm',date:'2010/05/10 19:14:18',caption:'TankSpot\'s Guide to the Fury Warrior (Part 2)',videoType:1,videoId:'VEfnuIcq7n8',type:13,typeId:1},
|
||||||
|
{id:117,user:'Ciderhelm',date:'2010/05/10 19:14:18',caption:'TankSpot\'s Protection Warrior Guide',videoType:1,videoId:'vF-7kmvJZXY',type:13,typeId:1,sticky:1}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* todo: administration of content */
|
||||||
|
|
||||||
|
class CommunityContent
|
||||||
|
{
|
||||||
|
private static $jsGlobals = [];
|
||||||
|
private static $subjCache = [];
|
||||||
|
|
||||||
|
private static $commentQuery = '
|
||||||
|
SELECT
|
||||||
|
c.*,
|
||||||
|
a1.displayName AS user,
|
||||||
|
a2.displayName AS editUser,
|
||||||
|
a3.displayName AS deleteUser,
|
||||||
|
a4.displayName AS responseUser,
|
||||||
|
IFNULL(SUM(cr.value), 0) AS rating,
|
||||||
|
SUM(IF (cr.userId = ?d, value, 0)) AS userRating,
|
||||||
|
SUM(IF (r.userId = ?d, 1, 0)) AS userReported
|
||||||
|
FROM
|
||||||
|
?_comments c
|
||||||
|
JOIN
|
||||||
|
?_account a1 ON c.userId = a1.id
|
||||||
|
LEFT JOIN
|
||||||
|
?_account a2 ON c.editUserId = a2.id
|
||||||
|
LEFT JOIN
|
||||||
|
?_account a3 ON c.deleteUserId = a3.id
|
||||||
|
LEFT JOIN
|
||||||
|
?_account a4 ON c.responseUserId = a4.id
|
||||||
|
LEFT JOIN
|
||||||
|
?_comments_rates cr ON c.id = cr.commentId
|
||||||
|
LEFT JOIN
|
||||||
|
?_reports r ON r.subject = c.id AND r.mode = 1 AND r.reason = 19
|
||||||
|
WHERE
|
||||||
|
c.replyTo = ?d AND c.type = ?d AND c.typeId = ?d AND
|
||||||
|
((c.flags & ?d) = 0 OR c.userId = ?d OR ?d)
|
||||||
|
GROUP BY
|
||||||
|
c.id
|
||||||
|
ORDER BY
|
||||||
|
rating ASC
|
||||||
|
';
|
||||||
|
|
||||||
|
private static $previewQuery = '
|
||||||
|
SELECT
|
||||||
|
c.id,
|
||||||
|
c.body AS preview,
|
||||||
|
c.date,
|
||||||
|
c.replyTo AS commentid,
|
||||||
|
UNIX_TIMESTAMP() - c.date AS elapsed,
|
||||||
|
IF(c.flags & ?d, 1, 0) AS deleted,
|
||||||
|
IF(c.type <> 0, c.type, c2.type) AS type,
|
||||||
|
IF(c.typeId <> 0, c.typeId, c2.typeId) AS typeId,
|
||||||
|
IFNULL(SUM(cr.value), 0) AS rating,
|
||||||
|
a.displayName AS user
|
||||||
|
FROM
|
||||||
|
?_comments c
|
||||||
|
JOIN
|
||||||
|
?_account a ON c.userId = a.id
|
||||||
|
LEFT JOIN
|
||||||
|
?_comments_rates cr ON cr.commentId = c.id
|
||||||
|
LEFT JOIN
|
||||||
|
?_comments c2 ON c.replyTo = c2.id
|
||||||
|
WHERE
|
||||||
|
{c.userId = ?d AND}
|
||||||
|
{c.replyTo <> ?d AND}
|
||||||
|
{c.replyTo = ?d AND}
|
||||||
|
((c.flags & ?d) = 0 OR c.userId = ?d OR ?d)
|
||||||
|
GROUP BY
|
||||||
|
c.id
|
||||||
|
ORDER BY
|
||||||
|
date DESC
|
||||||
|
LIMIT
|
||||||
|
?d
|
||||||
|
';
|
||||||
|
|
||||||
|
private static function addSubject($type, $typeId)
|
||||||
|
{
|
||||||
|
if (!isset(self::$subjCache[$type][$typeId]))
|
||||||
|
self::$subjCache[$type][$typeId] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getSubjects()
|
||||||
|
{
|
||||||
|
foreach (self::$subjCache as $type => $ids)
|
||||||
|
{
|
||||||
|
$_ = array_filter(array_keys($ids), 'is_numeric');
|
||||||
|
if (!$_)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$cnd = [CFG_SQL_LIMIT_NONE, ['id', $_]];
|
||||||
|
|
||||||
|
switch ($type)
|
||||||
|
{
|
||||||
|
case TYPE_NPC: $obj = new CreatureList($cnd); break;
|
||||||
|
case TYPE_OBJECT: $obj = new GameobjectList($cnd); break;
|
||||||
|
case TYPE_ITEM: $obj = new ItemList($cnd); break;
|
||||||
|
case TYPE_ITEMSET: $obj = new ItemsetList($cnd); break;
|
||||||
|
case TYPE_QUEST: $obj = new QuestList($cnd); break;
|
||||||
|
case TYPE_SPELL: $obj = new SpellList($cnd); break;
|
||||||
|
case TYPE_ZONE: $obj = new ZoneList($cnd); break;
|
||||||
|
case TYPE_FACTION: $obj = new FactionList($cnd); break;
|
||||||
|
case TYPE_PET: $obj = new PetList($cnd); break;
|
||||||
|
case TYPE_ACHIEVEMENT: $obj = new AchievementList($cnd); break;
|
||||||
|
case TYPE_TITLE: $obj = new TitleList($cnd); break;
|
||||||
|
case TYPE_WORLDEVENT: $obj = new WorldEventList($cnd); break;
|
||||||
|
case TYPE_CLASS: $obj = new CharClassList($cnd); break;
|
||||||
|
case TYPE_RACE: $obj = new CharRaceList($cnd); break;
|
||||||
|
case TYPE_SKILL: $obj = new SkillList($cnd); break;
|
||||||
|
case TYPE_CURRENCY: $obj = new CurrencyList($cnd); break;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($obj->iterate() as $id => $__)
|
||||||
|
self::$subjCache[$type][$id] = $obj->getField('name', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCommentPreviews($params = [], &$nFound = 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
purged:0, <- doesnt seem to be used anymore
|
||||||
|
domain:'live' <- irrelevant for our case
|
||||||
|
*/
|
||||||
|
|
||||||
|
$comments = DB::Aowow()->selectPage(
|
||||||
|
$nFound,
|
||||||
|
self::$previewQuery,
|
||||||
|
CC_FLAG_DELETED,
|
||||||
|
empty($params['user']) ? DBSIMPLE_SKIP : $params['user'],
|
||||||
|
empty($params['replies']) ? DBSIMPLE_SKIP : 0, // i dont know, how to switch the sign around
|
||||||
|
!empty($params['replies']) ? DBSIMPLE_SKIP : 0,
|
||||||
|
CC_FLAG_DELETED,
|
||||||
|
User::$id,
|
||||||
|
User::isInGroup(U_GROUP_COMMENTS_MODERATOR),
|
||||||
|
CFG_SQL_LIMIT_DEFAULT
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($comments as $c)
|
||||||
|
self::addSubject($c['type'], $c['typeId']);
|
||||||
|
|
||||||
|
self::getSubjects();
|
||||||
|
|
||||||
|
foreach ($comments as $idx => &$c)
|
||||||
|
{
|
||||||
|
if (!empty(self::$subjCache[$c['type']][$c['typeId']]))
|
||||||
|
{
|
||||||
|
// apply subject
|
||||||
|
$c['subject'] = self::$subjCache[$c['type']][$c['typeId']];
|
||||||
|
|
||||||
|
// format date
|
||||||
|
$c['date'] = date(Util::$dateFormatInternal, $c['date']);
|
||||||
|
|
||||||
|
// remove commentid if not looking for replies
|
||||||
|
if (empty($params['replies']))
|
||||||
|
unset($c['commentid']);
|
||||||
|
|
||||||
|
// remove line breaks
|
||||||
|
$c['preview'] = strtr($c['preview'], ["\n" => ' ', "\r" => ' ']);
|
||||||
|
// limit whitespaces to one at a time
|
||||||
|
$c['preview'] = preg_replace('/\s+/', ' ', $c['preview']);
|
||||||
|
// limit previews to 100 chars + whatever it takes to make the last word full
|
||||||
|
if (strlen($c['preview']) > 100)
|
||||||
|
{
|
||||||
|
$n = 0;
|
||||||
|
$b = [];
|
||||||
|
$parts = explode(' ', $c['preview']);
|
||||||
|
while ($n < 100 && $parts)
|
||||||
|
{
|
||||||
|
$_ = array_shift($parts);
|
||||||
|
$n += strlen($_);
|
||||||
|
$b[] = $_;
|
||||||
|
}
|
||||||
|
|
||||||
|
$c['preview'] = implode(' ', $b).'…';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Util::addNote(U_GROUP_STAFF, 'CommunityClass::getCommentPreviews - comment '.$c['id'].' belongs to nonexistant subject');
|
||||||
|
unset($comments[$idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getCommentReplies($commentId, $limit = 0, &$nFound = 0)
|
||||||
|
{
|
||||||
|
$replies = [];
|
||||||
|
$query = $limit > 0 ? self::$commentQuery.' LIMIT '.$limit : self::$commentQuery;
|
||||||
|
|
||||||
|
// get replies
|
||||||
|
$results = DB::Aowow()->selectPage($nFound, $query, User::$id, User::$id, $commentId, 0, 0, CC_FLAG_DELETED, User::$id, User::isInGroup(U_GROUP_COMMENTS_MODERATOR));
|
||||||
|
foreach ($results as $r)
|
||||||
|
{
|
||||||
|
(new Markup($r['body']))->parseGlobalsFromText(self::$jsGlobals);
|
||||||
|
|
||||||
|
$reply = array(
|
||||||
|
'commentid' => $commentId,
|
||||||
|
'id' => $r['id'],
|
||||||
|
'body' => $r['body'],
|
||||||
|
'username' => $r['user'],
|
||||||
|
'roles' => $r['roles'],
|
||||||
|
'creationdate' => date(Util::$dateFormatInternal, $r['date']),
|
||||||
|
'lasteditdate' => date(Util::$dateFormatInternal, $r['editDate']),
|
||||||
|
'rating' => (string)$r['rating']
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($r['userReported'])
|
||||||
|
$reply['reportedByUser'] = true;
|
||||||
|
|
||||||
|
if ($r['userRating'] > 0)
|
||||||
|
$reply['votedByUser'] = true;
|
||||||
|
else if ($r['userRating'] < 0)
|
||||||
|
$reply['downvotedByUser'] = true;
|
||||||
|
|
||||||
|
$replies[] = $reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $replies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getScreenshotsForManager($type, $typeId, $userId = 0)
|
||||||
|
{
|
||||||
|
$screenshots = DB::Aowow()->select('
|
||||||
|
SELECT s.id, a.displayName AS user, s.date, s.width, s.height, s.type, s.typeId, s.caption, s.status, s.status AS "flags"
|
||||||
|
FROM ?_screenshots s,
|
||||||
|
LEFT JOIN ?_account a ON s.userIdOwner = a.id
|
||||||
|
WHERE
|
||||||
|
{ s.type = ?d}
|
||||||
|
{ AND s.typeId = ?d}
|
||||||
|
{ s.userIdOwner = ?d}
|
||||||
|
LIMIT 100',
|
||||||
|
$userId ? DBSIMPLE_SKIP : $type,
|
||||||
|
$userId ? DBSIMPLE_SKIP : $typeId,
|
||||||
|
$userId ? $userId : DBSIMPLE_SKIP
|
||||||
|
);
|
||||||
|
|
||||||
|
$num = [];
|
||||||
|
foreach ($screenshots as $s)
|
||||||
|
{
|
||||||
|
if (empty($num[$s['type']][$s['typeId']]))
|
||||||
|
$num[$s['type']][$s['typeId']] = 1;
|
||||||
|
else
|
||||||
|
$num[$s['type']][$s['typeId']]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// format data to meet requirements of the js
|
||||||
|
foreach ($screenshots as $idx => &$s)
|
||||||
|
{
|
||||||
|
$s['date'] = date(Util::$dateFormatInternal, $s['date']);
|
||||||
|
|
||||||
|
$s['name'] = "Screenshot #".$s['id']; // what should we REALLY name it?
|
||||||
|
|
||||||
|
if (isset($screenshots[$idx - 1]))
|
||||||
|
$s['prev'] = $idx - 1;
|
||||||
|
|
||||||
|
if (isset($screenshots[$idx + 1]))
|
||||||
|
$s['next'] = $idx + 1;
|
||||||
|
|
||||||
|
// order gives priority for 'status'
|
||||||
|
if (!($s['flags'] & CC_FLAG_APPROVED))
|
||||||
|
{
|
||||||
|
$s['pending'] = 1;
|
||||||
|
$s['status'] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$s['status'] = 100;
|
||||||
|
|
||||||
|
if ($s['flags'] & CC_FLAG_STICKY)
|
||||||
|
{
|
||||||
|
$s['sticky'] = 1;
|
||||||
|
$s['status'] = 105;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($s['flags'] & CC_FLAG_DELETED)
|
||||||
|
{
|
||||||
|
$s['deleted'] = 1;
|
||||||
|
$s['status'] = 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
// something todo with massSelect .. am i doing this right?
|
||||||
|
if ($num[$s['type']][$s['typeId']] == 1)
|
||||||
|
$s['unique'] = 1;
|
||||||
|
|
||||||
|
if (!$s['user'])
|
||||||
|
unset($s['user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $screenshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getScreenshotPagesForManager($all, &$nFound)
|
||||||
|
{
|
||||||
|
// i GUESS .. ss_getALL ? everything : unapproved
|
||||||
|
$nFound = 0;
|
||||||
|
$pages = DB::Aowow()->select('
|
||||||
|
SELECT s.`type`, s.`typeId`, count(1) AS "count", MIN(s.`date`) AS "date"
|
||||||
|
FROM ?_screenshots s
|
||||||
|
{WHERE (s.status & ?d) = 0}
|
||||||
|
GROUP BY s.`type`, s.`typeId`',
|
||||||
|
$all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($pages)
|
||||||
|
{
|
||||||
|
// limit to one actually existing type each
|
||||||
|
$types = array_intersect(array_unique(array_column($pages, 'type')), array_keys(Util::$typeClasses));
|
||||||
|
foreach ($types as $t)
|
||||||
|
{
|
||||||
|
$ids = [];
|
||||||
|
foreach ($pages as $row)
|
||||||
|
if ($row['type'] == $t)
|
||||||
|
$ids[] = $row['typeId'];
|
||||||
|
|
||||||
|
if (!$ids)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$cnd = [['id', $ids]];
|
||||||
|
if ($t == TYPE_WORLDEVENT) // FKIN HOLIDAYS
|
||||||
|
array_push($cnd, ['holidayId', $ids], 'OR');
|
||||||
|
|
||||||
|
$tClass = new Util::$typeClasses[$t]($cnd);
|
||||||
|
foreach ($pages as &$p)
|
||||||
|
if ($p['type'] == $t)
|
||||||
|
if ($tClass->getEntry($p['typeId']))
|
||||||
|
$p['name'] = $tClass->getField('name', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pages as &$p)
|
||||||
|
{
|
||||||
|
if (empty($p['name']))
|
||||||
|
{
|
||||||
|
Util::addNote(U_GROUP_STAFF | U_GROUP_SCREENSHOT, 'AdminPage::handleScreenshots() - Screenshot linked to nonexistant type/typeId combination '.$p['type'].'/'.$p['typeId']);
|
||||||
|
unset($p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$nFound += $p['count'];
|
||||||
|
$p['date'] = date(Util::$dateFormatInternal, $p['date']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getComments($type, $typeId)
|
||||||
|
{
|
||||||
|
|
||||||
|
$results = DB::Aowow()->query(self::$commentQuery, User::$id, User::$id, 0, $type, $typeId, CC_FLAG_DELETED, User::$id, (int)User::isInGroup(U_GROUP_COMMENTS_MODERATOR));
|
||||||
|
$comments = [];
|
||||||
|
|
||||||
|
// additional informations
|
||||||
|
$i = 0;
|
||||||
|
foreach ($results as $r)
|
||||||
|
{
|
||||||
|
(new Markup($r['body']))->parseGlobalsFromText(self::$jsGlobals);
|
||||||
|
|
||||||
|
self::$jsGlobals[TYPE_USER][$r['userId']] = $r['userId'];
|
||||||
|
|
||||||
|
$c = array(
|
||||||
|
'commentv2' => 1, // always 1.. enables some features i guess..?
|
||||||
|
'number' => $i++, // some iterator .. unsued?
|
||||||
|
'id' => $r['id'],
|
||||||
|
'date' => date(Util::$dateFormatInternal, $r['date']),
|
||||||
|
'roles' => $r['roles'],
|
||||||
|
'body' => $r['body'],
|
||||||
|
'rating' => $r['rating'],
|
||||||
|
'userRating' => $r['userRating'],
|
||||||
|
'user' => $r['user'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$c['replies'] = self::getCommentReplies($r['id'], 5, $c['nreplies']);
|
||||||
|
|
||||||
|
if ($r['responseBody']) // adminResponse
|
||||||
|
{
|
||||||
|
$c['response'] = $r['responseBody'];
|
||||||
|
$c['responseroles'] = $r['responseRoles'];
|
||||||
|
$c['responseuser'] = $r['responseUser'];
|
||||||
|
|
||||||
|
(new Markup($r['responseBody']))->parseGlobalsFromText(self::$jsGlobals);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($r['editCount']) // lastEdit
|
||||||
|
$c['lastEdit'] = [date(Util::$dateFormatInternal, $r['editDate']), $r['editCount'], $r['editUser']];
|
||||||
|
|
||||||
|
if ($r['flags'] & CC_FLAG_STICKY)
|
||||||
|
$c['sticky'] = true;
|
||||||
|
|
||||||
|
if ($r['flags'] & CC_FLAG_DELETED)
|
||||||
|
{
|
||||||
|
$c['deleted'] = true;
|
||||||
|
$c['deletedInfo'] = [date(Util::$dateFormatInternal, $r['deleteDate']), $r['deleteUser']];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($r['flags'] & CC_FLAG_OUTDATED)
|
||||||
|
$c['outofdate'] = true;
|
||||||
|
|
||||||
|
$comments[] = $c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getVideos($typeOrUser, $typeId = 0, &$nFound = 0)
|
||||||
|
{
|
||||||
|
$videos = DB::Aowow()->selectPage($nFound, "
|
||||||
|
SELECT v.id, a.displayName AS user, v.date, v.videoId, v.caption, IF(v.status & ?d, 1, 0) AS 'sticky', v.type, v.typeId
|
||||||
|
FROM ?_videos v
|
||||||
|
LEFT JOIN ?_account a ON v.userIdOwner = a.id
|
||||||
|
WHERE {v.userIdOwner = ?d }{v.type = ? }{AND v.typeId = ? }AND v.status & ?d AND (v.status & ?d) = 0",
|
||||||
|
CC_FLAG_STICKY,
|
||||||
|
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
|
||||||
|
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
|
||||||
|
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
|
||||||
|
CC_FLAG_APPROVED,
|
||||||
|
CC_FLAG_DELETED
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($typeOrUser < 0) // only for user page
|
||||||
|
{
|
||||||
|
foreach ($videos as $v)
|
||||||
|
self::addSubject($v['type'], $v['typeId']);
|
||||||
|
|
||||||
|
self::getSubjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
// format data to meet requirements of the js
|
||||||
|
foreach ($videos as &$v)
|
||||||
|
{
|
||||||
|
if ($typeOrUser < 0) // only for user page
|
||||||
|
{
|
||||||
|
if (!empty(self::$subjCache[$v['type']][$v['typeId']]) && !is_numeric(self::$subjCache[$v['type']][$v['typeId']]))
|
||||||
|
$v['subject'] = self::$subjCache[$v['type']][$v['typeId']];
|
||||||
|
else
|
||||||
|
$v['subject'] = Lang::user('removed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$v['date'] = date(Util::$dateFormatInternal, $v['date']);
|
||||||
|
$v['videoType'] = 1; // always youtube
|
||||||
|
|
||||||
|
if (!$v['sticky'])
|
||||||
|
unset($v['sticky']);
|
||||||
|
|
||||||
|
if (!$v['user'])
|
||||||
|
unset($v['user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $videos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getScreenshots($typeOrUser, $typeId = 0, &$nFound = 0)
|
||||||
|
{
|
||||||
|
$screenshots = DB::Aowow()->selectPage($nFound, "
|
||||||
|
SELECT s.id, a.displayName AS user, s.date, s.width, s.height, s.caption, IF(s.status & ?d, 1, 0) AS 'sticky', s.type, s.typeId
|
||||||
|
FROM ?_screenshots s
|
||||||
|
LEFT JOIN ?_account a ON s.userIdOwner = a.id
|
||||||
|
WHERE {s.userIdOwner = ?d }{s.type = ? }{AND s.typeId = ? }AND s.status & ?d AND (s.status & ?d) = 0",
|
||||||
|
CC_FLAG_STICKY,
|
||||||
|
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
|
||||||
|
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
|
||||||
|
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
|
||||||
|
CC_FLAG_APPROVED,
|
||||||
|
CC_FLAG_DELETED
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($typeOrUser < 0) // only for user page
|
||||||
|
{
|
||||||
|
foreach ($screenshots as $s)
|
||||||
|
self::addSubject($s['type'], $s['typeId']);
|
||||||
|
|
||||||
|
self::getSubjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
// format data to meet requirements of the js
|
||||||
|
foreach ($screenshots as &$s)
|
||||||
|
{
|
||||||
|
if ($typeOrUser < 0) // only for user page
|
||||||
|
{
|
||||||
|
if (!empty(self::$subjCache[$s['type']][$s['typeId']]) && !is_numeric(self::$subjCache[$s['type']][$s['typeId']]))
|
||||||
|
$s['subject'] = self::$subjCache[$s['type']][$s['typeId']];
|
||||||
|
else
|
||||||
|
$s['subject'] = Lang::user('removed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$s['date'] = date(Util::$dateFormatInternal, $s['date']);
|
||||||
|
|
||||||
|
if (!$s['sticky'])
|
||||||
|
unset($s['sticky']);
|
||||||
|
|
||||||
|
if (!$s['user'])
|
||||||
|
unset($s['user']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $screenshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAll($type, $typeId, &$jsg)
|
||||||
|
{
|
||||||
|
$result = array(
|
||||||
|
'vi' => self::getVideos($type, $typeId),
|
||||||
|
'sc' => self::getScreenshots($type, $typeId),
|
||||||
|
'co' => self::getComments($type, $typeId)
|
||||||
|
);
|
||||||
|
|
||||||
|
Util::mergeJsGlobals($jsg, self::$jsGlobals);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -1,746 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
// TrinityCore - Condition System
|
|
||||||
|
|
||||||
class Conditions
|
|
||||||
{
|
|
||||||
// enum TypeID
|
|
||||||
private const TYPEID_OBJECT = 0;
|
|
||||||
private const TYPEID_ITEM = 1;
|
|
||||||
private const TYPEID_CONTAINER = 2;
|
|
||||||
private const TYPEID_UNIT = 3;
|
|
||||||
private const TYPEID_PLAYER = 4;
|
|
||||||
private const TYPEID_GAMEOBJECT = 5;
|
|
||||||
private const TYPEID_DYNAMICOBJECT = 6;
|
|
||||||
private const TYPEID_CORPSE = 7;
|
|
||||||
|
|
||||||
public const OP_E = 0; // ==
|
|
||||||
public const OP_GT = 1; // >
|
|
||||||
public const OP_LT = 2; // <
|
|
||||||
public const OP_GT_E = 3; // >=
|
|
||||||
public const OP_LT_E = 4; // <=
|
|
||||||
// Group, Entry, Id
|
|
||||||
public const SRC_NONE = 0; // null, null, null - use when adding external conditions
|
|
||||||
public const SRC_CREATURE_LOOT_TEMPLATE = 1; // tplEntry, itemId, null
|
|
||||||
public const SRC_DISENCHANT_LOOT_TEMPLATE = 2; // tplEntry, itemId, null
|
|
||||||
public const SRC_FISHING_LOOT_TEMPLATE = 3; // tplEntry, itemId, null
|
|
||||||
public const SRC_GAMEOBJECT_LOOT_TEMPLATE = 4; // tplEntry, itemId, null
|
|
||||||
public const SRC_ITEM_LOOT_TEMPLATE = 5; // tplEntry, itemId, null
|
|
||||||
public const SRC_MAIL_LOOT_TEMPLATE = 6; // tplEntry, itemId, null
|
|
||||||
public const SRC_MILLING_LOOT_TEMPLATE = 7; // tplEntry, itemId, null
|
|
||||||
public const SRC_PICKPOCKETING_LOOT_TEMPLATE = 8; // tplEntry, itemId, null
|
|
||||||
public const SRC_PROSPECTING_LOOT_TEMPLATE = 9; // tplEntry, itemId, null
|
|
||||||
public const SRC_REFERENCE_LOOT_TEMPLATE = 10; // tplEntry, itemId, null
|
|
||||||
public const SRC_SKINNING_LOOT_TEMPLATE = 11; // tplEntry, itemId, null
|
|
||||||
public const SRC_SPELL_LOOT_TEMPLATE = 12; // tplEntry, itemId, null
|
|
||||||
public const SRC_SPELL_IMPLICIT_TARGET = 13; // effectMask, spellId, null
|
|
||||||
public const SRC_GOSSIP_MENU = 14; // menuId, textId, null
|
|
||||||
public const SRC_GOSSIP_MENU_OPTION = 15; // menuId, optionId, null
|
|
||||||
public const SRC_CREATURE_TEMPLATE_VEHICLE = 16; // npcId, null, null
|
|
||||||
public const SRC_SPELL = 17; // null, spellId, null
|
|
||||||
public const SRC_SPELL_CLICK_EVENT = 18; // npcId, spellId, null
|
|
||||||
public const SRC_QUEST_AVAILABLE = 19; // null, questId, null
|
|
||||||
public const SRC_QUEST_SHOW_MARK = 20; // null, questId, null - ⚠️ unused as of 01.05.2024
|
|
||||||
public const SRC_VEHICLE_SPELL = 21; // npcId, spellId, null
|
|
||||||
public const SRC_SMART_EVENT = 22; // id, entryGuid, srcType
|
|
||||||
public const SRC_NPC_VENDOR = 23; // npcId, itemId, null
|
|
||||||
public const SRC_SPELL_PROC = 24; // null, spellId, null
|
|
||||||
// public const SRC_SPELL_TERRAIN_SWAP = 25; // - ❌ reserved for TC master
|
|
||||||
// public const SRC_SPELL_PHASE = 26; // - ❌ reserved for TC master
|
|
||||||
// public const SRC_SPELL_GRAVEYARD = 27; // - ❌ reserved for TC master
|
|
||||||
// public const SRC_SPELL_AREATRIGGER = 28; // - ❌ reserved for TC master
|
|
||||||
// public const SRC_SPELL_CONVERSATION_LINE = 29; // - ❌ reserved for TC master
|
|
||||||
public const SRC_AREATRIGGER_CLIENT = 30; // null, atId, null
|
|
||||||
// public const SRC_SPELL_TRAINER_SPELL = 31; // - ❌ reserved for TC master
|
|
||||||
// public const SRC_SPELL_OBJECT_VISIBILITY = 32; // - ❌ reserved for TC master
|
|
||||||
// public const SRC_SPELL_SPAWN_GROUP = 33; // - ❌ reserved for TC master
|
|
||||||
|
|
||||||
public const NONE = 0; // always true: NULL, NULL, NULL
|
|
||||||
public const AURA = 1; // aura is applied: spellId, effIdx, NULL
|
|
||||||
public const ITEM = 2; // owns item: itemId, count, includeBank?
|
|
||||||
public const ITEM_EQUIPPED = 3; // has item equipped: itemId, NULL, NULL
|
|
||||||
public const ZONEID = 4; // is in zone: areaId, NULL, NULL
|
|
||||||
public const REPUTATION_RANK = 5; // reputation status: factionId, rankMask, NULL
|
|
||||||
public const TEAM = 6; // is on team: teamId, NULL, NULL
|
|
||||||
public const SKILL = 7; // has skill: skillId, value, NULL
|
|
||||||
public const QUESTREWARDED = 8; // has finished quest: questId, NULL, NULL
|
|
||||||
public const QUESTTAKEN = 9; // has accepted quest: questId, NULL, NULL
|
|
||||||
public const DRUNKENSTATE = 10; // has drunken status: stateId, NULL, NULL
|
|
||||||
public const WORLD_STATE = 11; // world var == value: worldStateId, value, NULL
|
|
||||||
public const ACTIVE_EVENT = 12; // world event is active: eventId, NULL, NULL
|
|
||||||
public const INSTANCE_INFO = 13; // instance var == data: entry data, type
|
|
||||||
public const QUEST_NONE = 14; // never seen quest: questId, NULL, NULL
|
|
||||||
public const CHR_CLASS = 15; // belongs to classes: classMask, NULL, NULL
|
|
||||||
public const CHR_RACE = 16; // belongs to races: raceMask, NULL, NULL
|
|
||||||
public const ACHIEVEMENT = 17; // obtained achievement: achievementId, NULL, NULL
|
|
||||||
public const TITLE = 18; // obtained title: titleId, NULL, NULL
|
|
||||||
public const SPAWNMASK = 19; // spawnMask, NULL, NULL
|
|
||||||
public const GENDER = 20; // has gender: genderId, NULL, NULL
|
|
||||||
public const UNIT_STATE = 21; // unit has state: unitState, NULL, NULL
|
|
||||||
public const MAPID = 22; // is on map: mapId, NULL, NULL
|
|
||||||
public const AREAID = 23; // is in area: areaId, NULL, NULL
|
|
||||||
public const CREATURE_TYPE = 24; // creature is of type: creaturetypeId, NULL, NULL
|
|
||||||
public const SPELL = 25; // knows spell: spellId, NULL, NULL
|
|
||||||
public const PHASEMASK = 26; // is in phase: phaseMask, NULL, NULL
|
|
||||||
public const LEVEL = 27; // player level is..: level, comparator, NULL
|
|
||||||
public const QUEST_COMPLETE = 28; // has completed quest: questId, NULL, NULL
|
|
||||||
public const NEAR_CREATURE = 29; // is near creature: creatureId, dist, includeCorpse?
|
|
||||||
public const NEAR_GAMEOBJECT = 30; // is near gameObject: gameObjectId, dist, NULL
|
|
||||||
public const OBJECT_ENTRY_GUID = 31; // target is ???: objectType, id, guid
|
|
||||||
public const TYPE_MASK = 32; // target matches type: typeMask, NULL, NULL
|
|
||||||
public const RELATION_TO = 33; // Cond.Target, relation, NULL
|
|
||||||
public const REACTION_TO = 34; // Cond.Target, rankMask, NULL
|
|
||||||
public const DISTANCE_TO = 35; // distance to target Cond.Target, dist, comparator
|
|
||||||
public const ALIVE = 36; // target is alive: NULL, NULL, NULL
|
|
||||||
public const HP_VAL = 37; // targets absolute health: amount, comparator, NULL
|
|
||||||
public const HP_PCT = 38; // targets relative health: amount, comparator, NULL
|
|
||||||
public const REALM_ACHIEVEMENT = 39; // realmfirst was achieved: achievementId, NULL, NULL
|
|
||||||
public const IN_WATER = 40; // unit is swimming: NULL, NULL, NULL
|
|
||||||
// public const TERRAIN_SWAP = 41; // ❌ reserved for TC master
|
|
||||||
public const STAND_STATE = 42; // stateType, state, NULL
|
|
||||||
public const DAILY_QUEST_DONE = 43; // repeatable quest done: questId, NULL, NULL
|
|
||||||
public const CHARMED = 44; // unit is charmed: NULL, NULL, NULL
|
|
||||||
public const PET_TYPE = 45; // player has pet of type: petType, NULL, NULL
|
|
||||||
public const TAXI = 46; // player is on taxi: NULL, NULL, NULL
|
|
||||||
public const QUESTSTATE = 47; // questId, stateMask, NULL
|
|
||||||
public const QUEST_OBJECTIVE_PROGRESS = 48; // questId, objectiveIdx, count
|
|
||||||
public const DIFFICULTY_ID = 49; // map has difficulty id: difficulty, NULL, NULL
|
|
||||||
public const GAMEMASTER = 50; // player is GM: canBeGM?, NULL, NULL
|
|
||||||
// public const OBJECT_ENTRY_GUID_MASTER = 51; // ❌ reserved for TC master
|
|
||||||
// public const TYPE_MASK_MASTER = 52; // ❌ reserved for TC master
|
|
||||||
// public const BATTLE_PET_COUNT = 53; // ❌ reserved for TC master
|
|
||||||
// public const SCENARIO_STEP = 54; // ❌ reserved for TC master
|
|
||||||
// public const SCENE_IN_PROGRESS = 55; // ❌ reserved for TC master
|
|
||||||
// public const PLAYER_CONDITION = 56; // ❌ reserved for TC master
|
|
||||||
|
|
||||||
private const IDX_SRC_GROUP = 0;
|
|
||||||
private const IDX_SRC_ENTRY = 1;
|
|
||||||
private const IDX_SRC_ID = 2;
|
|
||||||
private const IDX_SRC_FN = 3;
|
|
||||||
|
|
||||||
private static $source = array( // [Group, Entry, Id, typeResolverFN]
|
|
||||||
self::SRC_NONE => [null, null, null, null],
|
|
||||||
self::SRC_CREATURE_LOOT_TEMPLATE => [Type::NPC, Type::ITEM, null, 'lootIdToNpc'],
|
|
||||||
self::SRC_DISENCHANT_LOOT_TEMPLATE => [Type::ITEM, Type::ITEM, null, 'disenchantIdToItem'],
|
|
||||||
self::SRC_FISHING_LOOT_TEMPLATE => [Type::ZONE, Type::ITEM, null, null],
|
|
||||||
self::SRC_GAMEOBJECT_LOOT_TEMPLATE => [Type::OBJECT, Type::ITEM, null, 'lootIdToGObject'],
|
|
||||||
self::SRC_ITEM_LOOT_TEMPLATE => [Type::ITEM, Type::ITEM, null, null],
|
|
||||||
self::SRC_MAIL_LOOT_TEMPLATE => [Type::QUEST, Type::ITEM, null, 'RewardTemplateToQuest'],
|
|
||||||
self::SRC_MILLING_LOOT_TEMPLATE => [Type::ITEM, Type::ITEM, null, null],
|
|
||||||
self::SRC_PICKPOCKETING_LOOT_TEMPLATE => [Type::NPC, Type::ITEM, null, 'PickpocketLootToNpc'],
|
|
||||||
self::SRC_PROSPECTING_LOOT_TEMPLATE => [Type::ITEM, Type::ITEM, null, null],
|
|
||||||
self::SRC_REFERENCE_LOOT_TEMPLATE => [null, Type::ITEM, null, null],
|
|
||||||
self::SRC_SKINNING_LOOT_TEMPLATE => [Type::NPC, Type::ITEM, null, 'SkinLootToNpc'],
|
|
||||||
self::SRC_SPELL_LOOT_TEMPLATE => [Type::SPELL, Type::ITEM, null, null],
|
|
||||||
self::SRC_SPELL_IMPLICIT_TARGET => [true, Type::SPELL, null, null],
|
|
||||||
self::SRC_GOSSIP_MENU => [true, true, null, null],
|
|
||||||
self::SRC_GOSSIP_MENU_OPTION => [true, true, null, null],
|
|
||||||
self::SRC_CREATURE_TEMPLATE_VEHICLE => [null, Type::NPC, null, null],
|
|
||||||
self::SRC_SPELL => [null, Type::SPELL, null, null],
|
|
||||||
self::SRC_SPELL_CLICK_EVENT => [Type::NPC, Type::SPELL, null, null],
|
|
||||||
self::SRC_QUEST_AVAILABLE => [null, Type::QUEST, null, null],
|
|
||||||
self::SRC_QUEST_SHOW_MARK => [null, Type::QUEST, null, null],
|
|
||||||
self::SRC_VEHICLE_SPELL => [Type::NPC, Type::SPELL, null, null],
|
|
||||||
self::SRC_SMART_EVENT => [true, true, true, null],
|
|
||||||
self::SRC_NPC_VENDOR => [Type::NPC, Type::ITEM, null, null],
|
|
||||||
self::SRC_SPELL_PROC => [null, Type::SPELL, null, null],
|
|
||||||
self::SRC_AREATRIGGER_CLIENT => [null, Type::AREATRIGGER, null, null]
|
|
||||||
);
|
|
||||||
|
|
||||||
private const IDX_CND_VAL1 = 0;
|
|
||||||
private const IDX_CND_VAL2 = 1;
|
|
||||||
private const IDX_CND_VAL3 = 2;
|
|
||||||
private const IDX_CND_FN = 3;
|
|
||||||
|
|
||||||
private static $conditions = array(// [Value1, Value2, Value3, handlerFn]
|
|
||||||
self::NONE => [null, null, null, null],
|
|
||||||
self::AURA => [Type::SPELL, null, null, null],
|
|
||||||
self::ITEM => [Type::ITEM, true, true, null],
|
|
||||||
self::ITEM_EQUIPPED => [Type::ITEM, null, null, null],
|
|
||||||
self::ZONEID => [Type::ZONE, null, null, null],
|
|
||||||
self::REPUTATION_RANK => [Type::FACTION, true, null, null],
|
|
||||||
self::TEAM => [true, null, null, 'factionToSide'],
|
|
||||||
self::SKILL => [Type::SKILL, true, null, null],
|
|
||||||
self::QUESTREWARDED => [Type::QUEST, null, null, null],
|
|
||||||
self::QUESTTAKEN => [Type::QUEST, null, null, null],
|
|
||||||
self::DRUNKENSTATE => [true, null, null, null],
|
|
||||||
self::WORLD_STATE => [true, true, null, null],
|
|
||||||
self::ACTIVE_EVENT => [Type::WORLDEVENT, null, null, null],
|
|
||||||
self::INSTANCE_INFO => [true, true, true, null],
|
|
||||||
self::QUEST_NONE => [Type::QUEST, null, null, null],
|
|
||||||
self::CHR_CLASS => [Type::CHR_CLASS, null, null, 'maskToBits'],
|
|
||||||
self::CHR_RACE => [Type::CHR_RACE, null, null, 'maskToBits'],
|
|
||||||
self::ACHIEVEMENT => [Type::ACHIEVEMENT, null, null, null],
|
|
||||||
self::TITLE => [Type::TITLE, null, null, null],
|
|
||||||
self::SPAWNMASK => [true, null, null, null],
|
|
||||||
self::GENDER => [true, null, null, null],
|
|
||||||
self::UNIT_STATE => [true, null, null, null],
|
|
||||||
self::MAPID => [true, true, null, 'mapToZone'],
|
|
||||||
self::AREAID => [Type::ZONE, null, null, null],
|
|
||||||
self::CREATURE_TYPE => [true, null, null, null],
|
|
||||||
self::SPELL => [Type::SPELL, null, null, null],
|
|
||||||
self::PHASEMASK => [true, null, null, null],
|
|
||||||
self::LEVEL => [true, true, null, null],
|
|
||||||
self::QUEST_COMPLETE => [Type::QUEST, null, null, null],
|
|
||||||
self::NEAR_CREATURE => [Type::NPC, true, true, null],
|
|
||||||
self::NEAR_GAMEOBJECT => [Type::OBJECT, true, true, null],
|
|
||||||
self::OBJECT_ENTRY_GUID => [true, true, true, 'typeidToId'],
|
|
||||||
self::TYPE_MASK => [true, null, null, null],
|
|
||||||
self::RELATION_TO => [true, true, null, null],
|
|
||||||
self::REACTION_TO => [true, true, null, null],
|
|
||||||
self::DISTANCE_TO => [true, true, true, null],
|
|
||||||
self::ALIVE => [null, null, null, null],
|
|
||||||
self::HP_VAL => [true, true, null, null],
|
|
||||||
self::HP_PCT => [true, true, null, null],
|
|
||||||
self::REALM_ACHIEVEMENT => [Type::ACHIEVEMENT, null, null, null],
|
|
||||||
self::IN_WATER => [null, null, null, null],
|
|
||||||
self::STAND_STATE => [true, true, null, null],
|
|
||||||
self::DAILY_QUEST_DONE => [Type::QUEST, null, null, null],
|
|
||||||
self::CHARMED => [null, null, null, null],
|
|
||||||
self::PET_TYPE => [true, null, null, null],
|
|
||||||
self::TAXI => [null, null, null, null],
|
|
||||||
self::QUESTSTATE => [Type::QUEST, true, null, null],
|
|
||||||
self::QUEST_OBJECTIVE_PROGRESS => [Type::QUEST, true, true, null],
|
|
||||||
self::DIFFICULTY_ID => [true, null, null, null],
|
|
||||||
self::GAMEMASTER => [true, null, null, null]
|
|
||||||
);
|
|
||||||
|
|
||||||
private $jsGlobals = [];
|
|
||||||
private $rows = [];
|
|
||||||
private $result = [];
|
|
||||||
private $resultExtra = [];
|
|
||||||
|
|
||||||
|
|
||||||
/******/
|
|
||||||
/* IN */
|
|
||||||
/******/
|
|
||||||
|
|
||||||
public function getBySourceEntry(int $entry, int ...$srcType) : self
|
|
||||||
{
|
|
||||||
$this->rows = array_merge($this->rows, DB::World()->select(
|
|
||||||
'SELECT `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `SourceId`, `ElseGroup`,
|
|
||||||
`ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`
|
|
||||||
FROM conditions
|
|
||||||
WHERE `SourceTypeOrReferenceId` IN (?a) AND `SourceEntry` = ?d
|
|
||||||
ORDER BY `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `ElseGroup` ASC',
|
|
||||||
$srcType, $entry
|
|
||||||
));
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBySourceGroup(int $group, int ...$srcType) : self
|
|
||||||
{
|
|
||||||
$this->rows = array_merge($this->rows, DB::World()->select(
|
|
||||||
'SELECT `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `SourceId`, `ElseGroup`,
|
|
||||||
`ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`
|
|
||||||
FROM conditions
|
|
||||||
WHERE `SourceTypeOrReferenceId` IN (?a) AND `SourceGroup` = ?d
|
|
||||||
ORDER BY `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `ElseGroup` ASC',
|
|
||||||
$srcType, $group
|
|
||||||
));
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getByCondition(int $type, int $typeId/* , int ...$conditionIds */) : self
|
|
||||||
{
|
|
||||||
$lookups = []; // can only be in val1 for now
|
|
||||||
foreach (self::$conditions as $cId => [$cVal1, , , ])
|
|
||||||
if ($type === $cVal1 /* && (!$conditionIds || in_array($cId, $conditionIds)) */ )
|
|
||||||
{
|
|
||||||
if ($cId == self::CHR_CLASS || $cId == self::CHR_RACE)
|
|
||||||
$lookups[] = sprintf("(c2.`ConditionTypeOrReference` = %d AND (c2.`ConditionValue1` & %d) > 0)", $cId, 1 << ($typeId - 1));
|
|
||||||
else
|
|
||||||
$lookups[] = sprintf("(c2.`ConditionTypeOrReference` = %d AND c2.`ConditionValue1` = %d)", $cId, $typeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$lookups)
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
$this->rows = array_merge($this->rows, DB::World()->select(sprintf(
|
|
||||||
'SELECT c1.`SourceTypeOrReferenceId`, c1.`SourceEntry`, c1.`SourceGroup`, c1.`SourceId`, c1.`ElseGroup`,
|
|
||||||
c1.`ConditionTypeOrReference`, c1.`ConditionTarget`, c1.`ConditionValue1`, c1.`ConditionValue2`, c1.`ConditionValue3`, c1.`NegativeCondition`
|
|
||||||
FROM conditions c1
|
|
||||||
JOIN conditions c2 ON c1.SourceTypeOrReferenceId = c2.SourceTypeOrReferenceId AND c1.SourceEntry = c2.SourceEntry AND c1.SourceGroup = c2.SourceGroup AND c1.SourceId = c2.SourceId
|
|
||||||
WHERE %s
|
|
||||||
GROUP BY `SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`
|
|
||||||
ORDER BY `SourceTypeOrReferenceId`, `SourceEntry`, `SourceGroup`, `ElseGroup` ASC',
|
|
||||||
implode(' OR ', $lookups))
|
|
||||||
));
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addExternalCondition(int $srcType, string $groupKey, array $condition, bool $orGroup = false) : void
|
|
||||||
{
|
|
||||||
if (!isset(self::$source[$srcType]))
|
|
||||||
return;
|
|
||||||
|
|
||||||
[$cId, $cVal1, $cVal2, $cVal3] = array_pad($condition, 5, 0);
|
|
||||||
if (!isset(self::$conditions[abs($cId)]))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (substr_count($groupKey, ':') < 3)
|
|
||||||
$groupKey .= ':0'; // pad with missing srcEntry, SrcId, cndTarget to group key
|
|
||||||
|
|
||||||
if (!$this->prepareSource($srcType, ...explode(':', $groupKey)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($c = $this->prepareCondition($cId, $cVal1, $cVal2, $cVal3))
|
|
||||||
{
|
|
||||||
if ($orGroup)
|
|
||||||
$this->result[$srcType][$groupKey][] = [$c];
|
|
||||||
else if (!isset($this->result[$srcType][$groupKey][0]))
|
|
||||||
$this->result[$srcType][$groupKey][0] = [$c];
|
|
||||||
else
|
|
||||||
$this->result[$srcType][$groupKey][0][] = $c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*******/
|
|
||||||
/* OUT */
|
|
||||||
/*******/
|
|
||||||
|
|
||||||
public function toListviewTab(string $id = 'conditions', string $name = '') : array
|
|
||||||
{
|
|
||||||
if (!$this->result)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$out = [];
|
|
||||||
$nCnd = 0;
|
|
||||||
foreach ($this->result as $srcType => $srcData)
|
|
||||||
{
|
|
||||||
foreach ($srcData as $grpKey => $grpData)
|
|
||||||
{
|
|
||||||
if (!isset($this->resultExtra[$srcType][$grpKey]))
|
|
||||||
{
|
|
||||||
$nCnd++;
|
|
||||||
$out[$srcType][$grpKey] = $grpData;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$nCnd += count($this->resultExtra[$srcType][$grpKey]);
|
|
||||||
foreach ($this->resultExtra[$srcType][$grpKey] as $extraGrp)
|
|
||||||
$out[$srcType][$extraGrp] = $grpData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = "<script type=\"text/javascript\">\n" .
|
|
||||||
" var markup = ConditionList.createTab(".Util::toJSON($out).");\n" .
|
|
||||||
" Markup.printHtml(markup, 'tab-".$id."', { allow: Markup.CLASS_STAFF })\n" .
|
|
||||||
"</script>";
|
|
||||||
|
|
||||||
$tab = array(
|
|
||||||
'data' => $data,
|
|
||||||
'id' => $id,
|
|
||||||
'name' => ($name ?: '$LANG.tab_conditions') . '+" ('.$nCnd.')"'
|
|
||||||
);
|
|
||||||
|
|
||||||
return [null, $tab];
|
|
||||||
}
|
|
||||||
|
|
||||||
// $keyX params are string(ref to lv column) or int(fixed value)
|
|
||||||
public function toListviewColumn(array &$lvRows, ?array &$extraCols = [], $keyGroup = 'id', $keyEntry = 0, $keyId = 0) : bool
|
|
||||||
{
|
|
||||||
if (!$this->result)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$success = false;
|
|
||||||
foreach ($lvRows as &$row)
|
|
||||||
{
|
|
||||||
$srcKey = implode(':', array(
|
|
||||||
is_string($keyGroup) ? ($row[$keyGroup] ?? 0) : $keyGroup,
|
|
||||||
is_string($keyEntry) ? ($row[$keyEntry] ?? 0) : $keyEntry,
|
|
||||||
is_string($keyId) ? ($row[$keyId] ?? 0) : $keyId,
|
|
||||||
'' // cndTarget - 0 / 1
|
|
||||||
));
|
|
||||||
|
|
||||||
foreach ($this->result as $cndData)
|
|
||||||
{
|
|
||||||
if (isset($cndData[$srcKey.'0']))
|
|
||||||
{
|
|
||||||
$row['condition'][self::SRC_NONE][$srcKey.'0'] = $cndData[$srcKey.'0'];
|
|
||||||
$success = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($cndData[$srcKey.'1']))
|
|
||||||
{
|
|
||||||
$row['condition'][self::SRC_NONE][$srcKey.'1'] = $cndData[$srcKey.'1'];
|
|
||||||
$success = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($success)
|
|
||||||
$extraCols[] = '$Listview.extraCols.condition';
|
|
||||||
|
|
||||||
return $success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJsGlobals() : array
|
|
||||||
{
|
|
||||||
return $this->jsGlobals;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********/
|
|
||||||
/* Other */
|
|
||||||
/*********/
|
|
||||||
|
|
||||||
public static function lootTableToConditionSource(string $lootTable) : int
|
|
||||||
{
|
|
||||||
switch ($lootTable)
|
|
||||||
{
|
|
||||||
case LOOT_FISHING: return self::SRC_FISHING_LOOT_TEMPLATE;
|
|
||||||
case LOOT_CREATURE: return self::SRC_CREATURE_LOOT_TEMPLATE;
|
|
||||||
case LOOT_GAMEOBJECT: return self::SRC_GAMEOBJECT_LOOT_TEMPLATE;
|
|
||||||
case LOOT_ITEM: return self::SRC_ITEM_LOOT_TEMPLATE;
|
|
||||||
case LOOT_DISENCHANT: return self::SRC_DISENCHANT_LOOT_TEMPLATE;
|
|
||||||
case LOOT_PROSPECTING: return self::SRC_PROSPECTING_LOOT_TEMPLATE;
|
|
||||||
case LOOT_MILLING: return self::SRC_MILLING_LOOT_TEMPLATE;
|
|
||||||
case LOOT_PICKPOCKET: return self::SRC_PICKPOCKETING_LOOT_TEMPLATE;
|
|
||||||
case LOOT_SKINNING: return self::SRC_SKINNING_LOOT_TEMPLATE;
|
|
||||||
case LOOT_MAIL: return self::SRC_MAIL_LOOT_TEMPLATE;
|
|
||||||
case LOOT_SPELL: return self::SRC_SPELL_LOOT_TEMPLATE;
|
|
||||||
case LOOT_REFERENCE: return self::SRC_REFERENCE_LOOT_TEMPLATE;
|
|
||||||
default: return self::SRC_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function extendListviewRow(array &$lvRow, int $srcType, int $groupKey, array $condition) : bool
|
|
||||||
{
|
|
||||||
if (!isset(self::$source[$srcType]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
[$cId, $cVal1, $cVal2, $cVal3] = array_pad($condition, 5, 0);
|
|
||||||
if (!isset(self::$conditions[abs($cId)]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (substr_count($groupKey, ':') < 3)
|
|
||||||
$groupKey .= ':0'; // pad with missing srcEntry, SrcId, cndTarget to group key
|
|
||||||
|
|
||||||
if ($c = (new self())->prepareCondition($cId, $cVal1, $cVal2, $cVal3))
|
|
||||||
$lvRow['condition'][$srcType][$groupKey][] = [$c];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prepare() : bool
|
|
||||||
{
|
|
||||||
// itr over rows and prep data
|
|
||||||
if (!$this->rows)
|
|
||||||
return !empty($this->result); // respect previously added externalCnd
|
|
||||||
|
|
||||||
foreach ($this->rows as $r)
|
|
||||||
{
|
|
||||||
if (!isset(self::$source[$r['SourceTypeOrReferenceId']]))
|
|
||||||
{
|
|
||||||
trigger_error('Conditions: skipping condition with unknown SourceTypeOrReferenceId #'.$r['SourceTypeOrReferenceId'], E_USER_WARNING);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset(self::$conditions[$r['ConditionTypeOrReference']]))
|
|
||||||
{
|
|
||||||
trigger_error('Conditions: skipping condition with unknown ConditionTypeOrReference #'.$r['ConditionTypeOrReference'], E_USER_WARNING);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$sType, $sGroup, $sEntry, $sId, $cTarget] = $this->prepareSource($r['SourceTypeOrReferenceId'], $r['SourceGroup'], $r['SourceEntry'], $r['SourceId'], $r['ConditionTarget']);
|
|
||||||
if ($sType === null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$cnd = $this->prepareCondition(
|
|
||||||
$r['NegativeCondition'] ? -$r['ConditionTypeOrReference'] : $r['ConditionTypeOrReference'],
|
|
||||||
$r['ConditionValue1'],
|
|
||||||
$r['ConditionValue2'],
|
|
||||||
$r['ConditionValue3']
|
|
||||||
);
|
|
||||||
if (!$cnd)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
$this->result[$r['SourceTypeOrReferenceId']] [$group] [$r['ElseGroup']] [] = $cnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function prepareSource(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : array
|
|
||||||
{
|
|
||||||
// only one entry in array expected
|
|
||||||
if ($fn = self::$source[$sType][self::IDX_SRC_FN])
|
|
||||||
if (!$this->$fn($sType, $sGroup, $sEntry, $sId, $cTarget))
|
|
||||||
return [null, null, null, null, null];
|
|
||||||
|
|
||||||
[$grp, $entry, $id, $_] = self::$source[$sType];
|
|
||||||
if (is_int($grp))
|
|
||||||
$this->jsGlobals[$grp][$sGroup] = $sGroup;
|
|
||||||
if (is_int($entry))
|
|
||||||
$this->jsGlobals[$entry][$sEntry] = $sEntry;
|
|
||||||
// Note: sourceId currently has no typed content
|
|
||||||
// if (is_int($id))
|
|
||||||
// $this->jsGlobals[$id][$sId] = $sId;
|
|
||||||
|
|
||||||
// more checks? not all sources can retarget
|
|
||||||
$cTarget = min(1, max(0, $cTarget));
|
|
||||||
|
|
||||||
return [$sType, $sGroup, $sEntry, $sId, $cTarget];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function prepareCondition($cId, $cVal1, $cVal2, $cVal3) : array
|
|
||||||
{
|
|
||||||
if ($fn = self::$conditions[abs($cId)][self::IDX_CND_FN])
|
|
||||||
if (!$this->$fn(abs($cId), $cVal1, $cVal2, $cVal3))
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$result = [$cId];
|
|
||||||
|
|
||||||
for ($i = 0; $i < 3; $i++)
|
|
||||||
{
|
|
||||||
$field = self::$conditions[abs($cId)][$i];
|
|
||||||
|
|
||||||
if (is_int($field))
|
|
||||||
$this->jsGlobals[$field][${'cVal'.($i+1)}] = ${'cVal'.($i+1)};
|
|
||||||
if ($field)
|
|
||||||
$result[] = ${'cVal'.($i+1)}; // variable amount of condition values
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function factionToSide($cndId, &$cVal1, $cVal2, $cVal3) : bool
|
|
||||||
{
|
|
||||||
if ($cVal1 == 469)
|
|
||||||
$cVal1 = SIDE_ALLIANCE;
|
|
||||||
else if ($cVal1 == 67)
|
|
||||||
$cVal1 = SIDE_HORDE;
|
|
||||||
else
|
|
||||||
$cVal1 = SIDE_BOTH;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function mapToZone($cndId, &$cVal1, &$cVal2, $cVal3) : bool
|
|
||||||
{
|
|
||||||
// use g_zone_categories id
|
|
||||||
if ($cVal1 == 530) // outland
|
|
||||||
$cVal1 = 8;
|
|
||||||
else if ($cVal1 == 571) // northrend
|
|
||||||
$cVal1 = 10;
|
|
||||||
else if ($cVal1 == 0 || $cVal1 == 1) // eastern kingdoms / kalimdor
|
|
||||||
; // cVal alrady correct - NOP
|
|
||||||
else if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?d AND `parentArea` = 0 AND (`cuFlags` & ?d) = 0', $cVal1, CUSTOM_EXCLUDE_FOR_LISTVIEW))
|
|
||||||
{
|
|
||||||
// remap for instanced area - do not use List (pointless overhead)
|
|
||||||
$this->jsGlobals[Type::ZONE][$id] = $id;
|
|
||||||
$cVal2 = $id;
|
|
||||||
$cVal1 = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('Conditions - CONDITION_MAPID has invalid mapId #'.$cVal1, E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function maskToBits($cndId, &$cVal1, $cVal2, $cVal3) : bool
|
|
||||||
{
|
|
||||||
if ($cndId == self::CHR_CLASS)
|
|
||||||
{
|
|
||||||
$cVal1 &= ChrClass::MASK_ALL;
|
|
||||||
foreach (Util::mask2bits($cVal1, 1) as $cId)
|
|
||||||
$this->jsGlobals[Type::CHR_CLASS][$cId] = $cId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($cndId == self::CHR_RACE)
|
|
||||||
{
|
|
||||||
$cVal1 &= ChrRace::MASK_ALL;
|
|
||||||
foreach (Util::mask2bits($cVal1, 1) as $rId)
|
|
||||||
$this->jsGlobals[Type::CHR_RACE][$rId] = $rId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function typeidToId($cndId, $cVal1, &$cVal2, &$cVal3) : bool
|
|
||||||
{
|
|
||||||
if ($cVal1 == self::TYPEID_UNIT)
|
|
||||||
{
|
|
||||||
if ($cVal3 && ($_ = DB::Aowow()->selectCell('SELECT `typeId` FROM ?_spawns WHERE `type` = ?d AND `guid` = ?d', Type::NPC, $cVal3)))
|
|
||||||
$cVal2 = intVal($_);
|
|
||||||
|
|
||||||
if ($cVal2)
|
|
||||||
$this->jsGlobals[Type::NPC][$cVal2] = $cVal2;
|
|
||||||
}
|
|
||||||
else if ($cVal1 == self::TYPEID_GAMEOBJECT)
|
|
||||||
{
|
|
||||||
if ($cVal3 && ($_ = DB::Aowow()->selectCell('SELECT `typeId` FROM ?_spawns WHERE `type` = ?d AND `guid` = ?d', Type::OBJECT, $cVal3)))
|
|
||||||
$cVal2 = intVal($_);
|
|
||||||
|
|
||||||
if ($cVal2)
|
|
||||||
$this->jsGlobals[Type::OBJECT][$cVal2] = $cVal2;
|
|
||||||
}
|
|
||||||
else // Player or Corpse .. no guid
|
|
||||||
$cVal2 = $cVal3 = 0;
|
|
||||||
|
|
||||||
// maybe prepare other types?
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lootIdToNpc(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : bool
|
|
||||||
{
|
|
||||||
if (!$sGroup)
|
|
||||||
{
|
|
||||||
trigger_error('Conditions::lootToNpc - skipping reference to creature_loot_template entry 0', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($npcs = DB::Aowow()->selectCol('SELECT `id` FROM ?_creature WHERE `lootId` = ?d', $sGroup))
|
|
||||||
{
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
foreach ($npcs as $npcId)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$npcId] = $npcId;
|
|
||||||
$this->resultExtra[$sType][$group][] = $npcId . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Conditions::lootToNpc - creature_loot_template #'.$sGroup.' unreferenced?', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function disenchantIdToItem(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : bool
|
|
||||||
{
|
|
||||||
if (!$sGroup)
|
|
||||||
{
|
|
||||||
trigger_error('Conditions::disenchantIdToItem - skipping reference to disenchant_loot_template entry 0', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($items = DB::Aowow()->selectCol('SELECT `id` FROM ?_items WHERE `disenchantId` = ?d', $sGroup))
|
|
||||||
{
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
foreach ($items as $itemId)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::ITEM][$itemId] = $itemId;
|
|
||||||
$this->resultExtra[$sType][$group][] = $itemId . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Conditions::disenchantIdToItem - disenchant_loot_template #'.$sGroup.' unreferenced?', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lootIdToGObject(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : bool
|
|
||||||
{
|
|
||||||
if (!$sGroup)
|
|
||||||
{
|
|
||||||
trigger_error('Conditions::lootIdToGObject - skipping reference to gameobject_loot_template entry 0', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($gos = DB::Aowow()->selectCol('SELECT `id` FROM ?_objects WHERE `lootId` = ?d', $sGroup))
|
|
||||||
{
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
foreach ($gos as $goId)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::OBJECT][$goId] = $goId;
|
|
||||||
$this->resultExtra[$sType][$group][] = $goId . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Conditions::lootIdToGObject - gameobject_loot_template #'.$sGroup.' unreferenced?', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function RewardTemplateToQuest(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : bool
|
|
||||||
{
|
|
||||||
if (!$sGroup)
|
|
||||||
{
|
|
||||||
trigger_error('Conditions::RewardTemplateToQuest - skipping reference to mail_loot_template entry 0', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($quests = DB::Aowow()->selectCol('SELECT `id` FROM ?_quests WHERE `rewardMailTemplateId` = ?d', $sGroup))
|
|
||||||
{
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
foreach ($quests as $questId)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::QUEST][$questId] = $questId;
|
|
||||||
$this->resultExtra[$sType][$group][] = $questId . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Conditions::RewardTemplateToQuest - mail_loot_template #'.$sGroup.' unreferenced?', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function PickpocketLootToNpc(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : bool
|
|
||||||
{
|
|
||||||
if (!$sGroup)
|
|
||||||
{
|
|
||||||
trigger_error('Conditions::PickpocketLootToNpc - skipping reference to pickpocketing_loot_template entry 0', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($npcs = DB::Aowow()->selectCol('SELECT `id` FROM ?_creature WHERE `pickpocketLootId` = ?d', $sGroup))
|
|
||||||
{
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
foreach ($npcs as $npcId)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$npcId] = $npcId;
|
|
||||||
$this->resultExtra[$sType][$group][] = $npcId . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Conditions::PickpocketLootToNpc - pickpocketing_loot_template #'.$sGroup.' unreferenced?', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function SkinLootToNpc(int $sType, int $sGroup, int $sEntry, int $sId, int $cTarget) : bool
|
|
||||||
{
|
|
||||||
if (!$sGroup)
|
|
||||||
{
|
|
||||||
trigger_error('Conditions::SkinLootToNpc - skipping reference to skinning_loot_template entry 0', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($npcs = DB::Aowow()->selectCol('SELECT `id` FROM ?_creature WHERE `skinLootId` = ?d', $sGroup))
|
|
||||||
{
|
|
||||||
$group = $sGroup . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
foreach ($npcs as $npcId)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$npcId] = $npcId;
|
|
||||||
$this->resultExtra[$sType][$group][] = $npcId . ':' . $sEntry . ':' . $sId . ':' . $cTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Conditions::SkinLootToNpc - skinning_loot_template #'.$sGroup.' unreferenced?', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,756 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
// TrinityCore - SmartAI
|
|
||||||
|
|
||||||
trait SmartHelper
|
|
||||||
{
|
|
||||||
private function resolveGuid(int $type, int $guid) : ?int
|
|
||||||
{
|
|
||||||
if ($_ = DB::Aowow()->selectCell('SELECT `typeId` FROM ?_spawns WHERE `type` = ?d AND `guid` = ?d', $type, $guid))
|
|
||||||
return $_;
|
|
||||||
|
|
||||||
trigger_error('SmartAI::resolveGuid - failed to resolve guid '.$guid.' of type '.$type, E_USER_WARNING);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function numRange(int $min, int $max, bool $isTime) : string
|
|
||||||
{
|
|
||||||
if (!$min && !$max)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
$str = $isTime ? Util::formatTime($min, true) : $min;
|
|
||||||
if ($max > $min)
|
|
||||||
$str .= ' – '.($isTime ? Util::formatTime($max, true) : $max);
|
|
||||||
|
|
||||||
return $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatTime(int $time, int $_, bool $isMilliSec) : string
|
|
||||||
{
|
|
||||||
if (!$time)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return Util::formatTime($time * ($isMilliSec ? 1 : 1000), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function castFlags(int $flags) : string
|
|
||||||
{
|
|
||||||
$cf = [];
|
|
||||||
for ($i = 1; $i <= SmartAI::CAST_FLAG_COMBAT_MOVE; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::smartAI('castFlags', $i)))
|
|
||||||
$cf[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($cf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function npcFlags(int $flags) : string
|
|
||||||
{
|
|
||||||
$nf = [];
|
|
||||||
for ($i = 1; $i <= NPC_FLAG_MAILBOX; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::npc('npcFlags', $i)))
|
|
||||||
$nf[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($nf ?: [Lang::smartAI('empty')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function dynFlags(int $flags) : string
|
|
||||||
{
|
|
||||||
$df = [];
|
|
||||||
for ($i = 1; $i <= UNIT_DYNFLAG_TAPPED_BY_ALL_THREAT_LIST; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::unit('dynFlags', $i)))
|
|
||||||
$df[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($df ?: [Lang::smartAI('empty')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function goFlags(int $flags) : string
|
|
||||||
{
|
|
||||||
$gf = [];
|
|
||||||
for ($i = 1; $i <= GO_FLAG_DESTROYED; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::gameObject('goFlags', $i)))
|
|
||||||
$gf[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($gf ?: [Lang::smartAI('empty')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function spawnFlags(int $flags) : string
|
|
||||||
{
|
|
||||||
$sf = [];
|
|
||||||
for ($i = 1; $i <= SmartAI::SPAWN_FLAG_NOSAVE_RESPAWN; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::smartAI('spawnFlags', $i)))
|
|
||||||
$sf[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($sf ?: [Lang::smartAI('empty')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function unitFlags(int $flags, int $flags2) : string
|
|
||||||
{
|
|
||||||
$field = $flags2 ? 'flags2' : 'flags';
|
|
||||||
$max = $flags2 ? UNIT_FLAG2_ALLOW_CHEAT_SPELLS : UNIT_FLAG_UNK_31;
|
|
||||||
$uf = [];
|
|
||||||
|
|
||||||
for ($i = 1; $i <= $max; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::unit($field, $i)))
|
|
||||||
$uf[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($uf ?: [Lang::smartAI('empty')]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function unitFieldBytes1(int $flags, int $idx) : string
|
|
||||||
{
|
|
||||||
switch ($idx)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
case 3:
|
|
||||||
return Lang::unit('bytes1', 'bytesIdx', $idx).Lang::main('colon').(Lang::unit('bytes1', $idx, $flags) ?? Lang::unit('bytes1', 'valueUNK', [$flags, $idx]));
|
|
||||||
case 2:
|
|
||||||
$buff = [];
|
|
||||||
for ($i = 1; $i <= 0x10; $i <<= 1)
|
|
||||||
if (($flags & $i) && ($x = Lang::unit('bytes1', $idx, $flags)))
|
|
||||||
$buff[] = $x;
|
|
||||||
|
|
||||||
return Lang::unit('bytes1', 'bytesIdx', $idx).Lang::main('colon').($buff ? Lang::concat($buff) : Lang::unit('bytes1', 'valueUNK', [$flags, $idx]));
|
|
||||||
default:
|
|
||||||
return Lang::unit('bytes1', 'idxUNK', [$idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function summonType(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('summonTypes', $x) ?? Lang::smartAI('summonType', 'summonTypeUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function sheathState(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('sheaths', $x) ?? Lang::smartAI('sheathUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function aiTemplate(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('aiTpl', $x) ?? Lang::smartAI('aiTplUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function reactState(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('reactStates', $x) ?? Lang::smartAI('reactStateUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function powerType(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::spell('powerTypes', $x) ?? Lang::smartAI('powerTypeUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hostilityMode(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('hostilityModes', $x) ?? Lang::smartAI('hostilityModeUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function motionType(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('motionTypes', $x) ?? Lang::smartAI('motionTypeUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function lootState(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('lootStates', $x) ?? Lang::smartAI('lootStateUNK', [$x]);
|
|
||||||
}
|
|
||||||
private function weatherState(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::smartAI('weatherStates', $x) ?? Lang::smartAI('weatherStateUNK', [$x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function magicSchool(int $x) : string
|
|
||||||
{
|
|
||||||
return Lang::getMagicSchools($x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SmartAI
|
|
||||||
{
|
|
||||||
public const SRC_TYPE_CREATURE = 0;
|
|
||||||
public const SRC_TYPE_OBJECT = 1;
|
|
||||||
public const SRC_TYPE_AREATRIGGER = 2;
|
|
||||||
public const SRC_TYPE_ACTIONLIST = 9;
|
|
||||||
|
|
||||||
public const CAST_FLAG_INTERRUPT_PREV = 0x01; // Interrupt any spell casting
|
|
||||||
public const CAST_FLAG_TRIGGERED = 0x02; // Triggered (this makes spell cost zero mana and have no cast time)
|
|
||||||
// public const CAST_FORCE_CAST = 0x04; // Forces cast even if creature is out of mana or out of range
|
|
||||||
// public const CAST_NO_MELEE_IF_OOM = 0x08; // Prevents creature from entering melee if out of mana or out of range
|
|
||||||
// public const CAST_FORCE_TARGET_SELF = 0x10; // the target to cast this spell on itself
|
|
||||||
public const CAST_FLAG_AURA_MISSING = 0x20; // Only casts the spell if the target does not have an aura from the spell
|
|
||||||
public const CAST_FLAG_COMBAT_MOVE = 0x40; // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS
|
|
||||||
|
|
||||||
public const REACT_PASSIVE = 0;
|
|
||||||
public const REACT_DEFENSIVE = 1;
|
|
||||||
public const REACT_AGGRESSIVE = 2;
|
|
||||||
public const REACT_ASSIST = 3;
|
|
||||||
|
|
||||||
public const SUMMON_TIMED_OR_DEAD_DESPAWN = 1;
|
|
||||||
public const SUMMON_TIMED_OR_CORPSE_DESPAWN = 2;
|
|
||||||
public const SUMMON_TIMED_DESPAWN = 3;
|
|
||||||
public const SUMMON_TIMED_DESPAWN_OOC = 4;
|
|
||||||
public const SUMMON_CORPSE_DESPAWN = 5;
|
|
||||||
public const SUMMON_CORPSE_TIMED_DESPAWN = 6;
|
|
||||||
public const SUMMON_DEAD_DESPAWN = 7;
|
|
||||||
public const SUMMON_MANUAL_DESPAWN = 8;
|
|
||||||
|
|
||||||
public const TEMPLATE_BASIC = 0; //
|
|
||||||
public const TEMPLATE_CASTER = 1; // +JOIN: target_param1 as castFlag
|
|
||||||
public const TEMPLATE_TURRET = 2; // +JOIN: target_param1 as castflag
|
|
||||||
public const TEMPLATE_PASSIVE = 3; //
|
|
||||||
public const TEMPLATE_CAGED_GO_PART = 4; //
|
|
||||||
public const TEMPLATE_CAGED_NPC_PART = 5; //
|
|
||||||
|
|
||||||
public const SPAWN_FLAG_NONE = 0x00;
|
|
||||||
public const SPAWN_FLAG_IGNORE_RESPAWN = 0x01; // onSpawnIn - ignore & reset respawn timer
|
|
||||||
public const SPAWN_FLAG_FORCE_SPAWN = 0x02; // onSpawnIn - force additional spawn if already in world
|
|
||||||
public const SPAWN_FLAG_NOSAVE_RESPAWN = 0x04; // onDespawn - remove respawn time
|
|
||||||
|
|
||||||
private array $jsGlobals = [];
|
|
||||||
private array $rawData = [];
|
|
||||||
private array $result = [];
|
|
||||||
private array $tabs = [];
|
|
||||||
private array $itr = [];
|
|
||||||
|
|
||||||
private array $quotes = [];
|
|
||||||
|
|
||||||
// misc data
|
|
||||||
public readonly int $baseEntry; // I'm a timed action list belonging to this entry
|
|
||||||
public readonly string $title; // title appendix for the [toggle]
|
|
||||||
public readonly int $teleportTargetArea; // precalculated areaId so we don't have to look it up right now
|
|
||||||
|
|
||||||
public function __construct(public readonly int $srcType = 0, public readonly int $entry = 0, array $miscData = [])
|
|
||||||
{
|
|
||||||
$this->baseEntry = $miscData['baseEntry'] ?? 0;
|
|
||||||
$this->title = $miscData['title'] ?? '';
|
|
||||||
$this->teleportTargetArea = $miscData['teleportTargetArea'] ?? 0;
|
|
||||||
|
|
||||||
$raw = DB::World()->select(
|
|
||||||
'SELECT `id`, `link`,
|
|
||||||
`event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`,
|
|
||||||
`action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`,
|
|
||||||
`target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`
|
|
||||||
FROM smart_scripts
|
|
||||||
WHERE `entryorguid` = ?d AND `source_type` = ?d
|
|
||||||
ORDER BY `id` ASC',
|
|
||||||
$this->entry, $this->srcType);
|
|
||||||
|
|
||||||
foreach ($raw as $r)
|
|
||||||
{
|
|
||||||
$this->rawData[$r['id']] = array(
|
|
||||||
'id' => $r['id'],
|
|
||||||
'link' => $r['link'],
|
|
||||||
'event' => new SmartEvent($r['id'], $r['event_type'], $r['event_phase_mask'], $r['event_chance'], $r['event_flags'], [$r['event_param1'], $r['event_param2'], $r['event_param3'], $r['event_param4'], $r['event_param5']], $this),
|
|
||||||
'action' => new SmartAction($r['id'], $r['action_type'], [$r['action_param1'], $r['action_param2'], $r['action_param3'], $r['action_param4'], $r['action_param5'], $r['action_param6']], $this),
|
|
||||||
'target' => new SmartTarget($r['id'], $r['target_type'], [$r['target_param1'], $r['target_param2'], $r['target_param3'], $r['target_param4']], [$r['target_x'], $r['target_y'], $r['target_z'], $r['target_o']], $this)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************/
|
|
||||||
/* Lookups by action */
|
|
||||||
/*********************/
|
|
||||||
|
|
||||||
public static function getOwnerOfNPCSummon(int $npcId, int $typeFilter = 0) : array
|
|
||||||
{
|
|
||||||
if ($npcId <= 0)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_SUMMON_CREATURE => [1 => $npcId],
|
|
||||||
SmartAction::ACTION_MOUNT_TO_ENTRY_OR_MODEL => [1 => $npcId]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($npcGuids = DB::Aowow()->selectCol('SELECT `guid` FROM ?_spawns WHERE `type` = ?d AND `typeId` = ?d', Type::NPC, $npcId))
|
|
||||||
if ($groups = DB::World()->selectCol('SELECT `groupId` FROM spawn_group WHERE `spawnType` = 0 AND `spawnId` IN (?a)', $npcGuids))
|
|
||||||
foreach ($groups as $g)
|
|
||||||
$lookup[SmartAction::ACTION_SPAWN_SPAWNGROUP][1] = $g;
|
|
||||||
|
|
||||||
$result = self::getActionOwner($lookup, $typeFilter);
|
|
||||||
|
|
||||||
// can skip lookups for SmartAction::ACTION_SUMMON_CREATURE_GROUP as creature_summon_groups already contains summoner info
|
|
||||||
if ($sgs = DB::World()->select('SELECT `summonerType` AS "0", `summonerId` AS "1" FROM creature_summon_groups WHERE `entry` = ?d', $npcId))
|
|
||||||
foreach ($sgs as [$type, $typeId])
|
|
||||||
$result[$type][] = $typeId;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getOwnerOfObjectSummon(int $objectId, int $typeFilter = 0) : array
|
|
||||||
{
|
|
||||||
if ($objectId <= 0)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_SUMMON_GO => [1 => $objectId]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($objGuids = DB::Aowow()->selectCol('SELECT `guid` FROM ?_spawns WHERE `type` = ?d AND `typeId` = ?d', Type::OBJECT, $objectId))
|
|
||||||
if ($groups = DB::World()->selectCol('SELECT `groupId` FROM spawn_group WHERE `spawnType` = 1 AND `spawnId` IN (?a)', $objGuids))
|
|
||||||
foreach ($groups as $g)
|
|
||||||
$lookup[SmartAction::ACTION_SPAWN_SPAWNGROUP][1] = $g;
|
|
||||||
|
|
||||||
return self::getActionOwner($lookup, $typeFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getOwnerOfSpellCast(int $spellId, int $typeFilter = 0) : array
|
|
||||||
{
|
|
||||||
if ($spellId <= 0)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_CAST => [1 => $spellId],
|
|
||||||
SmartAction::ACTION_ADD_AURA => [1 => $spellId],
|
|
||||||
SmartAction::ACTION_SELF_CAST => [1 => $spellId],
|
|
||||||
SmartAction::ACTION_CROSS_CAST => [1 => $spellId],
|
|
||||||
SmartAction::ACTION_INVOKER_CAST => [1 => $spellId]
|
|
||||||
);
|
|
||||||
|
|
||||||
return self::getActionOwner($lookup, $typeFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getOwnerOfSoundPlayed(int $soundId, int $typeFilter = 0) : array
|
|
||||||
{
|
|
||||||
if ($soundId <= 0)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_SOUND => [1 => $soundId]
|
|
||||||
);
|
|
||||||
|
|
||||||
return self::getActionOwner($lookup, $typeFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup: SmartActionId => [[paramIdx => value], ...]
|
|
||||||
private static function getActionOwner(array $lookup, int $typeFilter = 0) : array
|
|
||||||
{
|
|
||||||
$qParts = [];
|
|
||||||
$result = [];
|
|
||||||
$genFilter = $talFilter = [];
|
|
||||||
switch ($typeFilter)
|
|
||||||
{
|
|
||||||
case Type::NPC:
|
|
||||||
$genFilter = [self::SRC_TYPE_CREATURE, self::SRC_TYPE_ACTIONLIST];
|
|
||||||
$talFilter = [self::SRC_TYPE_CREATURE];
|
|
||||||
break;
|
|
||||||
case Type::OBJECT:
|
|
||||||
$genFilter = [self::SRC_TYPE_OBJECT, self::SRC_TYPE_ACTIONLIST];
|
|
||||||
$talFilter = [self::SRC_TYPE_OBJECT];
|
|
||||||
break;
|
|
||||||
case Type::AREATRIGGER:
|
|
||||||
$genFilter = [self::SRC_TYPE_AREATRIGGER, self::SRC_TYPE_ACTIONLIST];
|
|
||||||
$talFilter = [self::SRC_TYPE_AREATRIGGER];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($lookup as $action => $params)
|
|
||||||
{
|
|
||||||
$aq = '(`action_type` = '.(int)$action.' AND (';
|
|
||||||
$pq = [];
|
|
||||||
foreach ($params as $idx => $p)
|
|
||||||
$pq[] = '`action_param'.(int)$idx.'` = '.(int)$p;
|
|
||||||
|
|
||||||
if ($pq)
|
|
||||||
$qParts[] = $aq.implode(' OR ', $pq).'))';
|
|
||||||
}
|
|
||||||
|
|
||||||
$smartS = DB::World()->select(sprintf('SELECT `source_type` AS "0", `entryOrGUID` AS "1" FROM smart_scripts WHERE (%s){ AND `source_type` IN (?a)}', $qParts ? implode(' OR ', $qParts) : '0'), $genFilter ?: DBSIMPLE_SKIP);
|
|
||||||
|
|
||||||
// filter for TAL shenanigans
|
|
||||||
if ($smartTAL = array_filter($smartS, fn($x) => $x[0] == self::SRC_TYPE_ACTIONLIST))
|
|
||||||
{
|
|
||||||
$smartS = array_diff_key($smartS, $smartTAL);
|
|
||||||
|
|
||||||
$q = [];
|
|
||||||
foreach ($smartTAL as [, $eog])
|
|
||||||
{
|
|
||||||
// SmartAction::ACTION_CALL_TIMED_ACTIONLIST
|
|
||||||
$q[] = '`action_type` = '.SmartAction::ACTION_CALL_TIMED_ACTIONLIST.' AND `action_param1` = '.$eog;
|
|
||||||
|
|
||||||
// SmartAction::ACTION_CALL_RANDOM_TIMED_ACTIONLIST
|
|
||||||
$q[] = '`action_type` = '.SmartAction::ACTION_CALL_RANDOM_TIMED_ACTIONLIST.' AND (`action_param1` = '.$eog.' OR `action_param2` = '.$eog.' OR `action_param3` = '.$eog.' OR `action_param4` = '.$eog.' OR `action_param5` = '.$eog.')';
|
|
||||||
|
|
||||||
// SmartAction::ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST
|
|
||||||
$q[] = '`action_type` = '.SmartAction::ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST.' AND `action_param1` <= '.$eog.' AND `action_param2` >= '.$eog;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_ = DB::World()->select(sprintf('SELECT `source_type` AS "0", `entryOrGUID` AS "1" FROM smart_scripts WHERE ((%s)){ AND `source_type` IN (?a)}', $q ? implode(') OR (', $q) : '0'), $talFilter ?: DBSIMPLE_SKIP))
|
|
||||||
$smartS = array_merge($smartS, $_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter guids for entries
|
|
||||||
if ($smartG = array_filter($smartS, fn($x) => $x[1] < 0))
|
|
||||||
{
|
|
||||||
$smartS = array_diff_key($smartS, $smartG);
|
|
||||||
|
|
||||||
$q = [];
|
|
||||||
foreach ($smartG as [$st, $eog])
|
|
||||||
{
|
|
||||||
if ($st == self::SRC_TYPE_CREATURE)
|
|
||||||
$q[] = '`type` = '.Type::NPC.' AND `guid` = '.-$eog;
|
|
||||||
else if ($st == self::SRC_TYPE_OBJECT)
|
|
||||||
$q[] = '`type` = '.Type::OBJECT.' AND `guid` = '.-$eog;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($q)
|
|
||||||
{
|
|
||||||
$owner = DB::Aowow()->select(sprintf('SELECT `type`, `typeId` FROM ?_spawns WHERE (%s)', implode(') OR (', $q)));
|
|
||||||
foreach ($owner as $o)
|
|
||||||
$result[$o['type']][] = $o['typeId'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($smartS as [$st, $eog])
|
|
||||||
{
|
|
||||||
if ($st == self::SRC_TYPE_CREATURE)
|
|
||||||
$result[Type::NPC][] = $eog;
|
|
||||||
else if ($st == self::SRC_TYPE_OBJECT)
|
|
||||||
$result[Type::OBJECT][] = $eog;
|
|
||||||
else if ($st == self::SRC_TYPE_AREATRIGGER)
|
|
||||||
$result[Type::AREATRIGGER][] = $eog;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/********************/
|
|
||||||
/* Lookups by owner */
|
|
||||||
/********************/
|
|
||||||
|
|
||||||
public static function getNPCSummonsForOwner(int $entry, int $srcType = self::SRC_TYPE_CREATURE) : array
|
|
||||||
{
|
|
||||||
// action => paramIdx with npcIds/spawnGoupIds
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_SUMMON_CREATURE => [1],
|
|
||||||
SmartAction::ACTION_MOUNT_TO_ENTRY_OR_MODEL => [1],
|
|
||||||
SmartAction::ACTION_SPAWN_SPAWNGROUP => [1]
|
|
||||||
);
|
|
||||||
|
|
||||||
$result = self::getOwnerAction($srcType, $entry, $lookup, $moreInfo);
|
|
||||||
|
|
||||||
// can skip lookups for SmartAction::ACTION_SUMMON_CREATURE_GROUP as creature_summon_groups already contains summoner info
|
|
||||||
if ($srcType == self::SRC_TYPE_CREATURE || $srcType == self::SRC_TYPE_OBJECT)
|
|
||||||
{
|
|
||||||
$st = $srcType == self::SRC_TYPE_CREATURE ? SUMMONER_TYPE_CREATURE : SUMMONER_TYPE_GAMEOBJECT;
|
|
||||||
if ($csg = DB::World()->selectCol('SELECT `entry` FROM creature_summon_groups WHERE `summonerType` = ?d AND `summonerId` = ?d', $st, $entry))
|
|
||||||
$result = array_merge($result, $csg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($moreInfo[SmartAction::ACTION_SPAWN_SPAWNGROUP]))
|
|
||||||
{
|
|
||||||
$grp = $moreInfo[SmartAction::ACTION_SPAWN_SPAWNGROUP];
|
|
||||||
if ($sgs = DB::World()->selectCol('SELECT `spawnId` FROM spawn_group WHERE `spawnType` = ?d AND `groupId` IN (?a)', SUMMONER_TYPE_CREATURE, $grp))
|
|
||||||
if ($ids = DB::Aowow()->selectCol('SELECT DISTINCT `typeId` FROM ?_spawns WHERE `type` = ?d AND `guid` IN (?a)', Type::NPC, $sgs))
|
|
||||||
$result = array_merge($result, $ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getObjectSummonsForOwner(int $entry, int $srcType = self::SRC_TYPE_CREATURE) : array
|
|
||||||
{
|
|
||||||
// action => paramIdx with gobIds/spawnGoupIds
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_SUMMON_GO => [1],
|
|
||||||
SmartAction::ACTION_SPAWN_SPAWNGROUP => [1]
|
|
||||||
);
|
|
||||||
|
|
||||||
$result = self::getOwnerAction($srcType, $entry, $lookup, $moreInfo);
|
|
||||||
|
|
||||||
if (!empty($moreInfo[SmartAction::ACTION_SPAWN_SPAWNGROUP]))
|
|
||||||
{
|
|
||||||
$grp = $moreInfo[SmartAction::ACTION_SPAWN_SPAWNGROUP];
|
|
||||||
if ($sgs = DB::World()->selectCol('SELECT `spawnId` FROM spawn_group WHERE `spawnType` = ?d AND `groupId` IN (?a)', SUMMONER_TYPE_GAMEOBJECT, $grp))
|
|
||||||
if ($ids = DB::Aowow()->selectCol('SELECT DISTINCT `typeId` FROM ?_spawns WHERE `type` = ?d AND `guid` IN (?a)', Type::OBJECT, $sgs))
|
|
||||||
$result = array_merge($result, $ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getSpellCastsForOwner(int $entry, int $srcType = self::SRC_TYPE_CREATURE) : array
|
|
||||||
{
|
|
||||||
// action => paramIdx with spellIds
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_CAST => [1],
|
|
||||||
SmartAction::ACTION_ADD_AURA => [1],
|
|
||||||
SmartAction::ACTION_INVOKER_CAST => [1],
|
|
||||||
SmartAction::ACTION_CROSS_CAST => [1]
|
|
||||||
);
|
|
||||||
|
|
||||||
return self::getOwnerAction($srcType, $entry, $lookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getSoundsPlayedForOwner(int $entry, int $srcType = self::SRC_TYPE_CREATURE) : array
|
|
||||||
{
|
|
||||||
// action => paramIdx with soundIds
|
|
||||||
$lookup = array(
|
|
||||||
SmartAction::ACTION_SOUND => [1]
|
|
||||||
);
|
|
||||||
|
|
||||||
return self::getOwnerAction($srcType, $entry, $lookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup: [SmartActionId => [paramIdx, ...], ...]
|
|
||||||
private static function getOwnerAction(int $sourceType, int $entry, array $lookup, ?array &$moreInfo = []) : array
|
|
||||||
{
|
|
||||||
if ($entry < 0) // no lookup by GUID
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$actionQuery = 'SELECT `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6` FROM smart_scripts WHERE `source_type` = ?d AND `action_type` IN (?a) AND `entryOrGUID` IN (?a)';
|
|
||||||
|
|
||||||
$smartScripts = DB::World()->select($actionQuery, $sourceType, array_merge(array_keys($lookup), SmartAction::ACTION_ALL_TIMED_ACTION_LISTS), [$entry]);
|
|
||||||
$smartResults = [];
|
|
||||||
$smartTALs = [];
|
|
||||||
foreach ($smartScripts as $s)
|
|
||||||
{
|
|
||||||
if ($s['action_type'] == SmartAction::ACTION_SPAWN_SPAWNGROUP)
|
|
||||||
$moreInfo[SmartAction::ACTION_SPAWN_SPAWNGROUP][] = $s['action_param1'];
|
|
||||||
else if (in_array($s['action_type'], array_keys($lookup)))
|
|
||||||
{
|
|
||||||
foreach ($lookup[$s['action_type']] as $p)
|
|
||||||
$smartResults[] = $s['action_param'.$p];
|
|
||||||
}
|
|
||||||
else if ($s['action_type'] == SmartAction::ACTION_CALL_TIMED_ACTIONLIST)
|
|
||||||
$smartTALs[] = $s['action_param1'];
|
|
||||||
else if ($s['action_type'] == SmartAction::ACTION_CALL_RANDOM_TIMED_ACTIONLIST)
|
|
||||||
{
|
|
||||||
for ($i = 1; $i < 7; $i++)
|
|
||||||
if ($s['action_param'.$i])
|
|
||||||
$smartTALs[] = $s['action_param'.$i];
|
|
||||||
}
|
|
||||||
else if ($s['action_type'] == SmartAction::ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST)
|
|
||||||
{
|
|
||||||
for ($i = $s['action_param1']; $i <= $s['action_param2']; $i++)
|
|
||||||
$smartTALs[] = $i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($smartTALs)
|
|
||||||
{
|
|
||||||
if ($TALActList = DB::World()->select($actionQuery, self::SRC_TYPE_ACTIONLIST, array_keys($lookup), $smartTALs))
|
|
||||||
{
|
|
||||||
foreach ($TALActList as $e)
|
|
||||||
{
|
|
||||||
foreach ($lookup[$e['action_type']] as $i)
|
|
||||||
{
|
|
||||||
if ($e['action_type'] == SmartAction::ACTION_SPAWN_SPAWNGROUP)
|
|
||||||
$moreInfo[SmartAction::ACTION_SPAWN_SPAWNGROUP][] = $e['action_param'.$i];
|
|
||||||
else
|
|
||||||
$smartResults[] = $e['action_param'.$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $smartResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************************/
|
|
||||||
/* Structured Lisview Display */
|
|
||||||
/******************************/
|
|
||||||
|
|
||||||
private function &iterate() : \Generator
|
|
||||||
{
|
|
||||||
reset($this->rawData);
|
|
||||||
|
|
||||||
foreach ($this->rawData as $k => $__)
|
|
||||||
{
|
|
||||||
$this->itr = &$this->rawData[$k];
|
|
||||||
|
|
||||||
yield $this->itr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prepare() : bool
|
|
||||||
{
|
|
||||||
if (!$this->rawData)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ($this->result)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
$hidePhase =
|
|
||||||
$hideChance = true;
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $id => $__)
|
|
||||||
{
|
|
||||||
$rowIdx = Util::createHash(8);
|
|
||||||
|
|
||||||
if ($this->itr['action']->type == SmartAction::ACTION_TALK || $this->itr['action']->type == SmartAction::ACTION_SIMPLE_TALK)
|
|
||||||
if ($ts = $this->itr['target']->getTalkSource())
|
|
||||||
$this->initQuotes($ts);
|
|
||||||
|
|
||||||
[$evtBody, $evtFooter] = $this->itr['event']->process();
|
|
||||||
[$actBody, $actFooter] = $this->itr['action']->process();
|
|
||||||
|
|
||||||
$evtBody = str_replace(['#target#', '#rowIdx#'], [$this->itr['target']->process(), $rowIdx], $evtBody);
|
|
||||||
$actBody = str_replace(['#target#', '#rowIdx#'], [$this->itr['target']->process(), $rowIdx], $actBody);
|
|
||||||
|
|
||||||
if (!$this->itr['event']->hasPhases())
|
|
||||||
$hidePhase = false;
|
|
||||||
|
|
||||||
if ($this->itr['event']->chance != 100)
|
|
||||||
$hideChance = false;
|
|
||||||
|
|
||||||
$this->result[] = array(
|
|
||||||
$this->itr['id'],
|
|
||||||
implode(', ', Util::mask2bits($this->itr['event']->phaseMask, 1)),
|
|
||||||
$evtBody.($evtFooter ? '[div float=right margin=0px clear=both][i][small class=q0]'.$evtFooter.'[/small][/i][/div]' : null),
|
|
||||||
$this->itr['event']->chance.'%',
|
|
||||||
$actBody.($actFooter ? '[div float=right margin=0px clear=both][i][small class=q0]'.$actFooter.'[/small][/i][/div]' : null)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$th = array(
|
|
||||||
'#' => 16,
|
|
||||||
'Phase' => 32,
|
|
||||||
'Event' => 350,
|
|
||||||
'Chance' => 24,
|
|
||||||
'Action' => 0
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($hidePhase)
|
|
||||||
{
|
|
||||||
unset($th['Phase']);
|
|
||||||
foreach ($this->result as &$r)
|
|
||||||
unset($r[1]);
|
|
||||||
}
|
|
||||||
unset($r);
|
|
||||||
|
|
||||||
if ($hideChance)
|
|
||||||
{
|
|
||||||
unset($th['Chance']);
|
|
||||||
foreach ($this->result as &$r)
|
|
||||||
unset($r[3]);
|
|
||||||
}
|
|
||||||
unset($r);
|
|
||||||
|
|
||||||
$tbl = '[tr]';
|
|
||||||
foreach ($th as $n => $w)
|
|
||||||
$tbl .= '[td header '.($w ? 'width='.$w.'px' : null).']'.$n.'[/td]';
|
|
||||||
$tbl .= '[/tr]';
|
|
||||||
|
|
||||||
foreach ($this->result as $r)
|
|
||||||
$tbl .= '[tr][td]'.implode('[/td][td]', $r).'[/td][/tr]';
|
|
||||||
|
|
||||||
if ($this->srcType == self::SRC_TYPE_ACTIONLIST)
|
|
||||||
$this->tabs[$this->entry] = $tbl;
|
|
||||||
else
|
|
||||||
$this->tabs[0] = $tbl;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMarkdown() : string
|
|
||||||
{
|
|
||||||
# id | event (footer phase) | chance | action + target
|
|
||||||
|
|
||||||
if (!$this->rawData)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
$return = '[style]#text-generic .grid { clear:left; } #text-generic .tabbed-contents { padding:0px; clear:left; }[/style][pad][h3][toggler id=sai]SmartAI'.$this->title.'[/toggler][/h3][div id=sai clear=left]%s[/div]';
|
|
||||||
if (count($this->tabs) > 1)
|
|
||||||
{
|
|
||||||
$wrapper = '[tabs name=sai width=942px]%s[/tabs]';
|
|
||||||
$return = '[script]function TalTabClick(id) { $(\'#dsf67g4d-sai\').find(\\\'[href=\\\\\'#sai-actionlist-\' + id + \'\\\\\']\\\').click(); }[/script]' . $return;
|
|
||||||
$tabs = '';
|
|
||||||
foreach ($this->tabs as $guid => $data)
|
|
||||||
{
|
|
||||||
$buff = '[tab name=\"'.($guid ? 'ActionList #'.$guid : 'Main').'\"][table class=grid width=940px]'.$data.'[/table][/tab]';
|
|
||||||
if ($guid)
|
|
||||||
$tabs .= $buff;
|
|
||||||
else
|
|
||||||
$tabs = $buff . $tabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprintf($return, sprintf($wrapper, $tabs));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return sprintf($return, '[table class=grid width=940px]'.$this->tabs[0].'[/table]');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addJsGlobals(array $jsg) : void
|
|
||||||
{
|
|
||||||
Util::mergeJsGlobals($this->jsGlobals, $jsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals() : array
|
|
||||||
{
|
|
||||||
return $this->jsGlobals;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTabs() : array
|
|
||||||
{
|
|
||||||
return $this->tabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addTab(int $guid, string $tt) : void
|
|
||||||
{
|
|
||||||
$this->tabs[$guid] = $tt;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTarget(int $id = -1) : ?SmartTarget
|
|
||||||
{
|
|
||||||
if ($id < 0)
|
|
||||||
return $this->itr['target'];
|
|
||||||
|
|
||||||
return $this->rawData[$id]['target'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAction(int $id = -1) : ?SmartAction
|
|
||||||
{
|
|
||||||
if ($id < 0)
|
|
||||||
return $this->itr['action'];
|
|
||||||
|
|
||||||
return $this->rawData[$id]['action'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEvent(int $id = -1) : ?SmartEvent
|
|
||||||
{
|
|
||||||
if ($id < 0)
|
|
||||||
return $this->itr['event'];
|
|
||||||
|
|
||||||
return $this->rawData[$id]['event'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getEntry() : int
|
|
||||||
{
|
|
||||||
return $this->baseEntry ?: $this->entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function initQuotes(int $creatureId) : void
|
|
||||||
{
|
|
||||||
if (isset($this->quotes[$creatureId]))
|
|
||||||
return;
|
|
||||||
|
|
||||||
[$quotes, , ] = Game::getQuotesForCreature($creatureId);
|
|
||||||
|
|
||||||
$this->quotes[$creatureId] = $quotes;
|
|
||||||
|
|
||||||
if (!empty($this->quotes[$creatureId]))
|
|
||||||
$this->quotes[$creatureId]['src'] = CreatureList::getName($creatureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getQuote(int $creatureId, int $group, ?string &$npcSrc) : array
|
|
||||||
{
|
|
||||||
if (isset($this->quotes[$creatureId][$group]))
|
|
||||||
{
|
|
||||||
$npcSrc = $this->quotes[$creatureId]['src'];
|
|
||||||
return $this->quotes[$creatureId][$group];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,748 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
// TrinityCore - SmartAI
|
|
||||||
class SmartAction
|
|
||||||
{
|
|
||||||
use SmartHelper;
|
|
||||||
|
|
||||||
public const ACTION_NONE = 0; // Do nothing
|
|
||||||
public const ACTION_TALK = 1; // Param2 in Milliseconds.
|
|
||||||
public const ACTION_SET_FACTION = 2; // Sets faction to creature.
|
|
||||||
public const ACTION_MORPH_TO_ENTRY_OR_MODEL = 3; // Take DisplayID of creature (param1) OR Turn to DisplayID (param2) OR Both = 0 for Demorph
|
|
||||||
public const ACTION_SOUND = 4; // TextRange = 0 only sends sound to self, TextRange = 1 sends sound to everyone in visibility range
|
|
||||||
public const ACTION_PLAY_EMOTE = 5; // Play Emote
|
|
||||||
public const ACTION_FAIL_QUEST = 6; // Fail Quest of Target
|
|
||||||
public const ACTION_OFFER_QUEST = 7; // Add Quest to Target
|
|
||||||
public const ACTION_SET_REACT_STATE = 8; // React State. Can be Passive (0), Defensive (1), Aggressive (2), Assist (3).
|
|
||||||
public const ACTION_ACTIVATE_GOBJECT = 9; // Activate Object
|
|
||||||
public const ACTION_RANDOM_EMOTE = 10; // Play Random Emote
|
|
||||||
public const ACTION_CAST = 11; // Cast Spell ID at Target
|
|
||||||
public const ACTION_SUMMON_CREATURE = 12; // Summon Unit
|
|
||||||
public const ACTION_THREAT_SINGLE_PCT = 13; // Change Threat Percentage for Single Target
|
|
||||||
public const ACTION_THREAT_ALL_PCT = 14; // Change Threat Percentage for All Enemies
|
|
||||||
public const ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS = 15; //
|
|
||||||
public const ACTION_SET_INGAME_PHASE_ID = 16; // [RESERVED] For 4.3.4 + only
|
|
||||||
public const ACTION_SET_EMOTE_STATE = 17; // Play Emote Continuously
|
|
||||||
public const ACTION_SET_UNIT_FLAG = 18; // [DEPRECATED] Can set Multi-able flags at once
|
|
||||||
public const ACTION_REMOVE_UNIT_FLAG = 19; // [DEPRECATED] Can Remove Multi-able flags at once
|
|
||||||
public const ACTION_AUTO_ATTACK = 20; // Stop or Continue Automatic Attack.
|
|
||||||
public const ACTION_ALLOW_COMBAT_MOVEMENT = 21; // Allow or Disable Combat Movement
|
|
||||||
public const ACTION_SET_EVENT_PHASE = 22; //
|
|
||||||
public const ACTION_INC_EVENT_PHASE = 23; // Set param1 OR param2 (not both). Value 0 has no effect.
|
|
||||||
public const ACTION_EVADE = 24; // Evade Incoming Attack
|
|
||||||
public const ACTION_FLEE_FOR_ASSIST = 25; // If you want the fleeing NPC to say '%s attempts to run away in fear' on flee, use 1 on param1. 0 for no message.
|
|
||||||
public const ACTION_CALL_GROUPEVENTHAPPENS = 26; //
|
|
||||||
public const ACTION_COMBAT_STOP = 27; //
|
|
||||||
public const ACTION_REMOVEAURASFROMSPELL = 28; // 0 removes all auras
|
|
||||||
public const ACTION_FOLLOW = 29; // Follow Target
|
|
||||||
public const ACTION_RANDOM_PHASE = 30; //
|
|
||||||
public const ACTION_RANDOM_PHASE_RANGE = 31; //
|
|
||||||
public const ACTION_RESET_GOBJECT = 32; // Reset Gameobject
|
|
||||||
public const ACTION_CALL_KILLEDMONSTER = 33; // This is the ID from quest_template.RequiredNpcOrGo
|
|
||||||
public const ACTION_SET_INST_DATA = 34; // Set Instance Data
|
|
||||||
public const ACTION_SET_INST_DATA64 = 35; // Set Instance Data uint64
|
|
||||||
public const ACTION_UPDATE_TEMPLATE = 36; // Updates creature_template to given entry
|
|
||||||
public const ACTION_DIE = 37; // Kill Target
|
|
||||||
public const ACTION_SET_IN_COMBAT_WITH_ZONE = 38; //
|
|
||||||
public const ACTION_CALL_FOR_HELP = 39; // If you want the NPC to say '%s calls for help!'. Use 1 on param1, 0 for no message.
|
|
||||||
public const ACTION_SET_SHEATH = 40; //
|
|
||||||
public const ACTION_FORCE_DESPAWN = 41; // Despawn Target after param1 in Milliseconds. If you want to set respawn time set param2 in seconds.
|
|
||||||
public const ACTION_SET_INVINCIBILITY_HP_LEVEL = 42; // If you use both params, only percent will be used.
|
|
||||||
public const ACTION_MOUNT_TO_ENTRY_OR_MODEL = 43; // Mount to Creature Entry (param1) OR Mount to Creature Display (param2) Or both = 0 for Unmount
|
|
||||||
public const ACTION_SET_INGAME_PHASE_MASK = 44; //
|
|
||||||
public const ACTION_SET_DATA = 45; // Set Data For Target, can be used with SMART_EVENT_DATA_SET
|
|
||||||
public const ACTION_ATTACK_STOP = 46; //
|
|
||||||
public const ACTION_SET_VISIBILITY = 47; // Makes creature Visible = 1 or Invisible = 0
|
|
||||||
public const ACTION_SET_ACTIVE = 48; //
|
|
||||||
public const ACTION_ATTACK_START = 49; // Allows basic melee swings to creature.
|
|
||||||
public const ACTION_SUMMON_GO = 50; // Spawns Gameobject, use target_type to set spawn position.
|
|
||||||
public const ACTION_KILL_UNIT = 51; // Kills Creature.
|
|
||||||
public const ACTION_ACTIVATE_TAXI = 52; // Sends player to flight path. You have to be close to Flight Master, which gives Taxi ID you need.
|
|
||||||
public const ACTION_WP_START = 53; // Creature starts Waypoint Movement. Use waypoints table to create movement.
|
|
||||||
public const ACTION_WP_PAUSE = 54; // Creature pauses its Waypoint Movement for given time.
|
|
||||||
public const ACTION_WP_STOP = 55; // Creature stops its Waypoint Movement.
|
|
||||||
public const ACTION_ADD_ITEM = 56; // Adds item(s) to player.
|
|
||||||
public const ACTION_REMOVE_ITEM = 57; // Removes item(s) from player.
|
|
||||||
public const ACTION_INSTALL_AI_TEMPLATE = 58; // [DEPRECATED]
|
|
||||||
public const ACTION_SET_RUN = 59; //
|
|
||||||
public const ACTION_SET_DISABLE_GRAVITY = 60; // Only works for creatures with inhabit air.
|
|
||||||
public const ACTION_SET_SWIM = 61; // [DEPRECATED]
|
|
||||||
public const ACTION_TELEPORT = 62; // Continue this action with the TARGET_TYPE column. Use any target_type (except 0), and use target_x, target_y, target_z, target_o as the coordinates
|
|
||||||
public const ACTION_SET_COUNTER = 63; //
|
|
||||||
public const ACTION_STORE_TARGET_LIST = 64; //
|
|
||||||
public const ACTION_WP_RESUME = 65; // Creature continues in its Waypoint Movement.
|
|
||||||
public const ACTION_SET_ORIENTATION = 66; //
|
|
||||||
public const ACTION_CREATE_TIMED_EVENT = 67; //
|
|
||||||
public const ACTION_PLAYMOVIE = 68; //
|
|
||||||
public const ACTION_MOVE_TO_POS = 69; // PointId is called by SMART_EVENT_MOVEMENTINFORM. Continue this action with the TARGET_TYPE column. Use any target_type, and use target_x, target_y, target_z, target_o as the coordinates
|
|
||||||
public const ACTION_ENABLE_TEMP_GOBJ = 70; // param1 = duration
|
|
||||||
public const ACTION_EQUIP = 71; // only slots with mask set will be sent to client, bits are 1, 2, 4, leaving mask 0 is defaulted to mask 7 (send all), Slots1-3 are only used if no Param1 is set
|
|
||||||
public const ACTION_CLOSE_GOSSIP = 72; // Closes gossip window.
|
|
||||||
public const ACTION_TRIGGER_TIMED_EVENT = 73; //
|
|
||||||
public const ACTION_REMOVE_TIMED_EVENT = 74; //
|
|
||||||
public const ACTION_ADD_AURA = 75; // [DEPRECATED] Adds aura to player(s). Use target_type 17 to make AoE aura.
|
|
||||||
public const ACTION_OVERRIDE_SCRIPT_BASE_OBJECT = 76; // [DEPRECATED] WARNING: CAN CRASH CORE, do not use if you dont know what you are doing
|
|
||||||
public const ACTION_RESET_SCRIPT_BASE_OBJECT = 77; // [DEPRECATED]
|
|
||||||
public const ACTION_CALL_SCRIPT_RESET = 78; //
|
|
||||||
public const ACTION_SET_RANGED_MOVEMENT = 79; // Sets movement to follow at a specific range to the target.
|
|
||||||
public const ACTION_CALL_TIMED_ACTIONLIST = 80; //
|
|
||||||
public const ACTION_SET_NPC_FLAG = 81; //
|
|
||||||
public const ACTION_ADD_NPC_FLAG = 82; //
|
|
||||||
public const ACTION_REMOVE_NPC_FLAG = 83; //
|
|
||||||
public const ACTION_SIMPLE_TALK = 84; // Makes a player say text. SMART_EVENT_TEXT_OVER is not triggered and whispers can not be used.
|
|
||||||
public const ACTION_SELF_CAST = 85; // spellID, castFlags
|
|
||||||
public const ACTION_CROSS_CAST = 86; // This action is used to make selected caster (in CasterTargetType) to cast spell. Actual target is entered in target_type as normally.
|
|
||||||
public const ACTION_CALL_RANDOM_TIMED_ACTIONLIST = 87; // Will select one entry from the ones provided. 0 is ignored.
|
|
||||||
public const ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST = 88; // 0 is ignored.
|
|
||||||
public const ACTION_RANDOM_MOVE = 89; // Creature moves to random position in given radius.
|
|
||||||
public const ACTION_SET_UNIT_FIELD_BYTES_1 = 90; //
|
|
||||||
public const ACTION_REMOVE_UNIT_FIELD_BYTES_1 = 91; //
|
|
||||||
public const ACTION_INTERRUPT_SPELL = 92; // This action allows you to interrupt the current spell being cast. If you do not set the spellId, the core will find the current spell depending on the withDelay and the withInstant values.
|
|
||||||
public const ACTION_SEND_GO_CUSTOM_ANIM = 93; // [DEPRECATED] oldFlag = newFlag
|
|
||||||
public const ACTION_SET_DYNAMIC_FLAG = 94; // [DEPRECATED] oldFlag |= newFlag
|
|
||||||
public const ACTION_ADD_DYNAMIC_FLAG = 95; // [DEPRECATED] oldFlag &= ~newFlag
|
|
||||||
public const ACTION_REMOVE_DYNAMIC_FLAG = 96; // [DEPRECATED]
|
|
||||||
public const ACTION_JUMP_TO_POS = 97; //
|
|
||||||
public const ACTION_SEND_GOSSIP_MENU = 98; // Can be used together with 'SMART_EVENT_GOSSIP_HELLO' to set custom gossip.
|
|
||||||
public const ACTION_GO_SET_LOOT_STATE = 99; //
|
|
||||||
public const ACTION_SEND_TARGET_TO_TARGET = 100; // Send targets previously stored with SMART_ACTION_STORE_TARGET, to another npc/go, the other npc/go can then access them as if it was its own stored list
|
|
||||||
public const ACTION_SET_HOME_POS = 101; // Use with SMART_TARGET_SELF or SMART_TARGET_POSITION
|
|
||||||
public const ACTION_SET_HEALTH_REGEN = 102; // Sets the current creatures health regen on or off.
|
|
||||||
public const ACTION_SET_ROOT = 103; // Enables or disables creature movement
|
|
||||||
public const ACTION_SET_GO_FLAG = 104; // [DEPRECATED] oldFlag = newFlag
|
|
||||||
public const ACTION_ADD_GO_FLAG = 105; // [DEPRECATED] oldFlag |= newFlag
|
|
||||||
public const ACTION_REMOVE_GO_FLAG = 106; // [DEPRECATED] oldFlag &= ~newFlag
|
|
||||||
public const ACTION_SUMMON_CREATURE_GROUP = 107; // Use creature_summon_groups table. SAI target has no effect, use 0
|
|
||||||
public const ACTION_SET_POWER = 108; //
|
|
||||||
public const ACTION_ADD_POWER = 109; //
|
|
||||||
public const ACTION_REMOVE_POWER = 110; //
|
|
||||||
public const ACTION_GAME_EVENT_STOP = 111; //
|
|
||||||
public const ACTION_GAME_EVENT_START = 112; //
|
|
||||||
public const ACTION_START_CLOSEST_WAYPOINT = 113; // Make target follow closest waypoint to its location
|
|
||||||
public const ACTION_MOVE_OFFSET = 114; // Use target_x, target_y, target_z With target_type=1
|
|
||||||
public const ACTION_RANDOM_SOUND = 115; //
|
|
||||||
public const ACTION_SET_CORPSE_DELAY = 116; //
|
|
||||||
public const ACTION_DISABLE_EVADE = 117; //
|
|
||||||
public const ACTION_GO_SET_GO_STATE = 118; //
|
|
||||||
public const ACTION_SET_CAN_FLY = 119; // [DEPRECATED]
|
|
||||||
public const ACTION_REMOVE_AURAS_BY_TYPE = 120; // [DEPRECATED]
|
|
||||||
public const ACTION_SET_SIGHT_DIST = 121; // [DEPRECATED]
|
|
||||||
public const ACTION_FLEE = 122; // [DEPRECATED]
|
|
||||||
public const ACTION_ADD_THREAT = 123; //
|
|
||||||
public const ACTION_LOAD_EQUIPMENT = 124; //
|
|
||||||
public const ACTION_TRIGGER_RANDOM_TIMED_EVENT = 125; //
|
|
||||||
public const ACTION_REMOVE_ALL_GAMEOBJECTS = 126; // [DEPRECATED]
|
|
||||||
public const ACTION_PAUSE_MOVEMENT = 127; // MovementSlot (default = 0, active = 1, controlled = 2), PauseTime (ms), Force
|
|
||||||
public const ACTION_PLAY_ANIMKIT = 128; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const ACTION_SCENE_PLAY = 129; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const ACTION_SCENE_CANCEL = 130; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const ACTION_SPAWN_SPAWNGROUP = 131; //
|
|
||||||
public const ACTION_DESPAWN_SPAWNGROUP = 132; //
|
|
||||||
public const ACTION_RESPAWN_BY_SPAWNID = 133; // type, typeGuid - Use to respawn npcs and gobs, the target in this case is always=1 and only a single unit could be a target via the spawnId (action_param1, action_param2)
|
|
||||||
public const ACTION_INVOKER_CAST = 134; // spellID, castFlags
|
|
||||||
public const ACTION_PLAY_CINEMATIC = 135; // cinematic
|
|
||||||
public const ACTION_SET_MOVEMENT_SPEED = 136; // movementType, speedInteger, speedFraction
|
|
||||||
public const ACTION_PLAY_SPELL_VISUAL_KIT = 137; // [RESERVED] spellVisualKitId
|
|
||||||
public const ACTION_OVERRIDE_LIGHT = 138; // zoneId, areaLightId, overrideLightID, transitionMilliseconds
|
|
||||||
public const ACTION_OVERRIDE_WEATHER = 139; // zoneId, weatherId, intensity
|
|
||||||
public const ACTION_SET_AI_ANIM_KIT = 140; // [RESERVED]
|
|
||||||
public const ACTION_SET_HOVER = 141; // Enable/Disable hover for target units.
|
|
||||||
public const ACTION_SET_HEALTH_PCT = 142; // Set current health percentage of target units.
|
|
||||||
public const ACTION_CREATE_CONVERSATION = 143; // [RESERVED]
|
|
||||||
public const ACTION_SET_IMMUNE_PC = 144; // Enable/Disable immunity to players of target units.
|
|
||||||
public const ACTION_SET_IMMUNE_NPC = 145; // Enable/Disable immunity to creatures of target units.
|
|
||||||
public const ACTION_SET_UNINTERACTIBLE = 146; // Make/Reset target units uninteractible.
|
|
||||||
public const ACTION_ACTIVATE_GAMEOBJECT = 147; // Activate target gameobjects, using given action.
|
|
||||||
public const ACTION_ADD_TO_STORED_TARGET_LIST = 148; // Add selected targets to varID for later use.
|
|
||||||
public const ACTION_BECOME_PERSONAL_CLONE_FOR_PLAYER = 149; // [RESERVED]
|
|
||||||
public const ACTION_TRIGGER_GAME_EVENT = 150; // [RESERVED]
|
|
||||||
public const ACTION_DO_ACTION = 151; // [RESERVED]
|
|
||||||
|
|
||||||
public const ACTION_ALL_SPELLCASTS = [self::ACTION_CAST, self::ACTION_ADD_AURA, self::ACTION_INVOKER_CAST, self::ACTION_SELF_CAST, self::ACTION_CROSS_CAST];
|
|
||||||
public const ACTION_ALL_TIMED_ACTION_LISTS = [self::ACTION_CALL_TIMED_ACTIONLIST, self::ACTION_CALL_RANDOM_TIMED_ACTIONLIST, self::ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST];
|
|
||||||
|
|
||||||
private const ACTION_CELL_TPL = '[tooltip name=a-#rowIdx#]%1$s[/tooltip][span tooltip=a-#rowIdx#]%2$s[/span]';
|
|
||||||
private const TAL_TAB_ANCHOR = '[url=#sai-actionlist-%1$d onclick=TalTabClick(%1$d)]#%1$d[/url]';
|
|
||||||
|
|
||||||
private array $data = array(
|
|
||||||
self::ACTION_NONE => [null, null, null, null, null, null, 0], // No action
|
|
||||||
self::ACTION_TALK => [null, ['formatTime', -1, true], null, null, null, null, 0], // groupID from creature_text, duration to wait before TEXT_OVER event is triggered, useTalkTarget (0/1) - use target as talk target
|
|
||||||
self::ACTION_SET_FACTION => [null, null, null, null, null, null, 0], // FactionId (or 0 for default)
|
|
||||||
self::ACTION_MORPH_TO_ENTRY_OR_MODEL => [Type::NPC, null, null, null, null, null, 0], // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to demorph)
|
|
||||||
self::ACTION_SOUND => [Type::SOUND, null, null, null, null, null, 0], // SoundId, onlySelf
|
|
||||||
self::ACTION_PLAY_EMOTE => [null, null, null, null, null, null, 0], // EmoteId
|
|
||||||
self::ACTION_FAIL_QUEST => [Type::QUEST, null, null, null, null, null, 0], // QuestID
|
|
||||||
self::ACTION_OFFER_QUEST => [Type::QUEST, null, null, null, null, null, 0], // QuestID, directAdd
|
|
||||||
self::ACTION_SET_REACT_STATE => [['reactState', 10, false], null, null, null, null, null, 0], // state
|
|
||||||
self::ACTION_ACTIVATE_GOBJECT => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_RANDOM_EMOTE => [null, null, null, null, null, null, 0], // EmoteId1, EmoteId2, EmoteId3...
|
|
||||||
self::ACTION_CAST => [Type::SPELL, ['castFlags', -1, false], null, null, null, null, 0], // SpellId, CastFlags, TriggeredFlags
|
|
||||||
self::ACTION_SUMMON_CREATURE => [Type::NPC, ['summonType', -1, false], ['formatTime', 10, true], null, null, null, 0], // CreatureID, summonType, duration in ms, attackInvoker, flags(SmartActionSummonCreatureFlags)
|
|
||||||
self::ACTION_THREAT_SINGLE_PCT => [null, null, null, null, null, null, 0], // Threat%
|
|
||||||
self::ACTION_THREAT_ALL_PCT => [null, null, null, null, null, null, 0], // Threat%
|
|
||||||
self::ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS => [Type::QUEST, null, null, null, null, null, 0], // QuestID
|
|
||||||
self::ACTION_SET_INGAME_PHASE_ID => [null, null, null, null, null, null, 2], // used on 4.3.4 and higher scripts
|
|
||||||
self::ACTION_SET_EMOTE_STATE => [null, null, null, null, null, null, 0], // emoteID
|
|
||||||
self::ACTION_SET_UNIT_FLAG => [['unitFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_REMOVE_UNIT_FLAG => [['unitFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_AUTO_ATTACK => [null, null, null, null, null, null, 0], // AllowAttackState (0 = stop attack, anything else means continue attacking)
|
|
||||||
self::ACTION_ALLOW_COMBAT_MOVEMENT => [null, null, null, null, null, null, 0], // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking)
|
|
||||||
self::ACTION_SET_EVENT_PHASE => [null, null, null, null, null, null, 0], // Phase
|
|
||||||
self::ACTION_INC_EVENT_PHASE => [null, null, null, null, null, null, 0], // Value (may be negative to decrement phase, should not be 0)
|
|
||||||
self::ACTION_EVADE => [null, null, null, null, null, null, 0], // toRespawnPosition (0 = Move to RespawnPosition, 1 = Move to last stored home position)
|
|
||||||
self::ACTION_FLEE_FOR_ASSIST => [null, null, null, null, null, null, 0], // With Emote
|
|
||||||
self::ACTION_CALL_GROUPEVENTHAPPENS => [Type::QUEST, null, null, null, null, null, 0], // QuestID
|
|
||||||
self::ACTION_COMBAT_STOP => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_REMOVEAURASFROMSPELL => [Type::SPELL, null, null, null, null, null, 0], // Spellid (0 removes all auras), charges (0 removes aura)
|
|
||||||
self::ACTION_FOLLOW => [null, null, null, null, null, null, 0], // Distance (0 = default), Angle (0 = default), EndCreatureEntry, credit, creditType (0monsterkill, 1event)
|
|
||||||
self::ACTION_RANDOM_PHASE => [null, null, null, null, null, null, 0], // PhaseId1, PhaseId2, PhaseId3...
|
|
||||||
self::ACTION_RANDOM_PHASE_RANGE => [null, null, null, null, null, null, 0], // PhaseMin, PhaseMax
|
|
||||||
self::ACTION_RESET_GOBJECT => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_CALL_KILLEDMONSTER => [Type::NPC, null, null, null, null, null, 0], // CreatureId,
|
|
||||||
self::ACTION_SET_INST_DATA => [null, null, null, null, null, null, 0], // Field, Data, Type (0 = SetData, 1 = SetBossState)
|
|
||||||
self::ACTION_SET_INST_DATA64 => [null, null, null, null, null, null, 0], // Field,
|
|
||||||
self::ACTION_UPDATE_TEMPLATE => [Type::NPC, null, null, null, null, null, 0], // Entry
|
|
||||||
self::ACTION_DIE => [null, null, null, null, null, null, 0], // No Params
|
|
||||||
self::ACTION_SET_IN_COMBAT_WITH_ZONE => [null, null, null, null, null, null, 0], // No Params
|
|
||||||
self::ACTION_CALL_FOR_HELP => [null, null, null, null, null, null, 0], // Radius, With Emote
|
|
||||||
self::ACTION_SET_SHEATH => [['sheathState', 10, false], null, null, null, null, null, 0], // Sheath (0-unarmed, 1-melee, 2-ranged)
|
|
||||||
self::ACTION_FORCE_DESPAWN => [['formatTime', 10, true], ['formatTime', 11, false], null, null, null, null, 0], // timer
|
|
||||||
self::ACTION_SET_INVINCIBILITY_HP_LEVEL => [null, null, null, null, null, null, 0], // MinHpValue(+pct, -flat)
|
|
||||||
self::ACTION_MOUNT_TO_ENTRY_OR_MODEL => [Type::NPC, null, null, null, null, null, 0], // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to dismount)
|
|
||||||
self::ACTION_SET_INGAME_PHASE_MASK => [null, null, null, null, null, null, 0], // mask
|
|
||||||
self::ACTION_SET_DATA => [null, null, null, null, null, null, 0], // Field, Data (only creature @todo)
|
|
||||||
self::ACTION_ATTACK_STOP => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_SET_VISIBILITY => [null, null, null, null, null, null, 0], // on/off
|
|
||||||
self::ACTION_SET_ACTIVE => [null, null, null, null, null, null, 0], // on/off
|
|
||||||
self::ACTION_ATTACK_START => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_SUMMON_GO => [Type::OBJECT, ['formatTime', 10, false], null, null, null, null, 0], // GameObjectID, DespawnTime in s
|
|
||||||
self::ACTION_KILL_UNIT => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_ACTIVATE_TAXI => [null, null, null, null, null, null, 0], // TaxiID
|
|
||||||
self::ACTION_WP_START => [null, null, null, Type::QUEST, ['formatTime', 10, true], ['reactState', 11, false], 0], // run/walk, pathID, canRepeat, quest, despawntime
|
|
||||||
self::ACTION_WP_PAUSE => [['formatTime', 10, true], null, null, null, null, null, 0], // time
|
|
||||||
self::ACTION_WP_STOP => [['formatTime', 10, true], Type::QUEST, null, null, null, null, 0], // despawnTime, quest, fail?
|
|
||||||
self::ACTION_ADD_ITEM => [Type::ITEM, null, null, null, null, null, 0], // itemID, count
|
|
||||||
self::ACTION_REMOVE_ITEM => [Type::ITEM, null, null, null, null, null, 0], // itemID, count
|
|
||||||
self::ACTION_INSTALL_AI_TEMPLATE => [['aiTemplate', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_SET_RUN => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_SET_DISABLE_GRAVITY => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_SET_SWIM => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_TELEPORT => [null, null, null, null, null, null, 0], // mapID,
|
|
||||||
self::ACTION_SET_COUNTER => [null, null, null, null, null, null, 0], // id, value, reset (0/1)
|
|
||||||
self::ACTION_STORE_TARGET_LIST => [null, null, null, null, null, null, 0], // varID,
|
|
||||||
self::ACTION_WP_RESUME => [null, null, null, null, null, null, 0], // none
|
|
||||||
self::ACTION_SET_ORIENTATION => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_CREATE_TIMED_EVENT => [null, ['numRange', 10, true], null, ['numRange', -1, true], null, null, 0], // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance
|
|
||||||
self::ACTION_PLAYMOVIE => [null, null, null, null, null, null, 0], // entry
|
|
||||||
self::ACTION_MOVE_TO_POS => [null, null, null, null, null, null, 0], // PointId, transport, disablePathfinding, ContactDistance
|
|
||||||
self::ACTION_ENABLE_TEMP_GOBJ => [['formatTime', 10, false], null, null, null, null, null, 0], // despawnTimer (sec)
|
|
||||||
self::ACTION_EQUIP => [null, null, Type::ITEM, Type::ITEM, Type::ITEM, null, 0], // entry, slotmask slot1, slot2, slot3 , only slots with mask set will be sent to client, bits are 1, 2, 4, leaving mask 0 is defaulted to mask 7 (send all), slots1-3 are only used if no entry is set
|
|
||||||
self::ACTION_CLOSE_GOSSIP => [null, null, null, null, null, null, 0], // none
|
|
||||||
self::ACTION_TRIGGER_TIMED_EVENT => [null, null, null, null, null, null, 0], // id(>1)
|
|
||||||
self::ACTION_REMOVE_TIMED_EVENT => [null, null, null, null, null, null, 0], // id(>1)
|
|
||||||
self::ACTION_ADD_AURA => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_OVERRIDE_SCRIPT_BASE_OBJECT => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_RESET_SCRIPT_BASE_OBJECT => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_CALL_SCRIPT_RESET => [null, null, null, null, null, null, 0], // none
|
|
||||||
self::ACTION_SET_RANGED_MOVEMENT => [null, null, null, null, null, null, 0], // Distance, angle
|
|
||||||
self::ACTION_CALL_TIMED_ACTIONLIST => [null, null, null, null, null, null, 0], // ID (overwrites already running actionlist), stop after combat?(0/1), timer update type(0-OOC, 1-IC, 2-ALWAYS)
|
|
||||||
self::ACTION_SET_NPC_FLAG => [['npcFlags', 10, false], null, null, null, null, null, 0], // Flags
|
|
||||||
self::ACTION_ADD_NPC_FLAG => [['npcFlags', 10, false], null, null, null, null, null, 0], // Flags
|
|
||||||
self::ACTION_REMOVE_NPC_FLAG => [['npcFlags', 10, false], null, null, null, null, null, 0], // Flags
|
|
||||||
self::ACTION_SIMPLE_TALK => [null, null, null, null, null, null, 0], // groupID, can be used to make players say groupID, Text_over event is not triggered, whisper can not be used (Target units will say the text)
|
|
||||||
self::ACTION_SELF_CAST => [Type::SPELL, ['castFlags', -1, false], null, null, null, null, 0], // spellID, castFlags
|
|
||||||
self::ACTION_CROSS_CAST => [Type::SPELL, ['castFlags', -1, false], null, null, null, null, 0], // spellID, castFlags, CasterTargetType, CasterTarget param1, CasterTarget param2, CasterTarget param3, ( + the origonal target fields as Destination target), CasterTargets will cast spellID on all Targets (use with caution if targeting multiple * multiple units)
|
|
||||||
self::ACTION_CALL_RANDOM_TIMED_ACTIONLIST => [null, null, null, null, null, null, 0], // script9 ids 1-9
|
|
||||||
self::ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST => [null, null, null, null, null, null, 0], // script9 id min, max
|
|
||||||
self::ACTION_RANDOM_MOVE => [null, null, null, null, null, null, 0], // maxDist
|
|
||||||
self::ACTION_SET_UNIT_FIELD_BYTES_1 => [['unitFieldBytes1', 10, false], null, null, null, null, null, 0], // bytes, target
|
|
||||||
self::ACTION_REMOVE_UNIT_FIELD_BYTES_1 => [['unitFieldBytes1', 10, false], null, null, null, null, null, 0], // bytes, target
|
|
||||||
self::ACTION_INTERRUPT_SPELL => [null, Type::SPELL, null, null, null, null, 0], //
|
|
||||||
self::ACTION_SEND_GO_CUSTOM_ANIM => [['dynFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_SET_DYNAMIC_FLAG => [['dynFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_ADD_DYNAMIC_FLAG => [['dynFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_REMOVE_DYNAMIC_FLAG => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_JUMP_TO_POS => [null, null, null, null, null, null, 0], // speedXY, speedZ, targetX, targetY, targetZ
|
|
||||||
self::ACTION_SEND_GOSSIP_MENU => [null, null, null, null, null, null, 0], // menuId, optionId
|
|
||||||
self::ACTION_GO_SET_LOOT_STATE => [['lootState', 10, false], null, null, null, null, null, 0], // state
|
|
||||||
self::ACTION_SEND_TARGET_TO_TARGET => [null, null, null, null, null, null, 0], // id
|
|
||||||
self::ACTION_SET_HOME_POS => [null, null, null, null, null, null, 0], // none
|
|
||||||
self::ACTION_SET_HEALTH_REGEN => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_SET_ROOT => [null, null, null, null, null, null, 0], // off/on
|
|
||||||
self::ACTION_SET_GO_FLAG => [['goFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_ADD_GO_FLAG => [['goFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_REMOVE_GO_FLAG => [['goFlags', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_SUMMON_CREATURE_GROUP => [null, null, null, null, null, null, 0], // Group, attackInvoker
|
|
||||||
self::ACTION_SET_POWER => [['powerType', 10, false], null, null, null, null, null, 0], // PowerType, newPower
|
|
||||||
self::ACTION_ADD_POWER => [['powerType', 10, false], null, null, null, null, null, 0], // PowerType, newPower
|
|
||||||
self::ACTION_REMOVE_POWER => [['powerType', 10, false], null, null, null, null, null, 0], // PowerType, newPower
|
|
||||||
self::ACTION_GAME_EVENT_STOP => [Type::WORLDEVENT, null, null, null, null, null, 0], // GameEventId
|
|
||||||
self::ACTION_GAME_EVENT_START => [Type::WORLDEVENT, null, null, null, null, null, 0], // GameEventId
|
|
||||||
self::ACTION_START_CLOSEST_WAYPOINT => [null, null, null, null, null, null, 0], // wp1, wp2, wp3, wp4, wp5, wp6, wp7
|
|
||||||
self::ACTION_MOVE_OFFSET => [null, null, null, null, null, null, 0], //
|
|
||||||
self::ACTION_RANDOM_SOUND => [Type::SOUND, Type::SOUND, Type::SOUND, Type::SOUND, null, null, 0], // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf
|
|
||||||
self::ACTION_SET_CORPSE_DELAY => [['formatTime', 10, false], null, null, null, null, null, 0], // timer
|
|
||||||
self::ACTION_DISABLE_EVADE => [null, null, null, null, null, null, 0], // 0/1 (1 = disabled, 0 = enabled)
|
|
||||||
self::ACTION_GO_SET_GO_STATE => [null, null, null, null, null, null, 0], // state
|
|
||||||
self::ACTION_SET_CAN_FLY => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_REMOVE_AURAS_BY_TYPE => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_SET_SIGHT_DIST => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_FLEE => [['formatTime', 10, false], null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_ADD_THREAT => [null, null, null, null, null, null, 0], // +threat, -threat
|
|
||||||
self::ACTION_LOAD_EQUIPMENT => [null, null, null, null, null, null, 0], // id
|
|
||||||
self::ACTION_TRIGGER_RANDOM_TIMED_EVENT => [['numRange', 10, false], null, null, null, null, null, 0], // id min range, id max range
|
|
||||||
self::ACTION_REMOVE_ALL_GAMEOBJECTS => [null, null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::ACTION_PAUSE_MOVEMENT => [null, ['formatTime', 10, true], null, null, null, null, 0], // MovementSlot (default = 0, active = 1, controlled = 2), PauseTime (ms), Force
|
|
||||||
self::ACTION_PLAY_ANIMKIT => [null, null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::ACTION_SCENE_PLAY => [null, null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::ACTION_SCENE_CANCEL => [null, null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::ACTION_SPAWN_SPAWNGROUP => [null, null, null, ['spawnFlags', 11, false], null, null, 0], // Group ID, min secs, max secs, spawnflags
|
|
||||||
self::ACTION_DESPAWN_SPAWNGROUP => [null, null, null, ['spawnFlags', 11, false], null, null, 0], // Group ID, min secs, max secs, spawnflags
|
|
||||||
self::ACTION_RESPAWN_BY_SPAWNID => [null, null, null, null, null, null, 0], // spawnType, spawnId
|
|
||||||
self::ACTION_INVOKER_CAST => [Type::SPELL, ['castFlags', -1, false], null, null, null, null, 0], // spellID, castFlags
|
|
||||||
self::ACTION_PLAY_CINEMATIC => [null, null, null, null, null, null, 0], // entry, cinematic
|
|
||||||
self::ACTION_SET_MOVEMENT_SPEED => [null, null, null, null, null, null, 0], // movementType, speedInteger, speedFraction
|
|
||||||
self::ACTION_PLAY_SPELL_VISUAL_KIT => [null, null, null, null, null, null, 2], // spellVisualKitId (RESERVED, PENDING CHERRYPICK)
|
|
||||||
self::ACTION_OVERRIDE_LIGHT => [Type::ZONE, null, null, ['formatTime', -1, true], null, null, 0], // zoneId, overrideLightID, transitionMilliseconds
|
|
||||||
self::ACTION_OVERRIDE_WEATHER => [Type::ZONE, ['weatherState', 10, false], null, null, null, null, 0], // zoneId, weatherId, intensity
|
|
||||||
self::ACTION_SET_AI_ANIM_KIT => [null, null, null, null, null, null, 2], // DEPRECATED, DO REUSE (it was never used in any branch, treat as free action id)
|
|
||||||
self::ACTION_SET_HOVER => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_SET_HEALTH_PCT => [null, null, null, null, null, null, 0], // percent
|
|
||||||
self::ACTION_CREATE_CONVERSATION => [null, null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::ACTION_SET_IMMUNE_PC => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_SET_IMMUNE_NPC => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_SET_UNINTERACTIBLE => [null, null, null, null, null, null, 0], // 0/1
|
|
||||||
self::ACTION_ACTIVATE_GAMEOBJECT => [null, null, null, null, null, null, 0], // GameObjectActions
|
|
||||||
self::ACTION_ADD_TO_STORED_TARGET_LIST => [null, null, null, null, null, null, 0], // varID
|
|
||||||
self::ACTION_BECOME_PERSONAL_CLONE_FOR_PLAYER => [null, null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::ACTION_TRIGGER_GAME_EVENT => [null, null, null, null, null, null, 2], // eventId, useSaiTargetAsGameEventSource (RESERVED, PENDING CHERRYPICK)
|
|
||||||
self::ACTION_DO_ACTION => [null, null, null, null, null, null, 2] // actionId (RESERVED, PENDING CHERRYPICK)
|
|
||||||
);
|
|
||||||
|
|
||||||
private array $jsGlobals = [];
|
|
||||||
private ?array $summons = null;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private int $id,
|
|
||||||
public readonly int $type,
|
|
||||||
private array $param,
|
|
||||||
private SmartAI &$smartAI)
|
|
||||||
{
|
|
||||||
// init additional parameters
|
|
||||||
Util::checkNumeric($this->param, NUM_CAST_INT);
|
|
||||||
$this->param = array_pad($this->param, 15, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function process() : array
|
|
||||||
{
|
|
||||||
$body =
|
|
||||||
$footer = '';
|
|
||||||
|
|
||||||
$actionTT = Lang::smartAI('actionTT', array_merge([$this->type], $this->param));
|
|
||||||
|
|
||||||
for ($i = 0; $i < 5; $i++)
|
|
||||||
{
|
|
||||||
$aParams = $this->data[$this->type];
|
|
||||||
|
|
||||||
if (is_array($aParams[$i]))
|
|
||||||
{
|
|
||||||
[$fn, $idx, $extraParam] = $aParams[$i];
|
|
||||||
|
|
||||||
if ($idx < 0)
|
|
||||||
$footer = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
|
|
||||||
else
|
|
||||||
$this->param[$idx] = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
|
|
||||||
}
|
|
||||||
else if (is_int($aParams[$i]) && $this->param[$i])
|
|
||||||
$this->jsGlobals[$aParams[$i]][$this->param[$i]] = $this->param[$i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-generic cases
|
|
||||||
switch ($this->type)
|
|
||||||
{
|
|
||||||
case self::ACTION_FLEE_FOR_ASSIST: // 25 -> none
|
|
||||||
case self::ACTION_CALL_FOR_HELP: // 39 -> self
|
|
||||||
if ($this->param[0])
|
|
||||||
$footer = $this->param;
|
|
||||||
break;
|
|
||||||
case self::ACTION_INTERRUPT_SPELL: // 92 -> self
|
|
||||||
if (!$this->param[1])
|
|
||||||
$footer = $this->param;
|
|
||||||
break;
|
|
||||||
case self::ACTION_UPDATE_TEMPLATE: // 36
|
|
||||||
case self::ACTION_SET_CORPSE_DELAY: // 116
|
|
||||||
if ($this->param[1])
|
|
||||||
$footer = $this->param;
|
|
||||||
break;
|
|
||||||
case self::ACTION_PAUSE_MOVEMENT: // 127 -> any target [ye, not gonna resolve this nonsense]
|
|
||||||
case self::ACTION_REMOVEAURASFROMSPELL: // 28 -> any target
|
|
||||||
case self::ACTION_SOUND: // 4 -> self [param3 set in DB but not used in core?]
|
|
||||||
case self::ACTION_SUMMON_GO: // 50 -> self, world coords
|
|
||||||
case self::ACTION_MOVE_TO_POS: // 69 -> any target
|
|
||||||
if ($this->param[2])
|
|
||||||
$footer = $this->param;
|
|
||||||
break;
|
|
||||||
case self::ACTION_WP_START: // 53 -> any .. why tho?
|
|
||||||
if ($this->param[2] || $this->param[5])
|
|
||||||
$footer = $this->param;
|
|
||||||
break;
|
|
||||||
case self::ACTION_PLAY_EMOTE: // 5 -> any target
|
|
||||||
case self::ACTION_SET_EMOTE_STATE: // 17 -> any target
|
|
||||||
if ($this->param[0])
|
|
||||||
{
|
|
||||||
$this->param[0] *= -1; // handle creature emote
|
|
||||||
$this->jsGlobals[Type::EMOTE][$this->param[0]] = $this->param[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::ACTION_RANDOM_EMOTE: // 10 -> any target
|
|
||||||
$buff = [];
|
|
||||||
for ($i = 0; $i < 6; $i++)
|
|
||||||
{
|
|
||||||
if (empty($this->param[$i]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$this->param[$i] *= -1; // handle creature emote
|
|
||||||
$buff[] = '[emote='.$this->param[$i].']';
|
|
||||||
$this->jsGlobals[Type::EMOTE][$this->param[$i]] = $this->param[$i];
|
|
||||||
}
|
|
||||||
$this->param[10] = Lang::concat($buff, Lang::CONCAT_OR);
|
|
||||||
break;
|
|
||||||
case self::ACTION_SET_FACTION: // 2 -> any target
|
|
||||||
if ($this->param[0])
|
|
||||||
{
|
|
||||||
$this->param[10] = DB::Aowow()->selectCell('SELECT `factionId` FROM ?_factiontemplate WHERE `id` = ?d', $this->param[0]);
|
|
||||||
$this->jsGlobals[Type::FACTION][$this->param[10]] = $this->param[10];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::ACTION_MORPH_TO_ENTRY_OR_MODEL: // 3 -> self
|
|
||||||
case self::ACTION_MOUNT_TO_ENTRY_OR_MODEL: // 43 -> self
|
|
||||||
if (!$this->param[0] && !$this->param[1])
|
|
||||||
$this->param[10] = 1;
|
|
||||||
break;
|
|
||||||
case self::ACTION_THREAT_SINGLE_PCT: // 13 -> victim
|
|
||||||
case self::ACTION_THREAT_ALL_PCT: // 14 -> self
|
|
||||||
case self::ACTION_ADD_THREAT: // 123 -> any target
|
|
||||||
$this->param[10] = $this->param[0] - $this->param[1];
|
|
||||||
break;
|
|
||||||
case self::ACTION_FOLLOW: // 29 -> any target
|
|
||||||
if ($this->param[1])
|
|
||||||
{
|
|
||||||
$this->param[10] = Util::O2Deg($this->param[1])[0];
|
|
||||||
$footer = $this->param;
|
|
||||||
}
|
|
||||||
if ($this->param[3])
|
|
||||||
{
|
|
||||||
if ($this->param[4])
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::QUEST][$this->param[3]] = $this->param[3];
|
|
||||||
$this->param[11] = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$this->param[3]] = $this->param[3];
|
|
||||||
$this->param[12] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::ACTION_RANDOM_PHASE: // 30 -> self
|
|
||||||
$buff = [];
|
|
||||||
for ($i = 0; $i < 7; $i++)
|
|
||||||
if ($_ = $this->param[$i])
|
|
||||||
$buff[] = $_;
|
|
||||||
|
|
||||||
$this->param[10] = Lang::concat($buff);
|
|
||||||
break;
|
|
||||||
case self::ACTION_ACTIVATE_TAXI: // 52 -> invoker
|
|
||||||
$nodes = DB::Aowow()->selectRow(
|
|
||||||
'SELECT tn1.`name_loc0` AS "start_loc0", tn1.name_loc?d AS start_loc?d, tn2.`name_loc0` AS "end_loc0", tn2.name_loc?d AS end_loc?d
|
|
||||||
FROM ?_taxipath tp
|
|
||||||
JOIN ?_taxinodes tn1 ON tp.`startNodeId` = tn1.`id`
|
|
||||||
JOIN ?_taxinodes tn2 ON tp.`endNodeId` = tn2.`id`
|
|
||||||
WHERE tp.`id` = ?d',
|
|
||||||
Lang::getLocale()->value, Lang::getLocale()->value, Lang::getLocale()->value, Lang::getLocale()->value, $this->param[0]
|
|
||||||
);
|
|
||||||
$this->param[10] = Util::jsEscape(Util::localizedString($nodes, 'start'));
|
|
||||||
$this->param[11] = Util::jsEscape(Util::localizedString($nodes, 'end'));
|
|
||||||
break;
|
|
||||||
case self::ACTION_SET_INGAME_PHASE_MASK: // 44 -> any target
|
|
||||||
if ($this->param[0])
|
|
||||||
$this->param[10] = Lang::concat(Util::mask2bits($this->param[0]));
|
|
||||||
break;
|
|
||||||
case self::ACTION_TELEPORT: // 62 -> invoker
|
|
||||||
[$x, $y, $z, $o] = $this->smartAI->getTarget()->getWorldPos();
|
|
||||||
// try from areatrigger setup data
|
|
||||||
if ($this->smartAI->teleportTargetArea)
|
|
||||||
$this->param[10] = $this->smartAI->teleportTargetArea;
|
|
||||||
// try calc from SmartTarget data
|
|
||||||
else if ($pos = WorldPosition::toZonePos($this->param[0], $x, $y))
|
|
||||||
{
|
|
||||||
$this->param[10] = $pos[0]['areaId'];
|
|
||||||
$this->param[11] = str_pad($pos[0]['posX'] * 10, 3, '0', STR_PAD_LEFT).str_pad($pos[0]['posY'] * 10, 3, '0', STR_PAD_LEFT);
|
|
||||||
}
|
|
||||||
// maybe the mapId is an instane map
|
|
||||||
else if ($areaId = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?d', $this->param[0]))
|
|
||||||
$this->param[10] = $areaId;
|
|
||||||
// ...whelp
|
|
||||||
else
|
|
||||||
trigger_error('SmartAction::process - could not resolve teleport target: map:'.$this->param[0].' x:'.$x.' y:'.$y);
|
|
||||||
|
|
||||||
if ($this->param[10])
|
|
||||||
$this->jsGlobals[Type::ZONE][$this->param[10]] = $this->param[10];
|
|
||||||
break;
|
|
||||||
case self::ACTION_SET_ORIENTATION: // 66 -> any target
|
|
||||||
if ($this->smartAI->getTarget()->type == SmartTarget::TARGET_POSITION)
|
|
||||||
$this->param[10] = Util::O2Deg($this->smartAI->getTarget()->getWorldPos()[3])[1];
|
|
||||||
else if ($this->smartAI->getTarget()->type != SmartTarget::TARGET_SELF)
|
|
||||||
$this->param[10] = '#target#';
|
|
||||||
break;
|
|
||||||
case self::ACTION_EQUIP: // 71 -> any
|
|
||||||
$equip = [];
|
|
||||||
|
|
||||||
if ($this->param[0])
|
|
||||||
{
|
|
||||||
$slots = $this->param[1] ? Util::mask2bits($this->param[1], 1) : [1, 2, 3];
|
|
||||||
$items = DB::World()->selectRow('SELECT `ItemID1`, `ItemID2`, `ItemID3` FROM creature_equip_template WHERE `CreatureID` = ?d AND `ID` = ?d', $this->smartAI->getEntry(), $this->param[0]);
|
|
||||||
|
|
||||||
foreach ($slots as $s)
|
|
||||||
if ($_ = $items['ItemID'.$s])
|
|
||||||
$equip[] = $_;
|
|
||||||
}
|
|
||||||
else if ($this->param[2] || $this->param[3] || $this->param[4])
|
|
||||||
{
|
|
||||||
if ($_ = $this->param[2])
|
|
||||||
$equip[] = $_;
|
|
||||||
if ($_ = $this->param[3])
|
|
||||||
$equip[] = $_;
|
|
||||||
if ($_ = $this->param[4])
|
|
||||||
$equip[] = $_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($equip)
|
|
||||||
{
|
|
||||||
$this->param[10] = Lang::concat($equip, callback: fn($x) => '[item='.$x.']');
|
|
||||||
$footer = true;
|
|
||||||
|
|
||||||
foreach ($equip as $_)
|
|
||||||
$this->jsGlobals[Type::ITEM][$_] = $_;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::ACTION_LOAD_EQUIPMENT: // 124 -> any target
|
|
||||||
$buff = [];
|
|
||||||
if ($this->param[0])
|
|
||||||
{
|
|
||||||
$items = DB::World()->selectRow('SELECT `ItemID1`, `ItemID2`, `ItemID3` FROM creature_equip_template WHERE `CreatureID` = ?d AND `ID` = ?d', $this->smartAI->getEntry(), $this->param[0]);
|
|
||||||
foreach ($items as $i)
|
|
||||||
{
|
|
||||||
if (!$i)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$this->jsGlobals[Type::ITEM][$i] = $i;
|
|
||||||
$buff[] = '[item='.$i.']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!$this->param[1])
|
|
||||||
trigger_error('SmartAI::action - action #124 (SmartAction::ACTION_LOAD_EQIPMENT) is malformed');
|
|
||||||
|
|
||||||
$this->param[10] = Lang::concat($buff);
|
|
||||||
$footer = true;
|
|
||||||
break;
|
|
||||||
case self::ACTION_CALL_TIMED_ACTIONLIST: // 80 -> any target
|
|
||||||
$this->param[10] = match ($this->param[1])
|
|
||||||
{
|
|
||||||
0, 1, 2 => Lang::smartAI('saiUpdate', $this->param[1]),
|
|
||||||
default => Lang::smartAI('saiUpdateUNK', [$this->param[1]])
|
|
||||||
};
|
|
||||||
|
|
||||||
$tal = new SmartAI(SmartAI::SRC_TYPE_ACTIONLIST, $this->param[0], ['baseEntry' => $this->smartAI->getEntry()]);
|
|
||||||
$tal->prepare();
|
|
||||||
|
|
||||||
Util::mergeJsGlobals($this->jsGlobals, $tal->getJSGlobals());
|
|
||||||
|
|
||||||
foreach ($tal->getTabs() as $guid => $tt)
|
|
||||||
$this->smartAI->addTab($guid, $tt);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case self::ACTION_CALL_KILLEDMONSTER: // 33: Note: If target is SMART_TARGET_NONE (0) or SMART_TARGET_SELF (1), the kill is credited to all players eligible for loot from this creature.
|
|
||||||
if ($this->smartAI->getTarget()->type == SmartTarget::TARGET_SELF || $this->smartAI->getTarget()->type == SmartTarget::TARGET_NONE)
|
|
||||||
$this->param[10] = (new SmartTarget($this->id, SmartTarget::TARGET_LOOT_RECIPIENTS, [], [], $this->smartAI))->process();
|
|
||||||
break;
|
|
||||||
case self::ACTION_CROSS_CAST: // 86 -> entity by TargetingBlock(param3, param4, param5, param6) cross cast spell <param1> at any target
|
|
||||||
$this->param[10] = (new SmartTarget($this->id, $this->param[2], [$this->param[3], $this->param[4], $this->param[5]], [], $this->smartAI))->process();
|
|
||||||
break;
|
|
||||||
case self::ACTION_CALL_RANDOM_TIMED_ACTIONLIST: // 87 -> self
|
|
||||||
$talBuff = [];
|
|
||||||
for ($i = 0; $i < 6; $i++)
|
|
||||||
{
|
|
||||||
if (!$this->param[$i])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$talBuff[] = sprintf(self::TAL_TAB_ANCHOR, $this->param[$i]);
|
|
||||||
|
|
||||||
$tal = new SmartAI(SmartAI::SRC_TYPE_ACTIONLIST, $this->param[$i], ['baseEntry' => $this->smartAI->getEntry()]);
|
|
||||||
$tal->prepare();
|
|
||||||
|
|
||||||
Util::mergeJsGlobals($this->jsGlobals, $tal->getJSGlobals());
|
|
||||||
|
|
||||||
foreach ($tal->getTabs() as $guid => $tt)
|
|
||||||
$this->smartAI->addTab($guid, $tt);
|
|
||||||
}
|
|
||||||
$this->param[10] = Lang::concat($talBuff, Lang::CONCAT_OR);
|
|
||||||
break;
|
|
||||||
case self::ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST:// 88 -> self
|
|
||||||
$talBuff = [];
|
|
||||||
for ($i = $this->param[0]; $i <= $this->param[1]; $i++)
|
|
||||||
{
|
|
||||||
$talBuff[] = sprintf(self::TAL_TAB_ANCHOR, $i);
|
|
||||||
|
|
||||||
$tal = new SmartAI(SmartAI::SRC_TYPE_ACTIONLIST, $i, ['baseEntry' => $this->smartAI->getEntry()]);
|
|
||||||
$tal->prepare();
|
|
||||||
|
|
||||||
Util::mergeJsGlobals($this->jsGlobals, $tal->getJSGlobals());
|
|
||||||
|
|
||||||
foreach ($tal->getTabs() as $guid => $tt)
|
|
||||||
$this->smartAI->addTab($guid, $tt);
|
|
||||||
}
|
|
||||||
$this->param[10] = Lang::concat($talBuff, Lang::CONCAT_OR);
|
|
||||||
break;
|
|
||||||
case self::ACTION_SET_HOME_POS: // 101 -> self
|
|
||||||
if ($this->smartAI->getTarget()?->type == Smarttarget::TARGET_SELF)
|
|
||||||
$this->param[10] = 1;
|
|
||||||
// do not break;
|
|
||||||
case self::ACTION_JUMP_TO_POS: // 97 -> self
|
|
||||||
case self::ACTION_MOVE_OFFSET: // 114 -> self
|
|
||||||
array_splice($this->param, 11, replacement: $this->smartAI->getTarget()->getWorldPos());
|
|
||||||
break;
|
|
||||||
case self::ACTION_SUMMON_CREATURE_GROUP: // 107 -> untargeted
|
|
||||||
if ($this->summons === null)
|
|
||||||
$this->summons = DB::World()->selectCol('SELECT `groupId` AS ARRAY_KEY, `entry` AS ARRAY_KEY2, COUNT(*) AS "n" FROM creature_summon_groups WHERE `summonerId` = ?d GROUP BY `groupId`, `entry`', $this->smartAI->getEntry());
|
|
||||||
|
|
||||||
$buff = [];
|
|
||||||
if (!empty($this->summons[$this->param[0]]))
|
|
||||||
{
|
|
||||||
foreach ($this->summons[$this->param[0]] as $id => $n)
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$id] = $id;
|
|
||||||
$buff[] = $n.'x [npc='.$id.']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($buff)
|
|
||||||
$this->param[10] = Lang::concat($buff);
|
|
||||||
break;
|
|
||||||
case self::ACTION_START_CLOSEST_WAYPOINT: // 113 -> any target
|
|
||||||
$this->param[10] = Lang::concat(array_filter($this->param), Lang::CONCAT_OR, fn($x) => '#[b]'.$x.'[/b]');
|
|
||||||
break;
|
|
||||||
case self::ACTION_RANDOM_SOUND: // 115 -> self
|
|
||||||
for ($i = 0; $i < 4; $i++)
|
|
||||||
{
|
|
||||||
if ($x = $this->param[$i])
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::SOUND][$x] = $x;
|
|
||||||
$this->param[10] .= '[sound='.$x.']';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->param[5])
|
|
||||||
$footer = true;
|
|
||||||
break;
|
|
||||||
case self::ACTION_GO_SET_GO_STATE: // 118 -> ???
|
|
||||||
$this->param[10] = match ($this->param[0])
|
|
||||||
{
|
|
||||||
0, 1, 2 => Lang::smartAI('GOStates', $this->param[0]),
|
|
||||||
default => Lang::smartAI('GOStateUNK', [$this->param[0]])
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case self::ACTION_REMOVE_AURAS_BY_TYPE: // 120 -> any target
|
|
||||||
$this->param[10] = Lang::spell('auras', $this->param[0]);
|
|
||||||
break;
|
|
||||||
case self::ACTION_SPAWN_SPAWNGROUP: // 131
|
|
||||||
case self::ACTION_DESPAWN_SPAWNGROUP: // 132
|
|
||||||
$this->param[10] = Util::jsEscape(DB::World()->selectCell('SELECT `GroupName` FROM spawn_group_template WHERE `groupId` = ?d', $this->param[0]));
|
|
||||||
$entities = DB::World()->select('SELECT `spawnType` AS "0", `spawnId` AS "1" FROM spawn_group WHERE `groupId` = ?d', $this->param[0]);
|
|
||||||
|
|
||||||
$n = 5;
|
|
||||||
$buff = [];
|
|
||||||
foreach ($entities as [$spawnType, $guid])
|
|
||||||
{
|
|
||||||
$type = Type::NPC;
|
|
||||||
if ($spawnType == 1)
|
|
||||||
$type == Type::OBJECT;
|
|
||||||
|
|
||||||
if ($_ = $this->resolveGuid($type, $guid))
|
|
||||||
{
|
|
||||||
$this->jsGlobals[$type][$_] = $_;
|
|
||||||
$buff[] = '['.Type::getFileString($type).'='.$_.'][small class=q0] (GUID: '.$guid.')[/small]';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$buff[] = Lang::smartAI('entityUNK').'[small class=q0] (GUID: '.$guid.')[/small]';
|
|
||||||
|
|
||||||
if (!--$n)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($entities) > 5)
|
|
||||||
$buff[] = '+'.(count($entities) - 5).'…';
|
|
||||||
|
|
||||||
$this->param[12] = '[ul][li]'.implode('[/li][li]', $buff).'[/li][/ul]';
|
|
||||||
|
|
||||||
// i'd like this stored in $data but numRange can only handle msec
|
|
||||||
if ($time = $this->numRange($this->param[1] * 1000, $this->param[2] * 1000, true))
|
|
||||||
$footer = [$time];
|
|
||||||
break;
|
|
||||||
case self::ACTION_RESPAWN_BY_SPAWNID: // 133
|
|
||||||
$type = Type::NPC;
|
|
||||||
if ($this->param[0] == 1)
|
|
||||||
$type == Type::OBJECT;
|
|
||||||
|
|
||||||
if ($_ = $this->resolveGuid($type, $this->param[1]))
|
|
||||||
{
|
|
||||||
$this->param[10] = '['.Type::getFileString($type).'='.$_.']';
|
|
||||||
$this->jsGlobals[$type][$_] = $_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$this->param[10] = Lang::smartAI('entityUNK');
|
|
||||||
break;
|
|
||||||
case self::ACTION_SET_MOVEMENT_SPEED: // 136
|
|
||||||
$this->param[10] = $this->param[1] + $this->param[2] / pow(10, floor(log10($this->param[2] ?: 1.0) + 1)); // i know string concatenation is a thing. don't @ me!
|
|
||||||
break;
|
|
||||||
case self::ACTION_TALK: // 1 -> any target
|
|
||||||
case self::ACTION_SIMPLE_TALK: // 84 -> any target
|
|
||||||
$noSrc = false;
|
|
||||||
if ($npcId = $this->smartAI->getTarget()->getTalkSource($noSrc))
|
|
||||||
{
|
|
||||||
if ($quotes = $this->smartAI->getQuote($npcId, $this->param[0], $npcSrc))
|
|
||||||
foreach ($quotes as ['text' => $text, 'prefix' => $prefix])
|
|
||||||
$this->param[10] .= sprintf($text, $noSrc ? '' : sprintf($prefix, $npcSrc), $npcSrc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('SmartAI::action - could not determine talk source for action #'.$this->type);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->smartAI->addJsGlobals($this->jsGlobals);
|
|
||||||
|
|
||||||
$body = Lang::smartAI('actions', $this->type, 0, $this->param) ?? Lang::smartAI('actionUNK', [$this->type]);
|
|
||||||
if ($footer)
|
|
||||||
$footer = Lang::smartAI('actions', $this->type, 1, (array)$footer);
|
|
||||||
|
|
||||||
// resolve conditionals
|
|
||||||
$i = 0;
|
|
||||||
while (strstr($body, ')?') && $i++ < 3)
|
|
||||||
$body = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $body);
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
while (strstr($footer, ')?') && $i++ < 3)
|
|
||||||
$footer = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $footer);
|
|
||||||
|
|
||||||
// wrap body in tooltip
|
|
||||||
return [sprintf(self::ACTION_CELL_TPL, $actionTT, $body), $footer];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,382 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
// TrinityCore - SmartAI
|
|
||||||
class SmartEvent
|
|
||||||
{
|
|
||||||
use SmartHelper;
|
|
||||||
|
|
||||||
public const EVENT_UPDATE_IC = 0; // In combat.
|
|
||||||
public const EVENT_UPDATE_OOC = 1; // Out of combat.
|
|
||||||
public const EVENT_HEALTH_PCT = 2; // Health Percentage
|
|
||||||
public const EVENT_MANA_PCT = 3; // Mana Percentage
|
|
||||||
public const EVENT_AGGRO = 4; // On Creature Aggro
|
|
||||||
public const EVENT_KILL = 5; // On Creature Kill
|
|
||||||
public const EVENT_DEATH = 6; // On Creature Death
|
|
||||||
public const EVENT_EVADE = 7; // On Creature Evade Attack
|
|
||||||
public const EVENT_SPELLHIT = 8; // On Creature/Gameobject Spell Hit
|
|
||||||
public const EVENT_RANGE = 9; // On Target In Range
|
|
||||||
public const EVENT_OOC_LOS = 10; // On Target In Distance Out of Combat
|
|
||||||
public const EVENT_RESPAWN = 11; // On Creature/Gameobject Respawn
|
|
||||||
public const EVENT_TARGET_HEALTH_PCT = 12; // [DEPRECATED] On Target Health Percentage
|
|
||||||
public const EVENT_VICTIM_CASTING = 13; // On Target Casting Spell
|
|
||||||
public const EVENT_FRIENDLY_HEALTH = 14; // [DEPRECATED] On Friendly Health Deficit
|
|
||||||
public const EVENT_FRIENDLY_IS_CC = 15; //
|
|
||||||
public const EVENT_FRIENDLY_MISSING_BUFF = 16; // On Friendly Lost Buff
|
|
||||||
public const EVENT_SUMMONED_UNIT = 17; // On Creature/Gameobject Summoned Unit
|
|
||||||
public const EVENT_TARGET_MANA_PCT = 18; // [DEPRECATED] On Target Mana Percentage
|
|
||||||
public const EVENT_ACCEPTED_QUEST = 19; // On Target Accepted Quest
|
|
||||||
public const EVENT_REWARD_QUEST = 20; // On Target Rewarded Quest
|
|
||||||
public const EVENT_REACHED_HOME = 21; // On Creature Reached Home
|
|
||||||
public const EVENT_RECEIVE_EMOTE = 22; // On Receive Emote.
|
|
||||||
public const EVENT_HAS_AURA = 23; // On Creature Has Aura
|
|
||||||
public const EVENT_TARGET_BUFFED = 24; // On Target Buffed With Spell
|
|
||||||
public const EVENT_RESET = 25; // After Combat, On Respawn or Spawn
|
|
||||||
public const EVENT_IC_LOS = 26; // On Target In Distance In Combat
|
|
||||||
public const EVENT_PASSENGER_BOARDED = 27; //
|
|
||||||
public const EVENT_PASSENGER_REMOVED = 28; //
|
|
||||||
public const EVENT_CHARMED = 29; // On Creature Charmed
|
|
||||||
public const EVENT_CHARMED_TARGET = 30; // [DEPRECATED] On Target Charmed
|
|
||||||
public const EVENT_SPELLHIT_TARGET = 31; // On Target Spell Hit
|
|
||||||
public const EVENT_DAMAGED = 32; // On Creature Damaged
|
|
||||||
public const EVENT_DAMAGED_TARGET = 33; // On Target Damaged
|
|
||||||
public const EVENT_MOVEMENTINFORM = 34; // WAYPOINT_MOTION_TYPE = 2, POINT_MOTION_TYPE = 8
|
|
||||||
public const EVENT_SUMMON_DESPAWNED = 35; // On Summoned Unit Despawned
|
|
||||||
public const EVENT_CORPSE_REMOVED = 36; // On Creature Corpse Removed
|
|
||||||
public const EVENT_AI_INIT = 37; //
|
|
||||||
public const EVENT_DATA_SET = 38; // On Creature/Gameobject Data Set, Can be used with SMART_ACTION_SET_DATA
|
|
||||||
public const EVENT_WAYPOINT_START = 39; // [DEPRECATED] On Creature Waypoint ID Started
|
|
||||||
public const EVENT_WAYPOINT_REACHED = 40; // On Creature Waypoint ID Reached
|
|
||||||
public const EVENT_TRANSPORT_ADDPLAYER = 41; // [RESERVED]
|
|
||||||
public const EVENT_TRANSPORT_ADDCREATURE = 42; // [RESERVED]
|
|
||||||
public const EVENT_TRANSPORT_REMOVE_PLAYER = 43; // [RESERVED]
|
|
||||||
public const EVENT_TRANSPORT_RELOCATE = 44; // [RESERVED]
|
|
||||||
public const EVENT_INSTANCE_PLAYER_ENTER = 45; // [RESERVED]
|
|
||||||
public const EVENT_AREATRIGGER_ONTRIGGER = 46; //
|
|
||||||
public const EVENT_QUEST_ACCEPTED = 47; // [RESERVED] On Target Quest Accepted
|
|
||||||
public const EVENT_QUEST_OBJ_COMPLETION = 48; // [RESERVED] On Target Quest Objective Completed
|
|
||||||
public const EVENT_QUEST_COMPLETION = 49; // [RESERVED] On Target Quest Completed
|
|
||||||
public const EVENT_QUEST_REWARDED = 50; // [RESERVED] On Target Quest Rewarded
|
|
||||||
public const EVENT_QUEST_FAIL = 51; // [RESERVED] On Target Quest Field
|
|
||||||
public const EVENT_TEXT_OVER = 52; // On TEXT_OVER Event Triggered After SMART_ACTION_TALK
|
|
||||||
public const EVENT_RECEIVE_HEAL = 53; // On Creature Received Healing
|
|
||||||
public const EVENT_JUST_SUMMONED = 54; // On Creature Just spawned
|
|
||||||
public const EVENT_WAYPOINT_PAUSED = 55; // On Creature Paused at Waypoint ID
|
|
||||||
public const EVENT_WAYPOINT_RESUMED = 56; // On Creature Resumed after Waypoint ID
|
|
||||||
public const EVENT_WAYPOINT_STOPPED = 57; // On Creature Stopped On Waypoint ID
|
|
||||||
public const EVENT_WAYPOINT_ENDED = 58; // On Creature Waypoint Path Ended
|
|
||||||
public const EVENT_TIMED_EVENT_TRIGGERED = 59; //
|
|
||||||
public const EVENT_UPDATE = 60; //
|
|
||||||
public const EVENT_LINK = 61; // Used to link together multiple events as a chain of events.
|
|
||||||
public const EVENT_GOSSIP_SELECT = 62; // On gossip clicked (gossip_menu_option335).
|
|
||||||
public const EVENT_JUST_CREATED = 63; //
|
|
||||||
public const EVENT_GOSSIP_HELLO = 64; // On Right-Click Creature/Gameobject that have gossip enabled.
|
|
||||||
public const EVENT_FOLLOW_COMPLETED = 65; //
|
|
||||||
public const EVENT_EVENT_PHASE_CHANGE = 66; // [DEPRECATED] On event phase mask set
|
|
||||||
public const EVENT_IS_BEHIND_TARGET = 67; // [DEPRECATED] On Creature is behind target.
|
|
||||||
public const EVENT_GAME_EVENT_START = 68; // On game_event started.
|
|
||||||
public const EVENT_GAME_EVENT_END = 69; // On game_event ended.
|
|
||||||
public const EVENT_GO_LOOT_STATE_CHANGED = 70; //
|
|
||||||
public const EVENT_GO_EVENT_INFORM = 71; //
|
|
||||||
public const EVENT_ACTION_DONE = 72; //
|
|
||||||
public const EVENT_ON_SPELLCLICK = 73; //
|
|
||||||
public const EVENT_FRIENDLY_HEALTH_PCT = 74; //
|
|
||||||
public const EVENT_DISTANCE_CREATURE = 75; // On creature guid OR any instance of creature entry is within distance.
|
|
||||||
public const EVENT_DISTANCE_GAMEOBJECT = 76; // On gameobject guid OR any instance of gameobject entry is within distance.
|
|
||||||
public const EVENT_COUNTER_SET = 77; // If the value of specified counterID is equal to a specified value
|
|
||||||
public const EVENT_SCENE_START = 78; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const EVENT_SCENE_TRIGGER = 79; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const EVENT_SCENE_CANCEL = 80; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const EVENT_SCENE_COMPLETE = 81; // [RESERVED] don't use on 3.3.5a
|
|
||||||
public const EVENT_SUMMONED_UNIT_DIES = 82; //
|
|
||||||
public const EVENT_ON_SPELL_CAST = 83; // On Spell::cast
|
|
||||||
public const EVENT_ON_SPELL_FAILED = 84; // On Unit::InterruptSpell
|
|
||||||
public const EVENT_ON_SPELL_START = 85; // On Spell::prapare
|
|
||||||
public const EVENT_ON_DESPAWN = 86; // On before creature removed
|
|
||||||
|
|
||||||
public const FLAG_NO_REPEAT = 0x0001;
|
|
||||||
public const FLAG_DIFFICULTY_0 = 0x0002;
|
|
||||||
public const FLAG_DIFFICULTY_1 = 0x0004;
|
|
||||||
public const FLAG_DIFFICULTY_2 = 0x0008;
|
|
||||||
public const FLAG_DIFFICULTY_3 = 0x0010;
|
|
||||||
public const FLAG_DEBUG_ONLY = 0x0080;
|
|
||||||
public const FLAG_NO_RESET = 0x0100;
|
|
||||||
public const FLAG_WHILE_CHARMED = 0x0200;
|
|
||||||
public const FLAG_ALL_DIFFICULTIES = self::FLAG_DIFFICULTY_0 | self::FLAG_DIFFICULTY_1 | self::FLAG_DIFFICULTY_2 | self::FLAG_DIFFICULTY_3;
|
|
||||||
|
|
||||||
private const EVENT_CELL_TPL = '[tooltip name=e-#rowIdx#]%1$s[/tooltip][span tooltip=e-#rowIdx#]%2$s[/span]';
|
|
||||||
|
|
||||||
private array $data = array( // param 1-5 - int > 0: type, array: [fn, newIdx, extraParam]; error class: int
|
|
||||||
self::EVENT_UPDATE_IC => [['numRange', 10, true], null, ['numRange', -1, true], null, null, 0], // InitialMin, InitialMax, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_UPDATE_OOC => [['numRange', 10, true], null, ['numRange', -1, true], null, null, 0], // InitialMin, InitialMax, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_HEALTH_PCT => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // HPMin%, HPMax%, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_MANA_PCT => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // ManaMin%, ManaMax%, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_AGGRO => [null, null, null, null, null, 0], // NONE
|
|
||||||
self::EVENT_KILL => [['numRange', -1, true], null, null, Type::NPC, null, 0], // CooldownMin0, CooldownMax1, playerOnly2, else creature entry3
|
|
||||||
self::EVENT_DEATH => [null, null, null, null, null, 0], // NONE
|
|
||||||
self::EVENT_EVADE => [null, null, null, null, null, 0], // NONE
|
|
||||||
self::EVENT_SPELLHIT => [Type::SPELL, ['magicSchool', 10, false], ['numRange', -1, true], null, null, 0], // SpellID, School, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_RANGE => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // MinDist, MaxDist, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_OOC_LOS => [['hostilityMode', 10, false], null, ['numRange', -1, true], null, null, 0], // hostilityModes, MaxRange, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_RESPAWN => [null, null, Type::ZONE, null, null, 0], // type, MapId, ZoneId
|
|
||||||
self::EVENT_TARGET_HEALTH_PCT => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_VICTIM_CASTING => [['numRange', -1, true], null, Type::SPELL, null, null, 0], // RepeatMin, RepeatMax, spellid
|
|
||||||
self::EVENT_FRIENDLY_HEALTH => [null, null, ['numRange', -1, true], null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_FRIENDLY_IS_CC => [null, ['numRange', -1, true], null, null, null, 0], // Radius, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_FRIENDLY_MISSING_BUFF => [Type::SPELL, null, ['numRange', -1, true], null, null, 0], // SpellId, Radius, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_SUMMONED_UNIT => [Type::NPC, ['numRange', -1, true], null, null, null, 0], // CreatureId(0 all), CooldownMin, CooldownMax
|
|
||||||
self::EVENT_TARGET_MANA_PCT => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_ACCEPTED_QUEST => [Type::QUEST, ['numRange', -1, true], null, null, null, 0], // QuestID (0 = any), CooldownMin, CooldownMax
|
|
||||||
self::EVENT_REWARD_QUEST => [Type::QUEST, ['numRange', -1, true], null, null, null, 0], // QuestID (0 = any), CooldownMin, CooldownMax
|
|
||||||
self::EVENT_REACHED_HOME => [null, null, null, null, null, 0], // NONE
|
|
||||||
self::EVENT_RECEIVE_EMOTE => [Type::EMOTE, ['numRange', -1, true], null, null, null, 0], // EmoteId, CooldownMin, CooldownMax, condition, val1, val2, val3
|
|
||||||
self::EVENT_HAS_AURA => [Type::SPELL, null, ['numRange', -1, true], null, null, 0], // Param1 = SpellID, Param2 = Stack amount, Param3/4 RepeatMin, RepeatMax
|
|
||||||
self::EVENT_TARGET_BUFFED => [Type::SPELL, null, ['numRange', -1, true], null, null, 0], // Param1 = SpellID, Param2 = Stack amount, Param3/4 RepeatMin, RepeatMax
|
|
||||||
self::EVENT_RESET => [null, null, null, null, null, 0], // Called after combat, when the creature respawn and spawn.
|
|
||||||
self::EVENT_IC_LOS => [['hostilityMode', 10, false], null, ['numRange', -1, true], null, null, 0], // hostilityModes, MaxRnage, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_PASSENGER_BOARDED => [['numRange', -1, true], null, null, null, null, 0], // CooldownMin, CooldownMax
|
|
||||||
self::EVENT_PASSENGER_REMOVED => [['numRange', -1, true], null, null, null, null, 0], // CooldownMin, CooldownMax
|
|
||||||
self::EVENT_CHARMED => [null, null, null, null, null, 0], // onRemove (0 - on apply, 1 - on remove)
|
|
||||||
self::EVENT_CHARMED_TARGET => [null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_SPELLHIT_TARGET => [Type::SPELL, ['magicSchool', 10, false], ['numRange', -1, true], null, null, 0], // SpellID, School, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_DAMAGED => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // MinDmg, MaxDmg, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_DAMAGED_TARGET => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // MinDmg, MaxDmg, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_MOVEMENTINFORM => [['motionType', 10, false], null, null, null, null, 0], // MovementType(any), PointID
|
|
||||||
self::EVENT_SUMMON_DESPAWNED => [Type::NPC, ['numRange', -1, true], null, null, null, 0], // Entry, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_CORPSE_REMOVED => [null, null, null, null, null, 0], // NONE
|
|
||||||
self::EVENT_AI_INIT => [null, null, null, null, null, 0], // NONE
|
|
||||||
self::EVENT_DATA_SET => [null, null, ['numRange', -1, true], null, null, 0], // Id, Value, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_WAYPOINT_START => [null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_WAYPOINT_REACHED => [null, null, null, null, null, 0], // PointId(0any), pathID(0any)
|
|
||||||
self::EVENT_TRANSPORT_ADDPLAYER => [null, null, null, null, null, 2], // NONE
|
|
||||||
self::EVENT_TRANSPORT_ADDCREATURE => [null, null, null, null, null, 2], // Entry (0 any)
|
|
||||||
self::EVENT_TRANSPORT_REMOVE_PLAYER => [null, null, null, null, null, 2], // NONE
|
|
||||||
self::EVENT_TRANSPORT_RELOCATE => [null, null, null, null, null, 2], // PointId
|
|
||||||
self::EVENT_INSTANCE_PLAYER_ENTER => [null, null, null, null, null, 2], // Team (0 any), CooldownMin, CooldownMax
|
|
||||||
self::EVENT_AREATRIGGER_ONTRIGGER => [Type::AREATRIGGER, null, null, null, null, 0], // TriggerId(0 any)
|
|
||||||
self::EVENT_QUEST_ACCEPTED => [null, null, null, null, null, 2], // none
|
|
||||||
self::EVENT_QUEST_OBJ_COMPLETION => [null, null, null, null, null, 2], // none
|
|
||||||
self::EVENT_QUEST_COMPLETION => [null, null, null, null, null, 2], // none
|
|
||||||
self::EVENT_QUEST_REWARDED => [null, null, null, null, null, 2], // none
|
|
||||||
self::EVENT_QUEST_FAIL => [null, null, null, null, null, 2], // none
|
|
||||||
self::EVENT_TEXT_OVER => [null, Type::NPC, null, null, null, 0], // GroupId from creature_text, creature entry who talks (0 any)
|
|
||||||
self::EVENT_RECEIVE_HEAL => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // MinHeal, MaxHeal, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_JUST_SUMMONED => [null, null, null, null, null, 0], // none
|
|
||||||
self::EVENT_WAYPOINT_PAUSED => [null, null, null, null, null, 0], // PointId(0any), pathID(0any)
|
|
||||||
self::EVENT_WAYPOINT_RESUMED => [null, null, null, null, null, 0], // PointId(0any), pathID(0any)
|
|
||||||
self::EVENT_WAYPOINT_STOPPED => [null, null, null, null, null, 0], // PointId(0any), pathID(0any)
|
|
||||||
self::EVENT_WAYPOINT_ENDED => [null, null, null, null, null, 0], // PointId(0any), pathID(0any)
|
|
||||||
self::EVENT_TIMED_EVENT_TRIGGERED => [null, null, null, null, null, 0], // id
|
|
||||||
self::EVENT_UPDATE => [['numRange', 10, true], null, ['numRange', -1, true], null, null, 0], // InitialMin, InitialMax, RepeatMin, RepeatMax
|
|
||||||
self::EVENT_LINK => [null, null, null, null, null, 0], // INTERNAL USAGE, no params, used to link together multiple events, does not use any extra resources to iterate event lists needlessly
|
|
||||||
self::EVENT_GOSSIP_SELECT => [null, null, null, null, null, 0], // menuID, actionID
|
|
||||||
self::EVENT_JUST_CREATED => [null, null, null, null, null, 0], // none
|
|
||||||
self::EVENT_GOSSIP_HELLO => [null, null, null, null, null, 0], // noReportUse (for GOs)
|
|
||||||
self::EVENT_FOLLOW_COMPLETED => [null, null, null, null, null, 0], // none
|
|
||||||
self::EVENT_EVENT_PHASE_CHANGE => [null, null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_IS_BEHIND_TARGET => [['numRange', -1, true], null, null, null, null, 1], // UNUSED, DO NOT REUSE
|
|
||||||
self::EVENT_GAME_EVENT_START => [Type::WORLDEVENT, null, null, null, null, 0], // game_event.Entry
|
|
||||||
self::EVENT_GAME_EVENT_END => [Type::WORLDEVENT, null, null, null, null, 0], // game_event.Entry
|
|
||||||
self::EVENT_GO_LOOT_STATE_CHANGED => [['lootState', 10, false], null, null, null, null, 0], // go LootState
|
|
||||||
self::EVENT_GO_EVENT_INFORM => [null, null, null, null, null, 0], // eventId
|
|
||||||
self::EVENT_ACTION_DONE => [null, null, null, null, null, 0], // eventId (SharedDefines.EventId)
|
|
||||||
self::EVENT_ON_SPELLCLICK => [null, null, null, null, null, 0], // clicker (unit)
|
|
||||||
self::EVENT_FRIENDLY_HEALTH_PCT => [['numRange', 10, false], null, ['numRange', -1, true], null, null, 0], // minHpPct, maxHpPct, repeatMin, repeatMax
|
|
||||||
self::EVENT_DISTANCE_CREATURE => [null, Type::NPC, null, ['numRange', -1, true], null, 0], // guid, entry, distance, repeat
|
|
||||||
self::EVENT_DISTANCE_GAMEOBJECT => [null, Type::OBJECT, null, ['numRange', -1, true], null, 0], // guid, entry, distance, repeat
|
|
||||||
self::EVENT_COUNTER_SET => [null, null, ['numRange', -1, true], null, null, 0], // id, value, cooldownMin, cooldownMax
|
|
||||||
self::EVENT_SCENE_START => [null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::EVENT_SCENE_TRIGGER => [null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::EVENT_SCENE_CANCEL => [null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::EVENT_SCENE_COMPLETE => [null, null, null, null, null, 2], // don't use on 3.3.5a
|
|
||||||
self::EVENT_SUMMONED_UNIT_DIES => [Type::NPC, ['numRange', -1, true], null, null, null, 0], // CreatureId(0 all), CooldownMin, CooldownMax
|
|
||||||
self::EVENT_ON_SPELL_CAST => [Type::SPELL, ['numRange', -1, true], null, null, null, 0], // SpellID, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_ON_SPELL_FAILED => [Type::SPELL, ['numRange', -1, true], null, null, null, 0], // SpellID, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_ON_SPELL_START => [Type::SPELL, ['numRange', -1, true], null, null, null, 0], // SpellID, CooldownMin, CooldownMax
|
|
||||||
self::EVENT_ON_DESPAWN => [null, null, null, null, null, 0] // NONE
|
|
||||||
);
|
|
||||||
|
|
||||||
private array $jsGlobals = [];
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private int $id,
|
|
||||||
public readonly int $type,
|
|
||||||
public readonly int $phaseMask,
|
|
||||||
public readonly int $chance,
|
|
||||||
private int $flags,
|
|
||||||
private array $param,
|
|
||||||
private SmartAI &$smartAI)
|
|
||||||
{
|
|
||||||
// additional parameters
|
|
||||||
Util::checkNumeric($this->param, NUM_CAST_INT);
|
|
||||||
$this->param = array_pad($this->param, 15, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function process() : array
|
|
||||||
{
|
|
||||||
$body =
|
|
||||||
$footer = '';
|
|
||||||
|
|
||||||
$phases = Util::mask2bits($this->phaseMask, 1) ?: [0];
|
|
||||||
$eventTT = Lang::smartAI('eventTT', array_merge([$this->type, $phases, $this->chance, $this->flags], $this->param));
|
|
||||||
|
|
||||||
for ($i = 0; $i < 5; $i++)
|
|
||||||
{
|
|
||||||
$eParams = $this->data[$this->type];
|
|
||||||
|
|
||||||
if (is_array($eParams[$i]))
|
|
||||||
{
|
|
||||||
[$fn, $idx, $extraParam] = $eParams[$i];
|
|
||||||
|
|
||||||
if ($idx < 0)
|
|
||||||
$footer = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
|
|
||||||
else
|
|
||||||
$this->param[$idx] = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
|
|
||||||
}
|
|
||||||
else if (is_int($eParams[$i]) && $this->param[$i])
|
|
||||||
$this->jsGlobals[$eParams[$i]][$this->param[$i]] = $this->param[$i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-generic cases
|
|
||||||
switch ($this->type)
|
|
||||||
{
|
|
||||||
case self::EVENT_UPDATE_IC: // 0 - In combat.
|
|
||||||
case self::EVENT_UPDATE_OOC: // 1 - Out of combat.
|
|
||||||
if ($this->smartAI->srcType == SmartAI::SRC_TYPE_ACTIONLIST)
|
|
||||||
$this->param[11] = 1;
|
|
||||||
// do not break;
|
|
||||||
case self::EVENT_GOSSIP_HELLO: // 64 - On Right-Click Creature/Gameobject that have gossip enabled.
|
|
||||||
if ($this->smartAI->srcType == SmartAI::SRC_TYPE_OBJECT)
|
|
||||||
$footer = array(
|
|
||||||
$this->param[0] == 1,
|
|
||||||
$this->param[0] == 2,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case self::EVENT_RESPAWN: // 11 - On Creature/Gameobject Respawn in Zone/Map
|
|
||||||
if ($this->param[0] == 1) // per map
|
|
||||||
{
|
|
||||||
switch ($this->param[1])
|
|
||||||
{
|
|
||||||
case 0: $this->param[10] = Lang::maps('EasternKingdoms'); break;
|
|
||||||
case 1: $this->param[10] = Lang::maps('Kalimdor'); break;
|
|
||||||
case 530: $this->param[10] = Lang::maps('Outland'); break;
|
|
||||||
case 571: $this->param[10] = Lang::maps('Northrend'); break;
|
|
||||||
default:
|
|
||||||
if ($aId = DB::Aowow()->selectCell('SELECT `id` FROM ?_zones WHERE `mapId` = ?d', $this->param[1]))
|
|
||||||
{
|
|
||||||
$this->param[11] = $aId;
|
|
||||||
$this->jsGlobals[Type::ZONE][$aId] = $aId;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$this->param[11] = '[span class=q10]Unknown Map[/span] #'.$this->param[1];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if ($this->param[0] == 2) // per zone
|
|
||||||
$this->param[11] = $this->param[2];
|
|
||||||
|
|
||||||
break;
|
|
||||||
case self::EVENT_LINK: // 61 - Used to link together multiple events as a chain of events.
|
|
||||||
if ($links = DB::World()->selectCol('SELECT `id` FROM smart_scripts WHERE `link` = ?d AND `entryorguid` = ?d AND `source_type` = ?d', $this->id, $this->smartAI->entry, $this->smartAI->srcType))
|
|
||||||
$this->param[10] = Lang::concat($links, Lang::CONCAT_OR, fn($x) => "#[b]".$x."[/b]");
|
|
||||||
break;
|
|
||||||
case self::EVENT_GOSSIP_SELECT: // 62 - On gossip clicked (gossip_menu_option335).
|
|
||||||
$gmo = DB::World()->selectRow(
|
|
||||||
'SELECT gmo.`OptionText` AS "text_loc0" {, gmol.`OptionText` AS text_loc?d }
|
|
||||||
FROM gossip_menu_option gmo
|
|
||||||
LEFT JOIN gossip_menu_option_locale gmol ON gmo.`MenuID` = gmol.`MenuID` AND gmo.`OptionID` = gmol.`OptionID` AND gmol.`Locale` = ?d
|
|
||||||
WHERE gmo.`MenuId` = ?d AND gmo.`OptionID` = ?d',
|
|
||||||
Lang::getLocale() != Locale::EN ? Lang::getLocale()->value : DBSIMPLE_SKIP,
|
|
||||||
Lang::getLocale()->json(),
|
|
||||||
$this->param[0], $this->param[1]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($gmo)
|
|
||||||
$this->param[10] = Util::jsEscape(Util::localizedString($gmo, 'text'));
|
|
||||||
else
|
|
||||||
trigger_error('SmartAI::event - could not find gossip menu option for event #'.$this->type);
|
|
||||||
break;
|
|
||||||
case self::EVENT_DISTANCE_CREATURE: // 75 - On creature guid OR any instance of creature entry is within distance.
|
|
||||||
if ($this->param[0])
|
|
||||||
if ($_ = $this->resolveGuid(Type::NPC, $this->param[0]))
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$this->param[0]] = $this->param[0];
|
|
||||||
$this->param[10] = $_;
|
|
||||||
}
|
|
||||||
// do not break;
|
|
||||||
case self::EVENT_DISTANCE_GAMEOBJECT: // 76 - On gameobject guid OR any instance of gameobject entry is within distance.
|
|
||||||
if ($this->param[0] && !$this->param[10])
|
|
||||||
{
|
|
||||||
if ($_ = $this->resolveGuid(Type::OBJECT, $this->param[0]))
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::OBJECT][$this->param[0]] = $this->param[0];
|
|
||||||
$this->param[10] = $_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($this->param[1])
|
|
||||||
$this->param[10] = $this->param[1];
|
|
||||||
else if (!$this->param[10])
|
|
||||||
trigger_error('SmartAI::event - entity for event #'.$this->type.' not defined');
|
|
||||||
break;
|
|
||||||
case self::EVENT_EVENT_PHASE_CHANGE: // 66 - On event phase mask set
|
|
||||||
$this->param[10] = Lang::concat(Util::mask2bits($this->param[0]), Lang::CONCAT_OR);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->smartAI->addJsGlobals($this->jsGlobals);
|
|
||||||
|
|
||||||
$body = Lang::smartAI('events', $this->type, 0, $this->param) ?? Lang::smartAI('eventUNK', [$this->type]);
|
|
||||||
if ($footer)
|
|
||||||
$footer = Lang::smartAI('events', $this->type, 1, (array)$footer);
|
|
||||||
|
|
||||||
// resolve conditionals
|
|
||||||
$i = 0;
|
|
||||||
while (strstr($body, ')?') && $i++ < 3)
|
|
||||||
$body = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $body);
|
|
||||||
|
|
||||||
$i = 0;
|
|
||||||
while (strstr($footer, ')?') && $i++ < 3)
|
|
||||||
$footer = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $footer);
|
|
||||||
|
|
||||||
if ($_ = $this->formatFlags())
|
|
||||||
$footer = $_ . ($footer ? '; '.$footer : '');
|
|
||||||
|
|
||||||
if (User::isInGroup(U_GROUP_EMPLOYEE))
|
|
||||||
{
|
|
||||||
if ($eParams[5] == 1)
|
|
||||||
$footer = '[span class=rep2]DEPRECATED[/span] ' . $footer;
|
|
||||||
else if ($eParams[5] == 2)
|
|
||||||
$footer = '[span class=rep0]RESERVED[/span] ' . $footer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap body in tooltip
|
|
||||||
return [sprintf(self::EVENT_CELL_TPL, $eventTT, $body), $footer];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasPhases() : bool
|
|
||||||
{
|
|
||||||
return $this->phaseMask == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatFlags() : string
|
|
||||||
{
|
|
||||||
$flags = $this->flags;
|
|
||||||
|
|
||||||
if (($flags & self::FLAG_ALL_DIFFICULTIES) == self::FLAG_ALL_DIFFICULTIES)
|
|
||||||
$flags &= ~self::FLAG_ALL_DIFFICULTIES;
|
|
||||||
|
|
||||||
$ef = [];
|
|
||||||
for ($i = 1; $i <= self::FLAG_WHILE_CHARMED; $i <<= 1)
|
|
||||||
if ($flags & $i)
|
|
||||||
if ($x = Lang::smartAI('eventFlags', $i))
|
|
||||||
$ef[] = $x;
|
|
||||||
|
|
||||||
return Lang::concat($ef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
// TrinityCore - SmartAI
|
|
||||||
class SmartTarget
|
|
||||||
{
|
|
||||||
use SmartHelper;
|
|
||||||
|
|
||||||
public const TARGET_NONE = 0; // None.
|
|
||||||
public const TARGET_SELF = 1; // Self cast.
|
|
||||||
public const TARGET_VICTIM = 2; // Our current target. (ie: highest aggro)
|
|
||||||
public const TARGET_HOSTILE_SECOND_AGGRO = 3; // Second highest aggro.
|
|
||||||
public const TARGET_HOSTILE_LAST_AGGRO = 4; // Dead last on aggro.
|
|
||||||
public const TARGET_HOSTILE_RANDOM = 5; // Just any random target on our threat list.
|
|
||||||
public const TARGET_HOSTILE_RANDOM_NOT_TOP = 6; // Any random target except top threat.
|
|
||||||
public const TARGET_ACTION_INVOKER = 7; // Unit who caused this Event to occur.
|
|
||||||
public const TARGET_POSITION = 8; // Use xyz from event params.
|
|
||||||
public const TARGET_CREATURE_RANGE = 9; // (Random?) creature with specified ID within specified range.
|
|
||||||
public const TARGET_CREATURE_GUID = 10; // Creature with specified GUID.
|
|
||||||
public const TARGET_CREATURE_DISTANCE = 11; // Creature with specified ID within distance. (Different from #9?)
|
|
||||||
public const TARGET_STORED = 12; // Uses pre-stored target(list)
|
|
||||||
public const TARGET_GAMEOBJECT_RANGE = 13; // (Random?) object with specified ID within specified range.
|
|
||||||
public const TARGET_GAMEOBJECT_GUID = 14; // Object with specified GUID.
|
|
||||||
public const TARGET_GAMEOBJECT_DISTANCE = 15; // Object with specified ID within distance. (Different from #13?)
|
|
||||||
public const TARGET_INVOKER_PARTY = 16; // Invoker's party members
|
|
||||||
public const TARGET_PLAYER_RANGE = 17; // (Random?) player within specified range.
|
|
||||||
public const TARGET_PLAYER_DISTANCE = 18; // (Random?) player within specified distance. (Different from #17?)
|
|
||||||
public const TARGET_CLOSEST_CREATURE = 19; // Closest creature with specified ID within specified range.
|
|
||||||
public const TARGET_CLOSEST_GAMEOBJECT = 20; // Closest object with specified ID within specified range.
|
|
||||||
public const TARGET_CLOSEST_PLAYER = 21; // Closest player within specified range.
|
|
||||||
public const TARGET_ACTION_INVOKER_VEHICLE = 22; // Unit's vehicle who caused this Event to occur
|
|
||||||
public const TARGET_OWNER_OR_SUMMONER = 23; // Unit's owner or summoner
|
|
||||||
public const TARGET_THREAT_LIST = 24; // All units on creature's threat list
|
|
||||||
public const TARGET_CLOSEST_ENEMY = 25; // Any attackable target (creature or player) within maxDist
|
|
||||||
public const TARGET_CLOSEST_FRIENDLY = 26; // Any friendly unit (creature, player or pet) within maxDist
|
|
||||||
public const TARGET_LOOT_RECIPIENTS = 27; // All tagging players
|
|
||||||
public const TARGET_FARTHEST = 28; // Farthest unit on the threat list
|
|
||||||
public const TARGET_VEHICLE_PASSENGER = 29; // Vehicle can target unit in given seat
|
|
||||||
public const TARGET_CLOSEST_UNSPAWNED_GO = 30; // entry(0any), maxDist
|
|
||||||
|
|
||||||
private const TARGET_TPL = '[tooltip name=t-#rowIdx#]%1$s[/tooltip][span class=tip tooltip=t-#rowIdx#]%2$s[/span]';
|
|
||||||
|
|
||||||
private array $targets = array(
|
|
||||||
self::TARGET_NONE => [null, null, null, null], // NONE
|
|
||||||
self::TARGET_SELF => [null, null, null, null], // Self cast
|
|
||||||
self::TARGET_VICTIM => [null, null, null, null], // Our current target (ie: highest aggro)
|
|
||||||
self::TARGET_HOSTILE_SECOND_AGGRO => [null, null, null, null], // Second highest aggro, maxdist, playerOnly, powerType + 1
|
|
||||||
self::TARGET_HOSTILE_LAST_AGGRO => [null, null, null, null], // Dead last on aggro, maxdist, playerOnly, powerType + 1
|
|
||||||
self::TARGET_HOSTILE_RANDOM => [null, null, null, null], // Just any random target on our threat list, maxdist, playerOnly, powerType + 1
|
|
||||||
self::TARGET_HOSTILE_RANDOM_NOT_TOP => [null, null, null, null], // Any random target except top threat, maxdist, playerOnly, powerType + 1
|
|
||||||
self::TARGET_ACTION_INVOKER => [null, null, null, null], // Unit who caused this Event to occur
|
|
||||||
self::TARGET_POSITION => [null, null, null, null], // use xyz from event params
|
|
||||||
self::TARGET_CREATURE_RANGE => [Type::NPC, ['numRange', 10, false], null, null], // CreatureEntry(0any), minDist, maxDist
|
|
||||||
self::TARGET_CREATURE_GUID => [null, Type::NPC, null, null], // guid, entry
|
|
||||||
self::TARGET_CREATURE_DISTANCE => [Type::NPC, null, null, null], // CreatureEntry(0any), maxDist
|
|
||||||
self::TARGET_STORED => [null, null, null, null], // id, uses pre-stored target(list)
|
|
||||||
self::TARGET_GAMEOBJECT_RANGE => [Type::OBJECT, ['numRange', 10, false], null, null], // entry(0any), min, max
|
|
||||||
self::TARGET_GAMEOBJECT_GUID => [null, Type::OBJECT, null, null], // guid, entry
|
|
||||||
self::TARGET_GAMEOBJECT_DISTANCE => [Type::OBJECT, null, null, null], // entry(0any), maxDist
|
|
||||||
self::TARGET_INVOKER_PARTY => [null, null, null, null], // invoker's party members
|
|
||||||
self::TARGET_PLAYER_RANGE => [['numRange', 10, false], null, null, null], // min, max
|
|
||||||
self::TARGET_PLAYER_DISTANCE => [null, null, null, null], // maxDist
|
|
||||||
self::TARGET_CLOSEST_CREATURE => [Type::NPC, null, null, null], // CreatureEntry(0any), maxDist, dead?
|
|
||||||
self::TARGET_CLOSEST_GAMEOBJECT => [Type::OBJECT, null, null, null], // entry(0any), maxDist
|
|
||||||
self::TARGET_CLOSEST_PLAYER => [null, null, null, null], // maxDist
|
|
||||||
self::TARGET_ACTION_INVOKER_VEHICLE => [null, null, null, null], // Unit's vehicle who caused this Event to occur
|
|
||||||
self::TARGET_OWNER_OR_SUMMONER => [null, null, null, null], // Unit's owner or summoner, Use Owner/Charmer of this unit
|
|
||||||
self::TARGET_THREAT_LIST => [null, null, null, null], // All units on creature's threat list, maxdist
|
|
||||||
self::TARGET_CLOSEST_ENEMY => [null, null, null, null], // maxDist, playerOnly
|
|
||||||
self::TARGET_CLOSEST_FRIENDLY => [null, null, null, null], // maxDist, playerOnly
|
|
||||||
self::TARGET_LOOT_RECIPIENTS => [null, null, null, null], // all players that have tagged this creature (for kill credit)
|
|
||||||
self::TARGET_FARTHEST => [null, null, null, null], // maxDist, playerOnly, isInLos
|
|
||||||
self::TARGET_VEHICLE_PASSENGER => [null, null, null, null], // seatMask (0 - all seats)
|
|
||||||
self::TARGET_CLOSEST_UNSPAWNED_GO => [Type::OBJECT, null, null, null] // entry(0any), maxDist
|
|
||||||
);
|
|
||||||
|
|
||||||
private array $jsGlobals = [];
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private int $id,
|
|
||||||
public readonly int $type,
|
|
||||||
private array $param,
|
|
||||||
private array $worldPos,
|
|
||||||
private SmartAI &$smartAI)
|
|
||||||
{
|
|
||||||
// additional parameters
|
|
||||||
Util::checkNumeric($this->param, NUM_CAST_INT);
|
|
||||||
Util::checkNumeric($this->worldPos, NUM_CAST_FLOAT);
|
|
||||||
$this->param = array_pad($this->param, 15, '');
|
|
||||||
$this->worldPos = array_pad($this->worldPos, 4, 0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function process() : string
|
|
||||||
{
|
|
||||||
$target = '';
|
|
||||||
|
|
||||||
$targetTT = Lang::smartAI('targetTT', array_merge([$this->type], $this->param, $this->worldPos));
|
|
||||||
|
|
||||||
for ($i = 0; $i < 4; $i++)
|
|
||||||
{
|
|
||||||
$tParams = $this->targets[$this->type];
|
|
||||||
|
|
||||||
if (is_array($tParams[$i]))
|
|
||||||
{
|
|
||||||
[$fn, $idx, $extraParam] = $tParams[$i];
|
|
||||||
|
|
||||||
$this->param[$idx] = $this->{$fn}($this->param[$i], $this->param[$i + 1], $extraParam);
|
|
||||||
}
|
|
||||||
else if (is_int($tParams[$i]) && $this->param[$i])
|
|
||||||
$this->jsGlobals[$tParams[$i]][$this->param[$i]] = $this->param[$i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-generic cases
|
|
||||||
switch ($this->type)
|
|
||||||
{
|
|
||||||
case self::TARGET_HOSTILE_SECOND_AGGRO:
|
|
||||||
case self::TARGET_HOSTILE_LAST_AGGRO:
|
|
||||||
case self::TARGET_HOSTILE_RANDOM:
|
|
||||||
case self::TARGET_HOSTILE_RANDOM_NOT_TOP:
|
|
||||||
if ($this->param[2])
|
|
||||||
$this->param[10] = Lang::spell('powerTypes', $this->param[2] - 1);
|
|
||||||
break;
|
|
||||||
case self::TARGET_VEHICLE_PASSENGER:
|
|
||||||
if ($this->param[0])
|
|
||||||
$this->param[10] = Lang::concat(Util::mask2bits($this->param[0]));
|
|
||||||
break;
|
|
||||||
case self::TARGET_CREATURE_GUID:
|
|
||||||
if ($_ = $this->resolveGuid(Type::NPC, $this->param[0]))
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::NPC][$_] = $_;
|
|
||||||
$this->param[10] = $_;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::TARGET_GAMEOBJECT_GUID:
|
|
||||||
if ($_ = $this->resolveGuid(Type::OBJECT, $this->param[0]))
|
|
||||||
{
|
|
||||||
$this->jsGlobals[Type::OBJECT][$_] = $_;
|
|
||||||
$this->param[10] = $_;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->smartAI->addJsGlobals($this->jsGlobals);
|
|
||||||
|
|
||||||
$target = Lang::smartAI('targets', $this->type, $this->param) ?? Lang::smartAI('targetUNK', [$this->type]);
|
|
||||||
|
|
||||||
// resolve conditionals
|
|
||||||
$i = 0;
|
|
||||||
while (strstr($target, ')?') && $i++ < 3)
|
|
||||||
$target = preg_replace_callback('/\(([^\)]*?)\)\?([^:]*):(([^;]*);*);/i', fn($m) => $m[1] ? $m[2] : $m[3], $target);
|
|
||||||
|
|
||||||
// wrap in tooltip (suspend action-tooltip)
|
|
||||||
return '[/span]'.sprintf(self::TARGET_TPL, $targetTT, $target).'[span tooltip=a-#rowIdx#]';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getWorldPos() : array
|
|
||||||
{
|
|
||||||
return $this->worldPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not really feasable. Too many target types can be players or creatures, depending on context
|
|
||||||
public function getTalkSource(bool &$playerSrc = false) : int
|
|
||||||
{
|
|
||||||
if ($this->type == SmartTarget::TARGET_CLOSEST_PLAYER)
|
|
||||||
$playerSrc = true;
|
|
||||||
|
|
||||||
return match ($this->type)
|
|
||||||
{
|
|
||||||
SmartTarget::TARGET_CREATURE_GUID => $this->resolveGuid(Type::NPC, $this->param[0]) ?? 0,
|
|
||||||
SmartTarget::TARGET_CREATURE_RANGE,
|
|
||||||
SmartTarget::TARGET_CREATURE_DISTANCE,
|
|
||||||
SmartTarget::TARGET_CLOSEST_CREATURE => $this->param[0],
|
|
||||||
SmartTarget::TARGET_CLOSEST_PLAYER,
|
|
||||||
SmartTarget::TARGET_SELF => $this->smartAI->getEntry(),
|
|
||||||
default => $this->smartAI->getEntry()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,546 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
/************
|
|
||||||
* get Community Content
|
|
||||||
************/
|
|
||||||
|
|
||||||
/*
|
|
||||||
{id:115,user:'Ciderhelm',date:'2010/05/10 19:14:18',caption:'TankSpot\'s Guide to the Fury Warrior (Part 1)',videoType:1,videoId:'VUvxFvVmttg',type:13,typeId:1},
|
|
||||||
{id:116,user:'Ciderhelm',date:'2010/05/10 19:14:18',caption:'TankSpot\'s Guide to the Fury Warrior (Part 2)',videoType:1,videoId:'VEfnuIcq7n8',type:13,typeId:1},
|
|
||||||
{id:117,user:'Ciderhelm',date:'2010/05/10 19:14:18',caption:'TankSpot\'s Protection Warrior Guide',videoType:1,videoId:'vF-7kmvJZXY',type:13,typeId:1,sticky:1}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* todo: administration of content */
|
|
||||||
|
|
||||||
class CommunityContent
|
|
||||||
{
|
|
||||||
private static array $jsGlobals = [];
|
|
||||||
private static array $subjCache = [];
|
|
||||||
|
|
||||||
private static string $coCountQuery =
|
|
||||||
'SELECT COUNT(1)
|
|
||||||
FROM ?_comments c
|
|
||||||
WHERE c.`replyTo` = ?d AND c.`type` = ?d AND c.`typeId` = ?d AND
|
|
||||||
((c.`flags` & ?d) = 0 OR c.`userId` = ?d OR ?d)';
|
|
||||||
|
|
||||||
private static string $coQuery =
|
|
||||||
'SELECT c.*,
|
|
||||||
a1.`username` AS "user",
|
|
||||||
a2.`username` AS "editUser",
|
|
||||||
a3.`username` AS "deleteUser",
|
|
||||||
a4.`username` AS "responseUser",
|
|
||||||
IFNULL(SUM(ur.`value`), 0) AS "rating",
|
|
||||||
SUM(IF(ur.`userId` > 0 AND ur.`userId` = ?d, ur.`value`, 0)) AS "userRating",
|
|
||||||
IF(r.`id` IS NULL, 0, 1) AS "userReported"
|
|
||||||
FROM ?_comments c
|
|
||||||
JOIN ?_account a1 ON c.`userId` = a1.`id`
|
|
||||||
LEFT JOIN ?_account a2 ON c.`editUserId` = a2.`id`
|
|
||||||
LEFT JOIN ?_account a3 ON c.`deleteUserId` = a3.`id`
|
|
||||||
LEFT JOIN ?_account a4 ON c.`responseUserId` = a4.`id`
|
|
||||||
LEFT JOIN ?_user_ratings ur ON c.`id` = ur.`entry` AND ur.`type` = ?d
|
|
||||||
LEFT JOIN ?_reports r ON r.`subject` = c.`id` AND r.`mode` = ?d AND r.`userId` = ?d
|
|
||||||
WHERE c.`replyTo` = ?d AND c.`type` = ?d AND c.`typeId` = ?d AND
|
|
||||||
((c.`flags` & ?d) = 0 OR c.`userId` = ?d OR ?d)
|
|
||||||
GROUP BY c.`id`
|
|
||||||
ORDER BY c.`date` ASC';
|
|
||||||
|
|
||||||
private static string $ssQuery =
|
|
||||||
'SELECT s.`id` AS ARRAY_KEY, s.`id`, a.`username` AS "user", s.`date`, s.`width`, s.`height`, s.`caption`, IF(s.`status` & ?d, 1, 0) AS "sticky", s.`type`, s.`typeId`
|
|
||||||
FROM ?_screenshots s
|
|
||||||
LEFT JOIN ?_account a ON s.`userIdOwner` = a.`id`
|
|
||||||
WHERE { s.`userIdOwner` = ?d AND }{ s.`type` = ? AND }{ s.`typeId` = ? AND } s.`status` & ?d AND (s.`status` & ?d) = 0
|
|
||||||
{ ORDER BY ?# DESC }
|
|
||||||
{ LIMIT ?d }';
|
|
||||||
|
|
||||||
private static string $viQuery =
|
|
||||||
'SELECT v.`id` AS ARRAY_KEY, v.`id`, a.`username` AS "user", v.`date`, v.`videoId`, v.`caption`, IF(v.`status` & ?d, 1, 0) AS "sticky", v.`type`, v.`typeId`
|
|
||||||
FROM ?_videos v
|
|
||||||
LEFT JOIN ?_account a ON v.`userIdOwner` = a.`id`
|
|
||||||
WHERE { v.`userIdOwner` = ?d AND }{ v.`type` = ? AND }{ v.`typeId` = ? AND } v.`status` & ?d AND (v.`status` & ?d) = 0
|
|
||||||
{ ORDER BY ?# DESC }
|
|
||||||
{ LIMIT ?d }';
|
|
||||||
|
|
||||||
private static string $previewQuery =
|
|
||||||
'SELECT c.`id`,
|
|
||||||
c.`body` AS "preview",
|
|
||||||
c.`date`,
|
|
||||||
c.`replyTo` AS "commentid",
|
|
||||||
IF(c.`flags` & ?d, 1, 0) AS "deleted",
|
|
||||||
IF(c.`type` <> 0, c.`type`, c2.`type`) AS "type",
|
|
||||||
IF(c.`typeId` <> 0, c.`typeId`, c2.`typeId`) AS "typeId",
|
|
||||||
IFNULL(SUM(ur.`value`), 0) AS "rating",
|
|
||||||
a.`username` AS "user"
|
|
||||||
FROM ?_comments c
|
|
||||||
JOIN ?_account a ON c.`userId` = a.`id`
|
|
||||||
LEFT JOIN ?_user_ratings ur ON ur.`entry` = c.`id` AND ur.`userId` <> 0 AND ur.`type` = 1
|
|
||||||
LEFT JOIN ?_comments c2 ON c.`replyTo` = c2.`id`
|
|
||||||
WHERE %s
|
|
||||||
((c.`flags` & ?d) = 0 OR c.`userId` = ?d OR ?d)
|
|
||||||
GROUP BY c.`id`
|
|
||||||
ORDER BY c.`date` DESC
|
|
||||||
{ LIMIT ?d }';
|
|
||||||
|
|
||||||
private static function addSubject(int $type, int $typeId) : void
|
|
||||||
{
|
|
||||||
if (!isset(self::$subjCache[$type][$typeId]))
|
|
||||||
self::$subjCache[$type][$typeId] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function getSubjects() : void
|
|
||||||
{
|
|
||||||
foreach (self::$subjCache as $type => $ids)
|
|
||||||
{
|
|
||||||
$_ = array_filter(array_keys($ids), 'is_numeric');
|
|
||||||
if (!$_)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$obj = Type::newList($type, [Cfg::get('SQL_LIMIT_NONE'), ['id', $_]]);
|
|
||||||
if (!$obj)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach ($obj->iterate() as $id => $__)
|
|
||||||
self::$subjCache[$type][$id] = $obj->getField('name', true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getCommentPreviews(array $opt = [], ?int &$nFound = 0, bool $dateFmt = true) : array
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
purged:0, <- doesnt seem to be used anymore
|
|
||||||
domain:'live' <- irrelevant for our case
|
|
||||||
*/
|
|
||||||
|
|
||||||
// add default values
|
|
||||||
$opt += ['user' => 0, 'unrated' => 0, 'comments' => 0, 'replies' => 0];
|
|
||||||
|
|
||||||
$w = [];
|
|
||||||
if ($opt['user'])
|
|
||||||
$w[] = sprintf('c.userId = %d AND', $opt['user']);
|
|
||||||
if ($opt['unrated'])
|
|
||||||
$w[] = 'ur.entry IS NULL AND';
|
|
||||||
if ($opt['comments'] && !$opt['replies'])
|
|
||||||
$w[] = 'c.replyTo = 0 AND';
|
|
||||||
else if (!$opt['comments'] && $opt['replies'])
|
|
||||||
$w[] = 'c.replyTo <> 0 AND';
|
|
||||||
// else
|
|
||||||
// pick both and no extra constraint needed for that
|
|
||||||
|
|
||||||
$query = sprintf(self::$previewQuery, implode(' ', $w));
|
|
||||||
|
|
||||||
$comments = DB::Aowow()->select(
|
|
||||||
$query,
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
User::$id,
|
|
||||||
User::isInGroup(U_GROUP_COMMENTS_MODERATOR),
|
|
||||||
Cfg::get('SQL_LIMIT_DEFAULT')
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$comments)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$nFound = DB::Aowow()->selectCell(
|
|
||||||
substr_replace($query, 'SELECT COUNT(*) ', 0, strpos($query, 'FROM')),
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
User::$id,
|
|
||||||
User::isInGroup(U_GROUP_COMMENTS_MODERATOR),
|
|
||||||
DBSIMPLE_SKIP
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($comments as $c)
|
|
||||||
self::addSubject($c['type'], $c['typeId']);
|
|
||||||
|
|
||||||
self::getSubjects();
|
|
||||||
|
|
||||||
foreach ($comments as $idx => &$c)
|
|
||||||
{
|
|
||||||
if (!empty(self::$subjCache[$c['type']][$c['typeId']]))
|
|
||||||
{
|
|
||||||
// apply subject
|
|
||||||
$c['subject'] = self::$subjCache[$c['type']][$c['typeId']];
|
|
||||||
|
|
||||||
// format date
|
|
||||||
$c['elapsed'] = time() - $c['date'];
|
|
||||||
$c['date'] = $dateFmt ? date(Util::$dateFormatInternal, $c['date']) : intVal($c['date']);
|
|
||||||
|
|
||||||
// remove commentid if not looking for replies
|
|
||||||
if (empty($opt['replies']))
|
|
||||||
unset($c['commentid']);
|
|
||||||
|
|
||||||
// format text for listview
|
|
||||||
$c['preview'] = Lang::trimTextClean($c['preview']);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('Comment '.$c['id'].' belongs to nonexistant subject.', E_USER_NOTICE);
|
|
||||||
unset($comments[$idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getCommentReplies(int $commentId, int $limit = 0, ?int &$nFound = 0) : array
|
|
||||||
{
|
|
||||||
$replies = [];
|
|
||||||
$query = $limit > 0 ? self::$coQuery.' LIMIT '.$limit : self::$coQuery;
|
|
||||||
|
|
||||||
// get replies
|
|
||||||
if ($results = DB::Aowow()->select($query, User::$id, RATING_COMMENT, Report::MODE_COMMENT, User::$id, $commentId, 0, 0, CC_FLAG_DELETED, User::$id, User::isInGroup(U_GROUP_COMMENTS_MODERATOR)))
|
|
||||||
{
|
|
||||||
$nFound = DB::Aowow()->selectCell(self::$coCountQuery, $commentId, 0, 0, CC_FLAG_DELETED, User::$id, User::isInGroup(U_GROUP_COMMENTS_MODERATOR));
|
|
||||||
|
|
||||||
foreach ($results as $r)
|
|
||||||
{
|
|
||||||
Markup::parseTags($r['body'], self::$jsGlobals);
|
|
||||||
|
|
||||||
$reply = array(
|
|
||||||
'commentid' => $commentId,
|
|
||||||
'id' => $r['id'],
|
|
||||||
'body' => $r['body'],
|
|
||||||
'username' => $r['user'],
|
|
||||||
'roles' => $r['roles'],
|
|
||||||
'creationdate' => date(Util::$dateFormatInternal, $r['date']),
|
|
||||||
'lasteditdate' => date(Util::$dateFormatInternal, $r['editDate']),
|
|
||||||
'rating' => (string)$r['rating']
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($r['userReported'])
|
|
||||||
$reply['reportedByUser'] = true;
|
|
||||||
|
|
||||||
if ($r['userRating'] > 0)
|
|
||||||
$reply['votedByUser'] = true;
|
|
||||||
else if ($r['userRating'] < 0)
|
|
||||||
$reply['downvotedByUser'] = true;
|
|
||||||
|
|
||||||
$replies[] = $reply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $replies;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getScreenshotsForManager($type, $typeId, $userId = 0)
|
|
||||||
{
|
|
||||||
$screenshots = DB::Aowow()->select(
|
|
||||||
'SELECT s.`id`, a.`username` AS "user", s.`date`, s.`width`, s.`height`, s.`type`, s.`typeId`, s.`caption`, s.`status`, s.`status` AS "flags"
|
|
||||||
FROM ?_screenshots s
|
|
||||||
LEFT JOIN ?_account a ON s.`userIdOwner` = a.`id`
|
|
||||||
WHERE
|
|
||||||
{ s.`type` = ?d}
|
|
||||||
{ AND s.`typeId` = ?d}
|
|
||||||
{ s.`userIdOwner` = ?d}
|
|
||||||
LIMIT 100',
|
|
||||||
$userId ? DBSIMPLE_SKIP : $type,
|
|
||||||
$userId ? DBSIMPLE_SKIP : $typeId,
|
|
||||||
$userId ? $userId : DBSIMPLE_SKIP
|
|
||||||
);
|
|
||||||
|
|
||||||
$num = [];
|
|
||||||
foreach ($screenshots as $s)
|
|
||||||
{
|
|
||||||
if (empty($num[$s['type']][$s['typeId']]))
|
|
||||||
$num[$s['type']][$s['typeId']] = 1;
|
|
||||||
else
|
|
||||||
$num[$s['type']][$s['typeId']]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// format data to meet requirements of the js
|
|
||||||
foreach ($screenshots as $idx => &$s)
|
|
||||||
{
|
|
||||||
$s['date'] = date(Util::$dateFormatInternal, $s['date']);
|
|
||||||
|
|
||||||
$s['name'] = "Screenshot #".$s['id']; // what should we REALLY name it?
|
|
||||||
|
|
||||||
if (isset($screenshots[$idx - 1]))
|
|
||||||
$s['prev'] = $idx - 1;
|
|
||||||
|
|
||||||
if (isset($screenshots[$idx + 1]))
|
|
||||||
$s['next'] = $idx + 1;
|
|
||||||
|
|
||||||
// order gives priority for 'status'
|
|
||||||
if (!($s['flags'] & CC_FLAG_APPROVED))
|
|
||||||
{
|
|
||||||
$s['pending'] = 1;
|
|
||||||
$s['status'] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$s['status'] = 100;
|
|
||||||
|
|
||||||
if ($s['flags'] & CC_FLAG_STICKY)
|
|
||||||
{
|
|
||||||
$s['sticky'] = 1;
|
|
||||||
$s['status'] = 105;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($s['flags'] & CC_FLAG_DELETED)
|
|
||||||
{
|
|
||||||
$s['deleted'] = 1;
|
|
||||||
$s['status'] = 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
// something todo with massSelect .. am i doing this right?
|
|
||||||
if ($num[$s['type']][$s['typeId']] == 1)
|
|
||||||
$s['unique'] = 1;
|
|
||||||
|
|
||||||
if (!$s['user'])
|
|
||||||
unset($s['user']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $screenshots;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getScreenshotPagesForManager($all, &$nFound)
|
|
||||||
{
|
|
||||||
// i GUESS .. ss_getALL ? everything : pending
|
|
||||||
$nFound = 0;
|
|
||||||
$pages = DB::Aowow()->select(
|
|
||||||
'SELECT s.`type`, s.`typeId`, COUNT(1) AS "count", MIN(s.`date`) AS "date"
|
|
||||||
FROM ?_screenshots s
|
|
||||||
{ WHERE (s.`status` & ?d) = 0 }
|
|
||||||
GROUP BY s.`type`, s.`typeId`',
|
|
||||||
$all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED | CC_FLAG_DELETED
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($pages)
|
|
||||||
{
|
|
||||||
// limit to one actually existing type each
|
|
||||||
foreach (array_unique(array_column($pages, 'type')) as $t)
|
|
||||||
{
|
|
||||||
$ids = [];
|
|
||||||
foreach ($pages as $row)
|
|
||||||
if ($row['type'] == $t)
|
|
||||||
$ids[] = $row['typeId'];
|
|
||||||
|
|
||||||
if (!$ids)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$obj = Type::newList($t, [Cfg::get('SQL_LIMIT_NONE'), ['id', $ids]]);
|
|
||||||
if (!$obj || $obj->error)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach ($pages as &$p)
|
|
||||||
if ($p['type'] == $t)
|
|
||||||
if ($obj->getEntry($p['typeId']))
|
|
||||||
$p['name'] = $obj->getField('name', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($pages as &$p)
|
|
||||||
{
|
|
||||||
if (empty($p['name']))
|
|
||||||
{
|
|
||||||
trigger_error('Screenshot linked to nonexistant type/typeId combination: '.$p['type'].'/'.$p['typeId'], E_USER_NOTICE);
|
|
||||||
unset($p);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$nFound += $p['count'];
|
|
||||||
$p['date'] = date(Util::$dateFormatInternal, $p['date']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getComments(int $type, int $typeId) : array
|
|
||||||
{
|
|
||||||
|
|
||||||
$results = DB::Aowow()->query(self::$coQuery, User::$id, RATING_COMMENT, Report::MODE_COMMENT, User::$id, 0, $type, $typeId, CC_FLAG_DELETED, User::$id, (int)User::isInGroup(U_GROUP_COMMENTS_MODERATOR));
|
|
||||||
$comments = [];
|
|
||||||
|
|
||||||
// additional informations
|
|
||||||
$i = 0;
|
|
||||||
foreach ($results as $r)
|
|
||||||
{
|
|
||||||
Markup::parseTags($r['body'], self::$jsGlobals);
|
|
||||||
|
|
||||||
self::$jsGlobals[Type::USER][$r['userId']] = $r['userId'];
|
|
||||||
|
|
||||||
$c = array(
|
|
||||||
'commentv2' => 1, // always 1.. enables some features i guess..?
|
|
||||||
'number' => $i++, // some iterator .. unsued?
|
|
||||||
'id' => $r['id'],
|
|
||||||
'date' => date(Util::$dateFormatInternal, $r['date']),
|
|
||||||
'roles' => $r['roles'],
|
|
||||||
'body' => $r['body'],
|
|
||||||
'rating' => $r['rating'],
|
|
||||||
'userRating' => $r['userRating'],
|
|
||||||
'user' => $r['user'],
|
|
||||||
'nreplies' => 0
|
|
||||||
);
|
|
||||||
|
|
||||||
$c['replies'] = self::getCommentReplies($r['id'], 5, $c['nreplies']);
|
|
||||||
|
|
||||||
if ($r['responseBody']) // adminResponse
|
|
||||||
{
|
|
||||||
$c['response'] = $r['responseBody'];
|
|
||||||
$c['responseroles'] = $r['responseRoles'];
|
|
||||||
$c['responseuser'] = $r['responseUser'];
|
|
||||||
|
|
||||||
Markup::parseTags($r['responseBody'], self::$jsGlobals);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($r['editCount']) // lastEdit
|
|
||||||
$c['lastEdit'] = [date(Util::$dateFormatInternal, $r['editDate']), $r['editCount'], $r['editUser']];
|
|
||||||
|
|
||||||
if ($r['flags'] & CC_FLAG_STICKY)
|
|
||||||
$c['sticky'] = true;
|
|
||||||
|
|
||||||
if ($r['flags'] & CC_FLAG_DELETED)
|
|
||||||
{
|
|
||||||
$c['deleted'] = true;
|
|
||||||
$c['deletedInfo'] = [date(Util::$dateFormatInternal, $r['deleteDate']), $r['deleteUser']];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($r['flags'] & CC_FLAG_OUTDATED)
|
|
||||||
$c['outofdate'] = true;
|
|
||||||
|
|
||||||
$comments[] = $c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getVideos(int $typeOrUser = 0, int $typeId = 0, ?int &$nFound = 0, bool $dateFmt = true) : array
|
|
||||||
{
|
|
||||||
$videos = DB::Aowow()->select(self::$viQuery,
|
|
||||||
CC_FLAG_STICKY,
|
|
||||||
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
|
|
||||||
CC_FLAG_APPROVED,
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
|
|
||||||
!$typeOrUser ? Cfg::get('SQL_LIMIT_SEARCH') : DBSIMPLE_SKIP
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$videos)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$nFound = DB::Aowow()->selectCell(
|
|
||||||
substr_replace(self::$viQuery, 'SELECT COUNT(*) ', 0, strpos(self::$viQuery, 'FROM')),
|
|
||||||
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
|
|
||||||
CC_FLAG_APPROVED,
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
|
|
||||||
DBSIMPLE_SKIP
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($typeOrUser <= 0) // not for search by type/typeId
|
|
||||||
{
|
|
||||||
foreach ($videos as $v)
|
|
||||||
self::addSubject($v['type'], $v['typeId']);
|
|
||||||
|
|
||||||
self::getSubjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
// format data to meet requirements of the js
|
|
||||||
foreach ($videos as &$v)
|
|
||||||
{
|
|
||||||
if ($typeOrUser <= 0) // not for search by type/typeId
|
|
||||||
{
|
|
||||||
if (!empty(self::$subjCache[$v['type']][$v['typeId']]) && !is_numeric(self::$subjCache[$v['type']][$v['typeId']]))
|
|
||||||
$v['subject'] = self::$subjCache[$v['type']][$v['typeId']];
|
|
||||||
else
|
|
||||||
$v['subject'] = Lang::user('removed');
|
|
||||||
}
|
|
||||||
|
|
||||||
$v['date'] = $dateFmt ? date(Util::$dateFormatInternal, $v['date']) : intVal($v['date']);
|
|
||||||
$v['videoType'] = 1; // always youtube
|
|
||||||
|
|
||||||
if (!$v['sticky'])
|
|
||||||
unset($v['sticky']);
|
|
||||||
|
|
||||||
if (!$v['user'])
|
|
||||||
unset($v['user']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($videos);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getScreenshots(int $typeOrUser = 0, int $typeId = 0, ?int &$nFound = 0, bool $dateFmt = true) : array
|
|
||||||
{
|
|
||||||
$screenshots = DB::Aowow()->select(self::$ssQuery,
|
|
||||||
CC_FLAG_STICKY,
|
|
||||||
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
|
|
||||||
CC_FLAG_APPROVED,
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
|
|
||||||
!$typeOrUser ? Cfg::get('SQL_LIMIT_SEARCH') : DBSIMPLE_SKIP
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$screenshots)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$nFound = DB::Aowow()->selectCell(
|
|
||||||
substr_replace(self::$ssQuery, 'SELECT COUNT(*) ', 0, strpos(self::$ssQuery, 'FROM')),
|
|
||||||
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
|
|
||||||
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
|
|
||||||
CC_FLAG_APPROVED,
|
|
||||||
CC_FLAG_DELETED,
|
|
||||||
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
|
|
||||||
DBSIMPLE_SKIP
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($typeOrUser <= 0) // not for search by type/typeId
|
|
||||||
{
|
|
||||||
foreach ($screenshots as $s)
|
|
||||||
self::addSubject($s['type'], $s['typeId']);
|
|
||||||
|
|
||||||
self::getSubjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
// format data to meet requirements of the js
|
|
||||||
foreach ($screenshots as &$s)
|
|
||||||
{
|
|
||||||
if ($typeOrUser <= 0) // not for search by type/typeId
|
|
||||||
{
|
|
||||||
if (!empty(self::$subjCache[$s['type']][$s['typeId']]) && !is_numeric(self::$subjCache[$s['type']][$s['typeId']]))
|
|
||||||
$s['subject'] = self::$subjCache[$s['type']][$s['typeId']];
|
|
||||||
else
|
|
||||||
$s['subject'] = Lang::user('removed');
|
|
||||||
}
|
|
||||||
|
|
||||||
$s['date'] = $dateFmt ? date(Util::$dateFormatInternal, $s['date']) : intVal($s['date']);
|
|
||||||
|
|
||||||
if (!$s['sticky'])
|
|
||||||
unset($s['sticky']);
|
|
||||||
|
|
||||||
if (!$s['user'])
|
|
||||||
unset($s['user']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($screenshots);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getAll(int $type, int $typeId, array &$jsg) : array
|
|
||||||
{
|
|
||||||
$result = array(
|
|
||||||
'vi' => self::getVideos($type, $typeId),
|
|
||||||
'ss' => self::getScreenshots($type, $typeId),
|
|
||||||
'co' => self::getComments($type, $typeId)
|
|
||||||
);
|
|
||||||
|
|
||||||
Util::mergeJsGlobals($jsg, self::$jsGlobals);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getJSGlobals() : array
|
|
||||||
{
|
|
||||||
return self::$jsGlobals;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
@@ -1,726 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die("illegal access");
|
|
||||||
|
|
||||||
|
|
||||||
trait TrProfilerFilter
|
|
||||||
{
|
|
||||||
protected array $parentCats = []; // used to validate ty-filter
|
|
||||||
|
|
||||||
protected function cbRegionCheck(string &$v) : bool
|
|
||||||
{
|
|
||||||
if (in_array($v, Util::$regions))
|
|
||||||
{
|
|
||||||
$this->parentCats[0] = $v; // directly redirect onto this region
|
|
||||||
$v = ''; // remove from filter
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbServerCheck(string &$v) : bool
|
|
||||||
{
|
|
||||||
foreach (Profiler::getRealms() as $realm)
|
|
||||||
if (Profiler::urlize($realm['name'], true) == $v)
|
|
||||||
{
|
|
||||||
$this->parentCats[1] = $v; // directly redirect onto this server
|
|
||||||
$v = ''; // remove from filter
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class Filter
|
|
||||||
{
|
|
||||||
private static $wCards = ['*' => '%', '?' => '_'];
|
|
||||||
|
|
||||||
public const CR_BOOLEAN = 1;
|
|
||||||
public const CR_FLAG = 2;
|
|
||||||
public const CR_NUMERIC = 3;
|
|
||||||
public const CR_STRING = 4;
|
|
||||||
public const CR_ENUM = 5;
|
|
||||||
public const CR_STAFFFLAG = 6;
|
|
||||||
public const CR_CALLBACK = 7;
|
|
||||||
public const CR_NYI_PH = 999;
|
|
||||||
|
|
||||||
public const V_EQUAL = 8;
|
|
||||||
public const V_RANGE = 9;
|
|
||||||
public const V_LIST = 10;
|
|
||||||
public const V_CALLBACK = 11;
|
|
||||||
public const V_REGEX = 12;
|
|
||||||
|
|
||||||
protected const ENUM_ANY = -2323;
|
|
||||||
protected const ENUM_NONE = -2324;
|
|
||||||
|
|
||||||
protected const PATTERN_NAME = '/[\p{C};%\\\\]/ui';
|
|
||||||
protected const PATTERN_CRV = '/[\p{C};:%\\\\]/ui';
|
|
||||||
protected const PATTERN_INT = '/\D/';
|
|
||||||
public const PATTERN_PARAM = '/^[\p{L}\p{Sm} \d\p{P}]+$/i';
|
|
||||||
|
|
||||||
protected const ENUM_FACTION = array( 469, 1037, 1106, 529, 1012, 87, 21, 910, 609, 942, 909, 530, 69, 577, 930, 1068, 1104, 729, 369, 92,
|
|
||||||
54, 946, 67, 1052, 749, 47, 989, 1090, 1098, 978, 1011, 93, 1015, 1038, 76, 470, 349, 1031, 1077, 809,
|
|
||||||
911, 890, 970, 169, 730, 72, 70, 932, 1156, 933, 510, 1126, 1067, 1073, 509, 941, 1105, 990, 934, 935,
|
|
||||||
1094, 1119, 1124, 1064, 967, 1091, 59, 947, 81, 576, 922, 68, 1050, 1085, 889, 589, 270);
|
|
||||||
protected const ENUM_CURRENCY = array(32572, 32569, 29736, 44128, 20560, 20559, 29434, 37829, 23247, 44990, 24368, 52027, 52030, 43016, 41596, 34052, 45624, 49426, 40752, 47241,
|
|
||||||
40753, 29024, 24245, 26045, 26044, 38425, 29735, 24579, 24581, 32897, 22484, 52026, 52029, 4291, 28558, 43228, 34664, 47242, 52025, 52028,
|
|
||||||
37836, 20558, 34597, 43589);
|
|
||||||
protected const ENUM_EVENT = array( 372, 283, 285, 353, 420, 400, 284, 201, 374, 409, 141, 324, 321, 424, 423, 327, 341, 181, 404, 398,
|
|
||||||
301);
|
|
||||||
protected const ENUM_ZONE = array( 4494, 36, 2597, 3358, 45, 331, 3790, 4277, 16, 3524, 3, 3959, 719, 1584, 25, 1583, 2677, 3702, 3522, 4,
|
|
||||||
3525, 3537, 46, 1941, 2918, 3905, 4024, 2817, 4395, 4378, 148, 393, 1657, 41, 2257, 405, 2557, 65, 4196, 1,
|
|
||||||
14, 10, 15, 139, 12, 3430, 3820, 361, 357, 3433, 721, 394, 3923, 4416, 2917, 4272, 4820, 4264, 3483, 3562,
|
|
||||||
267, 495, 4742, 3606, 210, 4812, 1537, 4710, 4080, 3457, 38, 4131, 3836, 3792, 2100, 2717, 493, 215, 3518, 3698,
|
|
||||||
3456, 3523, 2367, 2159, 1637, 4813, 4298, 2437, 722, 491, 44, 3429, 3968, 796, 2057, 51, 3607, 3791, 3789, 209,
|
|
||||||
3520, 3703, 3711, 1377, 3487, 130, 3679, 406, 1519, 4384, 33, 2017, 1477, 4075, 8, 440, 141, 3428, 3519, 3848,
|
|
||||||
17, 2366, 3840, 3713, 3847, 3775, 4100, 1581, 3557, 3845, 4500, 4809, 47, 3849, 4265, 4493, 4228, 3698, 4406, 3714,
|
|
||||||
3717, 3715, 717, 67, 3716, 457, 4415, 400, 1638, 1216, 85, 4723, 4722, 1337, 4273, 490, 1497, 206, 1196, 4603,
|
|
||||||
718, 3277, 28, 40, 11, 4197, 618, 3521, 3805, 66, 1176, 1977);
|
|
||||||
protected const ENUM_HEROICDUNGEON = array( 4494, 3790, 4277, 4196, 4416, 4272, 4820, 4264, 3562, 4131, 3792, 2367, 4813, 3791, 3789, 3848, 2366, 3713, 3847, 4100,
|
|
||||||
4809, 3849, 4265, 4228, 3714, 3717, 3715, 3716, 4415, 4723, 206, 1196);
|
|
||||||
protected const ENUM_MULTIMODERAID = array( 4812, 3456, 2159, 4500, 4493, 4722, 4273, 4603, 4987);
|
|
||||||
protected const ENUM_HEROICRAID = array( 4987, 4812, 4722);
|
|
||||||
protected const ENUM_CLASSS = array( null, 1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, true, false);
|
|
||||||
protected const ENUM_RACE = array( null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false);
|
|
||||||
protected const ENUM_PROFESSION = array( null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773);
|
|
||||||
|
|
||||||
public bool $error = false; // erroneous search fields
|
|
||||||
|
|
||||||
// item related
|
|
||||||
public array $upgrades = []; // [itemId => slotId]
|
|
||||||
public array $extraOpts = []; // score for statWeights
|
|
||||||
public array $wtCnd = []; // DBType condition for statWeights
|
|
||||||
|
|
||||||
private array $cndSet = []; // db type query storage
|
|
||||||
private array $rawData = [];
|
|
||||||
|
|
||||||
/* genericFilter: [FILTER_TYPE, colOrFnName, param1, param2]
|
|
||||||
[self::CR_BOOLEAN, <string:colName>, <bool:isString>, null]
|
|
||||||
[self::CR_FLAG, <string:colName>, <int:testBit>, <bool:matchAny>] # default param2: matchExact
|
|
||||||
[self::CR_NUMERIC, <string:colName>, <int:NUM_FLAGS>, <bool:addExtraCol>]
|
|
||||||
[self::CR_STRING, <string:colName>, <int:STR_FLAGS>, null]
|
|
||||||
[self::CR_ENUM, <string:colName>, <bool:ANYNONE>, <bool:isEnumVal>] # param3 ? crv is val in enum : key in enum
|
|
||||||
[self::CR_STAFFFLAG, <string:colName>, null, null]
|
|
||||||
[self::CR_CALLBACK, <string:fnName>, <mixed:param1>, <mixed:param2>]
|
|
||||||
[self::CR_NYI_PH, null, <int:returnVal>, param2] # mostly 1: to ignore this criterium; 0: to fail the whole query
|
|
||||||
*/
|
|
||||||
protected array $genericFilter = [];
|
|
||||||
protected array $inputFields = []; // list of input fields defined per page - fieldName => [checkType, checkValue[, fieldIsArray]]
|
|
||||||
protected array $enums = []; // validation for opt lists per page - criteriumID => [validOptionList]
|
|
||||||
protected string $type = ''; // set by child
|
|
||||||
protected array $parentCats = []; // used to validate ty-filter
|
|
||||||
|
|
||||||
// express Filters in template
|
|
||||||
public string $fiInit = ''; // str: filter template (and init html form)
|
|
||||||
public string $fiType = ''; // str: filter template (set without init)
|
|
||||||
public array $fiSetCriteria = []; // fn params (cr, crs, crv)
|
|
||||||
public array $fiSetWeights = []; // fn params (weights, nt, ids, stealth)
|
|
||||||
public array $fiReputationCols = []; // fn params ([[factionId, factionName], ...])
|
|
||||||
public array $fiExtraCols = []; //
|
|
||||||
public string $query = ''; // as in url query params
|
|
||||||
public array $values = []; // old fiData['v']
|
|
||||||
public array $criteria = []; // old fiData['c']
|
|
||||||
|
|
||||||
// parse the provided request into a usable format
|
|
||||||
public function __construct(string|array $data, array $opts = [])
|
|
||||||
{
|
|
||||||
$this->parentCats = $opts['parentCats'] ?? [];
|
|
||||||
|
|
||||||
// use fn fi_init() if we have a criteria selector, else use var fi_type
|
|
||||||
if ($this->genericFilter)
|
|
||||||
$this->fiInit = $this->type;
|
|
||||||
else
|
|
||||||
$this->fiType = $this->type;
|
|
||||||
|
|
||||||
if (is_array($data))
|
|
||||||
$this->rawData = $data; // could set >query for consistency sake, but is not used when converting from POST
|
|
||||||
|
|
||||||
if (is_string($data))
|
|
||||||
{
|
|
||||||
// an error occured, while processing POST
|
|
||||||
if (isset($_SESSION['error']['fi']))
|
|
||||||
{
|
|
||||||
$this->error = $_SESSION['error']['fi'] == get_class($this);
|
|
||||||
unset($_SESSION['error']['fi']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->query = $data;
|
|
||||||
$this->rawData = $this->transformGET($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->initFields();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function mergeCat(array &$cats) : void
|
|
||||||
{
|
|
||||||
foreach ($this->parentCats as $idx => $cat)
|
|
||||||
$cats[$idx] = $cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function &criteriaIterator() : \Generator
|
|
||||||
{
|
|
||||||
if (!$this->criteria)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($this->criteria['cr']); $i++)
|
|
||||||
{
|
|
||||||
// throws a notice if yielded directly "Only variable references should be yielded by reference"
|
|
||||||
$v = [&$this->criteria['cr'][$i], &$this->criteria['crs'][$i], &$this->criteria['crv'][$i]];
|
|
||||||
yield $i => $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************/
|
|
||||||
/* get prepared values */
|
|
||||||
/***********************/
|
|
||||||
|
|
||||||
public function buildGETParam(array $override = [], array $addCr = []) : string
|
|
||||||
{
|
|
||||||
$get = [];
|
|
||||||
foreach (array_merge($this->criteria, $this->values, $override) as $k => $v)
|
|
||||||
{
|
|
||||||
if (isset($addCr[$k]))
|
|
||||||
{
|
|
||||||
$v = $v ? array_merge((array)$v, (array)$addCr[$k]) : $addCr[$k];
|
|
||||||
unset($addCr[$k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($v === '' || $v === null || $v === [])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$get[$k] = $k.'='.(is_array($v) ? implode(':', $v) : $v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// no criteria were set, so no merge occured .. append
|
|
||||||
if ($addCr)
|
|
||||||
{
|
|
||||||
$get['cr'] = 'cr='.$addCr['cr'];
|
|
||||||
$get['crs'] = 'crs='.$addCr['crs'];
|
|
||||||
$get['crv'] = 'crv='.$addCr['crv'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode(';', $get);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConditions() : array
|
|
||||||
{
|
|
||||||
if (!$this->cndSet)
|
|
||||||
{
|
|
||||||
// values
|
|
||||||
$this->cndSet = $this->createSQLForValues();
|
|
||||||
|
|
||||||
// criteria
|
|
||||||
foreach ($this->criteriaIterator() as &$_cr)
|
|
||||||
if ($cnd = $this->createSQLForCriterium(...$_cr))
|
|
||||||
$this->cndSet[] = $cnd;
|
|
||||||
|
|
||||||
if ($this->cndSet) // Note: TYPE_SOUND does not use 'match any'
|
|
||||||
array_unshift($this->cndSet, empty($this->values['ma']) ? 'AND' : 'OR');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->cndSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSetCriteria(int ...$cr) : array
|
|
||||||
{
|
|
||||||
if (!$cr || !$this->fiSetCriteria)
|
|
||||||
return $this->fiSetCriteria;
|
|
||||||
|
|
||||||
return array_intersect($this->fiSetCriteria['cr'], $cr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********************/
|
|
||||||
/* input sanitization */
|
|
||||||
/**********************/
|
|
||||||
|
|
||||||
private function transformGET(string $get) : array
|
|
||||||
{
|
|
||||||
if (!$get)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
foreach (explode(';', $get) as $field)
|
|
||||||
{
|
|
||||||
if (!strstr($field, '='))
|
|
||||||
{
|
|
||||||
trigger_error('Filter::transformGET - malformed GET string', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$k, $v] = explode('=', $field);
|
|
||||||
|
|
||||||
if (!isset($this->inputFields[$k]))
|
|
||||||
{
|
|
||||||
trigger_error('Filter::transformGET - GET param not in filter: '.$k, E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$asArray = $this->inputFields[$k][2];
|
|
||||||
|
|
||||||
$data[$k] = $asArray ? explode(':', $v) : $v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function initFields() : void
|
|
||||||
{
|
|
||||||
foreach ($this->inputFields as $inp => [$type, $valid, $asArray])
|
|
||||||
{
|
|
||||||
$var = in_array($inp, ['cr', 'crs', 'crv']) ? 'criteria' : 'values';
|
|
||||||
|
|
||||||
if (!isset($this->rawData[$inp]) || $this->rawData[$inp] === '')
|
|
||||||
{
|
|
||||||
$this->$var[$inp] = $asArray ? [] : null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$val = $this->rawData[$inp];
|
|
||||||
|
|
||||||
if ($asArray)
|
|
||||||
{
|
|
||||||
// quirk: in the POST step criteria can be [[''], null, null] if not selected.
|
|
||||||
$buff = [];
|
|
||||||
foreach ((array)$val as $v) // can be string|int in POST step if only one value present
|
|
||||||
if ($v !== '' && $this->checkInput($type, $valid, $v))
|
|
||||||
$buff[] = $v;
|
|
||||||
|
|
||||||
$this->$var[$inp] = $buff;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$this->$var[$inp] = $this->checkInput($type, $valid, $val) ? $val : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function evalCriteria() : void // [cr]iterium, [cr].[s]ign, [cr].[v]alue
|
|
||||||
{
|
|
||||||
if (empty($this->criteria['cr']) && empty($this->criteria['crs']) && empty($this->criteria['crv']))
|
|
||||||
return;
|
|
||||||
else if (empty($this->criteria['cr']) || empty($this->criteria['crs']) || empty($this->criteria['crv']))
|
|
||||||
{
|
|
||||||
unset($this->criteria['cr']);
|
|
||||||
unset($this->criteria['crs']);
|
|
||||||
unset($this->criteria['crv']);
|
|
||||||
|
|
||||||
trigger_error('Filter::setCriteria - one of cr, crs, crv is missing', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_cr = &$this->criteria['cr'];
|
|
||||||
$_crs = &$this->criteria['crs'];
|
|
||||||
$_crv = &$this->criteria['crv'];
|
|
||||||
|
|
||||||
if (count($_cr) != count($_crv) || count($_cr) != count($_crs) || count($_cr) > 5 || count($_crs) > 5 /*|| count($_crv) > 5*/)
|
|
||||||
{
|
|
||||||
// use min provided criterion as basis; 5 criteria at most
|
|
||||||
$min = max(5, min(count($_cr), count($_crv), count($_crs)));
|
|
||||||
if (count($_cr) > $min)
|
|
||||||
array_splice($_cr, $min);
|
|
||||||
|
|
||||||
if (count($_crv) > $min)
|
|
||||||
array_splice($_crv, $min);
|
|
||||||
|
|
||||||
if (count($_crs) > $min)
|
|
||||||
array_splice($_crs, $min);
|
|
||||||
|
|
||||||
trigger_error('Filter::setCriteria - cr, crs, crv are imbalanced', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($_cr); $i++)
|
|
||||||
{
|
|
||||||
// conduct filter specific checks & casts here
|
|
||||||
$unsetme = false;
|
|
||||||
if (isset($this->genericFilter[$_cr[$i]]))
|
|
||||||
{
|
|
||||||
$gf = $this->genericFilter[$_cr[$i]];
|
|
||||||
switch ($gf[0])
|
|
||||||
{
|
|
||||||
case self::CR_NUMERIC:
|
|
||||||
$_ = $_crs[$i];
|
|
||||||
if (!Util::checkNumeric($_crv[$i], $gf[2]) || !$this->int2Op($_))
|
|
||||||
$unsetme = true;
|
|
||||||
break;
|
|
||||||
case self::CR_BOOLEAN:
|
|
||||||
case self::CR_FLAG:
|
|
||||||
$_ = $_crs[$i];
|
|
||||||
if (!$this->int2Bool($_))
|
|
||||||
$unsetme = true;
|
|
||||||
break;
|
|
||||||
case self::CR_ENUM:
|
|
||||||
case self::CR_STAFFFLAG:
|
|
||||||
if (!Util::checkNumeric($_crs[$i], NUM_CAST_INT))
|
|
||||||
$unsetme = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$unsetme && intval($_cr[$i]) && $_crs[$i] !== '' && $_crv[$i] !== '')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
unset($_cr[$i]);
|
|
||||||
unset($_crs[$i]);
|
|
||||||
unset($_crv[$i]);
|
|
||||||
|
|
||||||
trigger_error('Filter::setCriteria - generic check failed ["'.$_cr[$i].'", "'.$_crs[$i].'", "'.$_crv[$i].'"]', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->fiSetCriteria = array(
|
|
||||||
'cr' => $_cr,
|
|
||||||
'crs' => $_crs,
|
|
||||||
'crv' => $_crv
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function evalWeights() : void
|
|
||||||
{
|
|
||||||
// both empty: not in use; not an error
|
|
||||||
if (!$this->values['wt'] && !$this->values['wtv'])
|
|
||||||
return;
|
|
||||||
|
|
||||||
// one empty: erroneous manual input?
|
|
||||||
if (!$this->values['wt'] || !$this->values['wtv'])
|
|
||||||
{
|
|
||||||
unset($this->values['wt']);
|
|
||||||
unset($this->values['wtv']);
|
|
||||||
|
|
||||||
trigger_error('Filter::setWeights - one of wt, wtv is missing', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_wt = &$this->values['wt'];
|
|
||||||
$_wtv = &$this->values['wtv'];
|
|
||||||
|
|
||||||
$nwt = count($_wt);
|
|
||||||
$nwtv = count($_wtv);
|
|
||||||
|
|
||||||
if ($nwt != $nwtv)
|
|
||||||
{
|
|
||||||
trigger_error('Filter::setWeights - wt, wtv are imbalanced', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($nwt > $nwtv)
|
|
||||||
array_splice($_wt, $nwtv);
|
|
||||||
else if ($nwtv > $nwt)
|
|
||||||
array_splice($_wtv, $nwt);
|
|
||||||
|
|
||||||
$this->fiSetWeights = [$_wt, $_wtv];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function checkInput(int $type, mixed $valid, mixed &$val, bool $recursive = false) : bool
|
|
||||||
{
|
|
||||||
switch ($type)
|
|
||||||
{
|
|
||||||
case self::V_EQUAL:
|
|
||||||
if (gettype($valid) == 'integer')
|
|
||||||
$val = intval($val);
|
|
||||||
else if (gettype($valid) == 'double')
|
|
||||||
$val = floatval($val);
|
|
||||||
else /* if (gettype($valid) == 'string') */
|
|
||||||
$val = strval($val);
|
|
||||||
|
|
||||||
if ($valid == $val)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case self::V_LIST:
|
|
||||||
if (!Util::checkNumeric($val, NUM_CAST_INT))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach ($valid as $k => $v)
|
|
||||||
{
|
|
||||||
if (gettype($v) != 'array')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($this->checkInput(self::V_RANGE, $v, $val, true))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
unset($valid[$k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_array($val, $valid))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case self::V_RANGE:
|
|
||||||
if (Util::checkNumeric($val, NUM_CAST_INT) && $val >= $valid[0] && $val <= $valid[1])
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case self::V_CALLBACK:
|
|
||||||
if ($this->$valid($val))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case self::V_REGEX:
|
|
||||||
if (!preg_match($valid, $val))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$recursive)
|
|
||||||
{
|
|
||||||
trigger_error('Filter::checkInput - check failed [type: '.$type.' valid: '.((string)$valid).' val: '.((string)$val).']', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function transformToken(string $string, bool $exact) : string
|
|
||||||
{
|
|
||||||
// escape manually entered _; entering % should be prohibited
|
|
||||||
$string = str_replace('_', '\\_', $string);
|
|
||||||
|
|
||||||
// now replace search wildcards with sql wildcards
|
|
||||||
$string = strtr($string, self::$wCards);
|
|
||||||
|
|
||||||
return sprintf($exact ? '%s' : '%%%s%%', $string);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function tokenizeString(array $fields, string $string = '', bool $exact = false, bool $shortStr = false) : array
|
|
||||||
{
|
|
||||||
if (!$string && $this->values['na'])
|
|
||||||
$string = $this->values['na'];
|
|
||||||
|
|
||||||
$qry = [];
|
|
||||||
foreach ($fields as $f)
|
|
||||||
{
|
|
||||||
$sub = [];
|
|
||||||
$parts = $exact ? [$string] : array_filter(explode(' ', $string));
|
|
||||||
foreach ($parts as $p)
|
|
||||||
{
|
|
||||||
if ($p[0] == '-' && (mb_strlen($p) > 3 || $shortStr))
|
|
||||||
$sub[] = [$f, $this->transformToken(mb_substr($p, 1), $exact), '!'];
|
|
||||||
else if ($p[0] != '-' && (mb_strlen($p) > 2 || $shortStr))
|
|
||||||
$sub[] = [$f, $this->transformToken($p, $exact)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// single cnd?
|
|
||||||
if (!$sub)
|
|
||||||
continue;
|
|
||||||
else if (count($sub) > 1)
|
|
||||||
array_unshift($sub, 'AND');
|
|
||||||
else
|
|
||||||
$sub = $sub[0];
|
|
||||||
|
|
||||||
$qry[] = $sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
// single cnd?
|
|
||||||
if (!$qry)
|
|
||||||
{
|
|
||||||
trigger_error('Filter::tokenizeString - could not tokenize string: '.$string, E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
}
|
|
||||||
else if (count($qry) > 1)
|
|
||||||
array_unshift($qry, 'OR');
|
|
||||||
else
|
|
||||||
$qry = $qry[0];
|
|
||||||
|
|
||||||
return $qry;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function int2Op(mixed &$op) : bool
|
|
||||||
{
|
|
||||||
$op = match ($op) {
|
|
||||||
1 => '>',
|
|
||||||
2 => '>=',
|
|
||||||
3 => '=',
|
|
||||||
4 => '<=',
|
|
||||||
5 => '<',
|
|
||||||
6 => '!=',
|
|
||||||
default => null
|
|
||||||
};
|
|
||||||
|
|
||||||
return $op !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function int2Bool(mixed &$op) : bool
|
|
||||||
{
|
|
||||||
$op = match ($op) {
|
|
||||||
1 => true,
|
|
||||||
2 => false,
|
|
||||||
default => null
|
|
||||||
};
|
|
||||||
|
|
||||||
return $op !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function list2Mask(array $list, bool $noOffset = false) : int
|
|
||||||
{
|
|
||||||
$mask = 0x0;
|
|
||||||
$o = $noOffset ? 0 : 1; // schoolMask requires this..?
|
|
||||||
|
|
||||||
foreach ($list as $itm)
|
|
||||||
$mask += (1 << (intval($itm) - $o));
|
|
||||||
|
|
||||||
return $mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************************/
|
|
||||||
/* create conditions from */
|
|
||||||
/* generic criteria */
|
|
||||||
/**************************/
|
|
||||||
|
|
||||||
private function genericBoolean(string $field, int $op, bool $isString) : ?array
|
|
||||||
{
|
|
||||||
if ($this->int2Bool($op))
|
|
||||||
{
|
|
||||||
$value = $isString ? '' : 0;
|
|
||||||
$operator = $op ? '!' : null;
|
|
||||||
|
|
||||||
return [$field, $value, $operator];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function genericBooleanFlags(string $field, int $value, int $op, ?bool $matchAny = false) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($op))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!$op)
|
|
||||||
return [[$field, $value, '&'], 0];
|
|
||||||
else if ($matchAny)
|
|
||||||
return [[$field, $value, '&'], 0, '!'];
|
|
||||||
else
|
|
||||||
return [[$field, $value, '&'], $value];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function genericString(string $field, string $value, ?int $strFlags) : ?array
|
|
||||||
{
|
|
||||||
$strFlags ??= 0x0;
|
|
||||||
|
|
||||||
if ($strFlags & STR_LOCALIZED)
|
|
||||||
$field .= '_loc'.Lang::getLocale()->value;
|
|
||||||
|
|
||||||
return $this->tokenizeString([$field], $value, $strFlags & STR_MATCH_EXACT, $strFlags & STR_ALLOW_SHORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function genericNumeric(string $field, int|float $value, int $op, int $typeCast) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($value, $typeCast))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($this->int2Op($op))
|
|
||||||
return [$field, $value, $op];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function genericEnum(string $field, mixed $value) : ?array
|
|
||||||
{
|
|
||||||
if (is_bool($value))
|
|
||||||
return [$field, 0, ($value ? '>' : '<=')];
|
|
||||||
else if ($value == self::ENUM_ANY)
|
|
||||||
return [$field, 0, '!'];
|
|
||||||
else if ($value == self::ENUM_NONE)
|
|
||||||
return [$field, 0];
|
|
||||||
else if ($value !== null)
|
|
||||||
return [$field, $value];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function genericCriterion(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
[$crType, $colOrFn, $param1, $param2] = array_pad($this->genericFilter[$cr], 4, null);
|
|
||||||
$result = null;
|
|
||||||
|
|
||||||
switch ($crType)
|
|
||||||
{
|
|
||||||
case self::CR_NUMERIC:
|
|
||||||
$result = $this->genericNumeric($colOrFn, $crv, $crs, $param1);
|
|
||||||
break;
|
|
||||||
case self::CR_FLAG:
|
|
||||||
$result = $this->genericBooleanFlags($colOrFn, $param1, $crs, $param2);
|
|
||||||
break;
|
|
||||||
case self::CR_STAFFFLAG:
|
|
||||||
if (User::isInGroup(U_GROUP_EMPLOYEE) && $crs > 0)
|
|
||||||
$result = $this->genericBooleanFlags($colOrFn, (1 << ($crs - 1)), true);
|
|
||||||
break;
|
|
||||||
case self::CR_BOOLEAN:
|
|
||||||
$result = $this->genericBoolean($colOrFn, $crs, !empty($param1));
|
|
||||||
break;
|
|
||||||
case self::CR_STRING:
|
|
||||||
$result = $this->genericString($colOrFn, $crv, $param1);
|
|
||||||
break;
|
|
||||||
case self::CR_ENUM:
|
|
||||||
if (!$param2 && isset($this->enums[$cr][$crs]))
|
|
||||||
$result = $this->genericEnum($colOrFn, $this->enums[$cr][$crs]);
|
|
||||||
if ($param2 && in_array($crs, $this->enums[$cr]))
|
|
||||||
$result = $this->genericEnum($colOrFn, $crs);
|
|
||||||
else if ($param1 && ($crs == self::ENUM_ANY || $crs == self::ENUM_NONE))
|
|
||||||
$result = $this->genericEnum($colOrFn, $crs);
|
|
||||||
break;
|
|
||||||
case self::CR_CALLBACK:
|
|
||||||
$result = $this->{$colOrFn}($cr, $crs, $crv, $param1, $param2);
|
|
||||||
break;
|
|
||||||
case self::CR_NYI_PH: // do not limit with not implemented filters
|
|
||||||
if (is_int($param1))
|
|
||||||
return [$param1];
|
|
||||||
|
|
||||||
// for nonsensical values; compare against 0
|
|
||||||
if ($this->int2Op($crs) && Util::checkNumeric($crv))
|
|
||||||
{
|
|
||||||
if ($crs == '=')
|
|
||||||
$crs = '==';
|
|
||||||
|
|
||||||
return eval('return ('.$crv.' '.$crs.' 0);') ? [1] : [0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($result && $crType == self::CR_NUMERIC && !empty($param2))
|
|
||||||
$this->fiExtraCols[] = $cr;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************************/
|
|
||||||
/* create conditions from */
|
|
||||||
/* non-generic values and criteria */
|
|
||||||
/***********************************/
|
|
||||||
|
|
||||||
protected function createSQLForCriterium(int &$cr, int &$crs, string &$crv) : array
|
|
||||||
{
|
|
||||||
if (!$this->genericFilter) // criteria not in use - no error
|
|
||||||
return [];
|
|
||||||
|
|
||||||
if (isset($this->genericFilter[$cr]))
|
|
||||||
if ($genCr = $this->genericCriterion($cr, $crs, $crv))
|
|
||||||
return $genCr;
|
|
||||||
|
|
||||||
trigger_error('Filter::createSQLForCriterium - received unhandled criterium: ["'.$cr.'", "'.$crs.'", "'.$crv.'"]', E_USER_NOTICE);
|
|
||||||
$this->error = true;
|
|
||||||
|
|
||||||
unset($cr, $crs, $crv);
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function createSQLForValues() : array;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Announcement implements \JsonSerializable
|
|
||||||
{
|
|
||||||
public const MODE_PAGE_TOP = 0;
|
|
||||||
public const MODE_CONTENT_TOP = 1;
|
|
||||||
|
|
||||||
public const STATUS_DISABLED = 0;
|
|
||||||
public const STATUS_ENABLED = 1;
|
|
||||||
public const STATUS_DELETED = 2;
|
|
||||||
|
|
||||||
public readonly int $status;
|
|
||||||
private bool $editable = false;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public readonly int $id,
|
|
||||||
private string $name,
|
|
||||||
private LocString $text,
|
|
||||||
private int $mode = self::MODE_CONTENT_TOP,
|
|
||||||
int $status = self::STATUS_ENABLED,
|
|
||||||
private string $style = '')
|
|
||||||
{
|
|
||||||
// a negative id displays ENABLE/DISABLE and DELETE links for this announcement
|
|
||||||
// TODO - the ugroup check mirrors the js. Add other checks like ownership status? (ownership currently not stored)
|
|
||||||
if (User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU) /* && User::$id == $authorId */)
|
|
||||||
$this->editable = true;
|
|
||||||
|
|
||||||
if ($this->mode != self::MODE_PAGE_TOP && $this->mode != self::MODE_CONTENT_TOP)
|
|
||||||
$this->mode = self::MODE_PAGE_TOP;
|
|
||||||
|
|
||||||
if ($status != self::STATUS_DISABLED && $status != self::STATUS_ENABLED && $status != self::STATUS_DELETED)
|
|
||||||
$this->status = self::STATUS_DELETED;
|
|
||||||
else
|
|
||||||
$this->status = $status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$json = array(
|
|
||||||
'parent' => 'announcement-' . abs($this->id),
|
|
||||||
'id' => $this->editable ? -$this->id : $this->id,
|
|
||||||
'mode' => $this->mode,
|
|
||||||
'status' => $this->status,
|
|
||||||
'name' => $this->name,
|
|
||||||
'text' => (string)$this->text // force LocString to naive string for display
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->style)
|
|
||||||
$json['style'] = $this->style;
|
|
||||||
|
|
||||||
return $json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
if ($this->status == self::STATUS_DELETED)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return "new Announcement(".Util::toJSON($this).");\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Book implements \JsonSerializable
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
private array $pages, // js:array of html
|
|
||||||
private string $parent = 'book-generic', // HTMLNode.id
|
|
||||||
private ?int $page = null) // start page; defaults to 1
|
|
||||||
{
|
|
||||||
if (!$this->parent)
|
|
||||||
trigger_error(self::class.'::__construct - initialized without parent element', E_USER_WARNING);
|
|
||||||
|
|
||||||
if (!$this->pages)
|
|
||||||
trigger_error(self::class.'::__construct - initialized without content', E_USER_WARNING);
|
|
||||||
else
|
|
||||||
$this->pages = Util::parseHtmlText($this->pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function &iterate() : \Generator
|
|
||||||
{
|
|
||||||
reset($this->pages);
|
|
||||||
|
|
||||||
foreach ($this->pages as $idx => &$page)
|
|
||||||
yield $idx => $page;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this as $prop => $val)
|
|
||||||
if ($val !== null && $prop[0] != '_')
|
|
||||||
$result[$prop] = $val;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
return "new Book(".Util::toJSON($this).");\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class IconElement
|
|
||||||
{
|
|
||||||
public const SIZE_SMALL = 0;
|
|
||||||
public const SIZE_MEDIUM = 1;
|
|
||||||
public const SIZE_LARGE = 2;
|
|
||||||
|
|
||||||
private const CREATE_ICON_TPL = "\$WH.ge('%s%d').appendChild(%s.createIcon(%s));\n";
|
|
||||||
|
|
||||||
private int $idx = 0;
|
|
||||||
private string $href = '';
|
|
||||||
private bool $noIcon = false;
|
|
||||||
|
|
||||||
public readonly string $quality;
|
|
||||||
public readonly ?string $align;
|
|
||||||
public readonly int $size;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public readonly int $type,
|
|
||||||
public readonly int $typeId,
|
|
||||||
public readonly string $text,
|
|
||||||
public readonly int|string $num = '',
|
|
||||||
public readonly int|string $qty = '',
|
|
||||||
?string $quality = null,
|
|
||||||
int $size = self::SIZE_MEDIUM,
|
|
||||||
bool $link = true,
|
|
||||||
string $url = '',
|
|
||||||
?string $align = null,
|
|
||||||
public readonly string $element = 'icontab-icon',
|
|
||||||
public ?string $extraText = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (is_numeric($quality))
|
|
||||||
$this->quality = 'q'.$quality;
|
|
||||||
else if ($quality !== null)
|
|
||||||
$this->quality = 'q';
|
|
||||||
else
|
|
||||||
$this->quality = '';
|
|
||||||
|
|
||||||
if ($size < self::SIZE_SMALL || $size > self::SIZE_LARGE)
|
|
||||||
{
|
|
||||||
trigger_error('IconElement::__construct - invalid icon size '.$size.'. Normalied to 1 [small]', E_USER_WARNING);
|
|
||||||
$this->size = self::SIZE_SMALL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$this->size = $size;
|
|
||||||
|
|
||||||
if ($align && !in_array($align, ['left', 'right', 'center', 'justify']))
|
|
||||||
{
|
|
||||||
trigger_error('IconElement::__construct - unset invalid align value "'.$align.'".', E_USER_WARNING);
|
|
||||||
$this->align = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$this->align = $align;
|
|
||||||
|
|
||||||
if ($type && $typeId && !Type::validateIds($type, $typeId))
|
|
||||||
{
|
|
||||||
$link = false;
|
|
||||||
trigger_error('IconElement::__construct - invalid typeId '.$typeId.' for '.Type::getFileString($type).'.', E_USER_WARNING);
|
|
||||||
}
|
|
||||||
else if (!$type || !$typeId)
|
|
||||||
$link = false;
|
|
||||||
|
|
||||||
if ($link || $url)
|
|
||||||
$this->href = $url ?: '?'.Type::getFileString($this->type).'='.$this->typeId;
|
|
||||||
|
|
||||||
// see Spell/Tools having icon container but no actual icon and having to be inline with other IconElements
|
|
||||||
$this->noIcon = !$typeId || !Type::hasIcon($type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderContainer(int $lpad = 0, int &$iconIdxOffset = 0, bool $rowWrap = false) : string
|
|
||||||
{
|
|
||||||
if (!$this->noIcon)
|
|
||||||
$this->idx = ++$iconIdxOffset;
|
|
||||||
|
|
||||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
|
||||||
|
|
||||||
$td = $dom->createElement('td');
|
|
||||||
$th = $dom->createElement('th');
|
|
||||||
|
|
||||||
if ($this->noIcon) // see Spell/Tools or AchievementCriteria having no actual icon, but placeholder
|
|
||||||
{
|
|
||||||
$ul = $dom->createElement('ul');
|
|
||||||
$li = $dom->createElement('li');
|
|
||||||
$var = $dom->createElement('var', ' ');
|
|
||||||
$li->appendChild($var);
|
|
||||||
$ul->appendChild($li);
|
|
||||||
$th->appendChild($ul);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$th->setAttribute('id', $this->element . $this->idx);
|
|
||||||
if ($this->align)
|
|
||||||
$th->setAttribute('align', $this->align);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->href)
|
|
||||||
($a = $dom->createElement('a', $this->text))->setAttribute('href', $this->href);
|
|
||||||
else
|
|
||||||
$a = $dom->createTextNode($this->text);
|
|
||||||
|
|
||||||
if ($this->quality)
|
|
||||||
{
|
|
||||||
($sp = $dom->createElement('span'))->setAttribute('class', $this->quality);
|
|
||||||
$sp->appendChild($a);
|
|
||||||
$td->appendChild($sp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$td->appendChild($a);
|
|
||||||
|
|
||||||
// extraText can be HTML, so import it as a fragment
|
|
||||||
if ($this->extraText)
|
|
||||||
{
|
|
||||||
$fragment = $dom->createDocumentFragment();
|
|
||||||
$fragment->appendXML(' '.$this->extraText);
|
|
||||||
$td->appendChild($fragment);
|
|
||||||
}
|
|
||||||
// only for objectives list..?
|
|
||||||
if ($this->num && $this->size == self::SIZE_SMALL)
|
|
||||||
$td->appendChild($dom->createTextNode(' ('.$this->num.')'));
|
|
||||||
|
|
||||||
if ($rowWrap)
|
|
||||||
{
|
|
||||||
$tr = $dom->createElement('tr');
|
|
||||||
$tr->appendChild($th);
|
|
||||||
$tr->appendChild($td);
|
|
||||||
$dom->append($tr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$dom->append($th, $td);
|
|
||||||
|
|
||||||
return str_repeat(' ', $lpad) . $dom->saveHTML();
|
|
||||||
}
|
|
||||||
|
|
||||||
// $WH.ge('icontab-icon1').appendChild(g_spells.createIcon(40120, 1, '1-4', 0));
|
|
||||||
|
|
||||||
public function renderJS(int $lpad = 0) : string
|
|
||||||
{
|
|
||||||
if ($this->noIcon)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
$params = [$this->typeId, $this->size];
|
|
||||||
if ($this->num || $this->qty)
|
|
||||||
$params[] = is_numeric($this->num) ? $this->num : "'".$this->num."'";
|
|
||||||
if ($this->qty)
|
|
||||||
$params[] = is_numeric($this->qty) ? $this->qty : "'".$this->qty."'";
|
|
||||||
|
|
||||||
return str_repeat(' ', $lpad) . sprintf(self::CREATE_ICON_TPL, $this->element, $this->idx, Type::getJSGlobalString($this->type), implode(', ', $params));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class InfoboxMarkup extends Markup
|
|
||||||
{
|
|
||||||
public function __construct(private array $items = [], array $opts, string $parent = '')
|
|
||||||
{
|
|
||||||
parent::__construct('', $opts, $parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addItem(string $item, ?int $pos = null) : void
|
|
||||||
{
|
|
||||||
if (is_null($pos) || $pos >= count($this->items))
|
|
||||||
$this->items[] = $item;
|
|
||||||
else
|
|
||||||
array_splice($this->items, $pos, 0, $item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function append(string $text) : self
|
|
||||||
{
|
|
||||||
if ($this->items && !$this->__text)
|
|
||||||
$this->replace('[ul][li]' . implode('[/li][li]', $this->items) . '[/li][/ul]');
|
|
||||||
|
|
||||||
return parent::append($text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
if ($this->items && !$this->__text)
|
|
||||||
$this->replace('[ul][li]' . implode('[/li][li]', $this->items) . '[/li][/ul]');
|
|
||||||
|
|
||||||
return parent::__toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJsGlobals() : array
|
|
||||||
{
|
|
||||||
if ($this->items && !$this->__text)
|
|
||||||
$this->replace('[ul][li]' . implode('[/li][li]', $this->items) . '[/li][/ul]');
|
|
||||||
|
|
||||||
return parent::getJsGlobals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Listview implements \JsonSerializable
|
|
||||||
{
|
|
||||||
public const MODE_DEFAULT = 0;
|
|
||||||
public const MODE_CHECKBOX = 1;
|
|
||||||
public const MODE_DIV = 2;
|
|
||||||
public const MODE_TILED = 3;
|
|
||||||
public const MODE_CALENDAR = 4;
|
|
||||||
public const MODE_FLEXGRID = 5;
|
|
||||||
|
|
||||||
private const TEMPLATES = array(
|
|
||||||
'achievement' => ['template' => 'achievement', 'id' => 'achievements', 'name' => '$LANG.tab_achievements' ],
|
|
||||||
'areatrigger' => ['template' => 'areatrigger', 'id' => 'areatrigger', ],
|
|
||||||
'calendar' => ['template' => 'holidaycal', 'id' => 'calendar', 'name' => '$LANG.tab_calendar' ],
|
|
||||||
'class' => ['template' => 'classs', 'id' => 'classes', 'name' => '$LANG.tab_classes' ],
|
|
||||||
'commentpreview' => ['template' => 'commentpreview', 'id' => 'comments', 'name' => '$LANG.tab_comments' ],
|
|
||||||
'npc' => ['template' => 'npc', 'id' => 'npcs', 'name' => '$LANG.tab_npcs' ],
|
|
||||||
'currency' => ['template' => 'currency', 'id' => 'currencies', 'name' => '$LANG.tab_currencies' ],
|
|
||||||
'emote' => ['template' => 'emote', 'id' => 'emotes', ],
|
|
||||||
'enchantment' => ['template' => 'enchantment', 'id' => 'enchantments', ],
|
|
||||||
'event' => ['template' => 'holiday', 'id' => 'holidays', 'name' => '$LANG.tab_holidays' ],
|
|
||||||
'faction' => ['template' => 'faction', 'id' => 'factions', 'name' => '$LANG.tab_factions' ],
|
|
||||||
'genericmodel' => ['template' => 'genericmodel', 'id' => 'same-model-as', 'name' => '$LANG.tab_samemodelas' ],
|
|
||||||
'icongallery' => ['template' => 'icongallery', 'id' => 'icons', ],
|
|
||||||
'item' => ['template' => 'item', 'id' => 'items', 'name' => '$LANG.tab_items' ],
|
|
||||||
'itemset' => ['template' => 'itemset', 'id' => 'itemsets', 'name' => '$LANG.tab_itemsets' ],
|
|
||||||
'mail' => ['template' => 'mail', 'id' => 'mails', ],
|
|
||||||
'model' => ['template' => 'model', 'id' => 'gallery', 'name' => '$LANG.tab_gallery' ],
|
|
||||||
'object' => ['template' => 'object', 'id' => 'objects', 'name' => '$LANG.tab_objects' ],
|
|
||||||
'pet' => ['template' => 'pet', 'id' => 'hunter-pets', 'name' => '$LANG.tab_pets' ],
|
|
||||||
'profile' => ['template' => 'profile', 'id' => 'profiles', 'name' => '$LANG.tab_profiles' ],
|
|
||||||
'quest' => ['template' => 'quest', 'id' => 'quests', 'name' => '$LANG.tab_quests' ],
|
|
||||||
'race' => ['template' => 'race', 'id' => 'races', 'name' => '$LANG.tab_races' ],
|
|
||||||
'replypreview' => ['template' => 'replypreview', 'id' => 'comment-replies', 'name' => '$LANG.tab_commentreplies'],
|
|
||||||
'reputationhistory' => ['template' => 'reputationhistory', 'id' => 'reputation', 'name' => '$LANG.tab_reputation' ],
|
|
||||||
'screenshot' => ['template' => 'screenshot', 'id' => 'screenshots', 'name' => '$LANG.tab_screenshots' ],
|
|
||||||
'skill' => ['template' => 'skill', 'id' => 'skills', 'name' => '$LANG.tab_skills' ],
|
|
||||||
'sound' => ['template' => 'sound', 'id' => 'sounds', 'name' => '$LANG.types[19][2]' ],
|
|
||||||
'spell' => ['template' => 'spell', 'id' => 'spells', 'name' => '$LANG.tab_spells' ],
|
|
||||||
'title' => ['template' => 'title', 'id' => 'titles', 'name' => '$LANG.tab_titles' ],
|
|
||||||
'topusers' => ['template' => 'topusers', 'id' => 'topusers', 'name' => '$LANG.topusers' ],
|
|
||||||
'video' => ['template' => 'video', 'id' => 'videos', 'name' => '$LANG.tab_videos' ],
|
|
||||||
'zone' => ['template' => 'zone', 'id' => 'zones', 'name' => '$LANG.tab_zones' ],
|
|
||||||
'guide' => ['template' => 'guide', 'id' => 'guides', ]
|
|
||||||
);
|
|
||||||
|
|
||||||
private string $id = '';
|
|
||||||
private ?string $name = null;
|
|
||||||
private ?array $data = null; // js:array of object <RowDefinitions>
|
|
||||||
private ?string $tabs = null; // js:Object; instance of "Tabs"
|
|
||||||
private ?string $parent = 'lv-generic'; // HTMLNode.id; can be null but is pretty much always 'lv-generic'
|
|
||||||
private ?string $template = null;
|
|
||||||
private ?int $mode = null; // js:int; defaults to MODE_DEFAULT
|
|
||||||
private ?string $note = null; // text in top band
|
|
||||||
|
|
||||||
private ?int $poundable = null; // 0 (no); 1 (always); 2 (yes, w/o sorting); defaults to 1
|
|
||||||
private ?int $searchable = null; // js:bool; defaults to FALSE
|
|
||||||
private ?int $filtrable = null; // js:bool; defaults to FALSE
|
|
||||||
private ?int $sortable = null; // js:bool; defaults to FALSE
|
|
||||||
private ?int $searchDelay = null; // in ms; defalts to 333
|
|
||||||
private ?int $clickable = null; // js:bool; defaults to TRUE
|
|
||||||
private ?int $hideBands = null; // js:int; 1:top, 2:bottom, 3:both;
|
|
||||||
private ?int $hideNav = null; // js:int; 1:top, 2:bottom, 3:both;
|
|
||||||
private ?int $hideHeader = null; // js:bool
|
|
||||||
private ?int $hideCount = null; // js:bool
|
|
||||||
private ?int $debug = null; // js:bool
|
|
||||||
private ?int $_truncated = null; // js:bool; adds predefined note to top band, because there was too much data to display
|
|
||||||
private ?int $_errors = null; // js:bool; adds predefined note to top band, because there was an error
|
|
||||||
private ?int $_petTalents = null; // js:bool; applies modifier for talent levels
|
|
||||||
|
|
||||||
private ?int $nItemsPerPage = null; // js:int; defaults to 50
|
|
||||||
private ?int $_totalCount = null; // js:int; used by loot and comments
|
|
||||||
private ?array $clip = null; // js:array of int {w:<width>, h:<height>}
|
|
||||||
private ?string $customPound = null;
|
|
||||||
private ?string $genericlinktype = null; // sometimes set when expecting to display model
|
|
||||||
private ?array $_upgradeIds = null; // js:array of int (itemIds)
|
|
||||||
|
|
||||||
private null|array|string $extraCols = null; // js:callable or js:array of object <ColumnDefinition>
|
|
||||||
private null|array|string $visibleCols = null; // js:callable or js:array of string <colIds>
|
|
||||||
private null|array|string $hiddenCols = null; // js:callable or js:array of string <colIds>
|
|
||||||
private null|array|string $sort = null; // js:callable or js:array of colIndizes
|
|
||||||
|
|
||||||
private ?string $onBeforeCreate = null; // js:callable
|
|
||||||
private ?string $onAfterCreate = null; // js:callable
|
|
||||||
private ?string $onNoData = null; // js:callable
|
|
||||||
private ?string $computeDataFunc = null; // js:callable
|
|
||||||
private ?string $onSearchSubmit = null; // js:callable
|
|
||||||
private ?string $createNote = null; // js:callable
|
|
||||||
private ?string $createCbControls = null; // js:callable
|
|
||||||
private ?string $customFilter = null; // js:callable
|
|
||||||
private ?string $getItemLink = null; // js:callable
|
|
||||||
private ?array $sortOptions = null; // js:array of object {id:<colId>, name:<name>, hidden:<bool>, type:"text", sortFunc:<callable>}
|
|
||||||
|
|
||||||
private string $__addIn = '';
|
|
||||||
|
|
||||||
public function __construct(array $opts, string $template = '', string $addIn = '')
|
|
||||||
{
|
|
||||||
if ($template && isset(self::TEMPLATES[$template]))
|
|
||||||
foreach (self::TEMPLATES[$template] as $k => $v)
|
|
||||||
$this->$k = $v;
|
|
||||||
|
|
||||||
foreach ($opts as $k => $v)
|
|
||||||
{
|
|
||||||
if (property_exists($this, $k))
|
|
||||||
{
|
|
||||||
// reindex arrays to force json_encode to treat them as arrays
|
|
||||||
if (is_array($v)) // in_array($k, ['data', 'extraCols', 'visibleCols', 'hiddenCols', 'sort', 'sortOptions']))
|
|
||||||
$v = array_values($v);
|
|
||||||
$this->$k = $v;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($addIn && !Template\PageTemplate::test('listviews/', $addIn.'.tpl'))
|
|
||||||
trigger_error('Nonexistent Listview addin requested: template/listviews/'.$addIn.'.tpl', E_USER_ERROR);
|
|
||||||
else if ($addIn)
|
|
||||||
$this->__addIn = 'template/listviews/'.$addIn.'.tpl';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function &iterate() : \Generator
|
|
||||||
{
|
|
||||||
reset($this->data);
|
|
||||||
|
|
||||||
foreach ($this->data as $idx => &$row)
|
|
||||||
yield $idx => $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTemplate() : string
|
|
||||||
{
|
|
||||||
return $this->template;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setTabs(string $tabVar) : void
|
|
||||||
{
|
|
||||||
if ($tabVar[0] !== '$') // expects a jsVar, which we denote with a prefixed $
|
|
||||||
$tabVar = '$' . $tabVar;
|
|
||||||
|
|
||||||
$this->tabs = $tabVar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setError() : void
|
|
||||||
{
|
|
||||||
$this->_errors = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this as $prop => $val)
|
|
||||||
if ($val !== null && substr($prop, 0, 2) != '__')
|
|
||||||
$result[$prop] = $val;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
if ($this->__addIn)
|
|
||||||
include($this->__addIn);
|
|
||||||
|
|
||||||
return "new Listview(".Util::toJSON($this).");\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,291 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Markup implements \JsonSerializable
|
|
||||||
{
|
|
||||||
private const DB_TAG_PATTERN = '/(?<!\\\\)\[(npc|object|item|itemset|quest|spell|zone|faction|pet|achievement|statistic|title|event|class|race|skill|currency|emote|enchantment|money|sound|icondb)=(-?\d+)[^\]]*\]/i';
|
|
||||||
|
|
||||||
// const val
|
|
||||||
public const MARKUP_MODE_COMMENT = 1;
|
|
||||||
public const MARKUP_MODE_ARTICLE = 2;
|
|
||||||
public const MARKUP_MODE_QUICKFACTS = 3;
|
|
||||||
public const MARKUP_MODE_SIGNATURE = 4;
|
|
||||||
public const MARKUP_MODE_REPLY = 5;
|
|
||||||
|
|
||||||
// js var
|
|
||||||
public const MODE_COMMENT = '$Markup.MODE_COMMENT';
|
|
||||||
public const MODE_ARTICLE = '$Markup.MODE_ARTICLE';
|
|
||||||
public const MODE_QUICKFACTS = '$Markup.MODE_QUICKFACTS';
|
|
||||||
public const MODE_SIGNATURE = '$Markup.MODE_SIGNATURE';
|
|
||||||
public const MODE_REPLY = '$Markup.MODE_REPLY';
|
|
||||||
|
|
||||||
// const val
|
|
||||||
public const MARKUP_CLASS_ADMIN = 40;
|
|
||||||
public const MARKUP_CLASS_STAFF = 30;
|
|
||||||
public const MARKUP_CLASS_PREMIUM = 20;
|
|
||||||
public const MARKUP_CLASS_USER = 10;
|
|
||||||
public const MARKUP_CLASS_PENDING = 1;
|
|
||||||
|
|
||||||
// js var
|
|
||||||
public const CLASS_ADMIN = '$Markup.CLASS_ADMIN';
|
|
||||||
public const CLASS_STAFF = '$Markup.CLASS_STAFF';
|
|
||||||
public const CLASS_PREMIUM = '$Markup.CLASS_PREMIUM';
|
|
||||||
public const CLASS_USER = '$Markup.CLASS_USER';
|
|
||||||
public const CLASS_PENDING = '$Markup.CLASS_PENDING';
|
|
||||||
|
|
||||||
// options
|
|
||||||
private ?string $prepend = null; // html in front of article
|
|
||||||
private ?string $append = null; // html trailing the article
|
|
||||||
private ?int $locale = null; // forces tooltips in the article to adhere to another locale
|
|
||||||
private ?int $inBlog = null; // js:bool; unused by aowow
|
|
||||||
private ?string $mode = null; // defaults to Markup.MODE_ARTICLE, which is what we want.
|
|
||||||
private ?string $allow = null; // defaults to Markup.CLASS_STAFF
|
|
||||||
private ?int $roles = null; // if allow is null, get allow from roles (user group); also mode will be set to MODE_ARTICLE for staff groups
|
|
||||||
private ?int $stopAtBreak = null; // js:bool; only parses text until substring "[break]" is encountered; some debug option...?
|
|
||||||
private ?string $highlight = null; // HTMLNode selector
|
|
||||||
|
|
||||||
private ?int $skipReset = null; // js:bool; unsure, if TRUE the next settings in this block get skipped
|
|
||||||
private ?string $uid = null; // defaults to 'abc'; unsure, key under which media is stored and referenced in g_screenshots and g_videos
|
|
||||||
private ?string $root = null; // unsure, something to with Markup Tags that need to be subordinate to other tags (e.g.: [li] to [ol])
|
|
||||||
private ?int $preview = null; // unsure, appends '-preview' to the div created by the [tabs] tag and prevents scrolling. Forum feature?
|
|
||||||
private ?int $dbpage = null; // js:bool; set on db type detail pages; adds article edit links to admin menu
|
|
||||||
|
|
||||||
protected string $__text;
|
|
||||||
|
|
||||||
private string $__parent = 'article-generic';
|
|
||||||
|
|
||||||
public function __construct(string $text, array $opts, string $parent = '')
|
|
||||||
{
|
|
||||||
foreach ($opts as $k => $v)
|
|
||||||
{
|
|
||||||
if (property_exists($this, $k))
|
|
||||||
$this->$k = $v;
|
|
||||||
else
|
|
||||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->__text = $text;
|
|
||||||
|
|
||||||
if ($parent)
|
|
||||||
$this->__parent = $parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJsGlobals() : array
|
|
||||||
{
|
|
||||||
return $this->_parseTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getParent() : string
|
|
||||||
{
|
|
||||||
return $this->__parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************/
|
|
||||||
/* Markup tag handling */
|
|
||||||
/***********************/
|
|
||||||
|
|
||||||
private function _parseTags(array &$jsg = []) : array
|
|
||||||
{
|
|
||||||
return self::parseTags($this->__text, $jsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function parseTags(string $text, array &$jsg = []) : array
|
|
||||||
{
|
|
||||||
$jsGlobals = [];
|
|
||||||
|
|
||||||
if (preg_match_all(self::DB_TAG_PATTERN, $text, $matches, PREG_SET_ORDER))
|
|
||||||
{
|
|
||||||
foreach ($matches as $match)
|
|
||||||
{
|
|
||||||
if ($match[1] == 'statistic')
|
|
||||||
$match[1] = 'achievement';
|
|
||||||
else if ($match[1] == 'icondb')
|
|
||||||
$match[1] = 'icon';
|
|
||||||
|
|
||||||
if ($match[1] == 'money')
|
|
||||||
{
|
|
||||||
if (stripos($match[0], 'items'))
|
|
||||||
{
|
|
||||||
if (preg_match('/items=([0-9,]+)/i', $match[0], $submatch))
|
|
||||||
{
|
|
||||||
$sm = explode(',', $submatch[1]);
|
|
||||||
for ($i = 0; $i < count($sm); $i+=2)
|
|
||||||
$jsGlobals[Type::ITEM][$sm[$i]] = $sm[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripos($match[0], 'currency'))
|
|
||||||
{
|
|
||||||
if (preg_match('/currency=([0-9,]+)/i', $match[0], $submatch))
|
|
||||||
{
|
|
||||||
$sm = explode(',', $submatch[1]);
|
|
||||||
for ($i = 0; $i < count($sm); $i+=2)
|
|
||||||
$jsGlobals[Type::CURRENCY][$sm[$i]] = $sm[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ($type = Type::getIndexFrom(Type::IDX_FILE_STR, $match[1]))
|
|
||||||
$jsGlobals[$type][$match[2]] = $match[2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Util::mergeJsGlobals($jsg, $jsGlobals);
|
|
||||||
|
|
||||||
return $jsGlobals;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function _stripTags(array $jsgData = []) : string
|
|
||||||
{
|
|
||||||
return self::stripTags($this->__text, $jsgData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function stripTags(string $text, array $jsgData = []) : string
|
|
||||||
{
|
|
||||||
// replace DB Tags
|
|
||||||
$text = preg_replace_callback(self::DB_TAG_PATTERN, function ($match) use ($jsgData) {
|
|
||||||
if ($match[1] == 'statistic')
|
|
||||||
$match[1] = 'achievement';
|
|
||||||
else if ($match[1] == 'icondb')
|
|
||||||
$match[1] = 'icon';
|
|
||||||
else if ($match[1] == 'money')
|
|
||||||
{
|
|
||||||
$moneys = [];
|
|
||||||
if (stripos($match[0], 'items'))
|
|
||||||
{
|
|
||||||
if (preg_match('/items=([0-9,]+)/i', $match[0], $submatch))
|
|
||||||
{
|
|
||||||
$sm = explode(',', $submatch[1]);
|
|
||||||
for ($i = 0; $i < count($sm); $i += 2)
|
|
||||||
{
|
|
||||||
if (!empty($jsgData[Type::ITEM][1][$sm[$i]]))
|
|
||||||
$moneys[] = $jsgData[Type::ITEM][1][$sm[$i]]['name'] ?? $jsgData[Type::ITEM][1][$match[2]]['name_' . Lang::getLocale()->json()];
|
|
||||||
else
|
|
||||||
$moneys[] = Util::ucFirst(Lang::game('item')).' #'.$sm[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stripos($match[0], 'currency'))
|
|
||||||
{
|
|
||||||
if (preg_match('/currency=([0-9,]+)/i', $match[0], $submatch))
|
|
||||||
{
|
|
||||||
$sm = explode(',', $submatch[1]);
|
|
||||||
for ($i = 0; $i < count($sm); $i += 2)
|
|
||||||
{
|
|
||||||
if (!empty($jsgData[Type::CURRENCY][1][$sm[$i]]))
|
|
||||||
$moneys[] = $jsgData[Type::CURRENCY][1][$sm[$i]]['name'] ?? $jsgData[Type::CURRENCY][1][$match[2]]['name_' . Lang::getLocale()->json()];
|
|
||||||
else
|
|
||||||
$moneys[] = Util::ucFirst(Lang::game('curency')).' #'.$sm[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Lang::concat($moneys);
|
|
||||||
}
|
|
||||||
if ($type = Type::getIndexFrom(Type::IDX_FILE_STR, $match[1]))
|
|
||||||
{
|
|
||||||
if (!empty($jsgData[$type][1][$match[2]]))
|
|
||||||
return $jsgData[$type][1][$match[2]]['name'] ?? $jsgData[$type][1][$match[2]]['name_' . Lang::getLocale()->json()];
|
|
||||||
else
|
|
||||||
return Util::ucFirst(Lang::game($match[1])).' #'.$match[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error('Markup::stripTags() - encountered unhandled db-tag: '.var_export($match));
|
|
||||||
return '';
|
|
||||||
}, $text);
|
|
||||||
|
|
||||||
// replace line endings
|
|
||||||
$text = str_replace('[br]', "\n", $text);
|
|
||||||
|
|
||||||
// strip other Tags
|
|
||||||
$stripped = '';
|
|
||||||
$inTag = false;
|
|
||||||
for ($i = 0; $i < strlen($text); $i++)
|
|
||||||
{
|
|
||||||
if ($text[$i] == '[' && (!$i || $text[$i - 1] != '\\'))
|
|
||||||
$inTag = true;
|
|
||||||
if (!$inTag)
|
|
||||||
$stripped .= $text[$i];
|
|
||||||
if ($inTag && $text[$i] == ']' && (!$i || $text[$i - 1] != '\\'))
|
|
||||||
$inTag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $stripped;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************/
|
|
||||||
/* String Operations */
|
|
||||||
/*********************/
|
|
||||||
|
|
||||||
public function append(string $text) : self
|
|
||||||
{
|
|
||||||
$this->__text .= $text;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function prepend(string $text) : self
|
|
||||||
{
|
|
||||||
$this->__text = $text . $this->__text;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function apply(\Closure $fn) : void
|
|
||||||
{
|
|
||||||
$this->__text = $fn($this->__text);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function replace(string $middle, int $offset = 0, ?int $len = null) : self
|
|
||||||
{
|
|
||||||
// y no mb_substr_replace >:(
|
|
||||||
$start = $end = '';
|
|
||||||
|
|
||||||
if ($offset < 0)
|
|
||||||
$offset = mb_strlen($this->__text) + $offset;
|
|
||||||
|
|
||||||
$start = mb_substr($this->__text, 0, $offset);
|
|
||||||
|
|
||||||
if (!is_null($len) && $len >= 0)
|
|
||||||
$end = mb_substr($this->__text, $offset + $len);
|
|
||||||
else if (!is_null($len) && $len < 0)
|
|
||||||
$end = mb_substr($this->__text, $offset + mb_strlen($this->__text) + $len);
|
|
||||||
|
|
||||||
$this->__text = $start . $middle . $end;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function cleanText() : string
|
|
||||||
{
|
|
||||||
// break script-tags, unify newlines
|
|
||||||
$val = preg_replace(['/script\s*\>/i', "/\r\n/", "/\r/"], ['script>', "\n", "\n"], $this->__text);
|
|
||||||
|
|
||||||
return strtr(Util::jsEscape($val), ['script>' => 'scr"+"ipt>']);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this as $prop => $val)
|
|
||||||
if ($val !== null && $prop[0] != '_')
|
|
||||||
$result[$prop] = $val;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
if ($this->jsonSerialize())
|
|
||||||
return 'Markup.printHtml("'.$this->cleanText().'", "'.$this->__parent.'", '.Util::toJSON($this).");\n";
|
|
||||||
|
|
||||||
return 'Markup.printHtml("'.$this->cleanText().'", "'.$this->__parent."\");\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Summary implements \JsonSerializable
|
|
||||||
{
|
|
||||||
private string $id = ''; // HTMLNode.id
|
|
||||||
private ?string $parent = ''; // HTMLNode.id; if set $id is created and attached here instead of searched for
|
|
||||||
private string $template = ''; //
|
|
||||||
private ?int $editable = null; // js:bool; defaults to TRUE
|
|
||||||
private ?int $draggable = null; // js:bool; defaults to $editable
|
|
||||||
private ?int $searchable = null; // js:bool; defaults to $editable && $draggable
|
|
||||||
private ?int $weightable = null; // js:bool; defaults to $editable
|
|
||||||
private ?int $textable = null; // js:bool; defaults to FALSE
|
|
||||||
private ?int $enhanceable = null; // js:bool; defaults to $editable
|
|
||||||
private ?int $level = null; // js:int; defaults to 80
|
|
||||||
private array $groups = []; // js:array; defaults to GET-params
|
|
||||||
private ?array $weights = null; // js:array; defaults to GET-params
|
|
||||||
|
|
||||||
public function __construct(array $opts)
|
|
||||||
{
|
|
||||||
foreach ($opts as $k => $v)
|
|
||||||
{
|
|
||||||
if (property_exists($this, $k))
|
|
||||||
$this->$k = $v;
|
|
||||||
else
|
|
||||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->template)
|
|
||||||
trigger_error(self::class.'::__construct - initialized without template', E_USER_WARNING);
|
|
||||||
if (!$this->id)
|
|
||||||
trigger_error(self::class.'::__construct - initialized without HTMLNode#id to reference', E_USER_WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function &iterate() : \Generator
|
|
||||||
{
|
|
||||||
reset($this->groups);
|
|
||||||
|
|
||||||
foreach ($this->groups as $idx => &$group)
|
|
||||||
yield $idx => $group;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addGroup(array $group) : void
|
|
||||||
{
|
|
||||||
$this->groups[] = $group;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this as $prop => $val)
|
|
||||||
if ($val !== null && $prop[0] != '_')
|
|
||||||
$result[$prop] = $val;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
return "new Summary(".Util::toJSON($this).");\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Tabs implements \JsonSerializable, \Countable
|
|
||||||
{
|
|
||||||
private array $__tabs = [];
|
|
||||||
|
|
||||||
private string $parent = ''; // HTMLNode
|
|
||||||
private ?int $poundable = null; // js:bool
|
|
||||||
private ?int $forceScroll = null; // js:bool
|
|
||||||
private ?int $noScroll = null; // js:bool
|
|
||||||
private ?string $trackable = null; // String to track in Google Analytics .. often a DB Type
|
|
||||||
|
|
||||||
private ?string $onLoad = null; // js::callable
|
|
||||||
private ?string $onShow = null; // js::callable
|
|
||||||
private ?string $onHide = null; // js::callable
|
|
||||||
|
|
||||||
public function __construct(array $opts, public readonly string $__tabVar = 'myTabs', private bool $__forceTabs = false)
|
|
||||||
{
|
|
||||||
foreach ($opts as $k => $v)
|
|
||||||
{
|
|
||||||
if (property_exists($this, $k))
|
|
||||||
$this->$k = $v;
|
|
||||||
else
|
|
||||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function &iterate() : \Generator
|
|
||||||
{
|
|
||||||
reset($this->__tabs);
|
|
||||||
|
|
||||||
foreach ($this->__tabs as $idx => &$tab)
|
|
||||||
yield $idx => $tab;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addListviewTab(Listview $lv) : void
|
|
||||||
{
|
|
||||||
$this->__tabs[] = $lv;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addDataTab(string $id, string $name, string $data) : void
|
|
||||||
{
|
|
||||||
$this->__tabs[] = ['id' => $id, 'name' => $name, 'data' => $data];
|
|
||||||
$this->__forceTabs = true; // otherwise a single DataTab could not be accessed
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDataContainer() : \Generator
|
|
||||||
{
|
|
||||||
foreach ($this->__tabs as $tab)
|
|
||||||
if (is_array($tab))
|
|
||||||
yield '<div class="text tabbed-contents" id="tab-'.$tab['id'].'" style="display:none;">'.$tab['data'].'</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFlush() : string
|
|
||||||
{
|
|
||||||
if ($this->isTabbed())
|
|
||||||
return $this->__tabVar.".flush();";
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isTabbed() : bool
|
|
||||||
{
|
|
||||||
return count($this->__tabs) > 1 || $this->__forceTabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********************/
|
|
||||||
/* enable deep cloning */
|
|
||||||
/***********************/
|
|
||||||
|
|
||||||
public function __clone()
|
|
||||||
{
|
|
||||||
foreach ($this->__tabs as $idx => $tab)
|
|
||||||
{
|
|
||||||
if (is_array($tab))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$this->__tabs[$idx] = clone $tab;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/******************/
|
|
||||||
/* make countable */
|
|
||||||
/******************/
|
|
||||||
|
|
||||||
public function count() : int
|
|
||||||
{
|
|
||||||
return count($this->__tabs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************************/
|
|
||||||
/* make Tabs stringable */
|
|
||||||
/************************/
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this as $prop => $val)
|
|
||||||
if ($val !== null && $prop[0] != '_')
|
|
||||||
$result[$prop] = $val;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
$result = '';
|
|
||||||
|
|
||||||
if ($this->isTabbed())
|
|
||||||
$result .= "var ".$this->__tabVar." = new Tabs(".Util::toJSON($this).");\n";
|
|
||||||
|
|
||||||
foreach ($this->__tabs as $tab)
|
|
||||||
{
|
|
||||||
if (is_array($tab))
|
|
||||||
{
|
|
||||||
$n = $tab['name'][0] == '$' ? substr($tab['name'], 1) : "'".$tab['name']."'";
|
|
||||||
$result .= $this->__tabVar.".add(".$n.", { id: '".$tab['id']."' });\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ($this->isTabbed())
|
|
||||||
$tab->setTabs($this->__tabVar);
|
|
||||||
|
|
||||||
$result .= $tab; // Listview::__toString here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Tooltip implements \JsonSerializable
|
|
||||||
{
|
|
||||||
private ?string $name = null;
|
|
||||||
private ?string $tooltip = null;
|
|
||||||
private ?\StdClass $map = null; // secondary tooltip
|
|
||||||
private ?string $icon = null;
|
|
||||||
private ?int $quality = null; // icon border color coded
|
|
||||||
private ?bool $daily = null;
|
|
||||||
private ?array $spells = null;
|
|
||||||
private ?string $buff = null;
|
|
||||||
private ?array $buffspells = null;
|
|
||||||
|
|
||||||
public function __construct(private string $__powerTpl, private string $__subject, array $opts = [])
|
|
||||||
{
|
|
||||||
foreach ($opts as $k => $v)
|
|
||||||
{
|
|
||||||
if (property_exists($this, $k))
|
|
||||||
$this->$k = $v;
|
|
||||||
else
|
|
||||||
trigger_error(self::class.'::__construct - unrecognized option: ' . $k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function jsonSerialize() : array
|
|
||||||
{
|
|
||||||
$out = [];
|
|
||||||
|
|
||||||
$locString = Lang::getLocale()->json();
|
|
||||||
|
|
||||||
foreach ($this as $k => $v)
|
|
||||||
{
|
|
||||||
if ($v === null || $k[0] == '_')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($k == 'icon')
|
|
||||||
$out[$k] = rawurldecode($v);
|
|
||||||
else if ($k == 'quality' || $k == 'map' || $k == 'daily')
|
|
||||||
$out[$k] = $v;
|
|
||||||
else
|
|
||||||
$out[$k . '_' . $locString] = $v;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
return sprintf($this->__powerTpl, Util::toJSON($this->__subject, JSON_AOWOW_POWER), Lang::getLocale()->value, Util::toJSON($this, JSON_AOWOW_POWER))."\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class LocString
|
|
||||||
{
|
|
||||||
private \WeakMap $store;
|
|
||||||
|
|
||||||
public function __construct(array $data, string $key = 'name', ?callable $callback = null)
|
|
||||||
{
|
|
||||||
$this->store = new \WeakMap();
|
|
||||||
|
|
||||||
$callback ??= fn($x) => $x;
|
|
||||||
|
|
||||||
if (!array_filter($data, fn($v, $k) => $v && strstr($k, $key.'_loc'), ARRAY_FILTER_USE_BOTH))
|
|
||||||
trigger_error('LocString - is entrirely empty', E_USER_WARNING);
|
|
||||||
|
|
||||||
foreach (Locale::cases() as $l)
|
|
||||||
$this->store[$l] = (string)$callback($data[$key.'_loc'.$l->value] ?? '');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString() : string
|
|
||||||
{
|
|
||||||
if ($str = $this->store[Lang::getLocale()])
|
|
||||||
return $str;
|
|
||||||
|
|
||||||
foreach (Locale::cases() as $l) // desired loc not set, use any other
|
|
||||||
if (isset($this->store[$l]))
|
|
||||||
return Cfg::get('DEBUG') ? '['.$this->store[$l].']' : $this->store[$l];
|
|
||||||
|
|
||||||
return Cfg::get('DEBUG') ? '[LOCSTRING]' : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __serialize(): array
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
foreach (Locale::cases() as $l)
|
|
||||||
if (isset($this->store[$l]))
|
|
||||||
$data[$l->value] = $this->store[$l];
|
|
||||||
|
|
||||||
return ['store' => $data];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __unserialize(array $data): void
|
|
||||||
{
|
|
||||||
$this->store = new \WeakMap();
|
|
||||||
|
|
||||||
if (empty($data['store']))
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach ($data['store'] as $locId => $str)
|
|
||||||
if (($l = Locale::tryFrom($locId))?->validate())
|
|
||||||
$this->store[$l] = (string)$str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,274 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Report
|
|
||||||
{
|
|
||||||
public const MODE_GENERAL = 0;
|
|
||||||
public const MODE_COMMENT = 1;
|
|
||||||
public const MODE_FORUM_POST = 2;
|
|
||||||
public const MODE_SCREENSHOT = 3;
|
|
||||||
public const MODE_CHARACTER = 4;
|
|
||||||
public const MODE_VIDEO = 5;
|
|
||||||
public const MODE_GUIDE = 6;
|
|
||||||
|
|
||||||
public const GEN_FEEDBACK = 1;
|
|
||||||
public const GEN_BUG_REPORT = 2;
|
|
||||||
public const GEN_TYPO_TRANSLATION = 3;
|
|
||||||
public const GEN_OP_ADVERTISING = 4;
|
|
||||||
public const GEN_OP_PARTNERSHIP = 5;
|
|
||||||
public const GEN_PRESS_INQUIRY = 6;
|
|
||||||
public const GEN_MISCELLANEOUS = 7;
|
|
||||||
public const GEN_MISINFORMATION = 8;
|
|
||||||
public const CO_ADVERTISING = 15;
|
|
||||||
public const CO_INACCURATE = 16;
|
|
||||||
public const CO_OUT_OF_DATE = 17;
|
|
||||||
public const CO_SPAM = 18;
|
|
||||||
public const CO_INAPPROPRIATE = 19;
|
|
||||||
public const CO_MISCELLANEOUS = 20;
|
|
||||||
public const FO_ADVERTISING = 30;
|
|
||||||
public const FO_AVATAR = 31;
|
|
||||||
public const FO_INACCURATE = 32;
|
|
||||||
public const FO_OUT_OF_DATE = 33;
|
|
||||||
public const FO_SPAM = 34;
|
|
||||||
public const FO_STICKY_REQUEST = 35;
|
|
||||||
public const FO_INAPPROPRIATE = 36;
|
|
||||||
public const FO_MISCELLANEOUS = 37;
|
|
||||||
public const SS_INACCURATE = 45;
|
|
||||||
public const SS_OUT_OF_DATE = 46;
|
|
||||||
public const SS_INAPPROPRIATE = 47;
|
|
||||||
public const SS_MISCELLANEOUS = 48;
|
|
||||||
public const PR_INACCURATE_DATA = 60;
|
|
||||||
public const PR_MISCELLANEOUS = 61;
|
|
||||||
public const VI_INACCURATE = 45;
|
|
||||||
public const VI_OUT_OF_DATE = 46;
|
|
||||||
public const VI_INAPPROPRIATE = 47;
|
|
||||||
public const VI_MISCELLANEOUS = 48;
|
|
||||||
public const AR_INACCURATE = 45;
|
|
||||||
public const AR_OUT_OF_DATE = 46;
|
|
||||||
public const AR_MISCELLANEOUS = 48;
|
|
||||||
|
|
||||||
private array $context = array(
|
|
||||||
self::MODE_GENERAL => array(
|
|
||||||
self::GEN_FEEDBACK => true,
|
|
||||||
self::GEN_BUG_REPORT => true,
|
|
||||||
self::GEN_TYPO_TRANSLATION => true,
|
|
||||||
self::GEN_OP_ADVERTISING => true,
|
|
||||||
self::GEN_OP_PARTNERSHIP => true,
|
|
||||||
self::GEN_PRESS_INQUIRY => true,
|
|
||||||
self::GEN_MISCELLANEOUS => true,
|
|
||||||
self::GEN_MISINFORMATION => true
|
|
||||||
),
|
|
||||||
self::MODE_COMMENT => array(
|
|
||||||
self::CO_ADVERTISING => U_GROUP_MODERATOR,
|
|
||||||
self::CO_INACCURATE => true,
|
|
||||||
self::CO_OUT_OF_DATE => true,
|
|
||||||
self::CO_SPAM => U_GROUP_MODERATOR,
|
|
||||||
self::CO_INAPPROPRIATE => U_GROUP_MODERATOR,
|
|
||||||
self::CO_MISCELLANEOUS => U_GROUP_MODERATOR
|
|
||||||
),
|
|
||||||
self::MODE_FORUM_POST => array(
|
|
||||||
self::FO_ADVERTISING => U_GROUP_MODERATOR,
|
|
||||||
self::FO_AVATAR => true,
|
|
||||||
self::FO_INACCURATE => true,
|
|
||||||
self::FO_OUT_OF_DATE => U_GROUP_MODERATOR,
|
|
||||||
self::FO_SPAM => U_GROUP_MODERATOR,
|
|
||||||
self::FO_STICKY_REQUEST => U_GROUP_MODERATOR,
|
|
||||||
self::FO_INAPPROPRIATE => U_GROUP_MODERATOR
|
|
||||||
),
|
|
||||||
self::MODE_SCREENSHOT => array(
|
|
||||||
self::SS_INACCURATE => true,
|
|
||||||
self::SS_OUT_OF_DATE => true,
|
|
||||||
self::SS_INAPPROPRIATE => U_GROUP_MODERATOR,
|
|
||||||
self::SS_MISCELLANEOUS => U_GROUP_MODERATOR
|
|
||||||
),
|
|
||||||
self::MODE_CHARACTER => array(
|
|
||||||
self::PR_INACCURATE_DATA => true,
|
|
||||||
self::PR_MISCELLANEOUS => true
|
|
||||||
),
|
|
||||||
self::MODE_VIDEO => array(
|
|
||||||
self::VI_INACCURATE => true,
|
|
||||||
self::VI_OUT_OF_DATE => true,
|
|
||||||
self::VI_INAPPROPRIATE => U_GROUP_MODERATOR,
|
|
||||||
self::VI_MISCELLANEOUS => U_GROUP_MODERATOR
|
|
||||||
),
|
|
||||||
self::MODE_GUIDE => array(
|
|
||||||
self::AR_INACCURATE => true,
|
|
||||||
self::AR_OUT_OF_DATE => true,
|
|
||||||
self::AR_MISCELLANEOUS => true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
private const ERR_NONE = 0; // aka: success
|
|
||||||
private const ERR_INVALID_CAPTCHA = 1; // captcha not in use
|
|
||||||
private const ERR_DESC_TOO_LONG = 2;
|
|
||||||
private const ERR_NO_DESC = 3;
|
|
||||||
private const ERR_ALREADY_REPORTED = 7;
|
|
||||||
private const ERR_MISCELLANEOUS = -1;
|
|
||||||
|
|
||||||
public const STATUS_OPEN = 0;
|
|
||||||
public const STATUS_ASSIGNED = 1;
|
|
||||||
public const STATUS_CLOSED_WONTFIX = 2;
|
|
||||||
public const STATUS_CLOSED_SOLVED = 3;
|
|
||||||
|
|
||||||
private int $errorCode = self::ERR_NONE;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(private int $mode, private int $reason, private ?int $subject = 0)
|
|
||||||
{
|
|
||||||
if ($mode < 0 || $reason <= 0)
|
|
||||||
{
|
|
||||||
trigger_error('Report - malformed contact request received', E_USER_ERROR);
|
|
||||||
$this->errorCode = self::ERR_MISCELLANEOUS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($this->context[$mode][$reason]))
|
|
||||||
{
|
|
||||||
trigger_error('Report - report has invalid context (mode:'.$mode.' / reason:'.$reason.')', E_USER_ERROR);
|
|
||||||
$this->errorCode = self::ERR_MISCELLANEOUS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!User::isLoggedIn() && !User::$ip)
|
|
||||||
{
|
|
||||||
trigger_error('Report - could not determine IP for anonymous user', E_USER_ERROR);
|
|
||||||
$this->errorCode = self::ERR_MISCELLANEOUS;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->subject ??= 0; // 0 for utility, tools and misc pages?
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkTargetContext() : int
|
|
||||||
{
|
|
||||||
// check already reported
|
|
||||||
$field = User::isLoggedIn() ? 'userId' : 'ip';
|
|
||||||
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND ?# = ?', $this->mode, $this->reason, $this->subject, $field, User::$id ?: User::$ip))
|
|
||||||
return self::ERR_ALREADY_REPORTED;
|
|
||||||
|
|
||||||
// check targeted post/postOwner staff status
|
|
||||||
$ctxCheck = $this->context[$this->mode][$this->reason];
|
|
||||||
if (is_int($ctxCheck))
|
|
||||||
{
|
|
||||||
$roles = User::$groups;
|
|
||||||
if ($this->mode == self::MODE_COMMENT)
|
|
||||||
$roles = DB::Aowow()->selectCell('SELECT `roles` FROM ?_comments WHERE `id` = ?d', $this->subject);
|
|
||||||
// else if if ($this->mode == self::MODE_FORUM_POST)
|
|
||||||
// $roles = DB::Aowow()->selectCell('SELECT `roles` FROM ?_forum_posts WHERE `id` = ?d', $this->subject);
|
|
||||||
|
|
||||||
return $roles & $ctxCheck ? self::ERR_NONE : self::ERR_MISCELLANEOUS;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return $ctxCheck ? self::ERR_NONE : self::ERR_MISCELLANEOUS;
|
|
||||||
|
|
||||||
// Forum not in use, else:
|
|
||||||
// check post owner
|
|
||||||
// User::$id == post.op && !post.sticky;
|
|
||||||
// check user custom avatar
|
|
||||||
// g_users[post.user].avatar == 2 && (post.roles & U_GROUP_MODERATOR) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(string $desc, ?string $userAgent = null, ?string $appName = null, ?string $pageUrl = null, ?string $relUrl = null, ?string $email = null) : bool
|
|
||||||
{
|
|
||||||
if ($this->errorCode)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!$desc)
|
|
||||||
{
|
|
||||||
$this->errorCode = self::ERR_NO_DESC;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mb_strlen($desc) > 500)
|
|
||||||
{
|
|
||||||
$this->errorCode = self::ERR_DESC_TOO_LONG;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($err = $this->checkTargetContext())
|
|
||||||
{
|
|
||||||
$this->errorCode = $err;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$update = array(
|
|
||||||
'userId' => User::$id,
|
|
||||||
'createDate' => time(),
|
|
||||||
'mode' => $this->mode,
|
|
||||||
'reason' => $this->reason,
|
|
||||||
'subject' => $this->subject,
|
|
||||||
'ip' => User::$ip,
|
|
||||||
'description' => $desc,
|
|
||||||
'userAgent' => $userAgent ?: User::$agent,
|
|
||||||
'appName' => $appName ?: (get_browser(null, true)['browser'] ?: '')
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($pageUrl)
|
|
||||||
$update['url'] = $pageUrl;
|
|
||||||
|
|
||||||
if ($relUrl)
|
|
||||||
$update['relatedurl'] = $relUrl;
|
|
||||||
|
|
||||||
if ($email)
|
|
||||||
$update['email'] = $email;
|
|
||||||
|
|
||||||
return DB::Aowow()->query('INSERT INTO ?_reports (?#) VALUES (?a)', array_keys($update), array_values($update));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSimilar(int ...$status) : array
|
|
||||||
{
|
|
||||||
if ($this->errorCode)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
foreach ($status as &$s)
|
|
||||||
if ($s < self::STATUS_OPEN || $s > self::STATUS_CLOSED_SOLVED)
|
|
||||||
unset($s);
|
|
||||||
|
|
||||||
return DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, r.* FROM ?_reports r WHERE {`status` IN (?a) AND }`mode` = ?d AND `reason` = ?d AND `subject` = ?d',
|
|
||||||
$status ?: DBSIMPLE_SKIP, $this->mode, $this->reason, $this->subject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function close(int $closeStatus, bool $inclAssigned = false) : bool
|
|
||||||
{
|
|
||||||
if ($closeStatus != self::STATUS_CLOSED_SOLVED && $closeStatus != self::STATUS_CLOSED_WONTFIX)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_MOD))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$fromStatus = [self::STATUS_OPEN];
|
|
||||||
if ($inclAssigned)
|
|
||||||
$fromStatus[] = self::STATUS_ASSIGNED;
|
|
||||||
|
|
||||||
if ($reports = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `userId` FROM ?_reports WHERE `status` IN (?a) AND `mode` = ?d AND `reason` = ?d AND `subject` = ?d',
|
|
||||||
$fromStatus, $this->mode, $this->reason, $this->subject))
|
|
||||||
{
|
|
||||||
DB::Aowow()->query('UPDATE ?_reports SET `status` = ?d, `assigned` = 0 WHERE `id` IN (?a)', $closeStatus, array_keys($reports));
|
|
||||||
|
|
||||||
foreach ($reports as $rId => $uId)
|
|
||||||
Util::gainSiteReputation($uId, $closeStatus == self::STATUS_CLOSED_SOLVED ? SITEREP_ACTION_GOOD_REPORT : SITEREP_ACTION_BAD_REPORT, ['id' => $rId]);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reopen(int $assignedTo = 0) : bool
|
|
||||||
{
|
|
||||||
// assignedTo = 0 ? status = STATUS_OPEN : status = STATUS_ASSIGNED, userId = assignedTo
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getError() : int
|
|
||||||
{
|
|
||||||
return $this->errorCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -13,173 +11,111 @@ if (!defined('AOWOW_REVISION'))
|
|||||||
|
|
||||||
class DB
|
class DB
|
||||||
{
|
{
|
||||||
private static array $interfaceCache = [];
|
private static $interfaceCache = [];
|
||||||
private static array $optionsCache = [];
|
private static $optionsCache = [];
|
||||||
private static array $logs = [];
|
private static $connectionCache = [];
|
||||||
|
|
||||||
private static function createConnectSyntax(array &$options) : string
|
private static function createConnectSyntax(&$options)
|
||||||
{
|
{
|
||||||
return 'mysqli://'.$options['user'].':'.$options['pass'].'@'.$options['host'].'/'.$options['db'];
|
return 'mysqli://'.$options['user'].':'.$options['pass'].'@'.$options['host'].'/'.$options['db'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function connect(int $idx) : void
|
public static function connect($idx)
|
||||||
{
|
{
|
||||||
if (self::isConnected($idx))
|
if (self::isConnected($idx))
|
||||||
{
|
return;
|
||||||
self::$interfaceCache[$idx]->link->close();
|
|
||||||
self::$interfaceCache[$idx] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$options = &self::$optionsCache[$idx];
|
$options = &self::$optionsCache[$idx];
|
||||||
$interface = \DbSimple_Generic::connect(self::createConnectSyntax($options));
|
$interface = DbSimple_Generic::connect(self::createConnectSyntax($options));
|
||||||
|
|
||||||
$interface->setErrorHandler(self::errorHandler(...));
|
if (!$interface || $interface->error)
|
||||||
|
die('Failed to connect to database.');
|
||||||
|
|
||||||
|
$interface->setErrorHandler(['DB', 'errorHandler']);
|
||||||
|
$interface->query('SET NAMES ?', 'utf8');
|
||||||
if ($options['prefix'])
|
if ($options['prefix'])
|
||||||
$interface->setIdentPrefix($options['prefix']);
|
$interface->setIdentPrefix($options['prefix']);
|
||||||
|
|
||||||
self::$interfaceCache[$idx] = &$interface;
|
self::$interfaceCache[$idx] = &$interface;
|
||||||
|
self::$connectionCache[$idx] = true;
|
||||||
// should be caught by registered error handler
|
|
||||||
if (!$interface || !$interface->link)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$interface->query('SET NAMES ?', 'utf8mb4');
|
|
||||||
|
|
||||||
// disable STRICT_TRANS_TABLES and STRICT_ALL_TABLES off. It prevents usage of implicit default values.
|
|
||||||
// disable ONLY_FULL_GROUP_BY (Allows for non-aggregated selects in a group-by query)
|
|
||||||
$extraModes = ['STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'ONLY_FULL_GROUP_BY', 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE', 'ERROR_FOR_DIVISION_BY_ZERO'];
|
|
||||||
$oldModes = explode(',', $interface->selectCell('SELECT @@sql_mode'));
|
|
||||||
$newModes = array_diff($oldModes, $extraModes);
|
|
||||||
|
|
||||||
if ($oldModes != $newModes)
|
|
||||||
$interface->query("SET SESSION sql_mode = ?", implode(',', $newModes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function test(array $options, ?string &$err = '') : bool
|
public static function errorHandler($message, $data)
|
||||||
{
|
|
||||||
$defPort = ini_get('mysqli.default_port');
|
|
||||||
$port = 0;
|
|
||||||
if (strstr($options['host'], ':'))
|
|
||||||
[$options['host'], $port] = explode(':', $options['host']);
|
|
||||||
|
|
||||||
if ($link = mysqli_connect($options['host'], $options['user'], $options['pass'], $options['db'], $port ?: $defPort))
|
|
||||||
{
|
|
||||||
mysqli_close($link);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$err = '['.mysqli_connect_errno().'] '.mysqli_connect_error();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function errorHandler(string $message, array $data) : void
|
|
||||||
{
|
{
|
||||||
if (!error_reporting())
|
if (!error_reporting())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// continue on warning, end on error
|
$error = "DB ERROR:<br /><br />\n\n<pre>".print_r($data, true)."</pre>";
|
||||||
$isError = $data['code'] > 0;
|
|
||||||
|
|
||||||
// make number sensible again
|
echo CLI ? strip_tags($error) : $error;
|
||||||
$data['code'] = abs($data['code']);
|
exit;
|
||||||
|
|
||||||
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO)
|
|
||||||
{
|
|
||||||
echo "\nDB ERROR\n";
|
|
||||||
foreach ($data as $k => $v)
|
|
||||||
echo ' '.str_pad($k.':', 10).$v."\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
trigger_error($message, $isError ? E_USER_ERROR : E_USER_WARNING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function profiler(mixed $self, string $query, mixed $trace) : void
|
public static function getDB($idx)
|
||||||
{
|
|
||||||
if ($trace) // actual query
|
|
||||||
self::$logs[] = [str_replace("\n", ' ', $query)];
|
|
||||||
else // the statistics
|
|
||||||
{
|
|
||||||
end(self::$logs);
|
|
||||||
self::$logs[key(self::$logs)][] = substr(explode(';', $query)[0], 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getProfiles() : string
|
|
||||||
{
|
|
||||||
$out = '<pre><table style="font-size:12;"><tr><th></th><th>Time</th><th>Query</th></tr>';
|
|
||||||
foreach (self::$logs as $i => [$l, $t])
|
|
||||||
{
|
|
||||||
$c = 'inherit';
|
|
||||||
preg_match('/(\d+)/', $t, $m);
|
|
||||||
if ($m[1] > 100)
|
|
||||||
$c = '#FFA0A0';
|
|
||||||
else if ($m[1] > 20)
|
|
||||||
$c = '#FFFFA0';
|
|
||||||
|
|
||||||
$out .= '<tr><td>'.$i.'.</td><td style="background-color:'.$c.';">'.$t.'</td><td>'.$l.'</td></tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return Util::jsEscape($out).'</table></pre>';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getDB(int $idx) : ?\DbSimple_Mysqli
|
|
||||||
{
|
{
|
||||||
return self::$interfaceCache[$idx];
|
return self::$interfaceCache[$idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isConnected(int $idx) : bool
|
public static function isConnected($idx)
|
||||||
{
|
{
|
||||||
return isset(self::$interfaceCache[$idx]) && self::$interfaceCache[$idx]->link;
|
return isset(self::$connectionCache[$idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function isConnectable(int $idx) : bool
|
public static function isConnectable($idx)
|
||||||
{
|
{
|
||||||
return isset(self::$optionsCache[$idx]);
|
return isset(self::$optionsCache[$idx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static function safeGetDB($idx)
|
||||||
* @static
|
|
||||||
* @return DbSimple_Mysqli
|
|
||||||
*/
|
|
||||||
public static function Characters(int $realmId) : ?\DbSimple_Mysqli
|
|
||||||
{
|
{
|
||||||
if (!isset(self::$optionsCache[DB_CHARACTERS.$realmId]))
|
if (!self::isConnected($idx))
|
||||||
die('Connection info not found for live database of realm #'.$realmId.'. Aborted.');
|
self::connect($idx);
|
||||||
|
|
||||||
return self::getDB(DB_CHARACTERS.$realmId);
|
return self::getDB($idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @static
|
* @static
|
||||||
* @return DbSimple_Mysqli
|
* @return DbSimple_Mysql
|
||||||
*/
|
*/
|
||||||
public static function Auth() : ?\DbSimple_Mysqli
|
public static function Characters($realm)
|
||||||
{
|
{
|
||||||
return self::getDB(DB_AUTH);
|
if (!isset(self::$optionsCache[DB_CHARACTERS.$realm]))
|
||||||
|
die('Connection info not found for live database of realm #'.$realm.'. Aborted.');
|
||||||
|
|
||||||
|
return self::safeGetDB(DB_CHARACTERS.$realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @static
|
* @static
|
||||||
* @return DbSimple_Mysqli
|
* @return DbSimple_Mysql
|
||||||
*/
|
*/
|
||||||
public static function World() : ?\DbSimple_Mysqli
|
public static function Auth()
|
||||||
{
|
{
|
||||||
return self::getDB(DB_WORLD);
|
return self::safeGetDB(DB_AUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @static
|
* @static
|
||||||
* @return DbSimple_Mysqli
|
* @return DbSimple_Mysql
|
||||||
*/
|
*/
|
||||||
public static function Aowow() : ?\DbSimple_Mysqli
|
public static function World()
|
||||||
{
|
{
|
||||||
return self::getDB(DB_AOWOW);
|
return self::safeGetDB(DB_WORLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function load(int $idx, array $config) : void
|
/**
|
||||||
|
* @static
|
||||||
|
* @return DbSimple_Mysql
|
||||||
|
*/
|
||||||
|
public static function Aowow()
|
||||||
|
{
|
||||||
|
return self::safeGetDB(DB_AOWOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function load($idx, $config)
|
||||||
{
|
{
|
||||||
self::$optionsCache[$idx] = $config;
|
self::$optionsCache[$idx] = $config;
|
||||||
self::connect($idx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1532
includes/defines.php
1532
includes/defines.php
File diff suppressed because it is too large
Load Diff
@@ -1,79 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
enum ChrClass : int
|
|
||||||
{
|
|
||||||
case WARRIOR = 1;
|
|
||||||
case PALADIN = 2;
|
|
||||||
case HUNTER = 3;
|
|
||||||
case ROGUE = 4;
|
|
||||||
case PRIEST = 5;
|
|
||||||
case DEATHKNIGHT = 6;
|
|
||||||
case SHAMAN = 7;
|
|
||||||
case MAGE = 8;
|
|
||||||
case WARLOCK = 9;
|
|
||||||
case DRUID = 11;
|
|
||||||
|
|
||||||
public const MASK_ALL = 0x5FF;
|
|
||||||
|
|
||||||
public function matches(int $classMask) : bool
|
|
||||||
{
|
|
||||||
return !$classMask || $this->value & $classMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toMask() : int
|
|
||||||
{
|
|
||||||
return 1 << ($this->value - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromMask(int $classMask = self::MASK_ALL) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::cases() as $cl)
|
|
||||||
if ($cl->toMask() & $classMask)
|
|
||||||
$x[] = $cl->value;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function json() : string
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::WARRIOR => 'warrior',
|
|
||||||
self::PALADIN => 'paladin',
|
|
||||||
self::HUNTER => 'hunter',
|
|
||||||
self::ROGUE => 'rogue',
|
|
||||||
self::PRIEST => 'priest',
|
|
||||||
self::DEATHKNIGHT => 'deathknight',
|
|
||||||
self::SHAMAN => 'shaman',
|
|
||||||
self::MAGE => 'mage',
|
|
||||||
self::WARLOCK => 'warlock',
|
|
||||||
self::DRUID => 'druid'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function spellFamily() : int
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::WARRIOR => SPELLFAMILY_WARRIOR,
|
|
||||||
self::PALADIN => SPELLFAMILY_PALADIN,
|
|
||||||
self::HUNTER => SPELLFAMILY_HUNTER,
|
|
||||||
self::ROGUE => SPELLFAMILY_ROGUE,
|
|
||||||
self::PRIEST => SPELLFAMILY_PRIEST,
|
|
||||||
self::DEATHKNIGHT => SPELLFAMILY_DEATHKNIGHT,
|
|
||||||
self::SHAMAN => SPELLFAMILY_SHAMAN,
|
|
||||||
self::MAGE => SPELLFAMILY_MAGE,
|
|
||||||
self::WARLOCK => SPELLFAMILY_WARLOCK,
|
|
||||||
self::DRUID => SPELLFAMILY_DRUID
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
enum ChrRace : int
|
|
||||||
{
|
|
||||||
case HUMAN = 1;
|
|
||||||
case ORC = 2;
|
|
||||||
case DWARF = 3;
|
|
||||||
case NIGHTELF = 4;
|
|
||||||
case UNDEAD = 5;
|
|
||||||
case TAUREN = 6;
|
|
||||||
case GNOME = 7;
|
|
||||||
case TROLL = 8;
|
|
||||||
case BLOODELF = 10;
|
|
||||||
case DRAENEI = 11;
|
|
||||||
|
|
||||||
public const MASK_ALLIANCE = 0x44D;
|
|
||||||
public const MASK_HORDE = 0x2B2;
|
|
||||||
public const MASK_ALL = 0x6FF;
|
|
||||||
|
|
||||||
public function matches(int $raceMask) : bool
|
|
||||||
{
|
|
||||||
return !$raceMask || $this->value & $raceMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toMask() : int
|
|
||||||
{
|
|
||||||
return 1 << ($this->value - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAlliance() : bool
|
|
||||||
{
|
|
||||||
return $this->toMask() & self::MASK_ALLIANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isHorde() : bool
|
|
||||||
{
|
|
||||||
return $this->toMask() & self::MASK_HORDE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSide() : int
|
|
||||||
{
|
|
||||||
if ($this->isHorde() && $this->isAlliance())
|
|
||||||
return SIDE_BOTH;
|
|
||||||
else if ($this->isHorde())
|
|
||||||
return SIDE_HORDE;
|
|
||||||
else if ($this->isAlliance())
|
|
||||||
return SIDE_ALLIANCE;
|
|
||||||
else
|
|
||||||
return SIDE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTeam() : int
|
|
||||||
{
|
|
||||||
if ($this->isHorde() && $this->isAlliance())
|
|
||||||
return TEAM_NEUTRAL;
|
|
||||||
else if ($this->isHorde())
|
|
||||||
return TEAM_HORDE;
|
|
||||||
else if ($this->isAlliance())
|
|
||||||
return TEAM_ALLIANCE;
|
|
||||||
else
|
|
||||||
return TEAM_NEUTRAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function json() : string
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::HUMAN => 'human',
|
|
||||||
self::ORC => 'orc',
|
|
||||||
self::DWARF => 'dwarf',
|
|
||||||
self::NIGHTELF => 'nightelf',
|
|
||||||
self::UNDEAD => 'undead',
|
|
||||||
self::TAUREN => 'tauren',
|
|
||||||
self::GNOME => 'gnome',
|
|
||||||
self::TROLL => 'troll',
|
|
||||||
self::BLOODELF => 'bloodelf',
|
|
||||||
self::DRAENEI => 'draenei'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fromMask(int $raceMask = self::MASK_ALL) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::cases() as $cl)
|
|
||||||
if ($cl->toMask() & $raceMask)
|
|
||||||
$x[] = $cl->value;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function sideFromMask(int $raceMask) : int
|
|
||||||
{
|
|
||||||
// Any
|
|
||||||
if (!$raceMask || ($raceMask & self::MASK_ALL) == self::MASK_ALL)
|
|
||||||
return SIDE_BOTH;
|
|
||||||
|
|
||||||
// Horde
|
|
||||||
if ($raceMask & self::MASK_HORDE && !($raceMask & self::MASK_ALLIANCE))
|
|
||||||
return SIDE_HORDE;
|
|
||||||
|
|
||||||
// Alliance
|
|
||||||
if ($raceMask & self::MASK_ALLIANCE && !($raceMask & self::MASK_HORDE))
|
|
||||||
return SIDE_ALLIANCE;
|
|
||||||
|
|
||||||
return SIDE_BOTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function teamFromMask(int $raceMask) : int
|
|
||||||
{
|
|
||||||
// Any
|
|
||||||
if (!$raceMask || ($raceMask & self::MASK_ALL) == self::MASK_ALL)
|
|
||||||
return TEAM_NEUTRAL;
|
|
||||||
|
|
||||||
// Horde
|
|
||||||
if ($raceMask & self::MASK_HORDE && !($raceMask & self::MASK_ALLIANCE))
|
|
||||||
return TEAM_HORDE;
|
|
||||||
|
|
||||||
// Alliance
|
|
||||||
if ($raceMask & self::MASK_ALLIANCE && !($raceMask & self::MASK_HORDE))
|
|
||||||
return TEAM_ALLIANCE;
|
|
||||||
|
|
||||||
return TEAM_NEUTRAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,723 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
abstract class Stat // based on g_statToJson
|
|
||||||
{
|
|
||||||
public const HEALTH = 1;
|
|
||||||
public const MANA = 2; // note: mana is on idx 0 from 5.0 onwards; idx 2 is empty
|
|
||||||
public const AGILITY = 3;
|
|
||||||
public const STRENGTH = 4;
|
|
||||||
public const INTELLECT = 5;
|
|
||||||
public const SPIRIT = 6;
|
|
||||||
public const STAMINA = 7;
|
|
||||||
public const ENERGY = 8;
|
|
||||||
public const RAGE = 9;
|
|
||||||
public const FOCUS = 10;
|
|
||||||
public const RUNIC_POWER = 11;
|
|
||||||
public const DEFENSE_RTG = 12;
|
|
||||||
public const DODGE_RTG = 13;
|
|
||||||
public const PARRY_RTG = 14;
|
|
||||||
public const BLOCK_RTG = 15;
|
|
||||||
public const MELEE_HIT_RTG = 16;
|
|
||||||
public const RANGED_HIT_RTG = 17;
|
|
||||||
public const SPELL_HIT_RTG = 18;
|
|
||||||
public const MELEE_CRIT_RTG = 19;
|
|
||||||
public const RANGED_CRIT_RTG = 20;
|
|
||||||
public const SPELL_CRIT_RTG = 21;
|
|
||||||
public const MELEE_HIT_TAKEN_RTG = 22;
|
|
||||||
public const RANGED_HIT_TAKEN_RTG = 23;
|
|
||||||
public const SPELL_HIT_TAKEN_RTG = 24;
|
|
||||||
public const MELEE_CRIT_TAKEN_RTG = 25;
|
|
||||||
public const RANGED_CRIT_TAKEN_RTG = 26;
|
|
||||||
public const SPELL_CRIT_TAKEN_RTG = 27;
|
|
||||||
public const MELEE_HASTE_RTG = 28;
|
|
||||||
public const RANGED_HASTE_RTG = 29;
|
|
||||||
public const SPELL_HASTE_RTG = 30;
|
|
||||||
public const HIT_RTG = 31;
|
|
||||||
public const CRIT_RTG = 32;
|
|
||||||
public const HIT_TAKEN_RTG = 33;
|
|
||||||
public const CRIT_TAKEN_RTG = 34;
|
|
||||||
public const RESILIENCE_RTG = 35;
|
|
||||||
public const HASTE_RTG = 36;
|
|
||||||
public const EXPERTISE_RTG = 37;
|
|
||||||
public const ATTACK_POWER = 38;
|
|
||||||
public const RANGED_ATTACK_POWER = 39;
|
|
||||||
public const FERAL_ATTACK_POWER = 40; // unused in wow-3.3.5
|
|
||||||
public const HEALING_SPELL_POWER = 41; // deprecated
|
|
||||||
public const DAMAGE_SPELL_POWER = 42; // deprecated
|
|
||||||
public const MANA_REGENERATION = 43;
|
|
||||||
public const ARMOR_PENETRATION_RTG = 44;
|
|
||||||
public const SPELL_POWER = 45;
|
|
||||||
public const HEALTH_REGENERATION = 46; // no differentiation between IC (item mods / spells) and OOC (from spirit) for Profiler
|
|
||||||
public const SPELL_PENETRATION = 47;
|
|
||||||
public const BLOCK = 48;
|
|
||||||
// public const MASTERY_RTG = 49; // not in wow-3.3.5
|
|
||||||
public const ARMOR = 50;
|
|
||||||
public const FIRE_RESISTANCE = 51;
|
|
||||||
public const FROST_RESISTANCE = 52;
|
|
||||||
public const HOLY_RESISTANCE = 53;
|
|
||||||
public const SHADOW_RESISTANCE = 54;
|
|
||||||
public const NATURE_RESISTANCE = 55;
|
|
||||||
public const ARCANE_RESISTANCE = 56;
|
|
||||||
public const FIRE_SPELL_POWER = 57;
|
|
||||||
public const FROST_SPELL_POWER = 58;
|
|
||||||
public const HOLY_SPELL_POWER = 59;
|
|
||||||
public const SHADOW_SPELL_POWER = 60;
|
|
||||||
public const NATURE_SPELL_POWER = 61;
|
|
||||||
public const ARCANE_SPELL_POWER = 62;
|
|
||||||
// v for stats lookups v
|
|
||||||
public const WEAPON_DAMAGE = 200; // +weapon dmg from enchantments
|
|
||||||
public const WEAPON_DAMAGE_TYPE = 201;
|
|
||||||
public const WEAPON_DAMAGE_MIN = 202;
|
|
||||||
public const WEAPON_DAMAGE_MAX = 203;
|
|
||||||
public const WEAPON_SPEED = 204;
|
|
||||||
public const WEAPON_DPS = 205; // also +weapon dps from enchantments (rockbiter)
|
|
||||||
public const MELEE_DAMAGE_MIN = 206;
|
|
||||||
public const MELEE_DAMAGE_MAX = 207;
|
|
||||||
public const MELEE_SPEED = 208;
|
|
||||||
public const MELEE_DPS = 209;
|
|
||||||
public const RANGED_DAMAGE_MIN = 210;
|
|
||||||
public const RANGED_DAMAGE_MAX = 211;
|
|
||||||
public const RANGED_SPEED = 212;
|
|
||||||
public const RANGED_DPS = 213;
|
|
||||||
public const EXTRA_SOCKETS = 214;
|
|
||||||
public const ARMOR_BONUS = 215;
|
|
||||||
public const MELEE_ATTACK_POWER = 216;
|
|
||||||
// v only seen in profiler v
|
|
||||||
public const EXPERTISE = 500;
|
|
||||||
public const ARMOR_PENETRATION_PCT = 501;
|
|
||||||
public const MELEE_HIT_PCT = 502;
|
|
||||||
public const MELEE_CRIT_PCT = 503;
|
|
||||||
public const MELEE_HASTE_PCT = 504;
|
|
||||||
public const RANGED_HIT_PCT = 505;
|
|
||||||
public const RANGED_CRIT_PCT = 506;
|
|
||||||
public const RANGED_HASTE_PCT = 507;
|
|
||||||
public const SPELL_HIT_PCT = 508;
|
|
||||||
public const SPELL_CRIT_PCT = 509;
|
|
||||||
public const SPELL_HASTE_PCT = 510;
|
|
||||||
public const MANA_REGENERATION_SPI = 511; // mp5 from spirit, excluding other sources
|
|
||||||
public const MANA_REGENERATION_OC = 512; // mp5 out of combat, excluding other sources
|
|
||||||
public const MANA_REGENERATION_IC = 513; // mp5 in combat, excluding other sources
|
|
||||||
public const ARMOR_TOTAL = 514; // ARMOR + ARMOR_BONUS meta category .. can be skipped here like pet* stats?
|
|
||||||
public const DEFENSE = 515;
|
|
||||||
public const DODGE_PCT = 516;
|
|
||||||
public const PARRY_PCT = 517;
|
|
||||||
public const BLOCK_PCT = 518;
|
|
||||||
public const RESILIENCE_PCT = 519;
|
|
||||||
|
|
||||||
public const FLAG_NONE = 0x00;
|
|
||||||
public const FLAG_ITEM = 0x01; // found on items
|
|
||||||
public const FLAG_SERVERSIDE = 0x02; // not included in g_statToJson
|
|
||||||
public const FLAG_PROFILER = 0x04; // stat used in profiler only
|
|
||||||
public const FLAG_LVL_SCALING = 0x08; // rating effectivenes scales with level
|
|
||||||
public const FLAG_FLOAT_VALUE = 0x10; // not an int
|
|
||||||
|
|
||||||
public const IDX_JSON_STR = 0;
|
|
||||||
public const IDX_ITEM_MOD = 1; // granted by items
|
|
||||||
public const IDX_COMBAT_RATING = 2; // granted by spells + enchantments
|
|
||||||
public const IDX_FILTER_CR_ID = 3; // also references listview cols
|
|
||||||
public const IDX_FLAGS = 4;
|
|
||||||
|
|
||||||
private static /* array */ $data = array(
|
|
||||||
self::HEALTH => ['health', ITEM_MOD_HEALTH, null, 115, self::FLAG_ITEM],
|
|
||||||
self::MANA => ['mana', ITEM_MOD_MANA, null, 116, self::FLAG_ITEM],
|
|
||||||
self::AGILITY => ['agi', ITEM_MOD_AGILITY, null, 21, self::FLAG_ITEM],
|
|
||||||
self::STRENGTH => ['str', ITEM_MOD_STRENGTH, null, 20, self::FLAG_ITEM],
|
|
||||||
self::INTELLECT => ['int', ITEM_MOD_INTELLECT, null, 23, self::FLAG_ITEM],
|
|
||||||
self::SPIRIT => ['spi', ITEM_MOD_SPIRIT, null, 24, self::FLAG_ITEM],
|
|
||||||
self::STAMINA => ['sta', ITEM_MOD_STAMINA, null, 22, self::FLAG_ITEM],
|
|
||||||
self::ENERGY => ['energy', null, null, null, self::FLAG_ITEM],
|
|
||||||
self::RAGE => ['rage', null, null, null, self::FLAG_ITEM],
|
|
||||||
self::FOCUS => ['focus', null, null, null, self::FLAG_ITEM],
|
|
||||||
self::RUNIC_POWER => ['runic', null, null, null, self::FLAG_ITEM | self::FLAG_SERVERSIDE],
|
|
||||||
self::DEFENSE_RTG => ['defrtng', ITEM_MOD_DEFENSE_SKILL_RATING, CR_DEFENSE_SKILL, 42, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::DODGE_RTG => ['dodgertng', ITEM_MOD_DODGE_RATING, CR_DODGE, 45, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::PARRY_RTG => ['parryrtng', ITEM_MOD_PARRY_RATING, CR_PARRY, 46, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::BLOCK_RTG => ['blockrtng', ITEM_MOD_BLOCK_RATING, CR_BLOCK, 44, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::MELEE_HIT_RTG => ['mlehitrtng', ITEM_MOD_HIT_MELEE_RATING, CR_HIT_MELEE, 95, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::RANGED_HIT_RTG => ['rgdhitrtng', ITEM_MOD_HIT_RANGED_RATING, CR_HIT_RANGED, 39, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::SPELL_HIT_RTG => ['splhitrtng', ITEM_MOD_HIT_SPELL_RATING, CR_HIT_SPELL, 48, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::MELEE_CRIT_RTG => ['mlecritstrkrtng', ITEM_MOD_CRIT_MELEE_RATING, CR_CRIT_MELEE, 84, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::RANGED_CRIT_RTG => ['rgdcritstrkrtng', ITEM_MOD_CRIT_RANGED_RATING, CR_CRIT_RANGED, 40, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::SPELL_CRIT_RTG => ['splcritstrkrtng', ITEM_MOD_CRIT_SPELL_RATING, CR_CRIT_SPELL, 49, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::MELEE_HIT_TAKEN_RTG => ['_mlehitrtng', ITEM_MOD_HIT_TAKEN_MELEE_RATING, CR_HIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::RANGED_HIT_TAKEN_RTG => ['_rgdhitrtng', ITEM_MOD_HIT_TAKEN_RANGED_RATING, CR_HIT_TAKEN_RANGED, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::SPELL_HIT_TAKEN_RTG => ['_splhitrtng', ITEM_MOD_HIT_TAKEN_SPELL_RATING, CR_HIT_TAKEN_SPELL, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::MELEE_CRIT_TAKEN_RTG => ['_mlecritstrkrtng', ITEM_MOD_CRIT_TAKEN_MELEE_RATING, CR_CRIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::RANGED_CRIT_TAKEN_RTG => ['_rgdcritstrkrtng', ITEM_MOD_CRIT_TAKEN_RANGED_RATING, CR_CRIT_TAKEN_RANGED, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::SPELL_CRIT_TAKEN_RTG => ['_splcritstrkrtng', ITEM_MOD_CRIT_TAKEN_SPELL_RATING, CR_CRIT_TAKEN_SPELL, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::MELEE_HASTE_RTG => ['mlehastertng', ITEM_MOD_HASTE_MELEE_RATING, CR_HASTE_MELEE, 78, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::RANGED_HASTE_RTG => ['rgdhastertng', ITEM_MOD_HASTE_RANGED_RATING, CR_HASTE_RANGED, 101, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::SPELL_HASTE_RTG => ['splhastertng', ITEM_MOD_HASTE_SPELL_RATING, CR_HASTE_SPELL, 102, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::HIT_RTG => ['hitrtng', ITEM_MOD_HIT_RATING, -CR_HIT_MELEE, 119, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::CRIT_RTG => ['critstrkrtng', ITEM_MOD_CRIT_RATING, -CR_CRIT_MELEE, 96, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::HIT_TAKEN_RTG => ['_hitrtng', ITEM_MOD_HIT_TAKEN_RATING, -CR_HIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::CRIT_TAKEN_RTG => ['_critstrkrtng', ITEM_MOD_CRIT_TAKEN_RATING, -CR_CRIT_TAKEN_MELEE, null, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::RESILIENCE_RTG => ['resirtng', ITEM_MOD_RESILIENCE_RATING, -CR_CRIT_TAKEN_MELEE, 79, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::HASTE_RTG => ['hastertng', ITEM_MOD_HASTE_RATING, -CR_HASTE_MELEE, 103, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::EXPERTISE_RTG => ['exprtng', ITEM_MOD_EXPERTISE_RATING, CR_EXPERTISE, 117, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::ATTACK_POWER => ['atkpwr', ITEM_MOD_ATTACK_POWER, null, 77, self::FLAG_ITEM],
|
|
||||||
self::RANGED_ATTACK_POWER => ['rgdatkpwr', ITEM_MOD_RANGED_ATTACK_POWER, null, 38, self::FLAG_ITEM],
|
|
||||||
self::FERAL_ATTACK_POWER => ['feratkpwr', ITEM_MOD_FERAL_ATTACK_POWER, null, 97, self::FLAG_ITEM],
|
|
||||||
self::HEALING_SPELL_POWER => ['splheal', ITEM_MOD_SPELL_HEALING_DONE, null, 50, self::FLAG_ITEM],
|
|
||||||
self::DAMAGE_SPELL_POWER => ['spldmg', ITEM_MOD_SPELL_DAMAGE_DONE, null, 51, self::FLAG_ITEM],
|
|
||||||
self::MANA_REGENERATION => ['manargn', ITEM_MOD_MANA_REGENERATION, null, 61, self::FLAG_ITEM],
|
|
||||||
self::ARMOR_PENETRATION_RTG => ['armorpenrtng', ITEM_MOD_ARMOR_PENETRATION_RATING, CR_ARMOR_PENETRATION, 114, self::FLAG_ITEM | self::FLAG_LVL_SCALING],
|
|
||||||
self::SPELL_POWER => ['splpwr', ITEM_MOD_SPELL_POWER, null, 123, self::FLAG_ITEM],
|
|
||||||
self::HEALTH_REGENERATION => ['healthrgn', ITEM_MOD_HEALTH_REGEN, null, 60, self::FLAG_ITEM],
|
|
||||||
self::SPELL_PENETRATION => ['splpen', ITEM_MOD_SPELL_PENETRATION, null, 94, self::FLAG_ITEM],
|
|
||||||
self::BLOCK => ['block', ITEM_MOD_BLOCK_VALUE, null, 43, self::FLAG_ITEM],
|
|
||||||
// self::MASTERY_RTG => ['mastrtng', null, CR_MASTERY, null, self::FLAG_NONE],
|
|
||||||
self::ARMOR => ['armor', null, null, 41, self::FLAG_ITEM],
|
|
||||||
self::FIRE_RESISTANCE => ['firres', null, null, 26, self::FLAG_ITEM],
|
|
||||||
self::FROST_RESISTANCE => ['frores', null, null, 28, self::FLAG_ITEM],
|
|
||||||
self::HOLY_RESISTANCE => ['holres', null, null, 30, self::FLAG_ITEM],
|
|
||||||
self::SHADOW_RESISTANCE => ['shares', null, null, 29, self::FLAG_ITEM],
|
|
||||||
self::NATURE_RESISTANCE => ['natres', null, null, 27, self::FLAG_ITEM],
|
|
||||||
self::ARCANE_RESISTANCE => ['arcres', null, null, 25, self::FLAG_ITEM],
|
|
||||||
self::FIRE_SPELL_POWER => ['firsplpwr', null, null, 53, self::FLAG_ITEM],
|
|
||||||
self::FROST_SPELL_POWER => ['frosplpwr', null, null, 54, self::FLAG_ITEM],
|
|
||||||
self::HOLY_SPELL_POWER => ['holsplpwr', null, null, 55, self::FLAG_ITEM],
|
|
||||||
self::SHADOW_SPELL_POWER => ['shasplpwr', null, null, 57, self::FLAG_ITEM],
|
|
||||||
self::NATURE_SPELL_POWER => ['natsplpwr', null, null, 56, self::FLAG_ITEM],
|
|
||||||
self::ARCANE_SPELL_POWER => ['arcsplpwr', null, null, 52, self::FLAG_ITEM],
|
|
||||||
// v not part of g_statToJson v
|
|
||||||
self::WEAPON_DAMAGE => ['dmg', null, null, null, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
|
|
||||||
self::WEAPON_DAMAGE_TYPE => ['damagetype', null, null, 35, self::FLAG_SERVERSIDE],
|
|
||||||
self::WEAPON_DAMAGE_MIN => ['dmgmin1', null, null, 33, self::FLAG_SERVERSIDE],
|
|
||||||
self::WEAPON_DAMAGE_MAX => ['dmgmax1', null, null, 34, self::FLAG_SERVERSIDE],
|
|
||||||
self::WEAPON_SPEED => ['speed', null, null, 36, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
|
|
||||||
self::WEAPON_DPS => ['dps', null, null, 32, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
|
|
||||||
self::MELEE_DAMAGE_MIN => ['mledmgmin', null, null, 135, self::FLAG_SERVERSIDE],
|
|
||||||
self::MELEE_DAMAGE_MAX => ['mledmgmax', null, null, 136, self::FLAG_SERVERSIDE],
|
|
||||||
self::MELEE_SPEED => ['mlespeed', null, null, 137, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
|
|
||||||
self::MELEE_DPS => ['mledps', null, null, 134, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE | self::FLAG_PROFILER],
|
|
||||||
self::RANGED_DAMAGE_MIN => ['rgddmgmin', null, null, 139, self::FLAG_SERVERSIDE],
|
|
||||||
self::RANGED_DAMAGE_MAX => ['rgddmgmax', null, null, 140, self::FLAG_SERVERSIDE],
|
|
||||||
self::RANGED_SPEED => ['rgdspeed', null, null, 141, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE],
|
|
||||||
self::RANGED_DPS => ['rgddps', null, null, 138, self::FLAG_SERVERSIDE | self::FLAG_FLOAT_VALUE | self::FLAG_PROFILER],
|
|
||||||
self::EXTRA_SOCKETS => ['nsockets', null, null, 100, self::FLAG_SERVERSIDE],
|
|
||||||
self::ARMOR_BONUS => ['armorbonus', null, null, 109, self::FLAG_SERVERSIDE],
|
|
||||||
self::MELEE_ATTACK_POWER => ['mleatkpwr', null, null, 37, self::FLAG_SERVERSIDE | self::FLAG_PROFILER],
|
|
||||||
// v Profiler only v
|
|
||||||
self::EXPERTISE => ['exp', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::ARMOR_PENETRATION_PCT => ['armorpenpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::MELEE_HIT_PCT => ['mlehitpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::MELEE_CRIT_PCT => ['mlecritstrkpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::MELEE_HASTE_PCT => ['mlehastepct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::RANGED_HIT_PCT => ['rgdhitpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::RANGED_CRIT_PCT => ['rgdcritstrkpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::RANGED_HASTE_PCT => ['rgdhastepct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::SPELL_HIT_PCT => ['splhitpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::SPELL_CRIT_PCT => ['splcritstrkpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::SPELL_HASTE_PCT => ['splhastepct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::MANA_REGENERATION_SPI => ['spimanargn', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::MANA_REGENERATION_OC => ['oocmanargn', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::MANA_REGENERATION_IC => ['icmanargn', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::ARMOR_TOTAL => ['fullarmor', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::DEFENSE => ['def', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::DODGE_PCT => ['dodgepct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::PARRY_PCT => ['parrypct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::BLOCK_PCT => ['blockpct', null, null, null, self::FLAG_PROFILER],
|
|
||||||
self::RESILIENCE_PCT => ['resipct', null, null, null, self::FLAG_PROFILER]
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Combat Rating needed for 1% effect at level 60 (Note: Shaman, Druid, Paladin and Death Knight have a /1.3 modifier on HASTE not set here)
|
|
||||||
* Data taken from gtcombatratings.dbc for level 60 [idx % 100 = 59]
|
|
||||||
* Corrections from gtoctclasscombatratingscalar.dbc with Warrior as base [idx = ratingId + 1]
|
|
||||||
* Maybe create this data during setup, but then again it will never change for 3.3.5a
|
|
||||||
*/
|
|
||||||
private static $crPerPctPoint = array(
|
|
||||||
CR_WEAPON_SKILL => 2.50, CR_DEFENSE_SKILL => 1.50, CR_DODGE => 13.80, CR_PARRY => 13.80, CR_BLOCK => 5.00,
|
|
||||||
CR_HIT_MELEE => 10.00, CR_HIT_RANGED => 10.00, CR_HIT_SPELL => 8.00, CR_CRIT_MELEE => 14.00, CR_CRIT_RANGED => 14.00,
|
|
||||||
CR_CRIT_SPELL => 14.00, CR_HIT_TAKEN_MELEE => 10.00, CR_HIT_TAKEN_RANGED => 10.00, CR_HIT_TAKEN_SPELL => 8.00, CR_CRIT_TAKEN_MELEE => 28.75,
|
|
||||||
CR_CRIT_TAKEN_RANGED => 28.75, CR_CRIT_TAKEN_SPELL => 28.75, CR_HASTE_MELEE => 10.00, CR_HASTE_RANGED => 10.00, CR_HASTE_SPELL => 10.00,
|
|
||||||
CR_WEAPON_SKILL_MAINHAND => 2.50, CR_WEAPON_SKILL_OFFHAND => 2.50, CR_WEAPON_SKILL_RANGED => 2.50, CR_EXPERTISE => 2.50, CR_ARMOR_PENETRATION => 4.69512 / 1.1,
|
|
||||||
);
|
|
||||||
|
|
||||||
public static function isLevelIndependent(int $stat) : bool
|
|
||||||
{
|
|
||||||
if (!isset(self::$data[$stat]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return !(self::$data[$stat][self::IDX_FLAGS] & self::FLAG_LVL_SCALING);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getRatingPctFactor(int $stat) : float
|
|
||||||
{
|
|
||||||
// Note: this makes the weapon skill related combat ratings inaccessible. Is this relevant..?
|
|
||||||
if (!isset(self::$data[$stat]) || self::$data[$stat][self::IDX_COMBAT_RATING] === null)
|
|
||||||
return 0.0;
|
|
||||||
|
|
||||||
// note: originally any CRIT_TAKEN_RTG stat was set to 0 in favor of RESILIENCE_RTG
|
|
||||||
// we keep the dbc value and just link RESILIENCE_RTG to CRIT_TAKEN_RTG
|
|
||||||
// note2: the js expects some stats to be directly mapped to a combat rating that doesn't exist
|
|
||||||
// picked the next best one in this case and denoted it with a negative value in the $data dump
|
|
||||||
return self::$crPerPctPoint[abs(self::$data[$stat][self::IDX_COMBAT_RATING])];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getJsonString(int $stat) : string
|
|
||||||
{
|
|
||||||
if (!isset(self::$data[$stat]))
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return self::$data[$stat][self::IDX_JSON_STR];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFilterCriteriumId(int $stat) : ?int
|
|
||||||
{
|
|
||||||
if (!isset(self::$data[$stat]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return self::$data[$stat][self::IDX_FILTER_CR_ID];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFlags(int $stat) : int
|
|
||||||
{
|
|
||||||
if (!isset(self::$data[$stat]))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return self::$data[$stat][self::IDX_FLAGS];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getJsonStringsFor(int $flags = Stat::FLAG_NONE) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::$data as $k => [$s, , , , $f])
|
|
||||||
if ($s && (!$flags || $flags & $f))
|
|
||||||
$x[$k] = $s;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getCombatRatingsFor(int $flags = Stat::FLAG_NONE) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::$data as $k => [, , $c, , $f])
|
|
||||||
if ($c > 0 && (!$flags || $flags & $f))
|
|
||||||
$x[$k] = $c;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFilterCriteriumIdFor(int $flags = Stat::FLAG_NONE) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::$data as $k => [, , , $cr, $f])
|
|
||||||
if ($cr && (!$flags || $flags & $f))
|
|
||||||
$x[$k] = $cr;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getIndexFrom(int $idx, string $match) : int
|
|
||||||
{
|
|
||||||
$i = array_search($match, array_column(self::$data, $idx));
|
|
||||||
if ($i === false)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return array_keys(self::$data)[$i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class StatsContainer implements \Countable
|
|
||||||
{
|
|
||||||
private $store = [];
|
|
||||||
|
|
||||||
private $relSpells = [];
|
|
||||||
private $relEnchantments = [];
|
|
||||||
|
|
||||||
public function __construct(array $relSpells = [], array $relEnchantments = [])
|
|
||||||
{
|
|
||||||
if ($relSpells)
|
|
||||||
$this->relSpells = $relSpells;
|
|
||||||
|
|
||||||
if ($relEnchantments)
|
|
||||||
$this->relEnchantments = $relEnchantments;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**********/
|
|
||||||
/* Source */
|
|
||||||
/**********/
|
|
||||||
|
|
||||||
public function fromItem(array $item) : self
|
|
||||||
{
|
|
||||||
if (!$item)
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
// convert itemMods to stats
|
|
||||||
for ($i = 1; $i <= 10; $i++)
|
|
||||||
{
|
|
||||||
$mod = $item['statType'.$i];
|
|
||||||
$val = $item['statValue'.$i];
|
|
||||||
if (!$mod || !$val)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($idx = Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $mod))
|
|
||||||
Util::arraySumByKey($this->store, [$idx => $val]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// also occurs as seperate field (gets summed in calculation but not in tooltip)
|
|
||||||
if ($item['tplBlock'])
|
|
||||||
Util::arraySumByKey($this->store, [Stat::BLOCK => $item['tplBlock']]);
|
|
||||||
|
|
||||||
// convert spells to stats
|
|
||||||
for ($i = 1; $i <= 5; $i++)
|
|
||||||
if (in_array($item['spellTrigger'.$i], [SPELL_TRIGGER_EQUIP, SPELL_TRIGGER_USE, SPELL_TRIGGER_USE_NODELAY]))
|
|
||||||
if ($relS = $this->relS($item['spellId'.$i]))
|
|
||||||
$this->fromSpell($relS);
|
|
||||||
|
|
||||||
// for ITEM_CLASS_GEM get stats from enchantment
|
|
||||||
if ($relE = $this->relE($item['gemEnchantmentId']))
|
|
||||||
$this->fromEnchantment($relE);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fromEnchantment(array $enchantment) : self
|
|
||||||
{
|
|
||||||
if (!$enchantment)
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
for ($i = 1; $i <= 3; $i++)
|
|
||||||
{
|
|
||||||
$type = $enchantment['type'.$i];
|
|
||||||
$object = $enchantment['object'.$i];
|
|
||||||
$amount = $enchantment['amount'.$i]; // !CAUTION! scaling enchantments are initialized with "0" as amount. 0 is a valid amount!
|
|
||||||
|
|
||||||
if ($type == ENCHANTMENT_TYPE_EQUIP_SPELL && ($relS = $this->relS($object)))
|
|
||||||
$this->fromSpell($relS);
|
|
||||||
else
|
|
||||||
foreach ($this->convertEnchantment($type, $object) as $idx)
|
|
||||||
Util::arraySumByKey($this->store, [$idx => $amount]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fromSpell(array $spell) : self
|
|
||||||
{
|
|
||||||
if (!$spell)
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
// if spells grant an equal, non-zero amount of SPELL_DAMAGE and SPELL_HEALING, combine them to SPELL_POWER
|
|
||||||
// this probably does not affect enchantments
|
|
||||||
$tmpStore = [];
|
|
||||||
|
|
||||||
for ($i = 1; $i <= 3; $i++)
|
|
||||||
{
|
|
||||||
$eff = $spell['effect'.$i.'Id'];
|
|
||||||
$aura = $spell['effect'.$i.'AuraId'];
|
|
||||||
$mVal = $spell['effect'.$i.'MiscValue'];
|
|
||||||
$amt = $spell['effect'.$i.'BasePoints'] + $spell['effect'.$i.'DieSides'];
|
|
||||||
|
|
||||||
if (in_array($eff, SpellList::EFFECTS_ENCHANTMENT) && ($relE = $this->relE($mVal)))
|
|
||||||
$this->fromEnchantment($relE);
|
|
||||||
else
|
|
||||||
foreach ($this->convertSpellEffect($aura, $mVal, $amt) as $idx)
|
|
||||||
Util::arraySumByKey($tmpStore, [$idx => $amt]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($tmpStore[Stat::HEALING_SPELL_POWER]) && !empty($tmpStore[Stat::DAMAGE_SPELL_POWER]) && $tmpStore[Stat::HEALING_SPELL_POWER] == $tmpStore[Stat::DAMAGE_SPELL_POWER])
|
|
||||||
{
|
|
||||||
Util::arraySumByKey($tmpStore, [Stat::SPELL_POWER => $tmpStore[Stat::HEALING_SPELL_POWER]]);
|
|
||||||
unset($tmpStore[Stat::HEALING_SPELL_POWER]);
|
|
||||||
unset($tmpStore[Stat::DAMAGE_SPELL_POWER]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Util::arraySumByKey($this->store, $tmpStore);
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fromJson(array &$json, bool $pruneFromSrc = false) : self
|
|
||||||
{
|
|
||||||
if (!$json)
|
|
||||||
return $this;
|
|
||||||
|
|
||||||
foreach (Stat::getJsonStringsFor() as $idx => $key)
|
|
||||||
{
|
|
||||||
if (isset($json[$key])) // 0 is a valid amount!
|
|
||||||
{
|
|
||||||
if (Stat::getFlags($idx) & Stat::FLAG_FLOAT_VALUE)
|
|
||||||
Util::arraySumByKey($this->store, [$idx => (float)$json[$key]]);
|
|
||||||
else
|
|
||||||
Util::arraySumByKey($this->store, [$idx => (int)$json[$key]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($pruneFromSrc)
|
|
||||||
unset($json[$key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fromDB(int $type, int $typeId, int $fieldFlags = Stat::FLAG_NONE) : self
|
|
||||||
{
|
|
||||||
foreach (DB::Aowow()->selectRow('SELECT (?#) FROM ?_item_stats WHERE `type` = ?d AND `typeId` = ?d', Stat::getJsonStringsFor($fieldFlags ?: (Stat::FLAG_ITEM | Stat::FLAG_SERVERSIDE)), $type, $typeId) as $key => $amt)
|
|
||||||
{
|
|
||||||
if ($amt === null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$idx = Stat::getIndexFrom(Stat::IDX_JSON_STR, $key);
|
|
||||||
$float = Stat::getFlags($idx) & Stat::FLAG_FLOAT_VALUE;
|
|
||||||
|
|
||||||
if (Util::checkNumeric($amt, $float ? NUM_CAST_FLOAT : NUM_CAST_INT))
|
|
||||||
Util::arraySumByKey($this->store, [$idx => $amt]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fromContainer(StatsContainer ...$container) : self
|
|
||||||
{
|
|
||||||
foreach ($container as $c)
|
|
||||||
Util::arraySumByKey($this->store, $c->toRaw());
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**********/
|
|
||||||
/* Output */
|
|
||||||
/**********/
|
|
||||||
|
|
||||||
public function toJson(int $outFlags = Stat::FLAG_NONE) : array
|
|
||||||
{
|
|
||||||
$out = [];
|
|
||||||
foreach ($this->store as $stat => $amt)
|
|
||||||
if (!$outFlags || (Stat::getFlags($stat) & $outFlags))
|
|
||||||
$out[Stat::getJsonString($stat)] = $amt;
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function toRaw() : array
|
|
||||||
{
|
|
||||||
return $this->store;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filter(?callable $filterFn = null) : self
|
|
||||||
{
|
|
||||||
$this->store = array_filter($this->store, $filterFn, ARRAY_FILTER_USE_BOTH);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function count() : int
|
|
||||||
{
|
|
||||||
return count($this->store);
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************/
|
|
||||||
/* internal use */
|
|
||||||
/****************/
|
|
||||||
|
|
||||||
private function relE(int $enchantmentId) : array
|
|
||||||
{
|
|
||||||
if ($enchantmentId <= 0 || !isset($this->relEnchantments[$enchantmentId]))
|
|
||||||
return [];
|
|
||||||
|
|
||||||
return $this->relEnchantments[$enchantmentId];
|
|
||||||
}
|
|
||||||
|
|
||||||
private function relS(int $spellId) : array
|
|
||||||
{
|
|
||||||
if ($spellId <= 0 || !isset($this->relSpells[$spellId]))
|
|
||||||
return [];
|
|
||||||
|
|
||||||
return $this->relSpells[$spellId];
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function convertEnchantment(int $type, int $object) : array
|
|
||||||
{
|
|
||||||
switch ($type)
|
|
||||||
{
|
|
||||||
case ENCHANTMENT_TYPE_PRISMATIC_SOCKET:
|
|
||||||
return [Stat::EXTRA_SOCKETS];
|
|
||||||
case ENCHANTMENT_TYPE_DAMAGE:
|
|
||||||
return [Stat::WEAPON_DAMAGE];
|
|
||||||
case ENCHANTMENT_TYPE_TOTEM:
|
|
||||||
return [Stat::WEAPON_DPS];
|
|
||||||
case ENCHANTMENT_TYPE_STAT: // ITEM_MOD_*
|
|
||||||
return [Stat::getIndexFrom(Stat::IDX_ITEM_MOD, $object)];
|
|
||||||
case ENCHANTMENT_TYPE_RESISTANCE:
|
|
||||||
if ($object == SPELL_SCHOOL_NORMAL)
|
|
||||||
return [Stat::ARMOR];
|
|
||||||
if ($object == SPELL_SCHOOL_HOLY)
|
|
||||||
return [Stat::HOLY_RESISTANCE];
|
|
||||||
if ($object == SPELL_SCHOOL_FIRE)
|
|
||||||
return [Stat::FIRE_RESISTANCE];
|
|
||||||
if ($object == SPELL_SCHOOL_NATURE)
|
|
||||||
return [Stat::NATURE_RESISTANCE];
|
|
||||||
if ($object == SPELL_SCHOOL_FROST)
|
|
||||||
return [Stat::FROST_RESISTANCE];
|
|
||||||
if ($object == SPELL_SCHOOL_SHADOW)
|
|
||||||
return [Stat::SHADOW_RESISTANCE];
|
|
||||||
if ($object == SPELL_SCHOOL_ARCANE)
|
|
||||||
return [Stat::ARCANE_RESISTANCE];
|
|
||||||
|
|
||||||
return [];
|
|
||||||
case ENCHANTMENT_TYPE_EQUIP_SPELL: // handled one level up
|
|
||||||
case ENCHANTMENT_TYPE_COMBAT_SPELL: // we do not average effects, so skip
|
|
||||||
case ENCHANTMENT_TYPE_USE_SPELL:
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function convertCombatRating(int $mask) : array
|
|
||||||
{
|
|
||||||
$hitMask = (1 << CR_HIT_MELEE) | (1 << CR_HIT_RANGED) | (1 << CR_HIT_SPELL);
|
|
||||||
if (($mask & $hitMask) == $hitMask)
|
|
||||||
return [Stat::HIT_RTG]; // generic hit rating
|
|
||||||
|
|
||||||
$critMask = (1 << CR_CRIT_MELEE) | (1 << CR_CRIT_RANGED) | (1 << CR_CRIT_SPELL);
|
|
||||||
if (($mask & $critMask) == $critMask)
|
|
||||||
return [Stat::CRIT_RTG]; // generic crit rating
|
|
||||||
|
|
||||||
|
|
||||||
$takentMask = (1 << CR_CRIT_TAKEN_MELEE) | (1 << CR_CRIT_TAKEN_RANGED) | (1 << CR_CRIT_TAKEN_SPELL);
|
|
||||||
if (($mask & $takentMask) == $takentMask)
|
|
||||||
return [Stat::RESILIENCE_RTG]; // resilience
|
|
||||||
|
|
||||||
$result = []; // there really shouldn't be multiple ratings in that mask besides the cases above, but who knows..
|
|
||||||
foreach (Stat::getCombatRatingsFor() as $stat => $cr)
|
|
||||||
if ($mask & (1 << $cr))
|
|
||||||
$result[] = $stat;
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function convertSpellEffect(int $auraId, int $miscValue, int &$amount) : array
|
|
||||||
{
|
|
||||||
$stats = [];
|
|
||||||
|
|
||||||
switch ($auraId)
|
|
||||||
{
|
|
||||||
case SPELL_AURA_MOD_STAT:
|
|
||||||
if ($miscValue < 0) // all stats
|
|
||||||
return [Stat::AGILITY, Stat::STRENGTH, Stat::INTELLECT, Stat::SPIRIT, Stat::STAMINA];
|
|
||||||
if ($miscValue == STAT_STRENGTH) // one stat
|
|
||||||
return [Stat::STRENGTH];
|
|
||||||
if ($miscValue == STAT_AGILITY)
|
|
||||||
return [Stat::AGILITY];
|
|
||||||
if ($miscValue == STAT_STAMINA)
|
|
||||||
return [Stat::STAMINA];
|
|
||||||
if ($miscValue == STAT_INTELLECT)
|
|
||||||
return [Stat::INTELLECT];
|
|
||||||
if ($miscValue == STAT_SPIRIT)
|
|
||||||
return [Stat::SPIRIT];
|
|
||||||
|
|
||||||
return []; // one bullshit
|
|
||||||
case SPELL_AURA_MOD_INCREASE_HEALTH:
|
|
||||||
case SPELL_AURA_MOD_INCREASE_HEALTH_NONSTACK:
|
|
||||||
case SPELL_AURA_MOD_INCREASE_HEALTH_2:
|
|
||||||
return [Stat::HEALTH];
|
|
||||||
case SPELL_AURA_MOD_DAMAGE_DONE:
|
|
||||||
// + weapon damage
|
|
||||||
if ($miscValue == (1 << SPELL_SCHOOL_NORMAL))
|
|
||||||
return [Stat::WEAPON_DAMAGE];
|
|
||||||
|
|
||||||
// full magic mask
|
|
||||||
if ($miscValue == SPELL_MAGIC_SCHOOLS)
|
|
||||||
return [Stat::DAMAGE_SPELL_POWER];
|
|
||||||
|
|
||||||
// HolySpellpower (deprecated; still used in randomproperties)
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_HOLY))
|
|
||||||
$stats[] = Stat::HOLY_SPELL_POWER;
|
|
||||||
|
|
||||||
// FireSpellpower (deprecated; still used in randomproperties)
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_FIRE))
|
|
||||||
$stats[] = Stat::FIRE_SPELL_POWER;
|
|
||||||
|
|
||||||
// NatureSpellpower (deprecated; still used in randomproperties)
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_NATURE))
|
|
||||||
$stats[] = Stat::NATURE_SPELL_POWER;
|
|
||||||
|
|
||||||
// FrostSpellpower (deprecated; still used in randomproperties)
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_FROST))
|
|
||||||
$stats[] = Stat::FROST_SPELL_POWER;
|
|
||||||
|
|
||||||
// ShadowSpellpower (deprecated; still used in randomproperties)
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_SHADOW))
|
|
||||||
$stats[] = Stat::SHADOW_SPELL_POWER;
|
|
||||||
|
|
||||||
// ArcaneSpellpower (deprecated; still used in randomproperties)
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_ARCANE))
|
|
||||||
$stats[] = Stat::ARCANE_SPELL_POWER;
|
|
||||||
|
|
||||||
return $stats;
|
|
||||||
case SPELL_AURA_MOD_HEALING_DONE: // not as a mask..
|
|
||||||
return [Stat::HEALING_SPELL_POWER];
|
|
||||||
case SPELL_AURA_MOD_INCREASE_ENERGY: // MiscVal:type see defined Powers only energy/mana in use
|
|
||||||
if ($miscValue == POWER_ENERGY)
|
|
||||||
return [Stat::ENERGY];
|
|
||||||
if ($miscValue == POWER_RAGE)
|
|
||||||
return [Stat::RAGE];
|
|
||||||
if ($miscValue == POWER_MANA)
|
|
||||||
return [Stat::MANA];
|
|
||||||
if ($miscValue == POWER_RUNIC_POWER)
|
|
||||||
return [Stat::RUNIC_POWER];
|
|
||||||
|
|
||||||
return [];
|
|
||||||
case SPELL_AURA_MOD_RATING:
|
|
||||||
case SPELL_AURA_MOD_RATING_FROM_STAT:
|
|
||||||
if ($stat = self::convertCombatRating($miscValue))
|
|
||||||
return $stat;
|
|
||||||
|
|
||||||
return [];
|
|
||||||
case SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE:
|
|
||||||
case SPELL_AURA_MOD_BASE_RESISTANCE:
|
|
||||||
case SPELL_AURA_MOD_RESISTANCE:
|
|
||||||
// Armor only if explicitly specified
|
|
||||||
if ($miscValue == (1 << SPELL_SCHOOL_NORMAL))
|
|
||||||
return [Stat::ARMOR];
|
|
||||||
|
|
||||||
// Holy resistance only if explicitly specified (should it even exist...?)
|
|
||||||
if ($miscValue == (1 << SPELL_SCHOOL_HOLY))
|
|
||||||
return [Stat::HOLY_RESISTANCE];
|
|
||||||
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_FIRE))
|
|
||||||
$stats[] = Stat::FIRE_RESISTANCE;
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_NATURE))
|
|
||||||
$stats[] = Stat::NATURE_RESISTANCE;
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_FROST))
|
|
||||||
$stats[] = Stat::FROST_RESISTANCE;
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_SHADOW))
|
|
||||||
$stats[] = Stat::SHADOW_RESISTANCE;
|
|
||||||
if ($miscValue & (1 << SPELL_SCHOOL_ARCANE))
|
|
||||||
$stats[] = Stat::ARCANE_RESISTANCE;
|
|
||||||
|
|
||||||
return $stats;
|
|
||||||
case SPELL_AURA_PERIODIC_HEAL: // hp5
|
|
||||||
case SPELL_AURA_MOD_REGEN:
|
|
||||||
case SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT:
|
|
||||||
return [Stat::HEALTH_REGENERATION];
|
|
||||||
case SPELL_AURA_MOD_POWER_REGEN: // mp5
|
|
||||||
return [Stat::MANA_REGENERATION];
|
|
||||||
case SPELL_AURA_MOD_ATTACK_POWER:
|
|
||||||
return [Stat::ATTACK_POWER/*, Stat::RANGED_ATTACK_POWER*/];
|
|
||||||
case SPELL_AURA_MOD_RANGED_ATTACK_POWER:
|
|
||||||
return [Stat::RANGED_ATTACK_POWER];
|
|
||||||
case SPELL_AURA_MOD_SHIELD_BLOCKVALUE:
|
|
||||||
return [Stat::BLOCK];
|
|
||||||
case SPELL_AURA_MOD_EXPERTISE:
|
|
||||||
return [Stat::EXPERTISE];
|
|
||||||
case SPELL_AURA_MOD_TARGET_RESISTANCE:
|
|
||||||
$amount = abs($amount); // functionally negative, but we work with the absolute amount
|
|
||||||
if ($miscValue == 0x7C) // SPELL_MAGIC_SCHOOLS & ~SPELL_SCHOOL_HOLY
|
|
||||||
return [Stat::SPELL_PENETRATION];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Game
|
|
||||||
{
|
|
||||||
public static $resistanceFields = array(
|
|
||||||
null, 'resHoly', 'resFire', 'resNature', 'resFrost', 'resShadow', 'resArcane'
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $rarityColorStings = array( // zero-indexed
|
|
||||||
'9d9d9d', 'ffffff', '1eff00', '0070dd', 'a335ee', 'ff8000', 'e5cc80', 'e6cc80'
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $specIconStrings = array(
|
|
||||||
-1 => 'inv_misc_questionmark',
|
|
||||||
0 => 'spell_nature_elementalabsorption',
|
|
||||||
6 => ['spell_deathknight_bloodpresence', 'spell_deathknight_frostpresence', 'spell_deathknight_unholypresence' ],
|
|
||||||
11 => ['spell_nature_starfall', 'ability_racial_bearform', 'spell_nature_healingtouch' ],
|
|
||||||
3 => ['ability_hunter_beasttaming', 'ability_marksmanship', 'ability_hunter_swiftstrike' ],
|
|
||||||
8 => ['spell_holy_magicalsentry', 'spell_fire_firebolt02', 'spell_frost_frostbolt02' ],
|
|
||||||
2 => ['spell_holy_holybolt', 'spell_holy_devotionaura', 'spell_holy_auraoflight' ],
|
|
||||||
5 => ['spell_holy_wordfortitude', 'spell_holy_holybolt', 'spell_shadow_shadowwordpain' ],
|
|
||||||
4 => ['ability_rogue_eviscerate', 'ability_backstab', 'ability_stealth' ],
|
|
||||||
7 => ['spell_nature_lightning', 'spell_nature_lightningshield', 'spell_nature_magicimmunity' ],
|
|
||||||
9 => ['spell_shadow_deathcoil', 'spell_shadow_metamorphosis', 'spell_shadow_rainoffire' ],
|
|
||||||
1 => ['ability_rogue_eviscerate', 'ability_warrior_innerrage', 'ability_warrior_defensivestance' ]
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $questClasses = array(
|
|
||||||
-2 => [ 0],
|
|
||||||
0 => [ 1, 3, 4, 8, 9, 10, 11, 12, 25, 28, 33, 36, 38, 40, 41, 44, 45, 46, 47, 51, 85, 130, 132, 139, 154, 267, 1497, 1519, 1537, 2257, 3430, 3431, 3433, 3487, 4080, 4298],
|
|
||||||
1 => [ 14, 15, 16, 17, 141, 148, 188, 215, 220, 331, 357, 361, 363, 400, 405, 406, 440, 490, 493, 618, 1377, 1637, 1638, 1657, 1769, 3524, 3525, 3526, 3557],
|
|
||||||
2 => [ 206, 209, 491, 717, 718, 719, 721, 722, 796, 1176, 1196, 1337, 1477, 1581, 1583, 1584, 1941, 2017, 2057, 2100, 2366, 2367, 2437, 2557, 3535, 3562, 3688, 3713, 3714, 3715, 3716, 3717, 3789, 3790, 3791, 3792, 3842, 3847, 3848, 3849, 3905, 4100, 4131, 4196, 4228, 4264, 4265, 4272, 4277, 4415, 4416, 4494, 4522, 4723, 4809, 4813, 4820],
|
|
||||||
3 => [ 1977, 2159, 2677, 2717, 3428, 3429, 3456, 3457, 3606, 3607, 3805, 3836, 3845, 3923, 3959, 4075, 4273, 4493, 4500, 4603, 4722, 4812, 4987],
|
|
||||||
4 => [ -372, -263, -262, -261, -162, -161, -141, -82, -81, -61],
|
|
||||||
5 => [ -373, -371, -324, -304, -264, -201, -182, -181, -121, -101, -24],
|
|
||||||
6 => [ -25, 2597, 3277, 3358, 3820, 4384, 4710],
|
|
||||||
7 => [-1010, -368, -367, -365, -344, -241, -1],
|
|
||||||
8 => [ 3483, 3518, 3519, 3520, 3521, 3522, 3523, 3679, 3703],
|
|
||||||
9 => [-1005, -1003, -1002, -1001, -376, -375, -374, -370, -369, -366, -364, -41, -22], // 22: seasonal
|
|
||||||
10 => [ 65, 66, 67, 210, 394, 495, 2817, 3537, 3711, 4024, 4197, 4395, 4742]
|
|
||||||
);
|
|
||||||
|
|
||||||
// zoneorsort for quests need updating
|
|
||||||
// partially points non-instanced area with identical name for instance quests
|
|
||||||
public static $questSortFix = array(
|
|
||||||
-221 => 440, // Treasure Map => Tanaris
|
|
||||||
-284 => 0, // Special => Misc (some quests get shuffled into seasonal)
|
|
||||||
151 => 0, // Designer Island => Misc
|
|
||||||
22 => 0, // Programmer Isle
|
|
||||||
35 => 33, // Booty Bay => Stranglethorn Vale
|
|
||||||
131 => 132, // Kharanos => Coldridge Valley
|
|
||||||
24 => 9, // Northshire Abbey => Northshire Valley
|
|
||||||
279 => 36, // Dalaran Crater => Alterac Mountains
|
|
||||||
4342 => 4298, // Acherus: The Ebon Hold => The Scarlet Enclave
|
|
||||||
2079 => 15, // Alcaz Island => Dustwallow Marsh
|
|
||||||
1939 => 440, // Abyssal Sands => Tanaris
|
|
||||||
393 => 363, // Darkspeer Strand => Valley of Trials
|
|
||||||
702 => 141, // Rut'theran Village => Teldrassil
|
|
||||||
221 => 220, // Camp Narache => Red Cloud Mesa
|
|
||||||
1116 => 357, // Feathermoon Stronghold => Feralas
|
|
||||||
236 => 209, // Shadowfang Keep
|
|
||||||
4769 => 4742, // Hrothgar's Landing => Hrothgar's Landing
|
|
||||||
4613 => 4395, // Dalaran City => Dalaran
|
|
||||||
4522 => 210, // Icecrown Citadell => Icecrown
|
|
||||||
3896 => 3703, // Aldor Rise => Shattrath City
|
|
||||||
3696 => 3522, // The Barrier Hills => Blade's Edge Mountains
|
|
||||||
2839 => 2597, // Alterac Valley
|
|
||||||
19 => 1977, // Zul'Gurub
|
|
||||||
4445 => 4273, // Ulduar
|
|
||||||
2300 => 1941, // Caverns of Time
|
|
||||||
3545 => 3535, // Hellfire Citadel
|
|
||||||
2562 => 3457, // Karazhan
|
|
||||||
3840 => 3959, // Black Temple
|
|
||||||
1717 => 491, // Razorfen Kraul
|
|
||||||
978 => 1176, // Zul'Farrak
|
|
||||||
133 => 721, // Gnomeregan
|
|
||||||
3607 => 3905, // Serpentshrine Cavern
|
|
||||||
3845 => 3842, // Tempest Keep
|
|
||||||
1517 => 1337, // Uldaman
|
|
||||||
1417 => 1477 // Sunken Temple
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $questSubCats = array(
|
|
||||||
1 => [132], // Dun Morogh: Coldridge Valley
|
|
||||||
12 => [9], // Elwynn Forest: Northshire Valley
|
|
||||||
141 => [188], // Teldrassil: Shadowglen
|
|
||||||
3524 => [3526], // Azuremyst Isle: Ammen Vale
|
|
||||||
|
|
||||||
14 => [363], // Durotar: Valley of Trials
|
|
||||||
85 => [154], // Tirisfal Glades: Deathknell
|
|
||||||
215 => [220], // Mulgore: Red Cloud Mesa
|
|
||||||
3430 => [3431], // Eversong Woods: Sunstrider Isle
|
|
||||||
|
|
||||||
46 => [25], // Burning Steppes: Blackrock Mountain
|
|
||||||
361 => [1769], // Felwood: Timbermaw Hold
|
|
||||||
3519 => [3679], // Terokkar: Skettis
|
|
||||||
3535 => [3562, 3713, 3714], // Hellfire Citadel
|
|
||||||
3905 => [3715, 3716, 3717], // Coilfang Reservoir
|
|
||||||
3688 => [3789, 3790, 3792], // Auchindoun
|
|
||||||
1941 => [2366, 2367, 4100], // Caverns of Time
|
|
||||||
3842 => [3847, 3848, 3849], // Tempest Keep
|
|
||||||
4522 => [4809, 4813, 4820] // Icecrown Citadel
|
|
||||||
);
|
|
||||||
|
|
||||||
/* why:
|
|
||||||
Because petSkills (and ranged weapon skills) are the only ones with more than two skillLines attached. Because Left Joining ?_spell with ?_skillLineability causes more trouble than it has uses.
|
|
||||||
Because this is more or less the only reaonable way to fit all that information into one database field, so..
|
|
||||||
.. the indizes of this array are bits of skillLine2OrMask in ?_spell if skillLineId1 is negative
|
|
||||||
*/
|
|
||||||
public static $skillLineMask = array( // idx => [familyId, skillLineId]
|
|
||||||
-1 => array( // Pets (Hunter)
|
|
||||||
[ 1, 208], [ 2, 209], [ 3, 203], [ 4, 210], [ 5, 211], [ 6, 212], [ 7, 213], // Wolf, Cat, Spider, Bear, Boar, Crocolisk, Carrion Bird
|
|
||||||
[ 8, 214], [ 9, 215], [11, 217], [12, 218], [20, 236], [21, 251], [24, 653], // Crab, Gorilla, Raptor, Tallstrider, Scorpid, Turtle, Bat
|
|
||||||
[25, 654], [26, 655], [27, 656], [30, 763], [31, 767], [32, 766], [33, 765], // Hyena, Bird of Prey, Wind Serpent, Dragonhawk, Ravager, Warp Stalker, Sporebat
|
|
||||||
[34, 764], [35, 768], [37, 775], [38, 780], [39, 781], [41, 783], [42, 784], // Nether Ray, Serpent, Moth, Chimaera, Devilsaur, Silithid, Worm
|
|
||||||
[43, 786], [44, 785], [45, 787], [46, 788] // Rhino, Wasp, Core Hound, Spirit Beast
|
|
||||||
),
|
|
||||||
-2 => array( // Pets (Warlock)
|
|
||||||
[15, 189], [16, 204], [17, 205], [19, 207], [23, 188], [29, 761] // Felhunter, Voidwalker, Succubus, Doomguard, Imp, Felguard
|
|
||||||
),
|
|
||||||
-3 => array( // Ranged Weapons
|
|
||||||
[null, 45], [null, 46], [null, 226] // Bow, Gun, Crossbow
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $sockets = array( // jsStyle Strings
|
|
||||||
'meta', 'red', 'yellow', 'blue'
|
|
||||||
);
|
|
||||||
|
|
||||||
public static function getReputationLevelForPoints($pts)
|
|
||||||
{
|
|
||||||
if ($pts >= 41999)
|
|
||||||
return REP_EXALTED;
|
|
||||||
else if ($pts >= 20999)
|
|
||||||
return REP_REVERED;
|
|
||||||
else if ($pts >= 8999)
|
|
||||||
return REP_HONORED;
|
|
||||||
else if ($pts >= 2999)
|
|
||||||
return REP_FRIENDLY;
|
|
||||||
else if ($pts >= 0)
|
|
||||||
return REP_NEUTRAL;
|
|
||||||
else if ($pts >= -3000)
|
|
||||||
return REP_UNFRIENDLY;
|
|
||||||
else if ($pts >= -6000)
|
|
||||||
return REP_HOSTILE;
|
|
||||||
else
|
|
||||||
return REP_HATED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getTaughtSpells(&$spell)
|
|
||||||
{
|
|
||||||
$extraIds = [-1]; // init with -1 to prevent empty-array errors
|
|
||||||
$lookup = [-1];
|
|
||||||
switch (gettype($spell))
|
|
||||||
{
|
|
||||||
case 'object':
|
|
||||||
if (get_class($spell) != __NAMESPACE__.'\SpellList')
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$lookup[] = $spell->id;
|
|
||||||
foreach ($spell->canTeachSpell() as $idx)
|
|
||||||
$extraIds[] = $spell->getField('effect'.$idx.'TriggerSpell');
|
|
||||||
|
|
||||||
break;
|
|
||||||
case 'integer':
|
|
||||||
$lookup[] = $spell;
|
|
||||||
break;
|
|
||||||
case 'array':
|
|
||||||
$lookup = $spell;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: omits required spell and chance in skill_discovery_template
|
|
||||||
$data = array_merge(
|
|
||||||
DB::World()->selectCol('SELECT spellId FROM spell_learn_spell WHERE entry IN (?a)', $lookup),
|
|
||||||
DB::World()->selectCol('SELECT spellId FROM skill_discovery_template WHERE reqSpell IN (?a)', $lookup),
|
|
||||||
$extraIds
|
|
||||||
);
|
|
||||||
|
|
||||||
// return list of integers, not strings
|
|
||||||
$data = array_map('intVal', $data);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getPageText($ptId)
|
|
||||||
{
|
|
||||||
$pages = [];
|
|
||||||
while ($ptId)
|
|
||||||
{
|
|
||||||
if ($row = DB::World()->selectRow('SELECT ptl.Text AS Text_loc?d, pt.* FROM page_text pt LEFT JOIN page_text_locale ptl ON pt.ID = ptl.ID AND locale = ? WHERE pt.ID = ?d', Lang::getLocale()->value, Lang::getLocale()->json(), $ptId))
|
|
||||||
{
|
|
||||||
$ptId = $row['NextPageID'];
|
|
||||||
$pages[] = Util::parseHtmlText(Util::localizedString($row, 'Text'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('Referenced PageTextId #'.$ptId.' is not in DB', E_USER_WARNING);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $pages;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getQuotesForCreature(int $creatureId, bool $asHTML = false, string $talkSource = '') : array
|
|
||||||
{
|
|
||||||
$nQuotes = 0;
|
|
||||||
$quotes = [];
|
|
||||||
$soundIds = [];
|
|
||||||
|
|
||||||
$quoteSrc = DB::World()->select(
|
|
||||||
'SELECT ct.`GroupID` AS ARRAY_KEY, ct.`ID` AS ARRAY_KEY2, ct.`Type` AS "talkType", ct.TextRange AS "range",
|
|
||||||
IFNULL(bct.`LanguageID`, ct.`Language`) AS "lang",
|
|
||||||
IFNULL(NULLIF(bct.`Text`, ""), IFNULL(NULLIF(bct.`Text1`, ""), IFNULL(ct.`Text`, ""))) AS "text_loc0",
|
|
||||||
{ IFNULL(NULLIF(bctl.`Text`, ""), IFNULL(NULLIF(bctl.`Text1`, ""), IFNULL(ctl.`Text`, ""))) AS text_loc?d, }
|
|
||||||
IF(bct.`SoundEntriesID` > 0, bct.`SoundEntriesID`, ct.`Sound`) AS "soundId"
|
|
||||||
FROM creature_text ct
|
|
||||||
{ LEFT JOIN creature_text_locale ctl ON ct.`CreatureID` = ctl.`CreatureID` AND ct.`GroupID` = ctl.`GroupID` AND ct.`ID` = ctl.`ID` AND ctl.`Locale` = ? }
|
|
||||||
LEFT JOIN broadcast_text bct ON ct.`BroadcastTextId` = bct.`ID`
|
|
||||||
{ LEFT JOIN broadcast_text_locale bctl ON ct.`BroadcastTextId` = bctl.`ID` AND bctl.`locale` = ? }
|
|
||||||
WHERE ct.`CreatureID` = ?d',
|
|
||||||
Lang::getLocale()->value ?: DBSIMPLE_SKIP,
|
|
||||||
Lang::getLocale()->value ? Lang::getLocale()->json() : DBSIMPLE_SKIP,
|
|
||||||
Lang::getLocale()->value ? Lang::getLocale()->json() : DBSIMPLE_SKIP,
|
|
||||||
$creatureId
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($quoteSrc as $grp => $text)
|
|
||||||
{
|
|
||||||
$group = [];
|
|
||||||
foreach ($text as $t)
|
|
||||||
{
|
|
||||||
if ($t['soundId'])
|
|
||||||
$soundIds[] = $t['soundId'];
|
|
||||||
|
|
||||||
$msg = Util::localizedString($t, 'text');
|
|
||||||
if (!$msg)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// fixup .. either set %s for emotes or dont >.<
|
|
||||||
if (in_array($t['talkType'], [2, 16]) && strpos($msg, '%s') === false)
|
|
||||||
$msg = '%s '.$msg;
|
|
||||||
|
|
||||||
// fixup: bad case-insensitivity
|
|
||||||
$msg = Util::parseHtmlText(str_replace('%S', '%s', htmlentities($msg)), !$asHTML);
|
|
||||||
|
|
||||||
if ($talkSource)
|
|
||||||
$msg = sprintf($msg, $talkSource);
|
|
||||||
|
|
||||||
// make type css compatible
|
|
||||||
switch ($t['talkType'])
|
|
||||||
{
|
|
||||||
case 1: // yell:
|
|
||||||
case 14: $t['talkType'] = 1; break; // - dark red
|
|
||||||
case 2: // emote:
|
|
||||||
case 16: // "
|
|
||||||
case 3: // boss emote:
|
|
||||||
case 41: $t['talkType'] = 4; break; // - orange
|
|
||||||
case 4: // whisper:
|
|
||||||
case 15: // "
|
|
||||||
case 5: // boss whisper:
|
|
||||||
case 42: $t['talkType'] = 3; break; // - pink-ish
|
|
||||||
default: $t['talkType'] = 2; // [type: 0, 12] say: yellow-ish
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// prefix
|
|
||||||
$pre = '';
|
|
||||||
if ($t['talkType'] != 4)
|
|
||||||
$pre = ($talkSource ?: '%s').' '.Lang::npc('textTypes', $t['talkType']).Lang::main('colon').($t['lang'] ? '['.Lang::game('languages', $t['lang']).'] ' : null);
|
|
||||||
|
|
||||||
if ($asHTML)
|
|
||||||
$msg = '<div><span class="s'.$t['talkType'].'">%s'.($t['range'] ? sprintf(Util::$dfnString, Lang::npc('textRanges', $t['range']), $msg) : $msg).'</span></div>';
|
|
||||||
else
|
|
||||||
$msg = '[div][span class=s'.$t['talkType'].']%s'.html_entity_decode($msg).'[/span][/div]';
|
|
||||||
|
|
||||||
$line = array(
|
|
||||||
'range' => $t['range'],
|
|
||||||
'text' => $msg,
|
|
||||||
'prefix' => $pre
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
$nQuotes++;
|
|
||||||
$group[] = $line;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($group)
|
|
||||||
$quotes[$grp] = $group;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$quotes, $nQuotes, $soundIds];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getBreakpointsForSkill(int $skillId, int $reqLevel) : array
|
|
||||||
{
|
|
||||||
if ($skillId == SKILL_FISHING)
|
|
||||||
return array(
|
|
||||||
round(sqrt(.25) * $reqLevel), // 25% valid catches
|
|
||||||
round(sqrt(.50) * $reqLevel), // 50% valid catches
|
|
||||||
round(sqrt(.75) * $reqLevel), // 75% valid catches
|
|
||||||
$reqLevel // 100% valid catches
|
|
||||||
);
|
|
||||||
|
|
||||||
switch ($skillId)
|
|
||||||
{
|
|
||||||
case SKILL_SKINNING:
|
|
||||||
$reqLevel /= 5; // we pass creature level * 5 (so, skill value), but formula depends on actual creature level
|
|
||||||
if ($reqLevel < 10)
|
|
||||||
$reqLevel = 0;
|
|
||||||
else if ($reqLevel < 20)
|
|
||||||
$reqLevel = ($reqLevel - 10) * 10;
|
|
||||||
else
|
|
||||||
$reqLevel *= 5;
|
|
||||||
case SKILL_HERBALISM:
|
|
||||||
case SKILL_LOCKPICKING:
|
|
||||||
case SKILL_JEWELCRAFTING:
|
|
||||||
case SKILL_INSCRIPTION:
|
|
||||||
case SKILL_MINING:
|
|
||||||
case SKILL_ENGINEERING:
|
|
||||||
$points = [$reqLevel]; // red/orange
|
|
||||||
|
|
||||||
if ($reqLevel + 25 <= MAX_SKILL) // orange/yellow
|
|
||||||
$points[] = $reqLevel + 25;
|
|
||||||
|
|
||||||
if ($reqLevel + 50 <= MAX_SKILL) // yellow/green
|
|
||||||
$points[] = $reqLevel + 50;
|
|
||||||
|
|
||||||
if ($reqLevel + 100 <= MAX_SKILL) // green/grey
|
|
||||||
$points[] = $reqLevel + 100;
|
|
||||||
|
|
||||||
return $points;
|
|
||||||
default:
|
|
||||||
return [$reqLevel];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
abstract class WorldPosition
|
|
||||||
{
|
|
||||||
private static array $alphaMapCache = [];
|
|
||||||
private static array $capitalCities = array( // capitals take precedence over their surrounding area
|
|
||||||
1497, 1637, 1638, 3487, // Undercity, Ogrimmar, Thunder Bluff, Silvermoon City
|
|
||||||
1519, 1537, 1657, 3557, // Stormwind City, Ironforge, Darnassus, The Exodar
|
|
||||||
3703, 4395 // Shattrath City, Dalaran
|
|
||||||
);
|
|
||||||
|
|
||||||
private static function alphaMapCheck(int $areaId, array &$set) : bool
|
|
||||||
{
|
|
||||||
$file = 'cache/alphaMaps/'.$areaId.'.png';
|
|
||||||
if (!file_exists($file)) // file does not exist (probably instanced area)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// invalid and corner cases (literally)
|
|
||||||
if (empty($set['posX']) || empty($set['posY']) || $set['posX'] >= 100 || $set['posY'] >= 100)
|
|
||||||
{
|
|
||||||
$set = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty(self::$alphaMapCache[$areaId]))
|
|
||||||
self::$alphaMapCache[$areaId] = imagecreatefrompng($file);
|
|
||||||
|
|
||||||
// alphaMaps are 1000 x 1000, adapt points [black => valid point]
|
|
||||||
if (!imagecolorat(self::$alphaMapCache[$areaId], $set['posX'] * 10, $set['posY'] * 10))
|
|
||||||
$set = null;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function checkZonePos(array $points) : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($points as $res)
|
|
||||||
{
|
|
||||||
if (self::alphaMapCheck($res['areaId'], $res))
|
|
||||||
{
|
|
||||||
if (!$res)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// some rough measure how central the spawn is on the map (the lower the number, the better)
|
|
||||||
// 0: perfect center; 1: touches a border
|
|
||||||
$q = abs( (($res['posX'] - 50) / 50) * (($res['posY'] - 50) / 50) );
|
|
||||||
|
|
||||||
if (empty($result) || $result[0] > $q)
|
|
||||||
$result = [$q, $res];
|
|
||||||
}
|
|
||||||
// capitals (auto-discovered) and no hand-made alphaMap available
|
|
||||||
else if (in_array($res['areaId'], self::$capitalCities))
|
|
||||||
return $res;
|
|
||||||
// add with lowest quality if alpha map is missing
|
|
||||||
else if (empty($result))
|
|
||||||
$result = [1.0, $res];
|
|
||||||
}
|
|
||||||
|
|
||||||
// spawn does not really match on a map, but we need at least one result
|
|
||||||
if (!$result)
|
|
||||||
{
|
|
||||||
usort($points, function ($a, $b) { return ($a['dist'] < $b['dist']) ? -1 : 1; });
|
|
||||||
$result = [1.0, $points[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getForGUID(int $type, int ...$guids) : array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
switch ($type)
|
|
||||||
{
|
|
||||||
case Type::NPC:
|
|
||||||
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_x` AS `posX`, `position_y` AS `posY` FROM creature WHERE `guid` IN (?a)', $guids);
|
|
||||||
break;
|
|
||||||
case Type::OBJECT:
|
|
||||||
$result = DB::World()->select('SELECT `guid` AS ARRAY_KEY, `id`, `map` AS `mapId`, `position_x` AS `posX`, `position_y` AS `posY` FROM gameobject WHERE `guid` IN (?a)', $guids);
|
|
||||||
break;
|
|
||||||
case Type::SOUND:
|
|
||||||
$result = DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `soundId` AS `id`, `mapId`, `posX`, `posY` FROM ?_soundemitters WHERE `id` IN (?a)', $guids);
|
|
||||||
break;
|
|
||||||
case Type::ZONE:
|
|
||||||
$result = DB::Aowow()->select('SELECT -`id` AS ARRAY_KEY, `id`, `parentMapId` AS `mapId`, `parentX` AS `posX`, `parentY` AS `posY` FROM ?_zones WHERE -`id` IN (?a)', $guids);
|
|
||||||
break;
|
|
||||||
case Type::AREATRIGGER:
|
|
||||||
$result = [];
|
|
||||||
if ($base = array_filter($guids, fn($x) => $x > 0))
|
|
||||||
$result = array_replace($result, DB::AoWoW()->select('SELECT `id` AS ARRAY_KEY, `id`, `mapId`, `posX`, `posY` FROM ?_areatrigger WHERE `id` IN (?a)', $base));
|
|
||||||
if ($endpoints = array_filter($guids, fn($x) => $x < 0))
|
|
||||||
$result = array_replace($result, DB::World()->select(
|
|
||||||
'SELECT -`ID` AS ARRAY_KEY, ID AS `id`, `target_map` AS `mapId`, `target_position_x` AS `posX`, `target_position_y` AS `posY` FROM areatrigger_teleport WHERE -`id` IN (?a) UNION
|
|
||||||
SELECT -`entryorguid` AS ARRAY_KEY, entryorguid AS `id`, `action_param1` AS `mapId`, `target_x` AS `posX`, `target_y` AS `posY` FROM smart_scripts WHERE -`entryorguid` IN (?a) AND `source_type` = ?d AND `action_type` = ?d',
|
|
||||||
$endpoints, $endpoints, SmartAI::SRC_TYPE_AREATRIGGER, SmartAction::ACTION_TELEPORT
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
trigger_error('WorldPosition::getForGUID - unsupported TYPE #'.$type, E_USER_WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($diff = array_diff($guids, array_keys($result)))
|
|
||||||
trigger_error('WorldPosition::getForGUID - no spawn points for TYPE #'.$type.' GUIDS: '.implode(', ', $diff), E_USER_WARNING);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function toZonePos(int $mapId, float $mapX, float $mapY, int $preferedAreaId = 0, int $preferedFloor = -1) : array
|
|
||||||
{
|
|
||||||
if (!$mapId < 0)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$query =
|
|
||||||
'SELECT
|
|
||||||
x.`id`,
|
|
||||||
x.`areaId`,
|
|
||||||
IF(x.`defaultDungeonMapId` < 0, x.`floor` + 1, x.`floor`) AS `floor`,
|
|
||||||
IF(dm.`id` IS NOT NULL OR x.`defaultDungeonMapId` < 0, 1, 0) AS `multifloor`,
|
|
||||||
ROUND((x.`maxY` - ?d) * 100 / (x.`maxY` - x.`minY`), 1) AS `posX`,
|
|
||||||
ROUND((x.`maxX` - ?d) * 100 / (x.`maxX` - x.`minX`), 1) AS `posY`,
|
|
||||||
SQRT(POWER(ABS((x.`maxY` - ?d) * 100 / (x.`maxY` - x.`minY`) - 50), 2) +
|
|
||||||
POWER(ABS((x.`maxX` - ?d) * 100 / (x.`maxX` - x.`minX`) - 50), 2)) AS `dist`
|
|
||||||
FROM
|
|
||||||
(SELECT 0 AS `id`, `areaId`, `mapId`, `right` AS `minY`, `left` AS `maxY`, `top` AS `maxX`, `bottom` AS `minX`, 0 AS `floor`, 0 AS `worldMapAreaId`, `defaultDungeonMapId` FROM ?_worldmaparea wma UNION
|
|
||||||
SELECT dm.`id`, `areaId`, wma.`mapId`, `minY`, `maxY`, `maxX`, `minX`, `floor`, `worldMapAreaId`, `defaultDungeonMapId` FROM ?_worldmaparea wma
|
|
||||||
JOIN ?_dungeonmap dm ON dm.`mapId` = wma.`mapId` WHERE wma.`mapId` NOT IN (0, 1, 530, 571) OR wma.`areaId` = 4395) x
|
|
||||||
LEFT JOIN
|
|
||||||
?_dungeonmap dm ON dm.`mapId` = x.`mapId` AND dm.`worldMapAreaId` = x.`worldMapAreaId` AND dm.`floor` <> x.`floor` AND dm.`worldMapAreaId` > 0
|
|
||||||
WHERE
|
|
||||||
x.`mapId` = ?d AND IF(?d, x.`areaId` = ?d, x.`areaId` <> 0){ AND x.`floor` = ?d - IF(x.`defaultDungeonMapId` < 0, 1, 0)}
|
|
||||||
GROUP BY
|
|
||||||
x.`id`, x.`areaId`
|
|
||||||
HAVING
|
|
||||||
(`posX` BETWEEN 0.1 AND 99.9 AND `posY` BETWEEN 0.1 AND 99.9)
|
|
||||||
ORDER BY
|
|
||||||
`multifloor` DESC, `dist` ASC';
|
|
||||||
|
|
||||||
// dist BETWEEN 0 (center) AND 70.7 (corner)
|
|
||||||
$points = DB::Aowow()->select($query, $mapY, $mapX, $mapY, $mapX, $mapId, $preferedAreaId, $preferedAreaId, $preferedFloor < 0 ? DBSIMPLE_SKIP : $preferedFloor);
|
|
||||||
if (!$points) // retry: pre-instance subareas belong to the instance-maps but are displayed on the outside. There also cases where the zone reaches outside it's own map.
|
|
||||||
$points = DB::Aowow()->select($query, $mapY, $mapX, $mapY, $mapX, $mapId, 0, 0, DBSIMPLE_SKIP);
|
|
||||||
if (!is_array($points))
|
|
||||||
{
|
|
||||||
trigger_error('WorldPosition::toZonePos - query failed', E_USER_ERROR);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $points;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,243 +1,54 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
if (!defined('AOWOW_REVISION'))
|
||||||
|
die('illegal access');
|
||||||
mb_internal_encoding('UTF-8');
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
mysqli_report(MYSQLI_REPORT_ERROR);
|
|
||||||
|
|
||||||
define('AOWOW_REVISION', 40);
|
|
||||||
define('OS_WIN', substr(PHP_OS, 0, 3) == 'WIN'); // OS_WIN as per compile info of php
|
|
||||||
define('CLI', PHP_SAPI === 'cli');
|
|
||||||
define('CLI_HAS_E', CLI && // WIN10 and later usually support ANSI escape sequences
|
|
||||||
(!OS_WIN || (function_exists('sapi_windows_vt100_support') && sapi_windows_vt100_support(STDOUT))));
|
|
||||||
|
|
||||||
|
|
||||||
$reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring', 'fileinfo'/*, 'gmp'*/];
|
|
||||||
$badExt = [];
|
|
||||||
$error = '';
|
|
||||||
if ($ext = array_filter($reqExt, fn($x) => !extension_loaded($x)))
|
|
||||||
$error .= 'Required Extension <b>'.implode(', ', $ext)."</b> was not found. Please check if it should exist, using \"<i>php -m</i>\"\n\n";
|
|
||||||
|
|
||||||
if ($ext = array_filter($badExt, fn($x) => extension_loaded($x)))
|
|
||||||
$error .= 'Loaded Extension <b>'.implode(', ', $ext)."</b> is incompatible and must be disabled.\n\n";
|
|
||||||
|
|
||||||
if (version_compare(PHP_VERSION, '8.2.0') < 0)
|
|
||||||
$error .= 'PHP Version <b>8.2</b> or higher required! Your version is <b>'.PHP_VERSION."</b>.\nCore functions are unavailable!\n";
|
|
||||||
|
|
||||||
if ($error)
|
|
||||||
die(CLI ? strip_tags($error) : $error);
|
|
||||||
|
|
||||||
|
|
||||||
require_once 'includes/defines.php';
|
|
||||||
require_once 'includes/locale.class.php';
|
|
||||||
require_once 'localization/lang.class.php';
|
|
||||||
require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master)
|
|
||||||
require_once 'includes/database.class.php'; // wrap DBSimple
|
|
||||||
require_once 'includes/utilities.php'; // helper functions
|
|
||||||
require_once 'includes/type.class.php'; // DB types storage and factory
|
|
||||||
require_once 'includes/cfg.class.php'; // Config holder
|
|
||||||
require_once 'includes/user.class.php'; // Session handling (could be skipped for CLI context except for username and password validation used in account creation)
|
|
||||||
require_once 'includes/game/misc.php'; // Misc game related data & functions
|
|
||||||
|
|
||||||
// game client data interfaces
|
|
||||||
spl_autoload_register(function (string $class) : void
|
|
||||||
{
|
|
||||||
if ($i = strrpos($class, '\\'))
|
|
||||||
$class = substr($class, $i + 1);
|
|
||||||
|
|
||||||
if (preg_match('/[^\w]/i', $class))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ($class == 'Stat' || $class == 'StatsContainer') // entity statistics conversion
|
|
||||||
require_once 'includes/game/chrstatistics.php';
|
|
||||||
else if (file_exists('includes/game/'.strtolower($class).'.class.php'))
|
|
||||||
require_once 'includes/game/'.strtolower($class).'.class.php';
|
|
||||||
});
|
|
||||||
|
|
||||||
// our site components
|
|
||||||
spl_autoload_register(function (string $class) : void
|
|
||||||
{
|
|
||||||
if ($i = strrpos($class, '\\'))
|
|
||||||
$class = substr($class, $i + 1);
|
|
||||||
|
|
||||||
if (preg_match('/[^\w]/i', $class))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (file_exists('includes/components/'.strtolower($class).'.class.php'))
|
|
||||||
require_once 'includes/components/'.strtolower($class).'.class.php';
|
|
||||||
else if (file_exists('includes/components/frontend/'.strtolower($class).'.class.php'))
|
|
||||||
require_once 'includes/components/frontend/'.strtolower($class).'.class.php';
|
|
||||||
});
|
|
||||||
|
|
||||||
// TC systems in components
|
|
||||||
spl_autoload_register(function (string $class) : void
|
|
||||||
{
|
|
||||||
switch ($class)
|
|
||||||
{
|
|
||||||
case __NAMESPACE__.'\SmartAI':
|
|
||||||
case __NAMESPACE__.'\SmartEvent':
|
|
||||||
case __NAMESPACE__.'\SmartAction':
|
|
||||||
case __NAMESPACE__.'\SmartTarget':
|
|
||||||
require_once 'includes/components/SmartAI/SmartAI.class.php';
|
|
||||||
require_once 'includes/components/SmartAI/SmartEvent.class.php';
|
|
||||||
require_once 'includes/components/SmartAI/SmartAction.class.php';
|
|
||||||
require_once 'includes/components/SmartAI/SmartTarget.class.php';
|
|
||||||
break;
|
|
||||||
case __NAMESPACE__.'\Conditions':
|
|
||||||
require_once 'includes/components/Conditions/Conditions.class.php';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// autoload List-classes, associated filters
|
|
||||||
spl_autoload_register(function (string $class) : void
|
|
||||||
{
|
|
||||||
if ($i = strrpos($class, '\\'))
|
|
||||||
$class = substr($class, $i + 1);
|
|
||||||
|
|
||||||
if (preg_match('/[^\w]/i', $class))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!stripos($class, 'list'))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$class = strtolower(str_replace('ListFilter', 'List', $class));
|
|
||||||
|
|
||||||
$cl = match ($class)
|
|
||||||
{
|
|
||||||
'localprofilelist',
|
|
||||||
'remoteprofilelist' => 'profile',
|
|
||||||
'localarenateamlist',
|
|
||||||
'remotearenateamlist' => 'arenateam',
|
|
||||||
'localguildlist',
|
|
||||||
'remoteguildlist' => 'guild',
|
|
||||||
default => strtr($class, ['list' => ''])
|
|
||||||
};
|
|
||||||
|
|
||||||
if (file_exists('includes/types/'.$cl.'.class.php'))
|
|
||||||
{
|
|
||||||
require_once 'includes/types/basetype.class.php';
|
|
||||||
require_once 'includes/types/'.$cl.'.class.php';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new \Exception('could not register type class: '.$cl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// endpoint loader
|
|
||||||
spl_autoload_register(function (string $class) : void
|
|
||||||
{
|
|
||||||
if ($i = strrpos($class, '\\'))
|
|
||||||
$class = substr($class, $i + 1);
|
|
||||||
|
|
||||||
if (preg_match('/[^\w]/i', $class))
|
|
||||||
return;
|
|
||||||
|
|
||||||
$class = strtolower($class);
|
|
||||||
|
|
||||||
if (stripos($class, 'ajax') === 0) // handles ajax and jsonp requests
|
|
||||||
{
|
|
||||||
if (file_exists('includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php'))
|
|
||||||
{
|
|
||||||
require_once 'includes/ajaxHandler/ajaxHandler.class.php';
|
|
||||||
require_once 'includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw new \Exception('could not register ajaxHandler class: '.$class);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (stripos($class, 'page')) // handles templated pages
|
|
||||||
{
|
|
||||||
if (file_exists('pages/'.strtr($class, ['page' => '']).'.php'))
|
|
||||||
{
|
|
||||||
require_once 'pages/genericPage.class.php';
|
|
||||||
require_once 'pages/'.strtr($class, ['page' => '']).'.php';
|
|
||||||
}
|
|
||||||
else if ($class == 'genericpage') // may be called directly in fatal error case
|
|
||||||
require_once 'pages/genericPage.class.php';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
set_error_handler(function(int $errNo, string $errStr, string $errFile, int $errLine) : bool
|
|
||||||
{
|
|
||||||
// either from test function or handled separately
|
|
||||||
if (strstr($errStr, 'mysqli_connect') && $errNo == E_WARNING)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// we do not log deprecation notices
|
|
||||||
if ($errNo & (E_DEPRECATED | E_USER_DEPRECATED))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
$logLevel = match($errNo)
|
|
||||||
{
|
|
||||||
E_RECOVERABLE_ERROR, E_USER_ERROR => LOG_LEVEL_ERROR,
|
|
||||||
E_WARNING, E_USER_WARNING => LOG_LEVEL_WARN,
|
|
||||||
E_NOTICE, E_USER_NOTICE => LOG_LEVEL_INFO,
|
|
||||||
default => 0
|
|
||||||
};
|
|
||||||
$errName = match($errNo)
|
|
||||||
{
|
|
||||||
E_RECOVERABLE_ERROR => 'RECOVERABLE_ERROR',
|
|
||||||
E_USER_ERROR => 'USER_ERROR',
|
|
||||||
E_USER_WARNING, E_WARNING => 'WARNING',
|
|
||||||
E_USER_NOTICE, E_NOTICE => 'NOTICE',
|
|
||||||
default => 'UNKNOWN_ERROR' // errors not in this list can not be handled by set_error_handler (as per documentation) or are ignored
|
|
||||||
};
|
|
||||||
|
|
||||||
if (DB::isConnected(DB_AOWOW))
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `post`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
|
|
||||||
AOWOW_REVISION, $errNo, $errFile, $errLine, CLI ? 'CLI' : substr($_SERVER['QUERY_STRING'] ?? '', 0, 250), empty($_POST) ? '' : http_build_query($_POST), User::$groups, $errStr
|
|
||||||
);
|
|
||||||
|
|
||||||
if (CLI)
|
|
||||||
CLI::write($errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine, $logLevel);
|
|
||||||
else if (Cfg::get('DEBUG') >= $logLevel)
|
|
||||||
Util::addNote($errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine, U_GROUP_EMPLOYEE, $logLevel);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}, E_ALL);
|
|
||||||
|
|
||||||
// handle exceptions
|
|
||||||
set_exception_handler(function (\Throwable $e) : void
|
|
||||||
{
|
|
||||||
if (DB::isConnected(DB_AOWOW))
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `post`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
|
|
||||||
AOWOW_REVISION, $e->getCode(), $e->getFile(), $e->getLine(), CLI ? 'CLI' : substr($_SERVER['QUERY_STRING'] ?? '', 0, 250), empty($_POST) ? '' : http_build_query($_POST), User::$groups, $e->getMessage()
|
|
||||||
);
|
|
||||||
|
|
||||||
if (CLI)
|
|
||||||
fwrite(STDERR, "\nException - ".$e->getMessage()."\n ".$e->getFile(). '('.$e->getLine().")\n".$e->getTraceAsString()."\n\n");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Util::addNote('Exception - '.$e->getMessage().' @ '.$e->getFile(). ':'.$e->getLine()."\n".$e->getTraceAsString(), U_GROUP_EMPLOYEE, LOG_LEVEL_ERROR);
|
|
||||||
(new GenericPage())->error();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// handle fatal errors
|
|
||||||
register_shutdown_function(function() : void
|
|
||||||
{
|
|
||||||
if ($e = error_get_last())
|
|
||||||
{
|
|
||||||
if (DB::isConnected(DB_AOWOW))
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `post`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
|
|
||||||
AOWOW_REVISION, $e['type'], $e['file'], $e['line'], CLI ? 'CLI' : substr($_SERVER['QUERY_STRING'] ?? '', 0, 250), empty($_POST) ? '' : http_build_query($_POST), User::$groups, $e['message']
|
|
||||||
);
|
|
||||||
|
|
||||||
if (CLI)
|
|
||||||
fwrite(STDERR, "\nFatal Error - ".$e['message'].' @ '.$e['file']. ':'.$e['line']."\n\n");
|
|
||||||
else if (User::isInGroup(U_GROUP_EMPLOYEE))
|
|
||||||
echo "\nFatal Error - ".$e['message'].' @ '.$e['file']. ':'.$e['line']."\n\n";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setup DB-Wrapper
|
|
||||||
if (file_exists('config/config.php'))
|
if (file_exists('config/config.php'))
|
||||||
require_once 'config/config.php';
|
require_once 'config/config.php';
|
||||||
else
|
else
|
||||||
$AoWoWconf = [];
|
$AoWoWconf = [];
|
||||||
|
|
||||||
|
require_once 'includes/defines.php';
|
||||||
|
require_once 'includes/libs/DbSimple/Generic.php'; // Libraray: http://en.dklab.ru/lib/DbSimple (using variant: https://github.com/ivan1986/DbSimple/tree/master)
|
||||||
|
require_once 'includes/utilities.php'; // misc™ data 'n func
|
||||||
|
require_once 'includes/ajaxHandler.class.php'; // handles ajax and jsonp requests
|
||||||
|
require_once 'includes/user.class.php';
|
||||||
|
require_once 'includes/markup.class.php'; // manipulate markup text
|
||||||
|
require_once 'includes/database.class.php'; // wrap DBSimple
|
||||||
|
require_once 'includes/community.class.php'; // handle comments, screenshots and videos
|
||||||
|
require_once 'includes/loot.class.php'; // build lv-tabs containing loot-information
|
||||||
|
require_once 'localization/lang.class.php';
|
||||||
|
require_once 'pages/genericPage.class.php';
|
||||||
|
|
||||||
|
|
||||||
|
// autoload List-classes, associated filters and pages
|
||||||
|
spl_autoload_register(function ($class) {
|
||||||
|
$class = strtolower(str_replace('Filter', '', $class));
|
||||||
|
|
||||||
|
if (class_exists($class)) // already registered
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (preg_match('/[^\w]/i', $class)) // name should contain only letters
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (strpos($class, 'list'))
|
||||||
|
{
|
||||||
|
if (!class_exists('BaseType'))
|
||||||
|
require_once 'includes/types/basetype.class.php';
|
||||||
|
|
||||||
|
if (file_exists('includes/types/'.strtr($class, ['list' => '']).'.class.php'))
|
||||||
|
require_once 'includes/types/'.strtr($class, ['list' => '']).'.class.php';
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists('pages/'.strtr($class, ['page' => '']).'.php'))
|
||||||
|
require_once 'pages/'.strtr($class, ['page' => '']).'.php';
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Setup DB-Wrapper
|
||||||
if (!empty($AoWoWconf['aowow']['db']))
|
if (!empty($AoWoWconf['aowow']['db']))
|
||||||
DB::load(DB_AOWOW, $AoWoWconf['aowow']);
|
DB::load(DB_AOWOW, $AoWoWconf['aowow']);
|
||||||
|
|
||||||
@@ -252,63 +63,133 @@ if (!empty($AoWoWconf['characters']))
|
|||||||
if (!empty($charDBInfo))
|
if (!empty($charDBInfo))
|
||||||
DB::load(DB_CHARACTERS . $realm, $charDBInfo);
|
DB::load(DB_CHARACTERS . $realm, $charDBInfo);
|
||||||
|
|
||||||
$AoWoWconf = null; // empty auths
|
|
||||||
|
// load config to constants
|
||||||
|
$sets = DB::isConnectable(DB_AOWOW) ? DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value`, `flags` FROM ?_config') : [];
|
||||||
|
foreach ($sets as $k => $v)
|
||||||
|
{
|
||||||
|
// this should not have been possible
|
||||||
|
if (!strlen($v['value']))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$php = $v['flags'] & CON_FLAG_PHP;
|
||||||
|
|
||||||
|
if ($v['flags'] & CON_FLAG_TYPE_INT)
|
||||||
|
$val = intVal($v['value']);
|
||||||
|
else if ($v['flags'] & CON_FLAG_TYPE_FLOAT)
|
||||||
|
$val = floatVal($v['value']);
|
||||||
|
else if ($v['flags'] & CON_FLAG_TYPE_BOOL)
|
||||||
|
$val = (bool)$v['value'];
|
||||||
|
else if ($v['flags'] & CON_FLAG_TYPE_STRING)
|
||||||
|
$val = preg_replace('/[^\p{L}0-9~\s_\-\'\/\.:,]/ui', '', $v['value']);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Util::addNote(U_GROUP_ADMIN | U_GROUP_DEV, 'Kernel: '.($php ? 'PHP' : 'Aowow').' config value '.($php ? strtolower($k) : 'CFG_'.strtoupper($k)).' has no type set. Value forced to 0!');
|
||||||
|
$val = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($php)
|
||||||
|
ini_set(strtolower($k), $val);
|
||||||
|
else
|
||||||
|
define('CFG_'.strtoupper($k), $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// for CLI and early errors in erb context
|
// handle occuring errors
|
||||||
Lang::load(Locale::EN);
|
error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? (E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)) : 0);
|
||||||
|
$errHandled = false;
|
||||||
|
set_error_handler(function($errNo, $errStr, $errFile, $errLine) use (&$errHandled) {
|
||||||
|
$errName = 'unknown error'; // errors not in this list can not be handled by set_error_handler (as per documentation) or are ignored
|
||||||
|
if ($errNo == E_WARNING) // 0x0002
|
||||||
|
$errName = 'E_WARNING';
|
||||||
|
else if ($errNo == E_PARSE) // 0x0004
|
||||||
|
$errName = 'E_PARSE';
|
||||||
|
else if ($errNo == E_NOTICE) // 0x0008
|
||||||
|
$errName = 'E_NOTICE';
|
||||||
|
else if ($errNo == E_USER_ERROR) // 0x0100
|
||||||
|
$errName = 'E_USER_ERROR';
|
||||||
|
else if ($errNo == E_USER_WARNING) // 0x0200
|
||||||
|
$errName = 'E_USER_WARNING';
|
||||||
|
else if ($errNo == E_USER_NOTICE) // 0x0400
|
||||||
|
$errName = 'E_USER_NOTICE';
|
||||||
|
else if ($errNo == E_RECOVERABLE_ERROR) // 0x1000
|
||||||
|
$errName = 'E_RECOVERABLE_ERROR';
|
||||||
|
|
||||||
// load config from DB
|
if (User::isInGroup(U_GROUP_STAFF))
|
||||||
Cfg::load();
|
{
|
||||||
|
if (!$errHandled)
|
||||||
|
{
|
||||||
|
Util::addNote(U_GROUP_STAFF, 'one or more php related error occured, while generating this page.');
|
||||||
|
$errHandled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util::addNote(U_GROUP_STAFF, $errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DB::isConnectable(DB_AOWOW))
|
||||||
|
DB::Aowow()->query('INSERT INTO ?_errors (`date`, `version`, `phpError`, `file`, `line`, `query`, `userGroups`, `message`) VALUES (UNIX_TIMESTAMP(), ?d, ?d, ?, ?d, ?, ?d, ?) ON DUPLICATE KEY UPDATE `date` = UNIX_TIMESTAMP()',
|
||||||
|
AOWOW_REVISION, $errNo, $errFile, $errLine, CLI ? 'CLI' : $_SERVER['QUERY_STRING'], User::$groups, $errStr
|
||||||
|
);
|
||||||
|
|
||||||
|
return !((User::isInGroup(U_GROUP_STAFF) && defined('CFG_DEBUG') && CFG_DEBUG) || CLI);
|
||||||
|
}, E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT));
|
||||||
|
|
||||||
|
|
||||||
|
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($AoWoWconf['aowow']) && CFG_FORCE_SSL);
|
||||||
|
if (defined('CFG_STATIC_HOST')) // points js to images & scripts
|
||||||
|
define('STATIC_URL', ($secure ? 'https://' : 'http://').CFG_STATIC_HOST);
|
||||||
|
|
||||||
|
if (defined('CFG_SITE_HOST')) // points js to executable files
|
||||||
|
define('HOST_URL', ($secure ? 'https://' : 'http://').CFG_SITE_HOST);
|
||||||
|
|
||||||
|
|
||||||
if (!CLI)
|
if (!CLI)
|
||||||
{
|
{
|
||||||
// not displaying the brb gnomes as static_host is missing, but eh...
|
|
||||||
if (!DB::isConnected(DB_AOWOW) || !DB::isConnected(DB_WORLD) || !Cfg::get('HOST_URL') || !Cfg::get('STATIC_URL'))
|
|
||||||
(new GenericPage())->maintenance();
|
|
||||||
|
|
||||||
// Setup Session
|
// Setup Session
|
||||||
$cacheDir = Cfg::get('SESSION_CACHE_DIR');
|
session_set_cookie_params(15 * YEAR, '/', '', $secure, true);
|
||||||
if ($cacheDir && Util::writeDir($cacheDir))
|
|
||||||
session_save_path(getcwd().'/'.$cacheDir);
|
|
||||||
|
|
||||||
session_set_cookie_params(15 * YEAR, '/', '', (($_SERVER['HTTPS'] ?? 'off') != 'off') || Cfg::get('FORCE_SSL'), true);
|
|
||||||
session_cache_limiter('private');
|
session_cache_limiter('private');
|
||||||
if (!session_start())
|
session_start();
|
||||||
{
|
if (!empty($AoWoWconf['aowow']) && User::init())
|
||||||
trigger_error('failed to start session', E_USER_ERROR);
|
|
||||||
(new GenericPage())->error();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (User::init())
|
|
||||||
User::save(); // save user-variables in session
|
User::save(); // save user-variables in session
|
||||||
|
|
||||||
// hard override locale for this call (should this be here..?)
|
// todo: (low) - move to setup web-interface (when it begins its existance)
|
||||||
if (isset($_GET['locale']) && ($loc = Locale::tryFrom((int)$_GET['locale'])))
|
if (!defined('CFG_SITE_HOST') || !defined('CFG_STATIC_HOST'))
|
||||||
Lang::load($loc);
|
|
||||||
else
|
|
||||||
Lang::load(User::$preferedLoc);
|
|
||||||
|
|
||||||
// set up some logging (some queries will execute before we init the user and load the config)
|
|
||||||
if (Cfg::get('DEBUG') >= LOG_LEVEL_INFO && User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN))
|
|
||||||
{
|
{
|
||||||
DB::Aowow()->setLogger(DB::profiler(...));
|
$host = substr($_SERVER['SERVER_NAME'].strtr($_SERVER['SCRIPT_NAME'], ['index.php' => '']), 0, -1);
|
||||||
DB::World()->setLogger(DB::profiler(...));
|
|
||||||
if (DB::isConnected(DB_AUTH))
|
|
||||||
DB::Auth()->setLogger(DB::profiler(...));
|
|
||||||
|
|
||||||
if (!empty($AoWoWconf['characters']))
|
define('HOST_URL', ($secure ? 'https://' : 'http://').$host);
|
||||||
foreach ($AoWoWconf['characters'] as $idx => $__)
|
define('STATIC_URL', ($secure ? 'https://' : 'http://').$host.'/static');
|
||||||
if (DB::isConnected(DB_CHARACTERS . $idx))
|
|
||||||
DB::Characters($idx)->setLogger(DB::profiler(...));
|
if (User::isInGroup(U_GROUP_ADMIN)) // initial set
|
||||||
|
{
|
||||||
|
DB::Aowow()->query('REPLACE INTO ?_config VALUES (?a)',
|
||||||
|
[['site_host', $host, CON_FLAG_TYPE_STRING | CON_FLAG_PERSISTENT, 'default: '.$host.' - points js to executable files (automaticly set on first run)'],
|
||||||
|
['static_host', $host.'/static', CON_FLAG_TYPE_STRING | CON_FLAG_PERSISTENT, 'default: '.$host.'/static - points js to images & scripts (automaticly set on first run)']]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hard-override locale for this call (should this be here..?)
|
||||||
|
// all strings attached..
|
||||||
|
if (!empty($AoWoWconf['aowow']))
|
||||||
|
{
|
||||||
|
if (isset($_GET['locale']) && (CFG_LOCALES & (1 << (int)$_GET['locale'])))
|
||||||
|
User::useLocale($_GET['locale']);
|
||||||
|
|
||||||
|
Lang::load(User::$localeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse page-parameters .. sanitize before use!
|
// parse page-parameters .. sanitize before use!
|
||||||
$str = explode('&', $_SERVER['QUERY_STRING'] ?? '', 2)[0];
|
$str = explode('&', $_SERVER['QUERY_STRING'], 2)[0];
|
||||||
$_ = explode('=', $str, 2);
|
$_ = explode('=', $str, 2);
|
||||||
$pageCall = mb_strtolower($_[0]);
|
$pageCall = $_[0];
|
||||||
$pageParam = $_[1] ?? '';
|
$pageParam = isset($_[1]) ? $_[1] : null;
|
||||||
|
|
||||||
|
Util::$wowheadLink = 'http://'.Util::$subDomains[User::$localeId].'.wowhead.com/'.$str;
|
||||||
}
|
}
|
||||||
|
else if (!empty($AoWoWconf['aowow']))
|
||||||
|
Lang::load('enus');
|
||||||
|
|
||||||
|
$AoWoWconf = null; // empty auths
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -159,8 +159,9 @@ class DbSimple_Connect
|
|||||||
*
|
*
|
||||||
* @param string $query запрос
|
* @param string $query запрос
|
||||||
*/
|
*/
|
||||||
public function addInit(...$args)
|
public function addInit($query)
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
if ($this->DbSimple !== null)
|
if ($this->DbSimple !== null)
|
||||||
return call_user_func_array(array(&$this->DbSimple, 'query'), $args);
|
return call_user_func_array(array(&$this->DbSimple, 'query'), $args);
|
||||||
$this->init[] = $args;
|
$this->init[] = $args;
|
||||||
|
|||||||
@@ -93,8 +93,6 @@ require_once __DIR__ . '/CacherImpl.php';
|
|||||||
*/
|
*/
|
||||||
abstract class DbSimple_Database extends DbSimple_LastError
|
abstract class DbSimple_Database extends DbSimple_LastError
|
||||||
{
|
{
|
||||||
private $attributes;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Public methods.
|
* Public methods.
|
||||||
*/
|
*/
|
||||||
@@ -146,8 +144,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* mixed select(string $query [, $arg1] [,$arg2] ...)
|
* mixed select(string $query [, $arg1] [,$arg2] ...)
|
||||||
* Execute query and return the result.
|
* Execute query and return the result.
|
||||||
*/
|
*/
|
||||||
public function select(...$args)
|
public function select($query)
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
$total = false;
|
$total = false;
|
||||||
return $this->_query($args, $total);
|
return $this->_query($args, $total);
|
||||||
}
|
}
|
||||||
@@ -158,8 +157,10 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* Total number of found rows (independent to LIMIT) is returned in $total
|
* Total number of found rows (independent to LIMIT) is returned in $total
|
||||||
* (in most cases second query is performed to calculate $total).
|
* (in most cases second query is performed to calculate $total).
|
||||||
*/
|
*/
|
||||||
public function selectPage(&$total, ...$args)
|
public function selectPage(&$total, $query)
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
|
array_shift($args);
|
||||||
$total = true;
|
$total = true;
|
||||||
return $this->_query($args, $total);
|
return $this->_query($args, $total);
|
||||||
}
|
}
|
||||||
@@ -172,8 +173,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* because PHP DOES NOT generates notice on $row['abc'] if $row === null
|
* because PHP DOES NOT generates notice on $row['abc'] if $row === null
|
||||||
* or $row === false (but, if $row is empty array, notice is generated).
|
* or $row === false (but, if $row is empty array, notice is generated).
|
||||||
*/
|
*/
|
||||||
public function selectRow(...$args)
|
public function selectRow()
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
$total = false;
|
$total = false;
|
||||||
$rows = $this->_query($args, $total);
|
$rows = $this->_query($args, $total);
|
||||||
if (!is_array($rows)) return $rows;
|
if (!is_array($rows)) return $rows;
|
||||||
@@ -186,8 +188,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* array selectCol(string $query [, $arg1] [,$arg2] ...)
|
* array selectCol(string $query [, $arg1] [,$arg2] ...)
|
||||||
* Return the first column of query result as array.
|
* Return the first column of query result as array.
|
||||||
*/
|
*/
|
||||||
public function selectCol(...$args)
|
public function selectCol()
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
$total = false;
|
$total = false;
|
||||||
$rows = $this->_query($args, $total);
|
$rows = $this->_query($args, $total);
|
||||||
if (!is_array($rows)) return $rows;
|
if (!is_array($rows)) return $rows;
|
||||||
@@ -200,8 +203,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* Return the first cell of the first column of query result.
|
* Return the first cell of the first column of query result.
|
||||||
* If no one row selected, return null.
|
* If no one row selected, return null.
|
||||||
*/
|
*/
|
||||||
public function selectCell(...$args)
|
public function selectCell()
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
$total = false;
|
$total = false;
|
||||||
$rows = $this->_query($args, $total);
|
$rows = $this->_query($args, $total);
|
||||||
if (!is_array($rows)) return $rows;
|
if (!is_array($rows)) return $rows;
|
||||||
@@ -217,8 +221,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* mixed query(string $query [, $arg1] [,$arg2] ...)
|
* mixed query(string $query [, $arg1] [,$arg2] ...)
|
||||||
* Alias for select(). May be used for INSERT or UPDATE queries.
|
* Alias for select(). May be used for INSERT or UPDATE queries.
|
||||||
*/
|
*/
|
||||||
public function query(...$args)
|
public function query()
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
$total = false;
|
$total = false;
|
||||||
return $this->_query($args, $total);
|
return $this->_query($args, $total);
|
||||||
}
|
}
|
||||||
@@ -241,8 +246,9 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
* Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить
|
* Нужно для сложных запросов, состоящих из кусков, которые полезно сохранить
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function subquery(...$args)
|
public function subquery()
|
||||||
{
|
{
|
||||||
|
$args = func_get_args();
|
||||||
$this->_expandPlaceholders($args,$this->_placeholderNativeArgs !== null);
|
$this->_expandPlaceholders($args,$this->_placeholderNativeArgs !== null);
|
||||||
return new DbSimple_SubQuery($args);
|
return new DbSimple_SubQuery($args);
|
||||||
}
|
}
|
||||||
@@ -1144,7 +1150,7 @@ abstract class DbSimple_Database extends DbSimple_LastError
|
|||||||
$len = 0;
|
$len = 0;
|
||||||
$values = array();
|
$values = array();
|
||||||
foreach ($rows[0] as $k=>$v) {
|
foreach ($rows[0] as $k=>$v) {
|
||||||
$len += strlen($v ?? '');
|
$len += strlen($v);
|
||||||
if ($len > $this->MAX_LOG_ROW_LEN) {
|
if ($len > $this->MAX_LOG_ROW_LEN) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,11 @@ class DbSimple_Mysqli extends DbSimple_Database
|
|||||||
{
|
{
|
||||||
var $link;
|
var $link;
|
||||||
|
|
||||||
private $_lastQuery;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* constructor(string $dsn)
|
* constructor(string $dsn)
|
||||||
* Connect to MySQL server.
|
* Connect to MySQL server.
|
||||||
*/
|
*/
|
||||||
function __construct($dsn)
|
function DbSimple_Mysqli($dsn)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!is_callable("mysqli_connect"))
|
if (!is_callable("mysqli_connect"))
|
||||||
@@ -161,21 +159,9 @@ class DbSimple_Mysqli extends DbSimple_Database
|
|||||||
{
|
{
|
||||||
$this->_lastQuery = $queryMain;
|
$this->_lastQuery = $queryMain;
|
||||||
$this->_expandPlaceholders($queryMain, false);
|
$this->_expandPlaceholders($queryMain, false);
|
||||||
mysqli_ping($this->link);
|
|
||||||
$result = mysqli_query($this->link, $queryMain[0]);
|
$result = mysqli_query($this->link, $queryMain[0]);
|
||||||
if ($result === false)
|
if ($result === false)
|
||||||
return $this->_setDbError($queryMain[0]);
|
return $this->_setDbError($queryMain[0]);
|
||||||
|
|
||||||
if ($this->link->warning_count) {
|
|
||||||
if ($warn = $this->link->query("SHOW WARNINGS")) {
|
|
||||||
while ($warnRow = $warn->fetch_row())
|
|
||||||
if ($warnRow[0] === 'Warning')
|
|
||||||
$this->_setLastError(-$warnRow[1], $warnRow[2], $queryMain[0]);
|
|
||||||
|
|
||||||
$warn->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_object($result)) {
|
if (!is_object($result)) {
|
||||||
if (preg_match('/^\s* INSERT \s+/six', $queryMain[0]))
|
if (preg_match('/^\s* INSERT \s+/six', $queryMain[0]))
|
||||||
{
|
{
|
||||||
@@ -192,7 +178,7 @@ class DbSimple_Mysqli extends DbSimple_Database
|
|||||||
protected function _performFetch($result)
|
protected function _performFetch($result)
|
||||||
{
|
{
|
||||||
$row = mysqli_fetch_assoc($result);
|
$row = mysqli_fetch_assoc($result);
|
||||||
if (mysqli_error($this->link)) return $this->_setDbError($this->_lastQuery);
|
if (mysql_error()) return $this->_setDbError($this->_lastQuery);
|
||||||
if ($row === false) return null;
|
if ($row === false) return null;
|
||||||
return $row;
|
return $row;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/****************************************
|
|
||||||
Example of how to use this uploader class...
|
|
||||||
You can uncomment the following lines (minus the require) to use these as your defaults.
|
|
||||||
|
|
||||||
// list of valid extensions, ex. array("jpeg", "xml", "bmp")
|
|
||||||
$allowedExtensions = array();
|
|
||||||
// max file size in bytes
|
|
||||||
$sizeLimit = 10 * 1024 * 1024;
|
|
||||||
|
|
||||||
require('valums-file-uploader/server/php.php');
|
|
||||||
$uploader = new qqFileUploader($allowedExtensions, $sizeLimit);
|
|
||||||
|
|
||||||
// Call handleUpload() with the name of the folder, relative to PHP's getcwd()
|
|
||||||
$result = $uploader->handleUpload('uploads/');
|
|
||||||
|
|
||||||
// to pass data through iframe you will need to encode all html tags
|
|
||||||
echo htmlspecialchars(json_encode($result), ENT_NOQUOTES);
|
|
||||||
|
|
||||||
/******************************************/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle file uploads via XMLHttpRequest
|
|
||||||
*/
|
|
||||||
class qqUploadedFileXhr
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Save the file to the specified path
|
|
||||||
* @return boolean TRUE on success
|
|
||||||
*/
|
|
||||||
function save(string $path) : bool
|
|
||||||
{
|
|
||||||
$input = fopen("php://input", "r");
|
|
||||||
$temp = tmpfile();
|
|
||||||
$realSize = stream_copy_to_stream($input, $temp);
|
|
||||||
fclose($input);
|
|
||||||
|
|
||||||
if ($realSize != $this->getSize())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$target = fopen($path, "w");
|
|
||||||
fseek($temp, 0, SEEK_SET);
|
|
||||||
stream_copy_to_stream($temp, $target);
|
|
||||||
fclose($target);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getName() : string
|
|
||||||
{
|
|
||||||
return $_GET['qqfile'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSize(): int
|
|
||||||
{
|
|
||||||
if (isset($_SERVER["CONTENT_LENGTH"]))
|
|
||||||
return (int)$_SERVER["CONTENT_LENGTH"];
|
|
||||||
|
|
||||||
throw new Exception('Getting content length is not supported.');
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle file uploads via regular form post (uses the $_FILES array)
|
|
||||||
*/
|
|
||||||
class qqUploadedFileForm
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Save the file to the specified path
|
|
||||||
* @return boolean TRUE on success
|
|
||||||
*/
|
|
||||||
function save(string $path) : bool
|
|
||||||
{
|
|
||||||
if(!move_uploaded_file($_FILES['qqfile']['tmp_name'], $path))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getName() : string
|
|
||||||
{
|
|
||||||
return $_FILES['qqfile']['name'];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSize() : int
|
|
||||||
{
|
|
||||||
return $_FILES['qqfile']['size'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class qqFileUploader
|
|
||||||
{
|
|
||||||
private $allowedExtensions = array();
|
|
||||||
private $sizeLimit = 10485760;
|
|
||||||
private $file;
|
|
||||||
|
|
||||||
public function __construct(array $allowedExtensions = array(), $sizeLimit = 10485760)
|
|
||||||
{
|
|
||||||
$this->allowedExtensions = array_map("strtolower", $allowedExtensions);
|
|
||||||
$this->sizeLimit = $sizeLimit;
|
|
||||||
|
|
||||||
$this->checkServerSettings();
|
|
||||||
|
|
||||||
if (isset($_GET['qqfile']))
|
|
||||||
$this->file = new qqUploadedFileXhr();
|
|
||||||
else if (isset($_FILES['qqfile']))
|
|
||||||
$this->file = new qqUploadedFileForm();
|
|
||||||
else
|
|
||||||
$this->file = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getName() : string
|
|
||||||
{
|
|
||||||
return $this->file?->getName() ?? '';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function checkServerSettings() : void
|
|
||||||
{
|
|
||||||
$postSize = $this->toBytes(ini_get('post_max_size'));
|
|
||||||
$uploadSize = $this->toBytes(ini_get('upload_max_filesize'));
|
|
||||||
|
|
||||||
if ($postSize < $this->sizeLimit || $uploadSize < $this->sizeLimit)
|
|
||||||
{
|
|
||||||
$size = max(1, $this->sizeLimit / 1024 / 1024) . 'M';
|
|
||||||
die("{'error':'increase post_max_size and upload_max_filesize to $size'}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function toBytes(string $str) : int
|
|
||||||
{
|
|
||||||
$val = trim($str);
|
|
||||||
$last = strtolower(substr($str, -1, 1));
|
|
||||||
switch ($last)
|
|
||||||
{
|
|
||||||
case 'g': $val *= 1024;
|
|
||||||
case 'm': $val *= 1024;
|
|
||||||
case 'k': $val *= 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns array('success' => true, 'newFilename' => 'myDoc123.doc') or array('error' => 'error message')
|
|
||||||
*/
|
|
||||||
function handleUpload(string $uploadDirectory, string $newName = '', bool $replaceOldFile = FALSE) : array
|
|
||||||
{
|
|
||||||
if (!is_writable($uploadDirectory))
|
|
||||||
return ['error' => "Server error. Upload directory isn't writable."];
|
|
||||||
|
|
||||||
if (!$this->file)
|
|
||||||
return ['error' => 'No files were uploaded.'];
|
|
||||||
|
|
||||||
$size = $this->file->getSize();
|
|
||||||
|
|
||||||
if ($size == 0)
|
|
||||||
return ['error' => 'File is empty'];
|
|
||||||
|
|
||||||
if ($size > $this->sizeLimit)
|
|
||||||
return ['error' => 'File is too large'];
|
|
||||||
|
|
||||||
$pathinfo = pathinfo($this->getName());
|
|
||||||
$filename = $newName ?: $pathinfo['filename'];
|
|
||||||
//$filename = md5(uniqid());
|
|
||||||
$ext = @$pathinfo['extension']; // hide notices if extension is empty
|
|
||||||
|
|
||||||
if ($this->allowedExtensions && !in_array(strtolower($ext), $this->allowedExtensions))
|
|
||||||
{
|
|
||||||
$these = implode(', ', $this->allowedExtensions);
|
|
||||||
return ['error' => 'File has an invalid extension, it should be one of '. $these . '.'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't overwrite previous files that were uploaded
|
|
||||||
if (!$replaceOldFile)
|
|
||||||
while (file_exists($uploadDirectory . $filename . '.' . $ext))
|
|
||||||
$filename .= rand(10, 99);
|
|
||||||
|
|
||||||
if ($this->file->save($uploadDirectory . $filename . '.' . $ext))
|
|
||||||
return ['success' => true, 'newFilename' => $filename . '.' . $ext];
|
|
||||||
else
|
|
||||||
return ['error' => 'Could not save uploaded file. The upload was cancelled, or server error encountered'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
enum Locale : int
|
|
||||||
{ // unused by TC WoW self
|
|
||||||
case EN = 0; // enGB, enUS
|
|
||||||
case KR = 1; // koKR ?
|
|
||||||
case FR = 2; // frFR
|
|
||||||
case DE = 3; // deDE
|
|
||||||
case CN = 4; // zhCN, enCN
|
|
||||||
case TW = 5; // zhTW, enTW x
|
|
||||||
case ES = 6; // esES
|
|
||||||
case MX = 7; // esMX x
|
|
||||||
case RU = 8; // ruRU
|
|
||||||
case JP = 9; // jaJP x x x
|
|
||||||
case PT = 10; // ptPT, ptBR x ?
|
|
||||||
case IT = 11; // itIT x x x
|
|
||||||
|
|
||||||
private const MASK_ALL = 0b000101011101; // technically supported locales
|
|
||||||
|
|
||||||
public function domain() : string // our subdomain / locale in web context
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::EN => 'en',
|
|
||||||
self::KR => 'ko',
|
|
||||||
self::FR => 'fr',
|
|
||||||
self::DE => 'de',
|
|
||||||
self::CN => 'cn',
|
|
||||||
self::TW => 'tw',
|
|
||||||
self::ES => 'es',
|
|
||||||
self::MX => 'mx',
|
|
||||||
self::RU => 'ru',
|
|
||||||
self::JP => 'jp',
|
|
||||||
self::PT => 'pt',
|
|
||||||
self::IT => 'it'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function json() : string // internal usage / json string
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::EN => 'enus',
|
|
||||||
self::KR => 'kokr',
|
|
||||||
self::FR => 'frfr',
|
|
||||||
self::DE => 'dede',
|
|
||||||
self::CN => 'zhcn',
|
|
||||||
self::TW => 'zhtw',
|
|
||||||
self::ES => 'eses',
|
|
||||||
self::MX => 'esmx',
|
|
||||||
self::RU => 'ruru',
|
|
||||||
self::JP => 'jajp',
|
|
||||||
self::PT => 'ptpt',
|
|
||||||
self::IT => 'itit'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function title() : string // localized language name
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::EN => 'English',
|
|
||||||
self::KR => '한국어',
|
|
||||||
self::FR => 'Français',
|
|
||||||
self::DE => 'Deutsch',
|
|
||||||
self::CN => '简体中文',
|
|
||||||
self::TW => '繁體中文',
|
|
||||||
self::ES => 'Español',
|
|
||||||
self::MX => 'Mexicano',
|
|
||||||
self::RU => 'Русский',
|
|
||||||
self::JP => '日本語',
|
|
||||||
self::PT => 'Português',
|
|
||||||
self::IT => 'Italiano'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function gameDirs() : array // setup data source / wow client locale code
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::EN => ['enGB', 'enUS', ''],
|
|
||||||
self::KR => ['koKR'],
|
|
||||||
self::FR => ['frFR'],
|
|
||||||
self::DE => ['deDE'],
|
|
||||||
self::CN => ['zhCN', 'enCN'],
|
|
||||||
self::TW => ['zhTW', 'enTW'],
|
|
||||||
self::ES => ['esES'],
|
|
||||||
self::MX => ['esMX'],
|
|
||||||
self::RU => ['ruRU'],
|
|
||||||
self::JP => ['jaJP'],
|
|
||||||
self::PT => ['ptPT', 'ptBR'],
|
|
||||||
self::IT => ['itIT']
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function httpCode() : array // HTTP_ACCEPT_LANGUAGE
|
|
||||||
{
|
|
||||||
return match ($this)
|
|
||||||
{
|
|
||||||
self::EN => ['en', 'en-au', 'en-bz', 'en-ca', 'en-ie', 'en-jm', 'en-nz', 'en-ph', 'en-za', 'en-tt', 'en-gb', 'en-us', 'en-zw'],
|
|
||||||
self::KR => ['ko', 'ko-kp', 'ko-kr'],
|
|
||||||
self::FR => ['fr', 'fr-be', 'fr-ca', 'fr-fr', 'fr-lu', 'fr-mc', 'fr-ch'],
|
|
||||||
self::DE => ['de', 'de-at', 'de-de', 'de-li', 'de-lu', 'de-ch'],
|
|
||||||
self::CN => ['zh', 'zh-hk', 'zh-cn', 'zh-sg'],
|
|
||||||
self::TW => ['tw', 'zh-tw'],
|
|
||||||
self::ES => ['es', 'es-ar', 'es-bo', 'es-cl', 'es-co', 'es-cr', 'es-do', 'es-ec', 'es-sv', 'es-gt', 'es-hn', 'es-ni', 'es-pa', 'es-py', 'es-pe', 'es-pr', 'es-es', 'es-uy', 'es-ve'],
|
|
||||||
self::MX => ['mx', 'es-mx'],
|
|
||||||
self::RU => ['ru', 'ru-mo'],
|
|
||||||
self::JP => ['ja'],
|
|
||||||
self::PT => ['pt', 'pt-br'],
|
|
||||||
self::IT => ['it', 'it-ch']
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isLogographic() : bool
|
|
||||||
{
|
|
||||||
return $this == Locale::CN || $this == Locale::TW || $this == Locale::KR;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function validate() : ?self
|
|
||||||
{
|
|
||||||
return ($this->maskBit() & self::MASK_ALL & (Cfg::get('LOCALES') ?: 0xFFFF)) ? $this : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function maskBit() : int
|
|
||||||
{
|
|
||||||
return (1 << $this->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function tryFromDomain(string $str) : ?self
|
|
||||||
{
|
|
||||||
foreach (self::cases() as $l)
|
|
||||||
if ($l->validate() && $str == $l->domain())
|
|
||||||
return $l;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function tryFromHttpAcceptLanguage(string $httpAccept) : ?self
|
|
||||||
{
|
|
||||||
if (!$httpAccept)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$available = [];
|
|
||||||
|
|
||||||
// e.g.: de,de-DE;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
|
|
||||||
foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $loc)
|
|
||||||
if (preg_match('/([a-z\-]+)(?:\s*;\s*q\s*=\s*([\.\d]+))?/ui', $loc, $m, PREG_UNMATCHED_AS_NULL))
|
|
||||||
$available[Util::lower($m[1])] = floatVal($m[2] ?? 1); // no quality set: assume 100%
|
|
||||||
|
|
||||||
arsort($available, SORT_NUMERIC); // highest quality on top
|
|
||||||
|
|
||||||
foreach ($available as $code => $_)
|
|
||||||
foreach (self::cases() as $l)
|
|
||||||
if ($l->validate() && in_array($code, $l->httpCode()))
|
|
||||||
return $l;
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFallback() : self
|
|
||||||
{
|
|
||||||
foreach (Locale::cases() as $l)
|
|
||||||
if ($l->validate())
|
|
||||||
return $l;
|
|
||||||
|
|
||||||
// wow, you really fucked up your config mate!
|
|
||||||
trigger_error('Locale::getFallback - there are no valid locales', E_USER_ERROR);
|
|
||||||
return self::EN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('invalid access');
|
||||||
|
|
||||||
|
|
||||||
/* from TC wiki
|
/* from TC wiki
|
||||||
@@ -27,7 +25,6 @@ class Loot
|
|||||||
|
|
||||||
private $entry = 0; // depending on the lookup itemId oder templateId
|
private $entry = 0; // depending on the lookup itemId oder templateId
|
||||||
private $results = [];
|
private $results = [];
|
||||||
private $chanceMods = [];
|
|
||||||
private $lootTemplates = array(
|
private $lootTemplates = array(
|
||||||
LOOT_REFERENCE, // internal
|
LOOT_REFERENCE, // internal
|
||||||
LOOT_ITEM, // item
|
LOOT_ITEM, // item
|
||||||
@@ -43,33 +40,33 @@ class Loot
|
|||||||
LOOT_SPELL // spell
|
LOOT_SPELL // spell
|
||||||
);
|
);
|
||||||
|
|
||||||
public function &iterate() : iterable
|
public function &iterate()
|
||||||
{
|
{
|
||||||
reset($this->results);
|
reset($this->results);
|
||||||
|
|
||||||
foreach ($this->results as $k => $__)
|
while (list($k, $__) = each($this->results))
|
||||||
yield $k => $this->results[$k];
|
yield $k => $this->results[$k];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResult() : array
|
public function getResult()
|
||||||
{
|
{
|
||||||
return $this->results;
|
return $this->results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createStack(array $l) : string // issue: TC always has an equal distribution between min/max
|
private function createStack($l) // issue: TC always has an equal distribution between min/max
|
||||||
{
|
{
|
||||||
if (empty($l['min']) || empty($l['max']) || $l['max'] <= $l['min'])
|
if (empty($l['min']) || empty($l['max']) || $l['max'] <= $l['min'])
|
||||||
return '';
|
return null;
|
||||||
|
|
||||||
$stack = [];
|
$stack = [];
|
||||||
for ($i = $l['min']; $i <= $l['max']; $i++)
|
for ($i = $l['min']; $i <= $l['max']; $i++)
|
||||||
$stack[$i] = round(100 / (1 + $l['max'] - $l['min']), 3);
|
$stack[$i] = round(100 / (1 + $l['max'] - $l['min']), 3);
|
||||||
|
|
||||||
// yes, it wants a string .. how weired is that..
|
// yes, it wants a string .. how weired is that..
|
||||||
return json_encode($stack, JSON_NUMERIC_CHECK); // do not replace with Util::toJSON !
|
return json_encode($stack, JSON_NUMERIC_CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function storeJSGlobals(array $data) : void
|
private function storeJSGlobals($data)
|
||||||
{
|
{
|
||||||
foreach ($data as $type => $jsData)
|
foreach ($data as $type => $jsData)
|
||||||
{
|
{
|
||||||
@@ -84,91 +81,29 @@ class Loot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function calcChance(array $refs, array $parents = []) : array
|
private function getByContainerRecursive($tableName, $lootId, &$handledRefs, $groupId = 0, $baseChance = 1.0)
|
||||||
{
|
|
||||||
$retData = [];
|
|
||||||
$retKeys = [];
|
|
||||||
|
|
||||||
foreach ($refs as $rId => $ref)
|
|
||||||
{
|
|
||||||
// check for possible database inconsistencies
|
|
||||||
if (!$ref['chance'] && !$ref['isGrouped'])
|
|
||||||
trigger_error('Loot by Item: Ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!', E_USER_WARNING);
|
|
||||||
|
|
||||||
if ($ref['isGrouped'] && $ref['sumChance'] > 100)
|
|
||||||
trigger_error('Loot by Item: Group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!', E_USER_WARNING);
|
|
||||||
|
|
||||||
if ($ref['isGrouped'] && $ref['sumChance'] >= 100 && !$ref['chance'])
|
|
||||||
trigger_error('Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!', E_USER_WARNING);
|
|
||||||
|
|
||||||
$chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
|
|
||||||
|
|
||||||
// apply inherited chanceMods
|
|
||||||
if (isset($this->chanceMods[$ref['item']]))
|
|
||||||
{
|
|
||||||
$chance *= $this->chanceMods[$ref['item']][0];
|
|
||||||
$chance = 1 - pow(1 - $chance, $this->chanceMods[$ref['item']][1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// save chance for parent-ref
|
|
||||||
$this->chanceMods[$rId] = [$chance, $ref['multiplier']];
|
|
||||||
|
|
||||||
// refTemplate doesn't point to a new ref -> we are done
|
|
||||||
if (!in_array($rId, $parents))
|
|
||||||
{
|
|
||||||
$data = array(
|
|
||||||
'percent' => $chance,
|
|
||||||
'stack' => [$ref['min'], $ref['max']],
|
|
||||||
'count' => 1 // ..and one for the sort script
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($_ = self::createStack($ref))
|
|
||||||
$data['pctstack'] = $_;
|
|
||||||
|
|
||||||
// sort highest chances first
|
|
||||||
$i = 0;
|
|
||||||
for (; $i < count($retData); $i++)
|
|
||||||
if ($retData[$i]['percent'] < $data['percent'])
|
|
||||||
break;
|
|
||||||
|
|
||||||
array_splice($retData, $i, 0, [$data]);
|
|
||||||
array_splice($retKeys, $i, 0, [$rId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_combine($retKeys, $retData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getByContainerRecursive(string $tableName, int $lootId, array &$handledRefs, int $groupId = 0, float $baseChance = 1.0) : array
|
|
||||||
{
|
{
|
||||||
$loot = [];
|
$loot = [];
|
||||||
$rawItems = [];
|
$rawItems = [];
|
||||||
|
|
||||||
if (!$tableName || !$lootId)
|
if (!$tableName || !$lootId)
|
||||||
return [null, null];
|
return null;
|
||||||
|
|
||||||
$rows = DB::World()->select('SELECT * FROM ?# WHERE entry = ?d{ AND groupid = ?d}', $tableName, $lootId, $groupId ?: DBSIMPLE_SKIP);
|
$rows = DB::World()->select('SELECT * FROM ?# WHERE entry = ?d{ AND groupid = ?d}', $tableName, $lootId, $groupId ?: DBSIMPLE_SKIP);
|
||||||
if (!$rows)
|
if (!$rows)
|
||||||
return [null, null];
|
return null;
|
||||||
|
|
||||||
$groupChances = [];
|
$groupChances = [];
|
||||||
$nGroupEquals = [];
|
$nGroupEquals = [];
|
||||||
$cnd = new Conditions();
|
|
||||||
foreach ($rows as $entry)
|
foreach ($rows as $entry)
|
||||||
{
|
{
|
||||||
$set = array(
|
$set = array(
|
||||||
'quest' => $entry['QuestRequired'],
|
'quest' => $entry['QuestRequired'],
|
||||||
'group' => $entry['GroupId'],
|
'group' => $entry['GroupId'],
|
||||||
'parentRef' => $tableName == LOOT_REFERENCE ? $lootId : 0,
|
'parentRef' => $tableName == LOOT_REFERENCE ? $lootId : 0,
|
||||||
'realChanceMod' => $baseChance,
|
'realChanceMod' => $baseChance
|
||||||
'groupChance' => 0
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($entry['QuestRequired'])
|
|
||||||
foreach (DB::Aowow()->selectCol('SELECT id FROM ?_quests WHERE (`reqSourceItemId1` = ?d OR `reqSourceItemId2` = ?d OR `reqSourceItemId3` = ?d OR `reqSourceItemId4` = ?d OR `reqItemId1` = ?d OR `reqItemId2` = ?d OR `reqItemId3` = ?d OR `reqItemId4` = ?d OR `reqItemId5` = ?d OR `reqItemId6` = ?d) AND (`cuFlags` & ?d) = 0',
|
|
||||||
$entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], $entry['Item'], CUSTOM_EXCLUDE_FOR_LISTVIEW | CUSTOM_UNAVAILABLE) as $questId)
|
|
||||||
$cnd->addExternalCondition(Conditions::lootTableToConditionSource($tableName), $lootId . ':' . $entry['Item'], [Conditions::QUESTTAKEN, $questId], true);
|
|
||||||
|
|
||||||
// if ($entry['LootMode'] > 1)
|
// if ($entry['LootMode'] > 1)
|
||||||
// {
|
// {
|
||||||
$buff = [];
|
$buff = [];
|
||||||
@@ -199,7 +134,7 @@ class Loot
|
|||||||
// bandaid.. remove when propperly handling lootmodes
|
// bandaid.. remove when propperly handling lootmodes
|
||||||
if (!in_array($entry['Reference'], $handledRefs))
|
if (!in_array($entry['Reference'], $handledRefs))
|
||||||
{ // todo (high): find out, why i used this in the first place. (don't do drugs, kids)
|
{ // todo (high): find out, why i used this in the first place. (don't do drugs, kids)
|
||||||
[$data, $raw] = self::getByContainerRecursive(LOOT_REFERENCE, $entry['Reference'], $handledRefs, /*$entry['GroupId'],*/ 0, $entry['Chance'] / 100);
|
list($data, $raw) = self::getByContainerRecursive(LOOT_REFERENCE, $entry['Reference'], $handledRefs, /*$entry['GroupId'],*/ 0, $entry['Chance'] / 100);
|
||||||
|
|
||||||
$handledRefs[] = $entry['Reference'];
|
$handledRefs[] = $entry['Reference'];
|
||||||
|
|
||||||
@@ -232,19 +167,15 @@ class Loot
|
|||||||
}
|
}
|
||||||
else if ($entry['GroupId'] && $entry['Chance'])
|
else if ($entry['GroupId'] && $entry['Chance'])
|
||||||
{
|
{
|
||||||
|
if (empty($groupChances[$entry['GroupId']]))
|
||||||
|
$groupChances[$entry['GroupId']] = 0;
|
||||||
|
|
||||||
|
$groupChances[$entry['GroupId']] += $entry['Chance'];
|
||||||
$set['groupChance'] = $entry['Chance'];
|
$set['groupChance'] = $entry['Chance'];
|
||||||
|
|
||||||
if (!$entry['Reference'])
|
|
||||||
{
|
|
||||||
if (empty($groupChances[$entry['GroupId']]))
|
|
||||||
$groupChances[$entry['GroupId']] = 0;
|
|
||||||
|
|
||||||
$groupChances[$entry['GroupId']] += $entry['Chance'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else // shouldn't have happened
|
else // shouldn't have happened
|
||||||
{
|
{
|
||||||
trigger_error('Unhandled case in calculating chance for item '.$entry['Item'].'!', E_USER_WARNING);
|
Util::addNote(U_GROUP_EMPLOYEE, 'Loot::getByContainerRecursive: unhandled case in calculating chance for item '.$entry['Item'].'!');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,30 +189,30 @@ class Loot
|
|||||||
$sum = 0;
|
$sum = 0;
|
||||||
else if ($sum >= 100.01)
|
else if ($sum >= 100.01)
|
||||||
{
|
{
|
||||||
trigger_error('Loot entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!', E_USER_WARNING);
|
Util::addNote(U_GROUP_EMPLOYEE, 'Loot::getByContainerRecursive: entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!');
|
||||||
$sum = 100;
|
$sum = 100;
|
||||||
}
|
}
|
||||||
// is applied as backReference to items with 0-chance
|
|
||||||
$groupChances[$k] = (100 - $sum) / ($nGroupEquals[$k] ?: 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($cnd->getBySourceGroup($lootId, Conditions::lootTableToConditionSource($tableName))->prepare())
|
$cnt = empty($nGroupEquals[$k]) ? 1 : $nGroupEquals[$k];
|
||||||
{
|
|
||||||
self::storeJSGlobals($cnd->getJsGlobals());
|
$groupChances[$k] = (100 - $sum) / $cnt; // is applied as backReference to items with 0-chance
|
||||||
$cnd->toListviewColumn($loot, $this->extraCols, $lootId, 'content');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$loot, array_unique($rawItems)];
|
return [$loot, array_unique($rawItems)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByContainer(string $table, int $entry): bool
|
public function getByContainer($table, $entry)
|
||||||
{
|
{
|
||||||
$this->entry = intVal($entry);
|
$this->entry = intVal($entry);
|
||||||
|
|
||||||
if (!in_array($table, $this->lootTemplates) || !$this->entry)
|
if (!in_array($table, $this->lootTemplates) || !$this->entry)
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
todo (high): implement conditions on loot (and conditions in general)
|
||||||
|
|
||||||
|
also
|
||||||
|
|
||||||
// if (is_array($this->entry) && in_array($table, [LOOT_CREATURE, LOOT_GAMEOBJECT])
|
// if (is_array($this->entry) && in_array($table, [LOOT_CREATURE, LOOT_GAMEOBJECT])
|
||||||
// iterate over the 4 available difficulties and assign modes
|
// iterate over the 4 available difficulties and assign modes
|
||||||
|
|
||||||
@@ -289,16 +220,16 @@ class Loot
|
|||||||
modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}}
|
modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}}
|
||||||
*/
|
*/
|
||||||
$handledRefs = [];
|
$handledRefs = [];
|
||||||
[$lootRows, $itemIds] = self::getByContainerRecursive($table, $this->entry, $handledRefs);
|
$struct = self::getByContainerRecursive($table, $this->entry, $handledRefs);
|
||||||
if (!$lootRows)
|
if (!$struct)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
$items = new ItemList(array(['i.id', $itemIds], Cfg::get('SQL_LIMIT_NONE')));
|
$items = new ItemList(array(['i.id', $struct[1]], CFG_SQL_LIMIT_NONE));
|
||||||
self::storeJSGlobals($items->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
|
$this->jsGlobals = $items->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED);
|
||||||
$foo = $items->getListviewData();
|
$foo = $items->getListviewData();
|
||||||
|
|
||||||
// assign listview LV rows to loot rows, not the other way round! The same item may be contained multiple times
|
// assign listview LV rows to loot rows, not the other way round! The same item may be contained multiple times
|
||||||
foreach ($lootRows as $loot)
|
foreach ($struct[0] as $loot)
|
||||||
{
|
{
|
||||||
$base = array(
|
$base = array(
|
||||||
'percent' => round($loot['groupChance'] * $loot['realChanceMod'], 3),
|
'percent' => round($loot['groupChance'] * $loot['realChanceMod'], 3),
|
||||||
@@ -313,20 +244,11 @@ class Loot
|
|||||||
if ($_ = $loot['parentRef'])
|
if ($_ = $loot['parentRef'])
|
||||||
$base['reference'] = $_;
|
$base['reference'] = $_;
|
||||||
|
|
||||||
if (isset($loot['condition']))
|
|
||||||
$base['condition'] = $loot['condition'];
|
|
||||||
|
|
||||||
if ($_ = self::createStack($loot))
|
if ($_ = self::createStack($loot))
|
||||||
$base['pctstack'] = $_;
|
$base['pctstack'] = $_;
|
||||||
|
|
||||||
if (empty($loot['reference'])) // regular drop
|
if (empty($loot['reference'])) // regular drop
|
||||||
{
|
{
|
||||||
if (!isset($foo[$loot['content']]))
|
|
||||||
{
|
|
||||||
trigger_error('Item #'.$loot['content'].' referenced by loot does not exist!', E_USER_WARNING);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!User::isInGroup(U_GROUP_EMPLOYEE))
|
if (!User::isInGroup(U_GROUP_EMPLOYEE))
|
||||||
{
|
{
|
||||||
if (!isset($this->results[$loot['content']]))
|
if (!isset($this->results[$loot['content']]))
|
||||||
@@ -347,7 +269,7 @@ class Loot
|
|||||||
);
|
);
|
||||||
$this->results[] = array_merge($base, $data);
|
$this->results[] = array_merge($base, $data);
|
||||||
|
|
||||||
$this->jsGlobals[Type::ITEM][$loot['reference']] = $data;
|
$this->jsGlobals[TYPE_ITEM][$loot['reference']] = $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,53 +312,51 @@ class Loot
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->extraCols[] = "\$Listview.funcBox.createSimpleCol('group', 'Group', '7%', 'group')";
|
$this->extraCols[] = "Listview.funcBox.createSimpleCol('group', 'Group', '7%', 'group')";
|
||||||
foreach ($fields as $idx => $field)
|
foreach ($fields as $idx => $field)
|
||||||
if ($set & (1 << $idx))
|
if ($set & (1 << $idx))
|
||||||
$this->extraCols[] = "\$Listview.funcBox.createSimpleCol('".$field."', '".Util::ucFirst($field)."', '7%', '".$field."')";
|
$this->extraCols[] = "Listview.funcBox.createSimpleCol('".$field."', '".Util::ucFirst($field)."', '7%', '".$field."')";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByItem(int $entry, int $maxResults = -1, array $lootTableList = []) : bool
|
public function getByItem($entry, $maxResults = CFG_SQL_LIMIT_DEFAULT, $lootTableList = [])
|
||||||
{
|
{
|
||||||
$this->entry = $entry;
|
$this->entry = intVal($entry);
|
||||||
|
|
||||||
if (!$this->entry)
|
if (!$this->entry)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ($maxResults < 0)
|
|
||||||
$maxResults = Cfg::get('SQL_LIMIT_DEFAULT');
|
|
||||||
|
|
||||||
// [fileName, tabData, tabName, tabId, extraCols, hiddenCols, visibleCols]
|
// [fileName, tabData, tabName, tabId, extraCols, hiddenCols, visibleCols]
|
||||||
$tabsFinal = array(
|
$tabsFinal = array(
|
||||||
[Type::ITEM, [], '$LANG.tab_containedin', 'contained-in-item', [], [], []],
|
['item', [], '$LANG.tab_containedin', 'contained-in-item', [], [], []],
|
||||||
[Type::ITEM, [], '$LANG.tab_disenchantedfrom', 'disenchanted-from', [], [], []],
|
['item', [], '$LANG.tab_disenchantedfrom', 'disenchanted-from', [], [], []],
|
||||||
[Type::ITEM, [], '$LANG.tab_prospectedfrom', 'prospected-from', [], [], []],
|
['item', [], '$LANG.tab_prospectedfrom', 'prospected-from', [], [], []],
|
||||||
[Type::ITEM, [], '$LANG.tab_milledfrom', 'milled-from', [], [], []],
|
['item', [], '$LANG.tab_milledfrom', 'milled-from', [], [], []],
|
||||||
[Type::NPC, [], '$LANG.tab_droppedby', 'dropped-by', [], [], []],
|
['creature', [], '$LANG.tab_droppedby', 'dropped-by', [], [], []],
|
||||||
[Type::NPC, [], '$LANG.tab_pickpocketedfrom', 'pickpocketed-from', [], [], []],
|
['creature', [], '$LANG.tab_pickpocketedfrom', 'pickpocketed-from', [], [], []],
|
||||||
[Type::NPC, [], '$LANG.tab_skinnedfrom', 'skinned-from', [], [], []],
|
['creature', [], '$LANG.tab_skinnedfrom', 'skinned-from', [], [], []],
|
||||||
[Type::NPC, [], '$LANG.tab_minedfromnpc', 'mined-from-npc', [], [], []],
|
['creature', [], '$LANG.tab_minedfromnpc', 'mined-from-npc', [], [], []],
|
||||||
[Type::NPC, [], '$LANG.tab_salvagedfrom', 'salvaged-from', [], [], []],
|
['creature', [], '$LANG.tab_salvagedfrom', 'salvaged-from', [], [], []],
|
||||||
[Type::NPC, [], '$LANG.tab_gatheredfromnpc', 'gathered-from-npc', [], [], []],
|
['creature', [], '$LANG.tab_gatheredfromnpc', 'gathered-from-npc', [], [], []],
|
||||||
[Type::QUEST, [], '$LANG.tab_rewardfrom', 'reward-from-quest', [], [], []],
|
['quest', [], '$LANG.tab_rewardfrom', 'reward-from-quest', [], [], []],
|
||||||
[Type::ZONE, [], '$LANG.tab_fishedin', 'fished-in-zone', [], [], []],
|
['zone', [], '$LANG.tab_fishedin', 'fished-in-zone', [], [], []],
|
||||||
[Type::OBJECT, [], '$LANG.tab_containedin', 'contained-in-object', [], [], []],
|
['object', [], '$LANG.tab_containedin', 'contained-in-object', [], [], []],
|
||||||
[Type::OBJECT, [], '$LANG.tab_minedfrom', 'mined-from-object', [], [], []],
|
['object', [], '$LANG.tab_minedfrom', 'mined-from-object', [], [], []],
|
||||||
[Type::OBJECT, [], '$LANG.tab_gatheredfrom', 'gathered-from-object', [], [], []],
|
['object', [], '$LANG.tab_gatheredfrom', 'gathered-from-object', [], [], []],
|
||||||
[Type::OBJECT, [], '$LANG.tab_fishedin', 'fished-in-object', [], [], []],
|
['object', [], '$LANG.tab_fishedin', 'fished-in-object', [], [], []],
|
||||||
[Type::SPELL, [], '$LANG.tab_createdby', 'created-by', [], [], []],
|
['spell', [], '$LANG.tab_createdby', 'created-by', [], [], []],
|
||||||
[Type::ACHIEVEMENT, [], '$LANG.tab_rewardfrom', 'reward-from-achievement', [], [], []]
|
['achievement', [], '$LANG.tab_rewardfrom', 'reward-from-achievement', [], [], []]
|
||||||
);
|
);
|
||||||
$refResults = [];
|
$refResults = [];
|
||||||
|
$chanceMods = [];
|
||||||
$query = 'SELECT
|
$query = 'SELECT
|
||||||
lt1.entry AS ARRAY_KEY,
|
lt1.entry AS ARRAY_KEY,
|
||||||
IF(lt1.reference = 0, lt1.item, lt1.reference) AS item,
|
IF(lt1.reference = 0, lt1.item, lt1.reference) AS item,
|
||||||
lt1.chance,
|
lt1.chance,
|
||||||
SUM(IF(lt2.chance = 0, 1, 0)) AS nZeroItems,
|
SUM(IF(lt2.chance = 0, 1, 0)) AS nZeroItems,
|
||||||
SUM(IF(lt2.reference = 0, lt2.chance, 0)) AS sumChance,
|
SUM(lt2.chance) AS sumChance,
|
||||||
IF(lt1.groupid > 0, 1, 0) AS isGrouped,
|
IF(lt1.groupid > 0, 1, 0) AS isGrouped,
|
||||||
IF(lt1.reference = 0, lt1.mincount, 1) AS min,
|
IF(lt1.reference = 0, lt1.mincount, 1) AS min,
|
||||||
IF(lt1.reference = 0, lt1.maxcount, 1) AS max,
|
IF(lt1.reference = 0, lt1.maxcount, 1) AS max,
|
||||||
@@ -449,6 +369,61 @@ class Loot
|
|||||||
%s
|
%s
|
||||||
GROUP BY lt2.entry, lt2.groupid';
|
GROUP BY lt2.entry, lt2.groupid';
|
||||||
|
|
||||||
|
$calcChance = function ($refs, $parents = []) use (&$chanceMods)
|
||||||
|
{
|
||||||
|
$retData = [];
|
||||||
|
$retKeys = [];
|
||||||
|
|
||||||
|
foreach ($refs as $rId => $ref)
|
||||||
|
{
|
||||||
|
// check for possible database inconsistencies
|
||||||
|
if (!$ref['chance'] && !$ref['isGrouped'])
|
||||||
|
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!');
|
||||||
|
|
||||||
|
if ($ref['isGrouped'] && $ref['sumChance'] > 100)
|
||||||
|
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: group with Item/Ref '.$ref['item'].' has '.number_format($ref['sumChance'], 2).'% total chance! Some items cannot drop!');
|
||||||
|
|
||||||
|
if ($ref['isGrouped'] && $ref['sumChance'] >= 100 && !$ref['chance'])
|
||||||
|
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: Item/Ref '.$ref['item'].' with adaptive chance cannot drop. Group already at 100%!');
|
||||||
|
|
||||||
|
$chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
|
||||||
|
|
||||||
|
// apply inherited chanceMods
|
||||||
|
if (isset($chanceMods[$ref['item']]))
|
||||||
|
{
|
||||||
|
$chance *= $chanceMods[$ref['item']][0];
|
||||||
|
$chance = 1 - pow(1 - $chance, $chanceMods[$ref['item']][1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// save chance for parent-ref
|
||||||
|
$chanceMods[$rId] = [$chance, $ref['multiplier']];
|
||||||
|
|
||||||
|
// refTemplate doesn't point to a new ref -> we are done
|
||||||
|
if (!in_array($rId, $parents))
|
||||||
|
{
|
||||||
|
$data = array(
|
||||||
|
'percent' => $chance,
|
||||||
|
'stack' => [$ref['min'], $ref['max']],
|
||||||
|
'count' => 1 // ..and one for the sort script
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($_ = self::createStack($ref))
|
||||||
|
$data['pctstack'] = $_;
|
||||||
|
|
||||||
|
// sort highest chances first
|
||||||
|
$i = 0;
|
||||||
|
for (; $i < count($retData); $i++)
|
||||||
|
if ($retData[$i]['percent'] < $data['percent'])
|
||||||
|
break;
|
||||||
|
|
||||||
|
array_splice($retData, $i, 0, [$data]);
|
||||||
|
array_splice($retKeys, $i, 0, [$rId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_combine($retKeys, $retData);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
get references containing the item
|
get references containing the item
|
||||||
*/
|
*/
|
||||||
@@ -458,16 +433,6 @@ class Loot
|
|||||||
$this->entry
|
$this->entry
|
||||||
);
|
);
|
||||||
|
|
||||||
/* i'm currently not seeing a reasonable way to blend this into creature/gobject/etc tabs as one entity may drop the same item multiple times, with and without conditions.
|
|
||||||
if ($newRefs)
|
|
||||||
{
|
|
||||||
$cnd = new Conditions();
|
|
||||||
if ($cnd->getBySourceEntry($this->entry, Conditions::SRC_REFERENCE_LOOT_TEMPLATE))
|
|
||||||
if ($cnd->toListviewColumn($newRefs, $x, $this->entry))
|
|
||||||
self::storejsGlobals($cnd->getJsGlobals());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
while ($newRefs)
|
while ($newRefs)
|
||||||
{
|
{
|
||||||
$curRefs = $newRefs;
|
$curRefs = $newRefs;
|
||||||
@@ -477,23 +442,20 @@ class Loot
|
|||||||
array_keys($curRefs)
|
array_keys($curRefs)
|
||||||
);
|
);
|
||||||
|
|
||||||
$refResults += $this->calcChance($curRefs, array_column($newRefs, 'item'));
|
$refResults += $calcChance($curRefs, array_column($newRefs, 'item'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
search the real loot-templates for the itemId and gathered refds
|
search the real loot-templates for the itemId and gathered refds
|
||||||
*/
|
*/
|
||||||
foreach ($this->lootTemplates as $lootTemplate)
|
for ($i = 1; $i < count($this->lootTemplates); $i++)
|
||||||
{
|
{
|
||||||
if ($lootTableList && !in_array($lootTemplate, $lootTableList))
|
if ($lootTableList && !in_array($this->lootTemplates[$i], $lootTableList))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ($lootTemplate == LOOT_REFERENCE)
|
$result = $calcChance(DB::World()->select(
|
||||||
continue;
|
|
||||||
|
|
||||||
$result = $this->calcChance(DB::World()->select(
|
|
||||||
sprintf($query, '{lt1.reference IN (?a) OR }(lt1.reference = 0 AND lt1.item = ?d)'),
|
sprintf($query, '{lt1.reference IN (?a) OR }(lt1.reference = 0 AND lt1.item = ?d)'),
|
||||||
$lootTemplate, $lootTemplate,
|
$this->lootTemplates[$i], $this->lootTemplates[$i],
|
||||||
$refResults ? array_keys($refResults) : DBSIMPLE_SKIP,
|
$refResults ? array_keys($refResults) : DBSIMPLE_SKIP,
|
||||||
$this->entry
|
$this->entry
|
||||||
));
|
));
|
||||||
@@ -509,10 +471,10 @@ class Loot
|
|||||||
}
|
}
|
||||||
|
|
||||||
// cap fetched entries to the sql-limit to guarantee, that the highest chance items get selected first
|
// cap fetched entries to the sql-limit to guarantee, that the highest chance items get selected first
|
||||||
// screws with GO-loot and skinning-loot as these templates are shared for several tabs (fish, herb, ore) (herb, ore, leather)
|
// screws with GO-loot and skinnig-loot as these templates are shared for several tabs (fish, herb, ore) (herb, ore, leather)
|
||||||
$ids = array_slice(array_keys($result), 0, $maxResults);
|
$ids = array_slice(array_keys($result), 0, $maxResults);
|
||||||
|
|
||||||
switch ($lootTemplate)
|
switch ($this->lootTemplates[$i])
|
||||||
{
|
{
|
||||||
case LOOT_CREATURE: $field = 'lootId'; $tabId = 4; break;
|
case LOOT_CREATURE: $field = 'lootId'; $tabId = 4; break;
|
||||||
case LOOT_PICKPOCKET: $field = 'pickpocketLootId'; $tabId = 5; break;
|
case LOOT_PICKPOCKET: $field = 'pickpocketLootId'; $tabId = 5; break;
|
||||||
@@ -532,7 +494,7 @@ class Loot
|
|||||||
|
|
||||||
$srcData = $srcObj->getListviewData();
|
$srcData = $srcObj->getListviewData();
|
||||||
|
|
||||||
foreach ($srcObj->iterate() as $curTpl)
|
foreach ($srcObj->iterate() as $__id => $curTpl)
|
||||||
{
|
{
|
||||||
switch ($curTpl['typeCat'])
|
switch ($curTpl['typeCat'])
|
||||||
{
|
{
|
||||||
@@ -543,7 +505,7 @@ class Loot
|
|||||||
}
|
}
|
||||||
|
|
||||||
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField('lootId')]);
|
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField('lootId')]);
|
||||||
$tabsFinal[$tabId][4][] = '$Listview.extraCols.percent';
|
$tabsFinal[$tabId][4][] = 'Listview.extraCols.percent';
|
||||||
if ($tabId != 15)
|
if ($tabId != 15)
|
||||||
$tabsFinal[$tabId][6][] = 'skill';
|
$tabsFinal[$tabId][6][] = 'skill';
|
||||||
}
|
}
|
||||||
@@ -568,7 +530,7 @@ class Loot
|
|||||||
|
|
||||||
// achievement part
|
// achievement part
|
||||||
$conditions = array(['itemExtra', $this->entry]);
|
$conditions = array(['itemExtra', $this->entry]);
|
||||||
if ($ar = DB::World()->selectCol('SELECT ID FROM achievement_reward WHERE ItemID = ?d{ OR MailTemplateID IN (?a)}', $this->entry, $ids ?: DBSIMPLE_SKIP))
|
if ($ar = DB::World()->selectCol('SELECT entry FROM achievement_reward WHERE item = ?d{ OR mailTemplate IN (?a)}', $this->entry, $ids ?: DBSIMPLE_SKIP))
|
||||||
array_push($conditions, ['id', $ar], 'OR');
|
array_push($conditions, ['id', $ar], 'OR');
|
||||||
|
|
||||||
$srcObj = new AchievementList($conditions);
|
$srcObj = new AchievementList($conditions);
|
||||||
@@ -587,9 +549,9 @@ class Loot
|
|||||||
case LOOT_SPELL:
|
case LOOT_SPELL:
|
||||||
$conditions = array(
|
$conditions = array(
|
||||||
'OR',
|
'OR',
|
||||||
['AND', ['effect1CreateItemId', $this->entry], ['OR', ['effect1Id', SpellList::EFFECTS_ITEM_CREATE], ['effect1AuraId', SpellList::AURAS_ITEM_CREATE]]],
|
['AND', ['effect1CreateItemId', $this->entry], ['OR', ['effect1Id', SpellList::$effects['itemCreate']], ['effect1AuraId', SpellList::$auras['itemCreate']]]],
|
||||||
['AND', ['effect2CreateItemId', $this->entry], ['OR', ['effect2Id', SpellList::EFFECTS_ITEM_CREATE], ['effect2AuraId', SpellList::AURAS_ITEM_CREATE]]],
|
['AND', ['effect2CreateItemId', $this->entry], ['OR', ['effect2Id', SpellList::$effects['itemCreate']], ['effect2AuraId', SpellList::$auras['itemCreate']]]],
|
||||||
['AND', ['effect3CreateItemId', $this->entry], ['OR', ['effect3Id', SpellList::EFFECTS_ITEM_CREATE], ['effect3AuraId', SpellList::AURAS_ITEM_CREATE]]],
|
['AND', ['effect3CreateItemId', $this->entry], ['OR', ['effect3Id', SpellList::$effects['itemCreate']], ['effect3AuraId', SpellList::$auras['itemCreate']]]],
|
||||||
);
|
);
|
||||||
if ($ids)
|
if ($ids)
|
||||||
$conditions[] = ['id', $ids];
|
$conditions[] = ['id', $ids];
|
||||||
@@ -601,9 +563,9 @@ class Loot
|
|||||||
$srcData = $srcObj->getListviewData();
|
$srcData = $srcObj->getListviewData();
|
||||||
|
|
||||||
if (!empty($result))
|
if (!empty($result))
|
||||||
$tabsFinal[16][4][] = '$Listview.extraCols.percent';
|
$tabsFinal[16][4][] = 'Listview.extraCols.percent';
|
||||||
|
|
||||||
if ($srcObj->hasSetFields('reagent1', 'reagent2', 'reagent3', 'reagent4', 'reagent5', 'reagent6', 'reagent7', 'reagent8'))
|
if ($srcObj->hasSetFields(['reagent1']))
|
||||||
$tabsFinal[16][6][] = 'reagents';
|
$tabsFinal[16][6][] = 'reagents';
|
||||||
|
|
||||||
foreach ($srcObj->iterate() as $_)
|
foreach ($srcObj->iterate() as $_)
|
||||||
@@ -615,28 +577,13 @@ class Loot
|
|||||||
if (!$ids)
|
if (!$ids)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$parentData = [];
|
|
||||||
switch ($tabsFinal[abs($tabId)][0])
|
switch ($tabsFinal[abs($tabId)][0])
|
||||||
{
|
{
|
||||||
case TYPE::NPC: // new CreatureList
|
case 'creature': // new CreatureList
|
||||||
if ($baseIds = DB::Aowow()->selectCol(
|
case 'item': // new ItemList
|
||||||
'SELECT `difficultyEntry1` AS ARRAY_KEY, `id` FROM ?_creature WHERE difficultyEntry1 IN (?a) UNION
|
case 'zone': // new ZoneList
|
||||||
SELECT `difficultyEntry2` AS ARRAY_KEY, `id` FROM ?_creature WHERE difficultyEntry2 IN (?a) UNION
|
$oName = ucFirst($tabsFinal[abs($tabId)][0]).'List';
|
||||||
SELECT `difficultyEntry3` AS ARRAY_KEY, `id` FROM ?_creature WHERE difficultyEntry3 IN (?a)',
|
$srcObj = new $oName(array([$field, $ids]));
|
||||||
$ids, $ids, $ids))
|
|
||||||
{
|
|
||||||
$parentObj = new CreatureList(array(['id', $baseIds]));
|
|
||||||
if (!$parentObj->error)
|
|
||||||
{
|
|
||||||
self::storeJSGlobals($parentObj->getJSGlobals());
|
|
||||||
$parentData = $parentObj->getListviewData();
|
|
||||||
$ids = array_diff($ids, $baseIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case Type::ITEM: // new ItemList
|
|
||||||
case Type::ZONE: // new ZoneList
|
|
||||||
$srcObj = Type::newList($tabsFinal[abs($tabId)][0], array([$field, $ids]));
|
|
||||||
if (!$srcObj->error)
|
if (!$srcObj->error)
|
||||||
{
|
{
|
||||||
$srcData = $srcObj->getListviewData();
|
$srcData = $srcObj->getListviewData();
|
||||||
@@ -644,46 +591,24 @@ class Loot
|
|||||||
|
|
||||||
foreach ($srcObj->iterate() as $curTpl)
|
foreach ($srcObj->iterate() as $curTpl)
|
||||||
{
|
{
|
||||||
if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_SKIN_WITH_HERBALISM)
|
if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_HERBLOOT)
|
||||||
$tabId = 9;
|
$tabId = 9;
|
||||||
else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_SKIN_WITH_ENGINEERING)
|
else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_ENGINEERLOOT)
|
||||||
$tabId = 8;
|
$tabId = 8;
|
||||||
else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_SKIN_WITH_MINING)
|
else if ($tabId < 0 && $curTpl['typeFlags'] & NPC_TYPEFLAG_MININGLOOT)
|
||||||
$tabId = 7;
|
$tabId = 7;
|
||||||
else if ($tabId < 0)
|
else if ($tabId < 0)
|
||||||
$tabId = abs($tabId); // general case (skinning)
|
$tabId = abs($tabId); // general case (skinning)
|
||||||
|
|
||||||
if (($p = $srcObj->getField('parentId')) && ($d = $parentData[$p] ?? null))
|
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]);
|
||||||
$tabsFinal[$tabId][1][] = array_merge($d, $result[$srcObj->getField($field)]);
|
$tabsFinal[$tabId][4][] = 'Listview.extraCols.percent';
|
||||||
else
|
|
||||||
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]);
|
|
||||||
|
|
||||||
$tabsFinal[$tabId][4][] = '$Listview.extraCols.percent';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($tabsFinal as $tabId => $data)
|
$this->results = $tabsFinal;
|
||||||
{
|
|
||||||
$tabData = array(
|
|
||||||
'data' => $data[1],
|
|
||||||
'name' => $data[2],
|
|
||||||
'id' => $data[3]
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($data[4])
|
|
||||||
$tabData['extraCols'] = array_unique($data[4]);
|
|
||||||
|
|
||||||
if ($data[5])
|
|
||||||
$tabData['hiddenCols'] = array_unique($data[5]);
|
|
||||||
|
|
||||||
if ($data[6])
|
|
||||||
$tabData['visibleCols'] = array_unique($data[6]);
|
|
||||||
|
|
||||||
$this->results[$tabId] = [Type::getFileString($data[0]), $tabData];
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
50
includes/markup.class.php
Normal file
50
includes/markup.class.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (!defined('AOWOW_REVISION'))
|
||||||
|
die('invalid access');
|
||||||
|
|
||||||
|
/*
|
||||||
|
this is just a skeleton for now
|
||||||
|
at some point i'll need to (at least rudamentary) parse
|
||||||
|
back and forth between markup and html
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Markup
|
||||||
|
{
|
||||||
|
private $text = '';
|
||||||
|
private $jsGlobals = [];
|
||||||
|
|
||||||
|
public function __construct($text)
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseGlobalsFromText(&$jsg = [])
|
||||||
|
{
|
||||||
|
if (preg_match_all('/(?<!\\\\)\[(npc|object|item|itemset|quest|spell|zone|faction|pet|achievement|statistic|title|event|class|race|skill|currency)=(\d+)[^\]]*\]/i', $this->text, $matches, PREG_SET_ORDER))
|
||||||
|
{
|
||||||
|
foreach ($matches as $match)
|
||||||
|
{
|
||||||
|
if ($match[1] == 'statistic')
|
||||||
|
$match[1] = 'achievement';
|
||||||
|
|
||||||
|
if ($type = array_search($match[1], Util::$typeStrings))
|
||||||
|
$this->jsGlobals[$type][$match[2]] = $match[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Util::mergeJsGlobals($jsg, $this->jsGlobals);
|
||||||
|
|
||||||
|
return $this->jsGlobals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fromHtml()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toHtml()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
abstract class CLI
|
|
||||||
{
|
|
||||||
private const CHR_BELL = 7;
|
|
||||||
private const CHR_BACK = 8;
|
|
||||||
private const CHR_TAB = 9;
|
|
||||||
private const CHR_LF = 10;
|
|
||||||
private const CHR_CR = 13;
|
|
||||||
private const CHR_ESC = 27;
|
|
||||||
private const CHR_BACKSPACE = 127;
|
|
||||||
|
|
||||||
public const LOG_NONE = -1;
|
|
||||||
public const LOG_BLANK = 0;
|
|
||||||
public const LOG_ERROR = LOG_LEVEL_ERROR;
|
|
||||||
public const LOG_WARN = LOG_LEVEL_WARN;
|
|
||||||
public const LOG_INFO = LOG_LEVEL_INFO;
|
|
||||||
public const LOG_OK = 4;
|
|
||||||
|
|
||||||
private static $logHandle = null;
|
|
||||||
private static $hasReadline = null;
|
|
||||||
|
|
||||||
private static $overwriteLast = false;
|
|
||||||
|
|
||||||
/********************/
|
|
||||||
/* formatted output */
|
|
||||||
/********************/
|
|
||||||
|
|
||||||
public static function writeTable(array $out, bool $timestamp = false, bool $headless = false) : void
|
|
||||||
{
|
|
||||||
if (!$out)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$pads = [];
|
|
||||||
$nCols = 0;
|
|
||||||
|
|
||||||
foreach ($out as $i => $row)
|
|
||||||
{
|
|
||||||
if (!is_array($out[0]))
|
|
||||||
{
|
|
||||||
unset($out[$i]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nCols = max($nCols, count($row));
|
|
||||||
|
|
||||||
for ($j = 0; $j < $nCols; $j++)
|
|
||||||
$pads[$j] = max($pads[$j] ?? 0, mb_strlen(self::purgeEscapes($row[$j] ?? '')));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($out as $i => $row)
|
|
||||||
{
|
|
||||||
for ($j = 0; $j < $nCols; $j++)
|
|
||||||
{
|
|
||||||
if (!isset($row[$j]))
|
|
||||||
break;
|
|
||||||
|
|
||||||
$len = ($pads[$j] - mb_strlen(self::purgeEscapes($row[$j])));
|
|
||||||
for ($k = 0; $k < $len; $k++) // can't use str_pad(). it counts invisible chars.
|
|
||||||
$row[$j] .= ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($i || $headless)
|
|
||||||
self::write(' '.implode(' ' . self::tblDelim(' ') . ' ', $row), self::LOG_NONE, $timestamp);
|
|
||||||
else
|
|
||||||
self::write(self::tblHead(' '.implode(' ', $row)), self::LOG_NONE, $timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$headless)
|
|
||||||
self::write(self::tblHead(str_pad('', array_sum($pads) + count($pads) * 3 - 2)), self::LOG_NONE, $timestamp);
|
|
||||||
|
|
||||||
self::write();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/***********/
|
|
||||||
/* logging */
|
|
||||||
/***********/
|
|
||||||
|
|
||||||
public static function initLogFile(string $file = '') : void
|
|
||||||
{
|
|
||||||
if (!$file)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$file = self::nicePath($file);
|
|
||||||
if (!file_exists($file))
|
|
||||||
self::$logHandle = fopen($file, 'w');
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$logFileParts = pathinfo($file);
|
|
||||||
|
|
||||||
$i = 1;
|
|
||||||
while (file_exists($logFileParts['dirname'].'/'.$logFileParts['filename'].$i.(isset($logFileParts['extension']) ? '.'.$logFileParts['extension'] : '')))
|
|
||||||
$i++;
|
|
||||||
|
|
||||||
$file = $logFileParts['dirname'].'/'.$logFileParts['filename'].$i.(isset($logFileParts['extension']) ? '.'.$logFileParts['extension'] : '');
|
|
||||||
self::$logHandle = fopen($file, 'w');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function tblHead(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[1;48;5;236m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function tblDelim(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[48;5;236m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function grey(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[90m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function red(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[31m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function green(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[32m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function yellow(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[33m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function blue(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[36m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function bold(string $str) : string
|
|
||||||
{
|
|
||||||
return CLI_HAS_E ? "\e[1m".$str."\e[0m" : $str;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function write(string $txt = '', int $lvl = self::LOG_BLANK, bool $timestamp = true, bool $tmpRow = false) : void
|
|
||||||
{
|
|
||||||
$msg = '';
|
|
||||||
if ($txt)
|
|
||||||
{
|
|
||||||
if ($timestamp)
|
|
||||||
$msg = str_pad(date('H:i:s'), 10);
|
|
||||||
|
|
||||||
switch ($lvl)
|
|
||||||
{
|
|
||||||
case self::LOG_ERROR: // red critical error
|
|
||||||
$msg .= '['.self::red('ERR').'] ';
|
|
||||||
break;
|
|
||||||
case self::LOG_WARN: // yellow notice
|
|
||||||
$msg .= '['.self::yellow('WARN').'] ';
|
|
||||||
break;
|
|
||||||
case self::LOG_OK: // green success
|
|
||||||
$msg .= '['.self::green('OK').'] ';
|
|
||||||
break;
|
|
||||||
case self::LOG_INFO: // blue info
|
|
||||||
$msg .= '['.self::blue('INFO').'] ';
|
|
||||||
break;
|
|
||||||
case self::LOG_BLANK:
|
|
||||||
$msg .= ' ';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$msg .= $txt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://shiroyasha.svbtle.com/escape-sequences-a-quick-guide-1#movement_1
|
|
||||||
$msg = (self::$overwriteLast && CLI_HAS_E ? "\e[1G\e[0K" : "\n") . $msg;
|
|
||||||
self::$overwriteLast = $tmpRow;
|
|
||||||
|
|
||||||
fwrite($lvl == self::LOG_ERROR ? STDERR : STDOUT, $msg);
|
|
||||||
|
|
||||||
if (self::$logHandle) // remove control sequences from log
|
|
||||||
fwrite(self::$logHandle, self::purgeEscapes($msg));
|
|
||||||
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function purgeEscapes(string $msg) : string
|
|
||||||
{
|
|
||||||
return preg_replace(["/\e\[[\d;]+[mK]/", "/\e\[\d+G/"], ['', "\n"], $msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function nicePath(string $fileOrPath, string ...$pathParts) : string
|
|
||||||
{
|
|
||||||
$path = '';
|
|
||||||
|
|
||||||
if ($pathParts)
|
|
||||||
{
|
|
||||||
foreach ($pathParts as &$pp)
|
|
||||||
$pp = trim($pp);
|
|
||||||
|
|
||||||
$path .= implode(DIRECTORY_SEPARATOR, $pathParts);
|
|
||||||
}
|
|
||||||
|
|
||||||
$path .= ($path ? DIRECTORY_SEPARATOR : '').trim($fileOrPath);
|
|
||||||
|
|
||||||
// remove double quotes (from erroneous user input), single quotes are
|
|
||||||
// valid chars for filenames and removing those mutilates several wow icons
|
|
||||||
$path = str_replace('"', '', $path);
|
|
||||||
|
|
||||||
if (!$path) // empty strings given. (faulty dbc data?)
|
|
||||||
return '';
|
|
||||||
|
|
||||||
if (DIRECTORY_SEPARATOR == '/') // *nix
|
|
||||||
{
|
|
||||||
$path = str_replace('\\', '/', $path);
|
|
||||||
$path = preg_replace('/\/+/i', '/', $path);
|
|
||||||
}
|
|
||||||
else if (DIRECTORY_SEPARATOR == '\\') // win
|
|
||||||
{
|
|
||||||
$path = str_replace('/', '\\', $path);
|
|
||||||
$path = preg_replace('/\\\\+/i', '\\', $path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
self::write('Dafuq! Your directory separator is "'.DIRECTORY_SEPARATOR.'". Please report this!', self::LOG_ERROR);
|
|
||||||
|
|
||||||
// resolve *nix home shorthand
|
|
||||||
if (!OS_WIN)
|
|
||||||
{
|
|
||||||
if (preg_match('/^~(\w+)\/.*/i', $path, $m))
|
|
||||||
$path = '/home/'.substr($path, 1);
|
|
||||||
else if (substr($path, 0, 2) == '~/')
|
|
||||||
$path = getenv('HOME').substr($path, 1);
|
|
||||||
else if ($path[0] == DIRECTORY_SEPARATOR && substr($path, 0, 6) != '/home/')
|
|
||||||
$path = substr($path, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**************/
|
|
||||||
/* read input */
|
|
||||||
/**************/
|
|
||||||
|
|
||||||
/*
|
|
||||||
since the CLI on WIN ist not interactive, the following things have to be considered
|
|
||||||
you do not receive keystrokes but whole strings upon pressing <Enter> (wich also appends a \r)
|
|
||||||
as such <ESC> and probably other control chars can not be registered
|
|
||||||
this also means, you can't hide input at all, least process it
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static function read(array $fields, ?array &$userInput = []) : bool
|
|
||||||
{
|
|
||||||
// first time set
|
|
||||||
if (self::$hasReadline === null)
|
|
||||||
self::$hasReadline = function_exists('readline_callback_handler_install');
|
|
||||||
|
|
||||||
// prevent default output if able
|
|
||||||
if (self::$hasReadline)
|
|
||||||
readline_callback_handler_install('', function() { });
|
|
||||||
|
|
||||||
if (!STDIN)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
stream_set_blocking(STDIN, false);
|
|
||||||
|
|
||||||
// pad default values onto $fields
|
|
||||||
array_walk($fields, function(&$val, $_, $pad) { $val += $pad; }, ['', false, false, '']);
|
|
||||||
|
|
||||||
foreach ($fields as $name => [$desc, $isHidden, $singleChar, $validPattern])
|
|
||||||
{
|
|
||||||
$charBuff = '';
|
|
||||||
|
|
||||||
if ($desc)
|
|
||||||
fwrite(STDOUT, "\n".$desc.": ");
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (feof(STDIN))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$r = [STDIN];
|
|
||||||
$w = $e = null;
|
|
||||||
$n = stream_select($r, $w, $e, 200000);
|
|
||||||
|
|
||||||
if (!$n || !in_array(STDIN, $r))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// stream_get_contents is always blocking under WIN - fgets should work similary as php always receives a terminated line of text
|
|
||||||
$chars = str_split(OS_WIN ? fgets(STDIN) : stream_get_contents(STDIN));
|
|
||||||
$ordinals = array_map('ord', $chars);
|
|
||||||
|
|
||||||
if ($ordinals[0] == self::CHR_ESC)
|
|
||||||
{
|
|
||||||
if (count($ordinals) == 1)
|
|
||||||
{
|
|
||||||
fwrite(STDOUT, chr(self::CHR_BELL));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($chars as $idx => $char)
|
|
||||||
{
|
|
||||||
$keyId = $ordinals[$idx];
|
|
||||||
|
|
||||||
// skip char if horizontal tab or \r if followed by \n
|
|
||||||
if ($keyId == self::CHR_TAB || ($keyId == self::CHR_CR && ($ordinals[$idx + 1] ?? '') == self::CHR_LF))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if ($keyId == self::CHR_BACKSPACE)
|
|
||||||
{
|
|
||||||
if (!$charBuff)
|
|
||||||
continue 2;
|
|
||||||
|
|
||||||
$charBuff = mb_substr($charBuff, 0, -1);
|
|
||||||
if (!$isHidden && self::$hasReadline)
|
|
||||||
fwrite(STDOUT, chr(self::CHR_BACK)." ".chr(self::CHR_BACK));
|
|
||||||
}
|
|
||||||
// standalone \n or \r
|
|
||||||
else if ($keyId == self::CHR_LF || $keyId == self::CHR_CR)
|
|
||||||
{
|
|
||||||
$userInput[$name] = $charBuff;
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
else if (!$validPattern || preg_match($validPattern, $char))
|
|
||||||
{
|
|
||||||
$charBuff .= $char;
|
|
||||||
if (!$isHidden && self::$hasReadline)
|
|
||||||
fwrite(STDOUT, $char);
|
|
||||||
|
|
||||||
if ($singleChar && self::$hasReadline)
|
|
||||||
{
|
|
||||||
$userInput[$name] = $charBuff;
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite(STDOUT, chr(self::CHR_BELL));
|
|
||||||
|
|
||||||
foreach ($userInput as $ui)
|
|
||||||
if (strlen($ui))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
$userInput = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class Timer
|
|
||||||
{
|
|
||||||
private $t_cur = 0;
|
|
||||||
private $t_new = 0;
|
|
||||||
private $intv = 0;
|
|
||||||
|
|
||||||
public function __construct(int $intervall)
|
|
||||||
{
|
|
||||||
$this->intv = $intervall / 1000; // in msec
|
|
||||||
$this->t_cur = microtime(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update() : bool
|
|
||||||
{
|
|
||||||
$this->t_new = microtime(true);
|
|
||||||
if ($this->t_new > $this->t_cur + $this->intv)
|
|
||||||
{
|
|
||||||
$this->t_cur = $this->t_cur + $this->intv;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reset() : void
|
|
||||||
{
|
|
||||||
$this->t_cur = microtime(true) - $this->intv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
26
includes/shared.php
Normal file
26
includes/shared.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
define('AOWOW_REVISION', 1);
|
||||||
|
define('CLI', PHP_SAPI === 'cli');
|
||||||
|
|
||||||
|
|
||||||
|
$reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring'];
|
||||||
|
$error = '';
|
||||||
|
foreach ($reqExt as $r)
|
||||||
|
if (!extension_loaded($r))
|
||||||
|
$error .= 'Required Extension <b>'.$r."</b> was not found. Please check if it should exist, using \"<i>php -m</i>\"\n\n";
|
||||||
|
|
||||||
|
if (version_compare(PHP_VERSION, '5.5.0') < 0)
|
||||||
|
$error .= 'PHP Version <b>5.5.0</b> or higher required! Your version is <b>'.PHP_VERSION."</b>.\nCore functions are unavailable!\n";
|
||||||
|
|
||||||
|
if ($error)
|
||||||
|
{
|
||||||
|
echo CLI ? strip_tags($error) : $error;
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// include all necessities, set up basics
|
||||||
|
require_once 'includes/kernel.php';
|
||||||
|
|
||||||
|
?>
|
||||||
1
includes/tools/BLPConverter
Submodule
1
includes/tools/BLPConverter
Submodule
Submodule includes/tools/BLPConverter added at 7217e13598
1
includes/tools/MPQExtractor
Submodule
1
includes/tools/MPQExtractor
Submodule
Submodule includes/tools/MPQExtractor added at 32c72bb5f0
@@ -1,260 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
abstract class Type
|
|
||||||
{
|
|
||||||
public const NPC = 1;
|
|
||||||
public const OBJECT = 2;
|
|
||||||
public const ITEM = 3;
|
|
||||||
public const ITEMSET = 4;
|
|
||||||
public const QUEST = 5;
|
|
||||||
public const SPELL = 6;
|
|
||||||
public const ZONE = 7;
|
|
||||||
public const FACTION = 8;
|
|
||||||
public const PET = 9;
|
|
||||||
public const ACHIEVEMENT = 10;
|
|
||||||
public const TITLE = 11;
|
|
||||||
public const WORLDEVENT = 12;
|
|
||||||
public const CHR_CLASS = 13;
|
|
||||||
public const CHR_RACE = 14;
|
|
||||||
public const SKILL = 15;
|
|
||||||
public const STATISTIC = 16;
|
|
||||||
public const CURRENCY = 17;
|
|
||||||
// PROJECT = 18;
|
|
||||||
public const SOUND = 19;
|
|
||||||
// BUILDING = 20;
|
|
||||||
// FOLLOWER = 21;
|
|
||||||
// MISSION_ABILITY = 22;
|
|
||||||
// MISSION = 23;
|
|
||||||
// SHIP = 25;
|
|
||||||
// THREAT = 26;
|
|
||||||
// RESOURCE = 27;
|
|
||||||
// CHAMPION = 28;
|
|
||||||
public const ICON = 29;
|
|
||||||
// ORDER_ADVANCEMENT = 30;
|
|
||||||
// FOLLOWER_ALLIANCE = 31;
|
|
||||||
// FOLLOWER_HORDE = 32;
|
|
||||||
// SHIP_ALLIANCE = 33;
|
|
||||||
// SHIP_HORDE = 34;
|
|
||||||
// CHAMPION_ALLIANCE = 35;
|
|
||||||
// CHAMPION_HORDE = 36;
|
|
||||||
// TRANSMOG_ITEM = 37;
|
|
||||||
// BFA_CHAMPION = 38;
|
|
||||||
// BFA_CHAMPION_ALLIANCE = 39;
|
|
||||||
// AFFIX = 40;
|
|
||||||
// BFA_CHAMPION_HORDE = 41;
|
|
||||||
// AZERITE_ESSENCE_POWER = 42;
|
|
||||||
// AZERITE_ESSENCE = 43;
|
|
||||||
// STORYLINE = 44;
|
|
||||||
// ADVENTURE_COMBATANT_ABILITY = 46;
|
|
||||||
// ENCOUNTER = 47;
|
|
||||||
// COVENANT = 48;
|
|
||||||
// SOULBIND = 49;
|
|
||||||
// DI_ITEM = 50;
|
|
||||||
// GATHERER_SCREENSHOT = 91;
|
|
||||||
// GATHERER_GUIDE_IMAGE = 98;
|
|
||||||
public const PROFILE = 100;
|
|
||||||
// our own things
|
|
||||||
public const GUILD = 101;
|
|
||||||
// TRANSMOG_SET = 101; // future conflict inc.
|
|
||||||
public const ARENA_TEAM = 102;
|
|
||||||
// OUTFIT = 110;
|
|
||||||
// GEAR_SET = 111;
|
|
||||||
// GATHERER_LISTVIEW = 158;
|
|
||||||
// GATHERER_SURVEY_COVENANTS = 161;
|
|
||||||
// NEWS_POST = 162;
|
|
||||||
// BATTLE_PET_ABILITY = 200;
|
|
||||||
public const GUIDE = 300; // should have been 100, but conflicts with old version of Profile/List
|
|
||||||
public const USER = 500;
|
|
||||||
public const EMOTE = 501;
|
|
||||||
public const ENCHANTMENT = 502;
|
|
||||||
public const AREATRIGGER = 503;
|
|
||||||
public const MAIL = 504;
|
|
||||||
// Blizzard API things
|
|
||||||
// MOUNT = -1000;
|
|
||||||
// RECIPE = -1001;
|
|
||||||
// BATTLE_PET = -1002;
|
|
||||||
|
|
||||||
public const FLAG_NONE = 0x0;
|
|
||||||
public const FLAG_RANDOM_SEARCHABLE = 0x1;
|
|
||||||
public const FLAG_FILTRABLE = 0x2;
|
|
||||||
public const FLAG_DB_TYPE = 0x4;
|
|
||||||
public const FLAG_HAS_ICON = 0x8;
|
|
||||||
|
|
||||||
public const IDX_LIST_OBJ = 0;
|
|
||||||
public const IDX_FILE_STR = 1;
|
|
||||||
public const IDX_JSG_TPL = 2;
|
|
||||||
public const IDX_FLAGS = 3;
|
|
||||||
|
|
||||||
private static array $data = array(
|
|
||||||
self::NPC => [__NAMESPACE__ . '\CreatureList', 'npc', 'g_npcs', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::OBJECT => [__NAMESPACE__ . '\GameObjectList', 'object', 'g_objects', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::ITEM => [__NAMESPACE__ . '\ItemList', 'item', 'g_items', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::ITEMSET => [__NAMESPACE__ . '\ItemsetList', 'itemset', 'g_itemsets', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::QUEST => [__NAMESPACE__ . '\QuestList', 'quest', 'g_quests', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::SPELL => [__NAMESPACE__ . '\SpellList', 'spell', 'g_spells', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::ZONE => [__NAMESPACE__ . '\ZoneList', 'zone', 'g_gatheredzones', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::FACTION => [__NAMESPACE__ . '\FactionList', 'faction', 'g_factions', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::PET => [__NAMESPACE__ . '\PetList', 'pet', 'g_pets', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::ACHIEVEMENT => [__NAMESPACE__ . '\AchievementList', 'achievement', 'g_achievements', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::TITLE => [__NAMESPACE__ . '\TitleList', 'title', 'g_titles', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::WORLDEVENT => [__NAMESPACE__ . '\WorldEventList', 'event', 'g_holidays', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::CHR_CLASS => [__NAMESPACE__ . '\CharClassList', 'class', 'g_classes', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::CHR_RACE => [__NAMESPACE__ . '\CharRaceList', 'race', 'g_races', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::SKILL => [__NAMESPACE__ . '\SkillList', 'skill', 'g_skills', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::STATISTIC => [__NAMESPACE__ . '\AchievementList', 'achievement', 'g_achievements', self::FLAG_NONE], // alias for achievements; exists only for Markup
|
|
||||||
self::CURRENCY => [__NAMESPACE__ . '\CurrencyList', 'currency', 'g_gatheredcurrencies', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::SOUND => [__NAMESPACE__ . '\SoundList', 'sound', 'g_sounds', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::ICON => [__NAMESPACE__ . '\IconList', 'icon', 'g_icons', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE | self::FLAG_HAS_ICON],
|
|
||||||
self::GUIDE => [__NAMESPACE__ . '\GuideList', 'guide', '', self::FLAG_NONE],
|
|
||||||
self::PROFILE => [__NAMESPACE__ . '\ProfileList', 'profile', '', self::FLAG_FILTRABLE], // x - not known in javascript
|
|
||||||
self::GUILD => [__NAMESPACE__ . '\GuildList', 'guild', '', self::FLAG_FILTRABLE], // x
|
|
||||||
self::ARENA_TEAM => [__NAMESPACE__ . '\ArenaTeamList', 'arena-team', '', self::FLAG_FILTRABLE], // x
|
|
||||||
self::USER => [__NAMESPACE__ . '\UserList', 'user', 'g_users', self::FLAG_NONE], // x
|
|
||||||
self::EMOTE => [__NAMESPACE__ . '\EmoteList', 'emote', 'g_emotes', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::ENCHANTMENT => [__NAMESPACE__ . '\EnchantmentList', 'enchantment', 'g_enchantments', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::AREATRIGGER => [__NAMESPACE__ . '\AreatriggerList', 'areatrigger', '', self::FLAG_FILTRABLE | self::FLAG_DB_TYPE],
|
|
||||||
self::MAIL => [__NAMESPACE__ . '\MailList', 'mail', '', self::FLAG_RANDOM_SEARCHABLE | self::FLAG_DB_TYPE]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/********************/
|
|
||||||
/* Field Operations */
|
|
||||||
/********************/
|
|
||||||
|
|
||||||
public static function newList(int $type, array $conditions = []) : ?BaseType
|
|
||||||
{
|
|
||||||
if (!self::exists($type))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return new (self::$data[$type][self::IDX_LIST_OBJ])($conditions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function newFilter(string $fileStr, array|string $data, array $opts = []) : ?Filter
|
|
||||||
{
|
|
||||||
$x = self::getFileStringsFor(self::FLAG_FILTRABLE);
|
|
||||||
if ($type = array_search($fileStr, $x))
|
|
||||||
return new (self::$data[$type][self::IDX_LIST_OBJ].'Filter')($data, $opts);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function validateIds(int $type, int|array $ids) : array
|
|
||||||
{
|
|
||||||
if (!self::exists($type))
|
|
||||||
return [];
|
|
||||||
|
|
||||||
if (!(self::$data[$type][self::IDX_FLAGS] & self::FLAG_DB_TYPE))
|
|
||||||
return [];
|
|
||||||
|
|
||||||
return DB::Aowow()->selectCol('SELECT `id` FROM ?# WHERE `id` IN (?a)', self::$data[$type][self::IDX_LIST_OBJ]::$dataTable, (array)$ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function hasIcon(int $type) : bool
|
|
||||||
{
|
|
||||||
return self::exists($type) && self::$data[$type][self::IDX_FLAGS] & self::FLAG_HAS_ICON;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function isRandomSearchable(int $type) : bool
|
|
||||||
{
|
|
||||||
return self::exists($type) && self::$data[$type][self::IDX_FLAGS] & self::FLAG_RANDOM_SEARCHABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFileString(int $type) : string
|
|
||||||
{
|
|
||||||
if (!self::exists($type))
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return self::$data[$type][self::IDX_FILE_STR];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getJSGlobalString(int $type) : string
|
|
||||||
{
|
|
||||||
if (!self::exists($type))
|
|
||||||
return '';
|
|
||||||
|
|
||||||
return self::$data[$type][self::IDX_JSG_TPL];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getJSGlobalTemplate(int $type) : array
|
|
||||||
{
|
|
||||||
if (!self::exists($type) || !self::$data[$type][self::IDX_JSG_TPL])
|
|
||||||
return [];
|
|
||||||
|
|
||||||
// [key, [data], [extraData]]
|
|
||||||
return [self::$data[$type][self::IDX_JSG_TPL], [], []];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function checkClassAttrib(int $type, string $attr, ?int $attrVal = null) : bool
|
|
||||||
{
|
|
||||||
if (!self::exists($type))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return isset((self::$data[$type][self::IDX_LIST_OBJ])::$$attr) && ($attrVal === null || ((self::$data[$type][self::IDX_LIST_OBJ])::$$attr & $attrVal));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getClassAttrib(int $type, string $attr) : mixed
|
|
||||||
{
|
|
||||||
if (!self::exists($type))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return (self::$data[$type][self::IDX_LIST_OBJ])::$$attr ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function exists(int $type) : ?int
|
|
||||||
{
|
|
||||||
return !empty(self::$data[$type]) ? $type : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getIndexFrom(int $idx, string $match) : int
|
|
||||||
{
|
|
||||||
$i = array_search($match, array_column(self::$data, $idx));
|
|
||||||
if ($i === false)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return array_keys(self::$data)[$i];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*********************/
|
|
||||||
/* Column Operations */
|
|
||||||
/*********************/
|
|
||||||
|
|
||||||
public static function getClassesFor(int $flags = 0x0, string $attr = '', ?int $attrVal = null) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::$data as $k => [$o, , , $f])
|
|
||||||
if ($o && (!$flags || $flags & $f))
|
|
||||||
if (!$attr || self::checkClassAttrib($k, $attr, $attrVal))
|
|
||||||
$x[$k] = $o;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getFileStringsFor(int $flags = 0x0) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::$data as $k => [, $s, , $f])
|
|
||||||
if ($s && (!$flags || $flags & $f))
|
|
||||||
$x[$k] = $s;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getJSGTemplatesFor(int $flags = 0x0) : array
|
|
||||||
{
|
|
||||||
$x = [];
|
|
||||||
foreach (self::$data as $k => [, , $a, $f])
|
|
||||||
if ($a && (!$flags || $flags & $f))
|
|
||||||
$x[$k] = $a;
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,24 +8,23 @@ class AchievementList extends BaseType
|
|||||||
{
|
{
|
||||||
use listviewHelper;
|
use listviewHelper;
|
||||||
|
|
||||||
public static $type = Type::ACHIEVEMENT;
|
public static $type = TYPE_ACHIEVEMENT;
|
||||||
public static $brickFile = 'achievement';
|
public static $brickFile = 'achievement';
|
||||||
public static $dataTable = '?_achievement';
|
|
||||||
|
|
||||||
public $criteria = [];
|
public $criteria = [];
|
||||||
|
|
||||||
protected $queryBase = 'SELECT `a`.*, `a`.`id` AS ARRAY_KEY FROM ?_achievement a';
|
protected $queryBase = 'SELECT `a`.*, `a`.`id` AS ARRAY_KEY FROM ?_achievement a';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
'a' => [['ic'], 'o' => 'orderInGroup ASC'],
|
'a' => [['si'], 'o' => 'orderInGroup ASC'],
|
||||||
'ic' => ['j' => ['?_icons ic ON ic.id = a.iconId', true], 's' => ', ic.name AS iconString'],
|
'si' => ['j' => ['?_icons si ON si.id = a.iconId', true], 's' => ', si.iconString'],
|
||||||
'ac' => ['j' => ['?_achievementcriteria AS `ac` ON `ac`.`refAchievementId` = `a`.`id`', true], 'g' => '`a`.`id`']
|
'ac' => ['j' => ['?_achievementcriteria AS `ac` ON `ac`.`refAchievementId` = `a`.`id`', true], 'g' => '`a`.`id`']
|
||||||
);
|
);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
todo: evaluate TC custom-data-tables: a*_criteria_data should be merged on installation
|
todo: evaluate TC custom-data-tables: a*_criteria_data should be merged on installation
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [], $miscData = null)
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions, $miscData);
|
||||||
|
|
||||||
@@ -36,24 +33,7 @@ class AchievementList extends BaseType
|
|||||||
|
|
||||||
// post processing
|
// post processing
|
||||||
$rewards = DB::World()->select('
|
$rewards = DB::World()->select('
|
||||||
SELECT
|
SELECT ar.entry AS ARRAY_KEY, ar.*, lar.* FROM achievement_reward ar LEFT JOIN locales_achievement_reward lar ON lar.entry = ar.entry WHERE ar.entry IN (?a)',
|
||||||
ar.ID AS ARRAY_KEY, ar.TitleA, ar.TitleH, ar.ItemID, ar.Sender AS sender, ar.MailTemplateID,
|
|
||||||
ar.Subject AS subject_loc0, IFNULL(arl2.Subject, "") AS subject_loc2, IFNULL(arl3.Subject, "") AS subject_loc3, IFNULL(arl4.Subject, "") AS subject_loc4, IFNULL(arl6.Subject, "") AS subject_loc6, IFNULL(arl8.Subject, "") AS subject_loc8,
|
|
||||||
ar.Body AS text_loc0, IFNULL(arl2.Body, "") AS text_loc2, IFNULL(arl3.Body, "") AS text_loc3, IFNULL(arl4.Body, "") AS text_loc4, IFNULL(arl6.Body, "") AS text_loc6, IFNULL(arl8.Body, "") AS text_loc8
|
|
||||||
FROM
|
|
||||||
achievement_reward ar
|
|
||||||
LEFT JOIN
|
|
||||||
achievement_reward_locale arl2 ON arl2.ID = ar.ID AND arl2.Locale = "frFR"
|
|
||||||
LEFT JOIN
|
|
||||||
achievement_reward_locale arl3 ON arl3.ID = ar.ID AND arl3.Locale = "deDE"
|
|
||||||
LEFT JOIN
|
|
||||||
achievement_reward_locale arl4 ON arl4.ID = ar.ID AND arl4.Locale = "zhCN"
|
|
||||||
LEFT JOIN
|
|
||||||
achievement_reward_locale arl6 ON arl6.ID = ar.ID AND arl6.Locale = "esES"
|
|
||||||
LEFT JOIN
|
|
||||||
achievement_reward_locale arl8 ON arl8.ID = ar.ID AND arl8.Locale = "ruRU"
|
|
||||||
WHERE
|
|
||||||
ar.ID IN (?a)',
|
|
||||||
$this->getFoundIDs()
|
$this->getFoundIDs()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -65,33 +45,31 @@ class AchievementList extends BaseType
|
|||||||
{
|
{
|
||||||
$_curTpl = array_merge($rewards[$_id], $_curTpl);
|
$_curTpl = array_merge($rewards[$_id], $_curTpl);
|
||||||
|
|
||||||
$_curTpl['mailTemplate'] = $rewards[$_id]['MailTemplateID'];
|
if ($rewards[$_id]['mailTemplate'])
|
||||||
|
|
||||||
if ($rewards[$_id]['MailTemplateID'])
|
|
||||||
{
|
{
|
||||||
// using class Loot creates an inifinite loop cirling between Loot, ItemList and SpellList or something
|
// using class Loot creates an inifinite loop cirling between Loot, ItemList and SpellList or something
|
||||||
// $mailSrc = new Loot();
|
// $mailSrc = new Loot();
|
||||||
// $mailSrc->getByContainer(LOOT_MAIL, $rewards[$_id]['MailTemplateID']);
|
// $mailSrc->getByContainer(LOOT_MAIL, $rewards[$_id]['mailTemplate']);
|
||||||
// foreach ($mailSrc->iterate() as $loot)
|
// foreach ($mailSrc->iterate() as $loot)
|
||||||
// $_curTpl['rewards'][] = [Type::ITEM, $loot['id']];
|
// $_curTpl['rewards'][] = [TYPE_ITEM, $loot['id']];
|
||||||
|
|
||||||
// lets just assume for now, that mailRewards for achievements do not contain references
|
// lets just assume for now, that mailRewards for achievements do not contain references
|
||||||
$mailRew = DB::World()->selectCol('SELECT Item FROM mail_loot_template WHERE Reference <= 0 AND entry = ?d', $rewards[$_id]['MailTemplateID']);
|
$mailRew = DB::World()->selectCol('SELECT Item FROM mail_loot_template WHERE Reference <= 0 AND entry = ?d', $rewards[$_id]['mailTemplate']);
|
||||||
foreach ($mailRew AS $mr)
|
foreach ($mailRew AS $mr)
|
||||||
$_curTpl['rewards'][] = [Type::ITEM, $mr];
|
$_curTpl['rewards'][] = [TYPE_ITEM, $mr];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//"rewards":[[11,137],[3,138]] [type, typeId]
|
//"rewards":[[11,137],[3,138]] [type, typeId]
|
||||||
if (!empty($_curTpl['ItemID']))
|
if (!empty($_curTpl['item']))
|
||||||
$_curTpl['rewards'][] = [Type::ITEM, $_curTpl['ItemID']];
|
$_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['item']];
|
||||||
if (!empty($_curTpl['itemExtra']))
|
if (!empty($_curTpl['itemExtra']))
|
||||||
$_curTpl['rewards'][] = [Type::ITEM, $_curTpl['itemExtra']];
|
$_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['itemExtra']];
|
||||||
if (!empty($_curTpl['TitleA']))
|
if (!empty($_curTpl['title_A']))
|
||||||
$_curTpl['rewards'][] = [Type::TITLE, $_curTpl['TitleA']];
|
$_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['title_A']];
|
||||||
if (!empty($_curTpl['TitleH']))
|
if (!empty($_curTpl['title_H']))
|
||||||
if (empty($_curTpl['TitleA']) || $_curTpl['TitleA'] != $_curTpl['TitleH'])
|
if (empty($_curTpl['title_A']) || $_curTpl['title_A'] != $_curTpl['title_H'])
|
||||||
$_curTpl['rewards'][] = [Type::TITLE, $_curTpl['TitleH']];
|
$_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['title_H']];
|
||||||
|
|
||||||
// icon
|
// icon
|
||||||
$_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering';
|
$_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering';
|
||||||
@@ -105,7 +83,7 @@ class AchievementList extends BaseType
|
|||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if ($addMask & GLOBALINFO_SELF)
|
if ($addMask & GLOBALINFO_SELF)
|
||||||
$data[Type::ACHIEVEMENT][$this->id] = ['icon' => $this->curTpl['iconString'], 'name' => $this->getField('name', true)];
|
$data[TYPE_ACHIEVEMENT][$this->id] = ['icon' => $this->curTpl['iconString'], 'name' => $this->getField('name', true)];
|
||||||
|
|
||||||
if ($addMask & GLOBALINFO_REWARDS)
|
if ($addMask & GLOBALINFO_REWARDS)
|
||||||
foreach ($this->curTpl['rewards'] as $_)
|
foreach ($this->curTpl['rewards'] as $_)
|
||||||
@@ -153,7 +131,7 @@ class AchievementList extends BaseType
|
|||||||
if (isset($this->criteria[$this->id]))
|
if (isset($this->criteria[$this->id]))
|
||||||
return $this->criteria[$this->id];
|
return $this->criteria[$this->id];
|
||||||
|
|
||||||
$result = DB::Aowow()->Select('SELECT * FROM ?_achievementcriteria WHERE `refAchievementId` = ?d ORDER BY `order` ASC', $this->curTpl['refAchievement'] ?: $this->id);
|
$result = DB::Aowow()->Select('SELECT * FROM ?_achievementcriteria WHERE `refAchievementId` = ?d ORDER BY `order` ASC', $this->id);
|
||||||
if (!$result)
|
if (!$result)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
@@ -228,25 +206,23 @@ class AchievementList extends BaseType
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$criteria .= '<!--cr'.$crt['id'].':'.$crt['type'].':'.$crt['value1'].'-->- '.$crtName;
|
|
||||||
|
|
||||||
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER)
|
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER)
|
||||||
$criteria .= ' <span class="moneygold">'.Lang::nf($crt['value2' ] / 10000).'</span>';
|
$criteria .= '- '.Util::jsEscape($crtName).' <span class="moneygold">'.number_format($crt['value2' ] / 10000).'</span><br />';
|
||||||
|
else
|
||||||
$criteria .= '<br />';
|
$criteria .= '- '.Util::jsEscape($crtName).'<br />';
|
||||||
|
|
||||||
if (++$i == round(count($rows)/2))
|
if (++$i == round(count($rows)/2))
|
||||||
$criteria .= '</small></td><th class="q0" style="white-space: nowrap; text-align: left"><small>';
|
$criteria .= '</small></td><th class="q0" style="white-space: nowrap; text-align: left"><small>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$x = '<table><tr><td><b class="q">';
|
$x = '<table><tr><td><b class="q">';
|
||||||
$x .= $name;
|
$x .= Util::jsEscape($name);
|
||||||
$x .= '</b></td></tr></table>';
|
$x .= '</b></td></tr></table>';
|
||||||
if ($description || $criteria)
|
if ($description || $criteria)
|
||||||
$x .= '<table><tr><td>';
|
$x .= '<table><tr><td>';
|
||||||
|
|
||||||
if ($description)
|
if ($description)
|
||||||
$x .= '<br />'.$description.'<br />';
|
$x .= '<br />'.Util::jsEscape($description).'<br />';
|
||||||
|
|
||||||
if ($criteria)
|
if ($criteria)
|
||||||
{
|
{
|
||||||
@@ -259,19 +235,16 @@ class AchievementList extends BaseType
|
|||||||
return $x;
|
return $x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSourceData(int $id = 0) : array
|
public function getSourceData()
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if ($id && $id != $this->id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$data[$this->id] = array(
|
$data[$this->id] = array(
|
||||||
"n" => $this->getField('name', true),
|
"n" => $this->getField('name', true),
|
||||||
"s" => $this->curTpl['faction'],
|
"s" => $this->curTpl['faction'],
|
||||||
"t" => Type::ACHIEVEMENT,
|
"t" => TYPE_ACHIEVEMENT,
|
||||||
"ti" => $this->id
|
"ti" => $this->id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -283,12 +256,11 @@ class AchievementList extends BaseType
|
|||||||
|
|
||||||
class AchievementListFilter extends Filter
|
class AchievementListFilter extends Filter
|
||||||
{
|
{
|
||||||
protected string $type = 'achievements';
|
|
||||||
protected array $enums = array(
|
protected $enums = array(
|
||||||
4 => parent::ENUM_ZONE, // location
|
|
||||||
11 => array(
|
11 => array(
|
||||||
327 => 160, // Lunar Festival
|
327 => 160, // Lunar Festival
|
||||||
423 => 187, // Love is in the Air
|
335 => 187, // Love is in the Air
|
||||||
181 => 159, // Noblegarden
|
181 => 159, // Noblegarden
|
||||||
201 => 163, // Children's Week
|
201 => 163, // Children's Week
|
||||||
341 => 161, // Midsummer Fire Festival
|
341 => 161, // Midsummer Fire Festival
|
||||||
@@ -298,110 +270,134 @@ class AchievementListFilter extends Filter
|
|||||||
141 => 156, // Feast of Winter Veil
|
141 => 156, // Feast of Winter Veil
|
||||||
409 => -3456, // Day of the Dead
|
409 => -3456, // Day of the Dead
|
||||||
398 => -3457, // Pirates' Day
|
398 => -3457, // Pirates' Day
|
||||||
parent::ENUM_ANY => true,
|
FILTER_ENUM_ANY => true,
|
||||||
parent::ENUM_NONE => false,
|
FILTER_ENUM_NONE => false,
|
||||||
283 => -1, // valid events without achievements
|
283 => -1, // valid events without achievements
|
||||||
285 => -1, 353 => -1, 420 => -1,
|
285 => -1, 353 => -1, 420 => -1,
|
||||||
400 => -1, 284 => -1, 374 => -1,
|
400 => -1, 284 => -1, 374 => -1,
|
||||||
321 => -1, 424 => -1, 301 => -1
|
321 => -1, 424 => -1, 301 => -1
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
|
||||||
protected array $genericFilter = array(
|
2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward
|
||||||
2 => [parent::CR_BOOLEAN, 'reward_loc0', true ], // givesreward
|
3 => [FILTER_CR_STRING, 'reward', true ], // rewardtext
|
||||||
3 => [parent::CR_STRING, 'reward', STR_LOCALIZED ], // rewardtext
|
7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries
|
||||||
4 => [parent::CR_NYI_PH, null, 1, ], // location [enum]
|
9 => [FILTER_CR_NUMERIC, 'id', null, true], // id
|
||||||
5 => [parent::CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_FIRST_SERIES, null], // first in series [yn]
|
10 => [FILTER_CR_STRING, 'si.iconString', ], // icon
|
||||||
6 => [parent::CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_LAST_SERIES, null], // last in series [yn]
|
18 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags
|
||||||
7 => [parent::CR_BOOLEAN, 'chainId', ], // partseries
|
14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||||
9 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||||
10 => [parent::CR_STRING, 'ic.name', ], // icon
|
16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||||
11 => [parent::CR_CALLBACK, 'cbRelEvent', null, null], // related event [enum]
|
|
||||||
14 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
|
||||||
15 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
|
||||||
16 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
|
||||||
18 => [parent::CR_STAFFFLAG, 'flags', ] // flags
|
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $inputFields = array(
|
protected function createSQLForCriterium(&$cr)
|
||||||
'cr' => [parent::V_RANGE, [2, 18], true ], // criteria ids
|
{
|
||||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters
|
{
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / description - only printable chars, no delimiter
|
if ($genCr = $this->genericCriterion($cr))
|
||||||
'ex' => [parent::V_EQUAL, 'on', false], // extended name search
|
return $genCr;
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'si' => [parent::V_LIST, [SIDE_ALLIANCE, SIDE_HORDE, SIDE_BOTH, -SIDE_ALLIANCE, -SIDE_HORDE], false], // side
|
|
||||||
'minpt' => [parent::V_RANGE, [1, 99], false], // required level min
|
|
||||||
'maxpt' => [parent::V_RANGE, [1, 99], false] // required level max
|
|
||||||
);
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
unset($cr);
|
||||||
|
$this->error = true;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($cr[0])
|
||||||
|
{
|
||||||
|
case 4: // location [enum]
|
||||||
|
/* todo */ return [1]; // no plausible locations parsed yet
|
||||||
|
case 5: // first in series [yn]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', ACHIEVEMENT_CU_FIRST_SERIES, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', ACHIEVEMENT_CU_FIRST_SERIES, '&'], 0]];
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 6: // last in series [yn]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', ACHIEVEMENT_CU_LAST_SERIES, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', ACHIEVEMENT_CU_LAST_SERIES, '&'], 0]];
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 11: // Related Event [enum]
|
||||||
|
$_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null;
|
||||||
|
if ($_ !== null)
|
||||||
|
{
|
||||||
|
if (is_int($_))
|
||||||
|
return ($_ > 0) ? ['category', $_] : ['id', abs($_)];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$ids = array_filter($this->enums[$cr[0]], function($x) {
|
||||||
|
return is_int($x) && $x > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ['category', $ids, $_ ? null : '!'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($cr);
|
||||||
|
$this->error = true;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createSQLForValues()
|
||||||
{
|
{
|
||||||
$parts = [];
|
$parts = [];
|
||||||
$_v = &$this->values;
|
$_v = &$this->fiData['v'];
|
||||||
|
|
||||||
// name ex: +description, +rewards
|
// name ex: +description, +rewards
|
||||||
if ($_v['na'])
|
if (isset($_v['na']))
|
||||||
{
|
{
|
||||||
$_ = [];
|
$_ = [];
|
||||||
if ($_v['ex'] == 'on')
|
if (isset($_v['ex']) && $_v['ex'] == 'on')
|
||||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'reward_loc'.Lang::getLocale()->value, 'description_loc'.Lang::getLocale()->value]);
|
$_ = $this->modularizeString(['name_loc'.User::$localeId, 'reward_loc'.User::$localeId, 'description_loc'.User::$localeId]);
|
||||||
else
|
else
|
||||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]);
|
$_ = $this->modularizeString(['name_loc'.User::$localeId]);
|
||||||
|
|
||||||
if ($_)
|
if ($_)
|
||||||
$parts[] = $_;
|
$parts[] = $_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// points min
|
// points min
|
||||||
if ($_v['minpt'])
|
if (isset($_v['minpt']))
|
||||||
$parts[] = ['points', $_v['minpt'], '>='];
|
{
|
||||||
|
if ($this->isSaneNumeric($_v['minpt']))
|
||||||
|
$parts[] = ['points', $_v['minpt'], '>='];
|
||||||
|
else
|
||||||
|
unset($_v['minpt']);
|
||||||
|
}
|
||||||
|
|
||||||
// points max
|
// points max
|
||||||
if ($_v['maxpt'])
|
if (isset($_v['maxpt']))
|
||||||
$parts[] = ['points', $_v['maxpt'], '<='];
|
{
|
||||||
|
if ($this->isSaneNumeric($_v['maxpt']))
|
||||||
|
$parts[] = ['points', $_v['maxpt'], '<='];
|
||||||
|
else
|
||||||
|
unset($_v['maxpt']);
|
||||||
|
}
|
||||||
|
|
||||||
// faction (side)
|
// faction (side)
|
||||||
if ($_v['si'])
|
if (isset($_v['si']))
|
||||||
{
|
{
|
||||||
$parts[] = match ($_v['si'])
|
switch ($_v['si'])
|
||||||
{
|
{
|
||||||
-SIDE_ALLIANCE, // equals faction
|
case 3: // both
|
||||||
-SIDE_HORDE => ['faction', -$_v['si']],
|
$parts[] = ['faction', 0];
|
||||||
SIDE_ALLIANCE, // includes faction
|
break;
|
||||||
SIDE_HORDE,
|
case -1: // faction, exclusive both
|
||||||
SIDE_BOTH => ['faction', $_v['si'], '&']
|
case -2:
|
||||||
};
|
$parts[] = ['faction', -$_v['si']];
|
||||||
|
break;
|
||||||
|
case 1: // faction, inclusive both
|
||||||
|
case 2:
|
||||||
|
$parts[] = ['OR', ['faction', 0], ['faction', $_v['si']]];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unset($_v['si']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $parts;
|
return $parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cbRelEvent(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!isset($this->enums[$cr][$crs]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$_ = $this->enums[$cr][$crs];
|
|
||||||
if (is_int($_))
|
|
||||||
return ($_ > 0) ? ['category', $_] : ['id', abs($_)];
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$ids = array_filter($this->enums[$cr], fn($x) => is_int($x) && $x > 0);
|
|
||||||
|
|
||||||
return ['category', $ids, $_ ? null : '!'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbSeries(int $cr, int $crs, string $crv, int $seriesFlag) : ?array
|
|
||||||
{
|
|
||||||
if ($this->int2Bool($crs))
|
|
||||||
return $crs ? ['AND', ['chainId', 0, '!'], ['cuFlags', $seriesFlag, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', $seriesFlag, '&'], 0]];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class AreaTriggerList extends BaseType
|
|
||||||
{
|
|
||||||
use spawnHelper;
|
|
||||||
|
|
||||||
public static $type = Type::AREATRIGGER;
|
|
||||||
public static $brickFile = 'areatrigger';
|
|
||||||
public static $dataTable = '?_areatrigger';
|
|
||||||
public static $contribute = CONTRIBUTE_CO;
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT a.*, a.id AS ARRAY_KEY FROM ?_areatrigger a';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'a' => [['s']], // guid < 0 are teleporter targets, so exclude them here
|
|
||||||
's' => ['j' => ['?_spawns s ON s.`type` = 503 AND s.`typeId` = a.`id` AND s.`guid` > 0', true], 's' => ', GROUP_CONCAT(s.`areaId`) AS "areaId"', 'g' => 'a.`id`']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $id => &$_curTpl)
|
|
||||||
if (!$_curTpl['name'])
|
|
||||||
$_curTpl['name'] = 'Unnamed Areatrigger #' . $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getListviewData() : array
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->curTpl['id'],
|
|
||||||
'type' => $this->curTpl['type'],
|
|
||||||
'name' => $this->curTpl['name'],
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($_ = $this->curTpl['areaId'])
|
|
||||||
$data[$this->id]['location'] = explode(',', $_);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = GLOBALINFO_ANY)
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
class AreaTriggerListFilter extends Filter
|
|
||||||
{
|
|
||||||
protected string $type = 'areatrigger';
|
|
||||||
protected array $genericFilter = array(
|
|
||||||
2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT] // id
|
|
||||||
);
|
|
||||||
|
|
||||||
// fieldId => [checkType, checkValue[, fieldIsArray]]
|
|
||||||
protected array $inputFields = array(
|
|
||||||
'cr' => [parent::V_LIST, [2], true ], // criteria ids
|
|
||||||
'crs' => [parent::V_RANGE, [1, 6], true ], // criteria operators
|
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - all criteria are numeric here
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'ty' => [parent::V_RANGE, [0, 5], true ] // types
|
|
||||||
);
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = &$this->values;
|
|
||||||
|
|
||||||
// name [str]
|
|
||||||
if ($_v['na'])
|
|
||||||
if ($_ = $this->tokenizeString(['name']))
|
|
||||||
$parts[] = $_;
|
|
||||||
|
|
||||||
// type [list]
|
|
||||||
if ($_v['ty'])
|
|
||||||
$parts[] = ['type', $_v['ty']];
|
|
||||||
|
|
||||||
return $parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,383 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class ArenaTeamList extends BaseType
|
|
||||||
{
|
|
||||||
use profilerHelper, listviewHelper;
|
|
||||||
|
|
||||||
private $rankOrder = [];
|
|
||||||
|
|
||||||
public static $contribute = CONTRIBUTE_NONE;
|
|
||||||
|
|
||||||
public function getListviewData()
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'name' => $this->curTpl['name'],
|
|
||||||
'realm' => Profiler::urlize($this->curTpl['realmName'], true),
|
|
||||||
'realmname' => $this->curTpl['realmName'],
|
|
||||||
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
|
|
||||||
// 'battlegroupname' => $this->curTpl['battlegroup'],
|
|
||||||
'region' => Profiler::urlize($this->curTpl['region']),
|
|
||||||
'faction' => $this->curTpl['faction'],
|
|
||||||
'size' => $this->curTpl['type'],
|
|
||||||
'rank' => $this->curTpl['rank'],
|
|
||||||
'wins' => $this->curTpl['seasonWins'],
|
|
||||||
'games' => $this->curTpl['seasonGames'],
|
|
||||||
'rating' => $this->curTpl['rating'],
|
|
||||||
'members' => $this->curTpl['members']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() {}
|
|
||||||
public function getJSGlobals($addMask = 0) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ArenaTeamListFilter extends Filter
|
|
||||||
{
|
|
||||||
use TrProfilerFilter;
|
|
||||||
|
|
||||||
protected string $type = 'arenateams';
|
|
||||||
protected array $genericFilter = [];
|
|
||||||
protected array $inputFields = array(
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'ex' => [parent::V_EQUAL, 'on', false], // only match exact
|
|
||||||
'si' => [parent::V_LIST, [1, 2], false], // side
|
|
||||||
'sz' => [parent::V_LIST, [2, 3, 5], false], // tema size
|
|
||||||
'rg' => [parent::V_CALLBACK, 'cbRegionCheck', false], // region
|
|
||||||
'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server
|
|
||||||
);
|
|
||||||
|
|
||||||
public array $extraOpts = [];
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = $this->values;
|
|
||||||
|
|
||||||
// region (rg), battlegroup (bg) and server (sv) are passed to ArenaTeamList as miscData and handled there
|
|
||||||
|
|
||||||
// name [str]
|
|
||||||
if ($_v['na'])
|
|
||||||
if ($_ = $this->tokenizeString(['at.name'], $_v['na'], $_v['ex'] == 'on'))
|
|
||||||
$parts[] = $_;
|
|
||||||
|
|
||||||
// side [list]
|
|
||||||
if ($_v['si'] == SIDE_ALLIANCE)
|
|
||||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)];
|
|
||||||
else if ($_v['si'] == SIDE_HORDE)
|
|
||||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)];
|
|
||||||
|
|
||||||
// size [int]
|
|
||||||
if ($_v['sz'])
|
|
||||||
$parts[] = ['at.type', $_v['sz']];
|
|
||||||
|
|
||||||
return $parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteArenaTeamList extends ArenaTeamList
|
|
||||||
{
|
|
||||||
protected $queryBase = 'SELECT `at`.*, `at`.`arenaTeamId` AS ARRAY_KEY FROM arena_team at';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'at' => [['atm', 'c'], 'g' => 'ARRAY_KEY', 'o' => 'rating DESC'],
|
|
||||||
'atm' => ['j' => 'arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId'],
|
|
||||||
'c' => ['j' => 'characters c ON c.guid = atm.guid AND c.deleteInfos_Account IS NULL AND c.level <= 80 AND (c.extra_flags & '.Profiler::CHAR_GMFLAGS.') = 0', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
|
|
||||||
);
|
|
||||||
|
|
||||||
private $members = [];
|
|
||||||
private $rankOrder = [];
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
// select DB by realm
|
|
||||||
if (!$this->selectRealms($miscData))
|
|
||||||
{
|
|
||||||
trigger_error('RemoteArenaTeamList::__construct - cannot access any realm.', E_USER_WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ranks in DB are inaccurate. recalculate from rating (fetched as DESC from DB)
|
|
||||||
foreach ($this->dbNames as $rId => $__)
|
|
||||||
foreach ([2, 3, 5] as $type)
|
|
||||||
$this->rankOrder[$rId][$type] = DB::Characters($rId)->selectCol('SELECT arenaTeamId FROM arena_team WHERE `type` = ?d ORDER BY rating DESC', $type);
|
|
||||||
|
|
||||||
reset($this->dbNames); // only use when querying single realm
|
|
||||||
$realmId = key($this->dbNames);
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
$distrib = [];
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as $guid => &$curTpl)
|
|
||||||
{
|
|
||||||
// battlegroup
|
|
||||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
|
||||||
|
|
||||||
// realm, rank
|
|
||||||
$r = explode(':', $guid);
|
|
||||||
if (!empty($realms[$r[0]]))
|
|
||||||
{
|
|
||||||
$curTpl['realm'] = $r[0];
|
|
||||||
$curTpl['realmName'] = $realms[$r[0]]['name'];
|
|
||||||
$curTpl['region'] = $realms[$r[0]]['region'];
|
|
||||||
$curTpl['rank'] = array_search($curTpl['arenaTeamId'], $this->rankOrder[$r[0]][$curTpl['type']]) + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('arena team #'.$guid.' belongs to nonexistant realm #'.$r, E_USER_WARNING);
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty name
|
|
||||||
if (!$curTpl['name'])
|
|
||||||
{
|
|
||||||
trigger_error('arena team #'.$guid.' on realm #'.$r.' has empty name.', E_USER_WARNING);
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// team members
|
|
||||||
$this->members[$r[0]][$r[1]] = $r[1];
|
|
||||||
|
|
||||||
// equalize distribution
|
|
||||||
if (empty($distrib[$curTpl['realm']]))
|
|
||||||
$distrib[$curTpl['realm']] = 1;
|
|
||||||
else
|
|
||||||
$distrib[$curTpl['realm']]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get team members
|
|
||||||
foreach ($this->members as $realmId => &$teams)
|
|
||||||
$teams = DB::Characters($realmId)->select('
|
|
||||||
SELECT
|
|
||||||
at.arenaTeamId AS ARRAY_KEY, c.guid AS ARRAY_KEY2, c.name AS "0", c.class AS "1", IF(at.captainguid = c.guid, 1, 0) AS "2"
|
|
||||||
FROM
|
|
||||||
arena_team at
|
|
||||||
JOIN
|
|
||||||
arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId JOIN characters c ON c.guid = atm.guid
|
|
||||||
WHERE
|
|
||||||
at.arenaTeamId IN (?a) AND
|
|
||||||
c.deleteInfos_Account IS NULL AND
|
|
||||||
c.level <= ?d AND
|
|
||||||
(c.extra_flags & ?d) = 0',
|
|
||||||
$teams,
|
|
||||||
MAX_LEVEL,
|
|
||||||
Profiler::CHAR_GMFLAGS
|
|
||||||
);
|
|
||||||
|
|
||||||
// equalize subject distribution across realms
|
|
||||||
foreach ($conditions as $c)
|
|
||||||
if (is_int($c))
|
|
||||||
$limit = $c;
|
|
||||||
|
|
||||||
$limit ??= Cfg::get('SQL_LIMIT_DEFAULT');
|
|
||||||
if (!$limit) // int:0 means unlimited, so skip early
|
|
||||||
return;
|
|
||||||
|
|
||||||
$total = array_sum($distrib);
|
|
||||||
foreach ($distrib as &$d)
|
|
||||||
$d = ceil($limit * $d / $total);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $guid => &$curTpl)
|
|
||||||
{
|
|
||||||
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
|
|
||||||
{
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$r = explode(':', $guid);
|
|
||||||
if (isset($this->members[$r[0]][$r[1]]))
|
|
||||||
$curTpl['members'] = array_values($this->members[$r[0]][$r[1]]); // [name, classId, isCaptain]
|
|
||||||
|
|
||||||
$distrib[$curTpl['realm']]--;
|
|
||||||
$limit--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function initializeLocalEntries() : void
|
|
||||||
{
|
|
||||||
if (!$this->templates)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$profiles = [];
|
|
||||||
// init members for tooltips
|
|
||||||
foreach ($this->members as $realmId => $teams)
|
|
||||||
{
|
|
||||||
$gladiators = [];
|
|
||||||
foreach ($teams as $team)
|
|
||||||
$gladiators = array_merge($gladiators, array_keys($team));
|
|
||||||
|
|
||||||
$profiles[$realmId] = new RemoteProfileList(array(['c.guid', $gladiators], Cfg::get('SQL_LIMIT_NONE')), ['sv' => $realmId]);
|
|
||||||
|
|
||||||
if (!$profiles[$realmId]->error)
|
|
||||||
$profiles[$realmId]->initializeLocalEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
foreach ($this->iterate() as $guid => $__)
|
|
||||||
{
|
|
||||||
$data[$guid] = array(
|
|
||||||
'realm' => $this->getField('realm'),
|
|
||||||
'realmGUID' => $this->getField('arenaTeamId'),
|
|
||||||
'name' => $this->getField('name'),
|
|
||||||
'nameUrl' => Profiler::urlize($this->getField('name')),
|
|
||||||
'type' => $this->getField('type'),
|
|
||||||
'rating' => $this->getField('rating'),
|
|
||||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// basic arena team data
|
|
||||||
foreach (Util::createSqlBatchInsert($data) as $ins)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `id` = `id`', array_keys(reset($data)));
|
|
||||||
|
|
||||||
// merge back local ids
|
|
||||||
$localIds = DB::Aowow()->selectCol(
|
|
||||||
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id FROM ?_profiler_arena_team WHERE realm IN (?a) AND realmGUID IN (?a)',
|
|
||||||
array_column($data, 'realm'),
|
|
||||||
array_column($data, 'realmGUID')
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $guid => &$_curTpl)
|
|
||||||
if (isset($localIds[$guid]))
|
|
||||||
$_curTpl['id'] = $localIds[$guid];
|
|
||||||
|
|
||||||
|
|
||||||
// profiler_arena_team_member requires profiles and arena teams to be filled
|
|
||||||
foreach ($this->members as $realmId => $teams)
|
|
||||||
{
|
|
||||||
if (empty($profiles[$realmId]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$memberData = [];
|
|
||||||
foreach ($teams as $teamId => $team)
|
|
||||||
{
|
|
||||||
$clearMembers = [];
|
|
||||||
foreach ($team as $memberId => $member)
|
|
||||||
{
|
|
||||||
$clearMembers[] = $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'];
|
|
||||||
$memberData[] = array(
|
|
||||||
'arenaTeamId' => $localIds[$realmId.':'.$teamId],
|
|
||||||
'profileId' => $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'],
|
|
||||||
'captain' => $member[2]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete members from other teams of the same type
|
|
||||||
DB::Aowow()->query(
|
|
||||||
'DELETE atm
|
|
||||||
FROM ?_profiler_arena_team_member atm
|
|
||||||
JOIN ?_profiler_arena_team at ON atm.`arenaTeamId` = at.`id` AND at.`type` = ?d
|
|
||||||
WHERE atm.`profileId` IN (?a)',
|
|
||||||
$data[$realmId.':'.$teamId]['type'] ?? 0,
|
|
||||||
$clearMembers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (Util::createSqlBatchInsert($memberData) as $ins)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `profileId` = `profileId`', array_keys(reset($memberData)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LocalArenaTeamList extends ArenaTeamList
|
|
||||||
{
|
|
||||||
protected $queryBase = 'SELECT at.*, at.id AS ARRAY_KEY FROM ?_profiler_arena_team at';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'at' => [['atm', 'c'], 'g' => 'ARRAY_KEY', 'o' => 'rating DESC'],
|
|
||||||
'atm' => ['j' => '?_profiler_arena_team_member atm ON atm.arenaTeamId = at.id'],
|
|
||||||
'c' => ['j' => '?_profiler_profiles c ON c.id = atm.profileId', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
|
|
||||||
// graft realm selection from miscData onto conditions
|
|
||||||
if (isset($miscData['sv']))
|
|
||||||
$realms = array_filter($realms, fn($x) => Profiler::urlize($x['name']) == Profiler::urlize($miscData['sv']));
|
|
||||||
|
|
||||||
if (isset($miscData['rg']))
|
|
||||||
$realms = array_filter($realms, fn($x) => $x['region'] == $miscData['rg']);
|
|
||||||
|
|
||||||
if (!$realms)
|
|
||||||
{
|
|
||||||
trigger_error('LocalArenaTeamList::__construct - cannot access any realm.', E_USER_WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($conditions)
|
|
||||||
{
|
|
||||||
array_unshift($conditions, 'AND');
|
|
||||||
$conditions = ['AND', ['realm', array_keys($realms)], $conditions];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$conditions = [['realm', array_keys($realms)]];
|
|
||||||
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
$members = DB::Aowow()->select(
|
|
||||||
'SELECT `arenaTeamId` AS ARRAY_KEY, p.`id` AS ARRAY_KEY2, p.`name` AS "0", p.`class` AS "1", atm.`captain` AS "2"
|
|
||||||
FROM ?_profiler_arena_team_member atm
|
|
||||||
JOIN ?_profiler_profiles p ON p.`id` = atm.`profileId`
|
|
||||||
WHERE `arenaTeamId` IN (?a)',
|
|
||||||
$this->getFoundIDs()
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $id => &$curTpl)
|
|
||||||
{
|
|
||||||
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isset($realms[$curTpl['realm']]))
|
|
||||||
{
|
|
||||||
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
|
|
||||||
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// battlegroup
|
|
||||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
|
||||||
|
|
||||||
$curTpl['members'] = array_values($members[$id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getProfileUrl()
|
|
||||||
{
|
|
||||||
$url = '?arena-team=';
|
|
||||||
|
|
||||||
return $url.implode('.', array(
|
|
||||||
Profiler::urlize($this->getField('region')),
|
|
||||||
Profiler::urlize($this->getField('realmName')),
|
|
||||||
Profiler::urlize($this->getField('name'))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class CharClassList extends BaseType
|
class CharClassList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::CHR_CLASS;
|
public static $type = TYPE_CLASS;
|
||||||
public static $brickFile = 'class';
|
public static $brickFile = 'class';
|
||||||
public static $dataTable = '?_classes';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT c.*, id AS ARRAY_KEY FROM ?_classes c';
|
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_classes c';
|
||||||
|
|
||||||
public function __construct($conditions = [], array $miscData = [])
|
public function __construct($conditions = [])
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions);
|
||||||
|
|
||||||
foreach ($this->iterate() as $k => &$_curTpl)
|
foreach ($this->iterate() as $k => &$_curTpl)
|
||||||
$_curTpl['skills'] = explode(' ', $_curTpl['skills']);
|
$_curTpl['skills'] = explode(' ', $_curTpl['skills']);
|
||||||
@@ -58,6 +55,7 @@ class CharClassList extends BaseType
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addRewardsToJScript(&$ref) { }
|
||||||
public function renderTooltip() { }
|
public function renderTooltip() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class CharRaceList extends BaseType
|
class CharRaceList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::CHR_RACE;
|
public static $type = TYPE_RACE;
|
||||||
public static $brickFile = 'race';
|
public static $brickFile = 'race';
|
||||||
public static $dataTable = '?_races';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT r.*, id AS ARRAY_KEY FROM ?_races r';
|
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_races r';
|
||||||
|
|
||||||
public function getListviewData()
|
public function getListviewData()
|
||||||
{
|
{
|
||||||
@@ -42,11 +39,12 @@ class CharRaceList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[Type::CHR_RACE][$this->id] = ['name' => $this->getField('name', true)];
|
$data[TYPE_RACE][$this->id] = ['name' => $this->getField('name', true)];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addRewardsToJScript(&$ref) { }
|
||||||
public function renderTooltip() { }
|
public function renderTooltip() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,13 +8,12 @@ class CreatureList extends BaseType
|
|||||||
{
|
{
|
||||||
use spawnHelper;
|
use spawnHelper;
|
||||||
|
|
||||||
public static $type = Type::NPC;
|
public static $type = TYPE_NPC;
|
||||||
public static $brickFile = 'npc';
|
public static $brickFile = 'creature';
|
||||||
public static $dataTable = '?_creature';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT ct.*, ct.id AS ARRAY_KEY FROM ?_creature ct';
|
protected $queryBase = 'SELECT ct.*, ct.id AS ARRAY_KEY FROM ?_creature ct';
|
||||||
public $queryOpts = array(
|
public $queryOpts = array(
|
||||||
'ct' => [['ft', 'qse', 'dct1', 'dct2', 'dct3'], 's' => ', IFNULL(dct1.id, IFNULL(dct2.id, IFNULL(dct3.id, 0))) AS parentId, IFNULL(dct1.name_loc0, IFNULL(dct2.name_loc0, IFNULL(dct3.name_loc0, ""))) AS parent_loc0, IFNULL(dct1.name_loc2, IFNULL(dct2.name_loc2, IFNULL(dct3.name_loc2, ""))) AS parent_loc2, IFNULL(dct1.name_loc3, IFNULL(dct2.name_loc3, IFNULL(dct3.name_loc3, ""))) AS parent_loc3, IFNULL(dct1.name_loc4, IFNULL(dct2.name_loc4, IFNULL(dct3.name_loc4, ""))) AS parent_loc4, IFNULL(dct1.name_loc6, IFNULL(dct2.name_loc6, IFNULL(dct3.name_loc6, ""))) AS parent_loc6, IFNULL(dct1.name_loc8, IFNULL(dct2.name_loc8, IFNULL(dct3.name_loc8, ""))) AS parent_loc8, IF(dct1.difficultyEntry1 = ct.id, 1, IF(dct2.difficultyEntry2 = ct.id, 2, IF(dct3.difficultyEntry3 = ct.id, 3, 0))) AS difficultyMode'],
|
'ct' => [['ft', 'qse', 'dct1', 'dct2', 'dct3'], 's' => ', IFNULL(dct1.id, IFNULL(dct2.id, IFNULL(dct3.id, 0))) AS parentId, IFNULL(dct1.name_loc0, IFNULL(dct2.name_loc0, IFNULL(dct3.name_loc0, ""))) AS parent_loc0, IFNULL(dct1.name_loc2, IFNULL(dct2.name_loc2, IFNULL(dct3.name_loc2, ""))) AS parent_loc2, IFNULL(dct1.name_loc3, IFNULL(dct2.name_loc3, IFNULL(dct3.name_loc3, ""))) AS parent_loc3, IFNULL(dct1.name_loc6, IFNULL(dct2.name_loc6, IFNULL(dct3.name_loc6, ""))) AS parent_loc6, IFNULL(dct1.name_loc8, IFNULL(dct2.name_loc8, IFNULL(dct3.name_loc8, ""))) AS parent_loc8, IF(dct1.difficultyEntry1 = ct.id, 1, IF(dct2.difficultyEntry2 = ct.id, 2, IF(dct3.difficultyEntry3 = ct.id, 3, 0))) AS difficultyMode'],
|
||||||
'dct1' => ['j' => ['?_creature dct1 ON ct.cuFlags & 0x02 AND dct1.difficultyEntry1 = ct.id', true]],
|
'dct1' => ['j' => ['?_creature dct1 ON ct.cuFlags & 0x02 AND dct1.difficultyEntry1 = ct.id', true]],
|
||||||
'dct2' => ['j' => ['?_creature dct2 ON ct.cuFlags & 0x02 AND dct2.difficultyEntry2 = ct.id', true]],
|
'dct2' => ['j' => ['?_creature dct2 ON ct.cuFlags & 0x02 AND dct2.difficultyEntry2 = ct.id', true]],
|
||||||
'dct3' => ['j' => ['?_creature dct3 ON ct.cuFlags & 0x02 AND dct3.difficultyEntry3 = ct.id', true]],
|
'dct3' => ['j' => ['?_creature dct3 ON ct.cuFlags & 0x02 AND dct3.difficultyEntry3 = ct.id', true]],
|
||||||
@@ -26,7 +23,7 @@ class CreatureList extends BaseType
|
|||||||
's' => ['j' => ['?_spawns s ON s.type = 1 AND s.typeId = ct.id', true]]
|
's' => ['j' => ['?_spawns s ON s.type = 1 AND s.typeId = ct.id', true]]
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [], $miscData = null)
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions, $miscData);
|
||||||
|
|
||||||
@@ -51,7 +48,7 @@ class CreatureList extends BaseType
|
|||||||
|
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_creature WHERE id = ?d', $id);
|
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_creature WHERE id = ?d', $id);
|
||||||
return Util::localizedString($n, 'name');
|
return Util::localizedString($n, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,14 +76,13 @@ class CreatureList extends BaseType
|
|||||||
if ($type)
|
if ($type)
|
||||||
$row3[] = Lang::game('ct', $type);
|
$row3[] = Lang::game('ct', $type);
|
||||||
|
|
||||||
if ($_ = Lang::npc('rank', $this->curTpl['rank']))
|
$row3[] = '('.Lang::npc('rank', $this->curTpl['rank']).')';
|
||||||
$row3[] = '('.$_.')';
|
|
||||||
|
|
||||||
$x = '<table>';
|
$x = '<table>';
|
||||||
$x .= '<tr><td><b class="q">'.Util::htmlEscape($this->getField('name', true)).'</b></td></tr>';
|
$x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>';
|
||||||
|
|
||||||
if ($sn = $this->getField('subname', true))
|
if ($sn = $this->getField('subname', true))
|
||||||
$x .= '<tr><td>'.Util::htmlEscape($sn).'</td></tr>';
|
$x .= '<tr><td>'.$sn.'</td></tr>';
|
||||||
|
|
||||||
$x .= '<tr><td>'.implode(' ', $row3).'</td></tr>';
|
$x .= '<tr><td>'.implode(' ', $row3).'</td></tr>';
|
||||||
|
|
||||||
@@ -104,22 +100,27 @@ class CreatureList extends BaseType
|
|||||||
|
|
||||||
public function getRandomModelId()
|
public function getRandomModelId()
|
||||||
{
|
{
|
||||||
// dwarf?? [null, 30754, 30753, 30755, 30736]
|
|
||||||
// totems use hardcoded models, tauren model is base
|
// totems use hardcoded models, tauren model is base
|
||||||
$totems = [null, 4589, 4588, 4587, 4590]; // slot => modelId
|
$totems = array( // tauren => [orc, dwarf(?!), troll, tauren, draenei]
|
||||||
$data = [];
|
4589 => [30758, 30754, 30762, 4589, 19074], // fire
|
||||||
|
4588 => [30757, 30753, 30761, 4588, 19073], // earth
|
||||||
|
4587 => [30759, 30755, 30763, 4587, 19075], // water
|
||||||
|
4590 => [30756, 30736, 30760, 4590, 19071], // air
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
for ($i = 1; $i < 5; $i++)
|
for ($i = 1; $i < 5; $i++)
|
||||||
if ($_ = $this->curTpl['displayId'.$i])
|
if ($_ = $this->curTpl['displayId'.$i])
|
||||||
$data[] = $_;
|
$data[] = $_;
|
||||||
|
|
||||||
if (count($data) == 1 && ($slotId = array_search($data[0], $totems)))
|
if (count($data) == 1 && in_array($data[0], array_keys($totems)))
|
||||||
$data = DB::World()->selectCol('SELECT DisplayId FROM player_totem_model WHERE TotemSlot = ?d', $slotId);
|
$data = $totems[$data[0]];
|
||||||
|
|
||||||
return !$data ? 0 : $data[array_rand($data)];
|
return !$data ? 0 : $data[array_rand($data)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBaseStats(string $type) : array
|
public function getBaseStats($type)
|
||||||
{
|
{
|
||||||
// i'm aware of the BaseVariance/RangedVariance fields ... i'm just totaly unsure about the whole damage calculation
|
// i'm aware of the BaseVariance/RangedVariance fields ... i'm just totaly unsure about the whole damage calculation
|
||||||
switch ($type)
|
switch ($type)
|
||||||
@@ -144,14 +145,8 @@ class CreatureList extends BaseType
|
|||||||
$rngMin = ($this->getField('dmgMin') + ($this->getField('rngAtkPwrMin') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
|
$rngMin = ($this->getField('dmgMin') + ($this->getField('rngAtkPwrMin') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
|
||||||
$rngMax = ($this->getField('dmgMax') * 1.5 + ($this->getField('rngAtkPwrMax') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
|
$rngMax = ($this->getField('dmgMax') * 1.5 + ($this->getField('rngAtkPwrMax') / 14)) * $this->getField('dmgMultiplier') * $this->getField('rngAtkSpeed');
|
||||||
return [$rngMin, $rngMax];
|
return [$rngMin, $rngMax];
|
||||||
case 'resistance':
|
|
||||||
$r = [];
|
|
||||||
for ($i = SPELL_SCHOOL_HOLY; $i < SPELL_SCHOOL_ARCANE+1; $i++)
|
|
||||||
$r[$i] = $this->getField('resistance'.$i);
|
|
||||||
|
|
||||||
return $r;
|
|
||||||
default:
|
default:
|
||||||
return [];
|
return [0, 0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,22 +161,9 @@ class CreatureList extends BaseType
|
|||||||
*
|
*
|
||||||
* NPCINFO_TAMEABLE (0x1): include texture & react
|
* NPCINFO_TAMEABLE (0x1): include texture & react
|
||||||
* NPCINFO_MODEL (0x2):
|
* NPCINFO_MODEL (0x2):
|
||||||
* NPCINFO_REP (0x4): include repreward
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
$rewRep = [];
|
|
||||||
|
|
||||||
if ($addInfoMask & NPCINFO_REP && $this->getFoundIDs())
|
|
||||||
{
|
|
||||||
$rewRep = DB::World()->selectCol('
|
|
||||||
SELECT creature_id AS ARRAY_KEY, RewOnKillRepFaction1 AS ARRAY_KEY2, RewOnKillRepValue1 FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction1 > 0 UNION
|
|
||||||
SELECT creature_id AS ARRAY_KEY, RewOnKillRepFaction2 AS ARRAY_KEY2, RewOnKillRepValue2 FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction2 > 0',
|
|
||||||
$this->getFoundIDs(),
|
|
||||||
$this->getFoundIDs()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
@@ -225,7 +207,6 @@ class CreatureList extends BaseType
|
|||||||
'react' => [$this->curTpl['A'], $this->curTpl['H']],
|
'react' => [$this->curTpl['A'], $this->curTpl['H']],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
if ($this->getField('startsQuests'))
|
if ($this->getField('startsQuests'))
|
||||||
$data[$this->id]['hasQuests'] = 1;
|
$data[$this->id]['hasQuests'] = 1;
|
||||||
|
|
||||||
@@ -234,14 +215,6 @@ class CreatureList extends BaseType
|
|||||||
|
|
||||||
if ($addInfoMask & NPCINFO_TAMEABLE) // only first skin of first model ... we're omitting potentially 11 skins here .. but the lv accepts only one .. w/e
|
if ($addInfoMask & NPCINFO_TAMEABLE) // only first skin of first model ... we're omitting potentially 11 skins here .. but the lv accepts only one .. w/e
|
||||||
$data[$this->id]['skin'] = $this->curTpl['textureString'];
|
$data[$this->id]['skin'] = $this->curTpl['textureString'];
|
||||||
|
|
||||||
if ($addInfoMask & NPCINFO_REP)
|
|
||||||
{
|
|
||||||
$data[$this->id]['reprewards'] = [];
|
|
||||||
if ($rewRep[$this->id])
|
|
||||||
foreach ($rewRep[$this->id] as $fac => $val)
|
|
||||||
$data[$this->id]['reprewards'][] = [$fac, $val];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,291 +227,353 @@ class CreatureList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[Type::NPC][$this->id] = ['name' => $this->getField('name', true)];
|
$data[TYPE_NPC][$this->id] = ['name' => $this->getField('name', true)];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSourceData(int $id = 0) : array
|
public function getSourceData()
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if ($id && $id != $this->id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$data[$this->id] = array(
|
$data[$this->id] = array(
|
||||||
'n' => $this->getField('parentId') ? $this->getField('parent', true) : $this->getField('name', true),
|
'n' => $this->getField('parentId') ? $this->getField('parent', true) : $this->getField('name', true),
|
||||||
't' => Type::NPC,
|
't' => TYPE_NPC,
|
||||||
'ti' => $this->getField('parentId') ?: $this->id
|
'ti' => $this->getField('parentId') ?: $this->id,
|
||||||
|
// 'bd' => (int)($this->curTpl['cuFlags'] & NPC_CU_INSTANCE_BOSS || ($this->curTpl['typeFlags'] & 0x4 && $this->curTpl['rank']))
|
||||||
|
// 'z' where am i spawned
|
||||||
|
// 'dd' DungeonDifficulty requires 'z'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addRewardsToJScript(&$refs) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CreatureListFilter extends Filter
|
class CreatureListFilter extends Filter
|
||||||
{
|
{
|
||||||
protected string $type = 'npcs';
|
public $extraOpts = null;
|
||||||
protected array $enums = array(
|
protected $enums = array(
|
||||||
3 => parent::ENUM_FACTION, // faction
|
3 => array( 469, 1037, 1106, 529, 1012, 87, 21, 910, 609, 942, 909, 530, 69, 577, 930, 1068, 1104, 729, 369, 92, 54, 946, 67, 1052, 749,
|
||||||
6 => parent::ENUM_ZONE, // foundin
|
47, 989, 1090, 1098, 978, 1011, 93, 1015, 1038, 76, 470, 349, 1031, 1077, 809, 911, 890, 970, 169, 730, 72, 70, 932, 1156, 933,
|
||||||
42 => parent::ENUM_FACTION, // increasesrepwith
|
510, 1126, 1067, 1073, 509, 941, 1105, 990, 934, 935, 1094, 1119, 1124, 1064, 967, 1091, 59, 947, 81, 576, 922, 68, 1050, 1085, 889,
|
||||||
43 => parent::ENUM_FACTION, // decreasesrepwith
|
589, 270)
|
||||||
38 => parent::ENUM_EVENT // relatedevent
|
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $genericFilter = array(
|
// cr => [type, field, misc, extraCol]
|
||||||
1 => [parent::CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num]
|
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
|
||||||
2 => [parent::CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num]
|
5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair
|
||||||
3 => [parent::CR_CALLBACK, 'cbFaction', null, null ], // faction [enum]
|
6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin
|
||||||
5 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair
|
9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable
|
||||||
6 => [parent::CR_ENUM, 's.areaId', false, true ], // foundin
|
11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable
|
||||||
7 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum]
|
18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer
|
||||||
8 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum]
|
19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
|
||||||
9 => [parent::CR_BOOLEAN, 'lootId', ], // lootable
|
20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster
|
||||||
10 => [parent::CR_CALLBACK, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn]
|
21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster
|
||||||
11 => [parent::CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable
|
22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster
|
||||||
12 => [parent::CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int]
|
23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper
|
||||||
15 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_HERBALISM, null ], // gatherable [yn]
|
24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner
|
||||||
16 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_MINING, null ], // minable [yn]
|
25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor
|
||||||
18 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer
|
27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster
|
||||||
19 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
|
28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer
|
||||||
20 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster
|
29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor
|
||||||
21 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster
|
19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
|
||||||
22 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster
|
37 => [FILTER_CR_NUMERIC, 'id', null, true], // id
|
||||||
23 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper
|
35 => [FILTER_CR_STRING, 'textureString' ], // useskin
|
||||||
24 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner
|
32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss
|
||||||
25 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor
|
33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||||
27 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster
|
31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||||
28 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer
|
40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||||
29 => [parent::CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor
|
|
||||||
31 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
|
||||||
32 => [parent::CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss
|
|
||||||
33 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
|
||||||
34 => [parent::CR_STRING, 'modelId', STR_MATCH_EXACT | STR_ALLOW_SHORT ], // usemodel [str] (wants int in string fmt <_<)
|
|
||||||
35 => [parent::CR_STRING, 'textureString' ], // useskin [str]
|
|
||||||
37 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id
|
|
||||||
38 => [parent::CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum]
|
|
||||||
40 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
|
||||||
41 => [parent::CR_NYI_PH, 1, null ], // haslocation [yn] [staff]
|
|
||||||
42 => [parent::CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum]
|
|
||||||
43 => [parent::CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum]
|
|
||||||
44 => [parent::CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_SKIN_WITH_ENGINEERING, null ] // salvageable [yn]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $inputFields = array(
|
protected function createSQLForCriterium(&$cr)
|
||||||
'cr' => [parent::V_LIST, [[1, 3],[5, 12], 15, 16, [18, 25], [27, 29], [31, 35], 37, 38, [40, 44]], true ], // criteria ids
|
{
|
||||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 9999]], true ], // criteria operators
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiter
|
{
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / subname - only printable chars, no delimiter
|
if ($genCr = $this->genericCriterion($cr))
|
||||||
'ex' => [parent::V_EQUAL, 'on', false], // also match subname
|
return $genCr;
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'fa' => [parent::V_CALLBACK, 'cbPetFamily', true ], // pet family [list] - cat[0] == 1
|
|
||||||
'minle' => [parent::V_RANGE, [1, 99], false], // min level [int]
|
|
||||||
'maxle' => [parent::V_RANGE, [1, 99], false], // max level [int]
|
|
||||||
'cl' => [parent::V_RANGE, [0, 4], true ], // classification [list]
|
|
||||||
'ra' => [parent::V_LIST, [-1, 0, 1], false], // react alliance [int]
|
|
||||||
'rh' => [parent::V_LIST, [-1, 0, 1], false] // react horde [int]
|
|
||||||
);
|
|
||||||
|
|
||||||
public array $extraOpts = [];
|
unset($cr);
|
||||||
|
$this->error = true;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
switch ($cr[0])
|
||||||
|
{
|
||||||
|
case 1: // health [num]
|
||||||
|
if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// remap OP for this special case
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case '=': // min > max is totally possible
|
||||||
|
$this->extraOpts['ct']['h'][] = 'healthMin = healthMax AND healthMin = '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMax, healthMin) > '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '>=':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMax, healthMin) >= '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMin, healthMax) < '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '<=':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(healthMin > healthMax, healthMin, healthMax) <= '.$cr[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return [1]; // always true, use post-filter
|
||||||
|
case 2: // mana [num]
|
||||||
|
if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// remap OP for this special case
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case '=':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'manaMin = manaMax AND manaMin = '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMin, manaMax) > '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '>=':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMin, manaMax) >= '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMax, manaMin) < '.$cr[2];
|
||||||
|
break;
|
||||||
|
case '<=':
|
||||||
|
$this->extraOpts['ct']['h'][] = 'IF(manaMin > manaMax, manaMax, manaMin) <= '.$cr[2];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return [1]; // always true, use post-filter
|
||||||
|
case 7: // startsquest [enum]
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case 1: // any
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!']];
|
||||||
|
case 2: // alliance
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
|
||||||
|
case 3: // horde
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
|
||||||
|
case 4: // both
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
||||||
|
case 5: // none
|
||||||
|
$this->extraOpts['ct']['h'][] = 'startsQuests = 0';
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 8: // endsquest [enum]
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case 1: // any
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!']];
|
||||||
|
case 2: // alliance
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
|
||||||
|
case 3: // horde
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
|
||||||
|
case 4: // both
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
||||||
|
case 5: // none
|
||||||
|
$this->extraOpts['ct']['h'][] = 'endsQuests = 0';
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3: // faction [enum]
|
||||||
|
if (in_array($cr[1], $this->enums[$cr[0]]))
|
||||||
|
{
|
||||||
|
$facTpls = [];
|
||||||
|
$facs = new FactionList(array('OR', ['parentFactionId', $cr[1]], ['id', $cr[1]]));
|
||||||
|
foreach ($facs->iterate() as $__)
|
||||||
|
$facTpls = array_merge($facTpls, $facs->getField('templateIds'));
|
||||||
|
|
||||||
|
if (!$facTpls)
|
||||||
|
return [0];
|
||||||
|
|
||||||
|
return ['faction', $facTpls];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 38; // relatedevent
|
||||||
|
if (!$this->isSaneNumeric($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ($cr[1] == FILTER_ENUM_ANY)
|
||||||
|
{
|
||||||
|
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
|
||||||
|
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
|
||||||
|
return ['s.guid', $cGuids];
|
||||||
|
}
|
||||||
|
else if ($cr[1] == FILTER_ENUM_NONE)
|
||||||
|
{
|
||||||
|
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
|
||||||
|
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
|
||||||
|
return ['s.guid', $cGuids, '!'];
|
||||||
|
}
|
||||||
|
else if ($cr[1])
|
||||||
|
{
|
||||||
|
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]);
|
||||||
|
$cGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_creature WHERE eventEntry IN (?a)', $eventIds);
|
||||||
|
return ['s.guid', $cGuids];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 42: // increasesrepwith [enum]
|
||||||
|
if (in_array($cr[1], $this->enums[3])) // reuse
|
||||||
|
{
|
||||||
|
if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 > 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 > 0)', $cr[1], $cr[1]))
|
||||||
|
return ['id', $cIds];
|
||||||
|
else
|
||||||
|
return [0];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 43: // decreasesrepwith [enum]
|
||||||
|
if (in_array($cr[1], $this->enums[3])) // reuse
|
||||||
|
{
|
||||||
|
if ($cIds = DB::World()->selectCol('SELECT creature_id FROM creature_onkill_reputation WHERE (RewOnKillRepFaction1 = ?d AND RewOnKillRepValue1 < 0) OR (RewOnKillRepFaction2 = ?d AND RewOnKillRepValue2 < 0)', $cr[1], $cr[1]))
|
||||||
|
return ['id', $cIds];
|
||||||
|
else
|
||||||
|
return [0];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 12: // averagemoneydropped [op] [int]
|
||||||
|
if (!$this->isSaneNumeric($cr[2]) || !$this->int2Op($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
return ['AND', ['((minGold + maxGold) / 2)', $cr[2], $cr[1]]];
|
||||||
|
case 15: // gatherable [yn]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_HERBLOOT, '&']];
|
||||||
|
else
|
||||||
|
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_HERBLOOT, '&'], 0]];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 44: // salvageable [yn]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_ENGINEERLOOT, '&']];
|
||||||
|
else
|
||||||
|
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_ENGINEERLOOT, '&'], 0]];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 16: // minable [yn]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', NPC_TYPEFLAG_MININGLOOT, '&']];
|
||||||
|
else
|
||||||
|
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_MININGLOOT, '&'], 0]];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10: // skinnable [yn]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['AND', ['skinLootId', 0, '>'], [['typeFlags', NPC_TYPEFLAG_SPECIALLOOT, '&'], 0]];
|
||||||
|
else
|
||||||
|
return ['OR', ['skinLootId', 0], [['typeFlags', NPC_TYPEFLAG_SPECIALLOOT, '&'], 0, '!']];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 34: // usemodel [str] // displayId -> id:creatureDisplayInfo.dbc/model -> id:cratureModelData.dbc/modelPath
|
||||||
|
case 41: // haslocation [yn] [staff]
|
||||||
|
/* todo */ return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($cr);
|
||||||
|
$this->error = true;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createSQLForValues()
|
||||||
{
|
{
|
||||||
$parts = [];
|
$parts = [];
|
||||||
$_v = &$this->values;
|
$_v = &$this->fiData['v'];
|
||||||
|
|
||||||
// name [str]
|
// name [str]
|
||||||
if ($_v['na'])
|
if (isset($_v['na']))
|
||||||
{
|
{
|
||||||
$_ = [];
|
$_ = [];
|
||||||
if ($_v['ex'] == 'on')
|
if (isset($_v['ex']) && $_v['ex'] == 'on')
|
||||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'subname_loc'.Lang::getLocale()->value]);
|
$_ = $this->modularizeString(['name_loc'.User::$localeId, 'subname_loc'.User::$localeId]);
|
||||||
else
|
else
|
||||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]);
|
$_ = $this->modularizeString(['name_loc'.User::$localeId]);
|
||||||
|
|
||||||
if ($_)
|
if ($_)
|
||||||
$parts[] = $_;
|
$parts[] = $_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pet family [list]
|
// pet family [list]
|
||||||
if ($_v['fa'])
|
if (isset($_v['fa']))
|
||||||
$parts[] = ['family', $_v['fa']];
|
{
|
||||||
|
$_ = (array)$_v['fa'];
|
||||||
|
if (!array_diff($_, [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 20, 21, 24, 25, 26, 27, 30, 31, 32, 33, 34, 35, 37, 38, 39, 41, 42, 43, 44, 45, 46]))
|
||||||
|
$parts[] = ['family', $_];
|
||||||
|
else
|
||||||
|
unset($_v['cl']);
|
||||||
|
}
|
||||||
|
|
||||||
// creatureLevel min [int]
|
// creatureLevel min [int]
|
||||||
if ($_v['minle'])
|
if (isset($_v['minle']))
|
||||||
$parts[] = ['minLevel', $_v['minle'], '>='];
|
{
|
||||||
|
if (is_int($_v['minle']) && $_v['minle'] > 0)
|
||||||
|
$parts[] = ['minLevel', $_v['minle'], '>='];
|
||||||
|
else
|
||||||
|
unset($_v['minle']);
|
||||||
|
}
|
||||||
|
|
||||||
// creatureLevel max [int]
|
// creatureLevel max [int]
|
||||||
if ($_v['maxle'])
|
if (isset($_v['maxle']))
|
||||||
$parts[] = ['maxLevel', $_v['maxle'], '<='];
|
{
|
||||||
|
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
|
||||||
|
$parts[] = ['maxLevel', $_v['maxle'], '<='];
|
||||||
|
else
|
||||||
|
unset($_v['maxle']);
|
||||||
|
}
|
||||||
|
|
||||||
// classification [list]
|
// classification [list]
|
||||||
if ($_v['cl'])
|
if (isset($_v['cl']))
|
||||||
$parts[] = ['rank', $_v['cl']];
|
{
|
||||||
|
$_ = (array)$_v['cl'];
|
||||||
|
if (!array_diff($_, [0, 1, 2, 3, 4]))
|
||||||
|
$parts[] = ['rank', $_];
|
||||||
|
else
|
||||||
|
unset($_v['cl']);
|
||||||
|
}
|
||||||
|
|
||||||
// react Alliance [int]
|
// react Alliance [int]
|
||||||
if ($_v['ra'])
|
if (isset($_v['ra']))
|
||||||
$parts[] = ['ft.A', $_v['ra']];
|
{
|
||||||
|
$_ = (int)$_v['ra'];
|
||||||
|
if (in_array($_, [-1, 0, 1]))
|
||||||
|
$parts[] = ['ft.A', $_];
|
||||||
|
else
|
||||||
|
unset($_v['ra']);
|
||||||
|
}
|
||||||
|
|
||||||
// react Horde [int]
|
// react Horde [int]
|
||||||
if ($_v['rh'])
|
if (isset($_v['rh']))
|
||||||
$parts[] = ['ft.H', $_v['rh']];
|
{
|
||||||
|
$_ = (int)$_v['rh'];
|
||||||
|
if (in_array($_, [-1, 0, 1]))
|
||||||
|
$parts[] = ['ft.H', $_];
|
||||||
|
else
|
||||||
|
unset($_v['rh']);
|
||||||
|
}
|
||||||
|
|
||||||
return $parts;
|
return $parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cbPetFamily(string &$val) : bool
|
|
||||||
{
|
|
||||||
if (!$this->parentCats || $this->parentCats[0] != 1)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!Util::checkNumeric($val, NUM_CAST_INT))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
$type = parent::V_LIST;
|
|
||||||
$valid = [[1, 9], 11, 12, 20, 21, [24, 27], [30, 35], [37, 39], [41, 46]];
|
|
||||||
|
|
||||||
return $this->checkInput($type, $valid, $val);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbRelEvent(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if ($crs == parent::ENUM_ANY)
|
|
||||||
{
|
|
||||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
|
||||||
if ($cGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_creature WHERE `eventEntry` IN (?a)', $eventIds))
|
|
||||||
return ['s.guid', $cGuids];
|
|
||||||
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
else if ($crs == parent::ENUM_NONE)
|
|
||||||
{
|
|
||||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
|
||||||
if ($cGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_creature WHERE `eventEntry` IN (?a)', $eventIds))
|
|
||||||
return ['s.guid', $cGuids, '!'];
|
|
||||||
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
else if (in_array($crs, $this->enums[$cr]))
|
|
||||||
{
|
|
||||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` = ?d', $crs))
|
|
||||||
if ($cGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM `game_event_creature` WHERE `eventEntry` IN (?a)', $eventIds))
|
|
||||||
return ['s.guid', $cGuids];
|
|
||||||
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbMoneyDrop(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return ['AND', ['((minGold + maxGold) / 2)', $crv, $crs]];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbQuestRelation(int $cr, int $crs, string $crv, $field, $val) : ?array
|
|
||||||
{
|
|
||||||
switch ($crs)
|
|
||||||
{
|
|
||||||
case 1: // any
|
|
||||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!']];
|
|
||||||
case 2: // alliance
|
|
||||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_HORDE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&']];
|
|
||||||
case 3: // horde
|
|
||||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']];
|
|
||||||
case 4: // both
|
|
||||||
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
|
||||||
case 5: // none
|
|
||||||
$this->extraOpts['ct']['h'][] = $field.' = 0';
|
|
||||||
return [1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbHealthMana(int $cr, int $crs, string $crv, $minField, $maxField) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// remap OP for this special case
|
|
||||||
switch ($crs)
|
|
||||||
{
|
|
||||||
case '=': // min > max is totally possible
|
|
||||||
$this->extraOpts['ct']['h'][] = $minField.' = '.$maxField.' AND '.$minField.' = '.$crv;
|
|
||||||
break;
|
|
||||||
case '>':
|
|
||||||
case '>=':
|
|
||||||
case '<':
|
|
||||||
case '<=':
|
|
||||||
$this->extraOpts['ct']['h'][] = 'IF('.$minField.' > '.$maxField.', '.$maxField.', '.$minField.') '.$crs.' '.$crv;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return [1]; // always true, use post-filter
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbSpecialSkinLoot(int $cr, int $crs, string $crv, $typeFlag) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
|
|
||||||
if ($crs)
|
|
||||||
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', $typeFlag, '&']];
|
|
||||||
else
|
|
||||||
return ['OR', ['skinLootId', 0], [['typeFlags', $typeFlag, '&'], 0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbRegularSkinLoot(int $cr, int $crs, string $crv, $typeFlag) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($crs)
|
|
||||||
return ['AND', ['skinLootId', 0, '>'], [['typeFlags', $typeFlag, '&'], 0]];
|
|
||||||
else
|
|
||||||
return ['OR', ['skinLootId', 0], ['typeFlags', $typeFlag, '&']];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbReputation(int $cr, int $crs, string $crv, $op) : ?array
|
|
||||||
{
|
|
||||||
if (!in_array($crs, $this->enums[$cr]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE `id` = ?d', $crs))
|
|
||||||
$this->fiReputationCols[] = [$crs, Util::localizedString($_, 'name')];
|
|
||||||
|
|
||||||
if ($cIds = DB::World()->selectCol('SELECT `creature_id` FROM creature_onkill_reputation WHERE (`RewOnKillRepFaction1` = ?d AND `RewOnKillRepValue1` '.$op.' 0) OR (`RewOnKillRepFaction2` = ?d AND `RewOnKillRepValue2` '.$op.' 0)', $crs, $crs))
|
|
||||||
return ['id', $cIds];
|
|
||||||
else
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbFaction(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!in_array($crs, $this->enums[$cr]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$facTpls = [];
|
|
||||||
$facs = new FactionList(array('OR', ['parentFactionId', $crs], ['id', $crs]));
|
|
||||||
foreach ($facs->iterate() as $__)
|
|
||||||
$facTpls = array_merge($facTpls, $facs->getField('templateIds'));
|
|
||||||
|
|
||||||
return $facTpls ? ['faction', $facTpls] : [0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,31 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class CurrencyList extends BaseType
|
class CurrencyList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::CURRENCY;
|
public static $type = TYPE_CURRENCY;
|
||||||
public static $brickFile = 'currency';
|
public static $brickFile = 'currency';
|
||||||
public static $dataTable = '?_currencies';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT c.*, c.id AS ARRAY_KEY FROM ?_currencies c';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'c' => [['ic']],
|
|
||||||
'ic' => ['j' => ['?_icons ic ON ic.id = c.iconId', true], 's' => ', ic.name AS iconString']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as &$_curTpl)
|
|
||||||
$_curTpl['iconString'] = $_curTpl['iconString'] ?: DEFAULT_ICON;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
protected $queryBase = 'SELECT *, c.id AS ARRAY_KEY FROM ?_currencies c';
|
||||||
|
protected $queryOpts = array(
|
||||||
|
'c' => [['ic']],
|
||||||
|
'ic' => ['j' => ['?_icons ic ON ic.id = c.iconId', true], 's' => ', ic.iconString']
|
||||||
|
);
|
||||||
|
|
||||||
public function getListviewData()
|
public function getListviewData()
|
||||||
{
|
{
|
||||||
@@ -52,37 +40,19 @@ class CurrencyList extends BaseType
|
|||||||
{
|
{
|
||||||
// todo (low): find out, why i did this in the first place
|
// todo (low): find out, why i did this in the first place
|
||||||
if ($this->id == 104) // in case of honor commit sebbuku
|
if ($this->id == 104) // in case of honor commit sebbuku
|
||||||
$icon = ['pvp-currency-alliance', 'pvp-currency-horde'];
|
$icon = ['inv_bannerpvp_02', 'inv_bannerpvp_01']; // ['alliance', 'horde'];
|
||||||
else if ($this->id == 103) // also arena-icon diffs from item-icon
|
else if ($this->id == 103) // also arena-icon diffs from item-icon
|
||||||
$icon = ['pvp-arenapoints-icon', 'pvp-arenapoints-icon'];
|
$icon = ['money_arena', 'money_arena'];
|
||||||
else
|
else
|
||||||
$icon = [$this->curTpl['iconString'], $this->curTpl['iconString']];
|
$icon = [$this->curTpl['iconString'], $this->curTpl['iconString']];
|
||||||
|
|
||||||
$data[Type::CURRENCY][$this->id] = ['name' => $this->getField('name', true), 'icon' => $icon];
|
$data[TYPE_CURRENCY][$this->id] = ['name' => $this->getField('name', true), 'icon' => $icon];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderTooltip()
|
public function renderTooltip() { }
|
||||||
{
|
|
||||||
if (!$this->curTpl)
|
|
||||||
return array();
|
|
||||||
|
|
||||||
$x = '<table><tr><td>';
|
|
||||||
$x .= '<b>'.$this->getField('name', true).'</b><br>';
|
|
||||||
|
|
||||||
// cata+ (or go fill it by hand)
|
|
||||||
if ($_ = $this->getField('description', true))
|
|
||||||
$x .= '<div style="max-width: 300px" class="q">'.$_.'</div>';
|
|
||||||
|
|
||||||
if ($_ = $this->getField('cap'))
|
|
||||||
$x .= '<br><span class="q">'.Lang::currency('cap').Lang::main('colon').'</span>'.Lang::nf($_).'<br>';
|
|
||||||
|
|
||||||
$x .= '</td></tr></table>';
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class EmoteList extends BaseType
|
|
||||||
{
|
|
||||||
public static $type = Type::EMOTE;
|
|
||||||
public static $brickFile = 'emote';
|
|
||||||
public static $dataTable = '?_emotes';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT e.*, e.id AS ARRAY_KEY FROM ?_emotes e';
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as &$curTpl)
|
|
||||||
{
|
|
||||||
// remap for generic access
|
|
||||||
$curTpl['name'] = $curTpl['cmd'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getListviewData()
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->curTpl['id'],
|
|
||||||
'name' => $this->curTpl['cmd'],
|
|
||||||
'preview' => Util::parseHtmlText($this->getField('meToExt', true) ?: $this->getField('meToNone', true) ?: $this->getField('extToMe', true) ?: $this->getField('extToExt', true) ?: $this->getField('extToNone', true), true)
|
|
||||||
);
|
|
||||||
|
|
||||||
// [nyi] sounds
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = GLOBALINFO_ANY)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
$data[Type::EMOTE][$this->id] = ['name' => $this->getField('cmd')];
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class EnchantmentList extends BaseType
|
|
||||||
{
|
|
||||||
use listviewHelper;
|
|
||||||
|
|
||||||
public static $type = Type::ENCHANTMENT;
|
|
||||||
public static $brickFile = 'enchantment';
|
|
||||||
public static $dataTable = '?_itemenchantment';
|
|
||||||
|
|
||||||
private array $jsonStats = [];
|
|
||||||
private ?SpellList $relSpells = null;
|
|
||||||
private array $triggerIds = [];
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT ie.*, ie.id AS ARRAY_KEY FROM ?_itemenchantment ie';
|
|
||||||
protected $queryOpts = array( // 502 => Type::ENCHANTMENT
|
|
||||||
'ie' => [['is']],
|
|
||||||
'is' => ['j' => ['?_item_stats `is` ON `is`.`type` = 502 AND `is`.`typeId` = `ie`.`id`', true], 's' => ', `is`.*'],
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
$relSpells = [];
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as &$curTpl)
|
|
||||||
{
|
|
||||||
$curTpl['spells'] = []; // [spellId, triggerType, charges, chanceOrPpm]
|
|
||||||
for ($i = 1; $i <=3; $i++)
|
|
||||||
{
|
|
||||||
if ($curTpl['object'.$i] <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch ($curTpl['type'.$i]) // SPELL_TRIGGER_* just reused for wording
|
|
||||||
{
|
|
||||||
case ENCHANTMENT_TYPE_COMBAT_SPELL:
|
|
||||||
$proc = -$this->getField('ppmRate') ?: ($this->getField('procChance') ?: $this->getField('amount'.$i));
|
|
||||||
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_HIT, $curTpl['charges'], $proc];
|
|
||||||
$relSpells[] = $curTpl['object'.$i];
|
|
||||||
break;
|
|
||||||
case ENCHANTMENT_TYPE_EQUIP_SPELL:
|
|
||||||
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_EQUIP, $curTpl['charges'], 0];
|
|
||||||
$relSpells[] = $curTpl['object'.$i];
|
|
||||||
break;
|
|
||||||
case ENCHANTMENT_TYPE_USE_SPELL:
|
|
||||||
$curTpl['spells'][$i] = [$curTpl['object'.$i], SPELL_TRIGGER_USE, $curTpl['charges'], 0];
|
|
||||||
$relSpells[] = $curTpl['object'.$i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// issue with scaling stats enchantments
|
|
||||||
// stats are stored as NOT NULL to be usable by the search filters and such become indistinguishable from scaling enchantments that _actually_ use the value 0
|
|
||||||
// so filter the stats container and if it is empty, rebuild from self. .. there are no mixed scaling/static enchantments, right!?
|
|
||||||
$this->jsonStats[$this->id] = (new StatsContainer())->fromJson($curTpl, true)->filter();
|
|
||||||
if (!count($this->jsonStats[$this->id]))
|
|
||||||
$this->jsonStats[$this->id]->fromEnchantment($curTpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($relSpells)
|
|
||||||
$this->relSpells = new SpellList(array(['id', $relSpells]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// use if you JUST need the name
|
|
||||||
public static function getName($id)
|
|
||||||
{
|
|
||||||
$n = DB::Aowow()->SelectRow('SELECT `name_loc0`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc6`, `name_loc8` FROM ?_itemenchantment WHERE `id` = ?d', $id );
|
|
||||||
return Util::localizedString($n, 'name');
|
|
||||||
}
|
|
||||||
// end static use
|
|
||||||
|
|
||||||
public function getListviewData($addInfoMask = 0x0)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->id,
|
|
||||||
'name' => $this->getField('name', true),
|
|
||||||
'spells' => []
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->curTpl['skillLine'] > 0)
|
|
||||||
$data[$this->id]['reqskill'] = $this->curTpl['skillLine'];
|
|
||||||
|
|
||||||
if ($this->curTpl['skillLevel'] > 0)
|
|
||||||
$data[$this->id]['reqskillrank'] = $this->curTpl['skillLevel'];
|
|
||||||
|
|
||||||
if ($this->curTpl['requiredLevel'] > 0)
|
|
||||||
$data[$this->id]['reqlevel'] = $this->curTpl['requiredLevel'];
|
|
||||||
|
|
||||||
foreach ($this->curTpl['spells'] as [$spellId, $trigger, $charges, $procChance])
|
|
||||||
{
|
|
||||||
// spell is procing
|
|
||||||
$trgSpell = 0;
|
|
||||||
if ($this->relSpells && $this->relSpells->getEntry($spellId) && ($_ = $this->relSpells->canTriggerSpell()))
|
|
||||||
{
|
|
||||||
foreach ($_ as $idx)
|
|
||||||
{
|
|
||||||
if ($trgSpell = $this->relSpells->getField('effect'.$idx.'TriggerSpell'))
|
|
||||||
{
|
|
||||||
$this->triggerIds[] = $trgSpell;
|
|
||||||
$data[$this->id]['spells'][$trgSpell] = $charges;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// spell was not proccing
|
|
||||||
if (!$trgSpell)
|
|
||||||
$data[$this->id]['spells'][$spellId] = $charges;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$data[$this->id]['spells'])
|
|
||||||
unset($data[$this->id]['spells']);
|
|
||||||
|
|
||||||
Util::arraySumByKey($data[$this->id], $this->jsonStats[$this->id]->toJson());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStatGainForCurrent() : array
|
|
||||||
{
|
|
||||||
return $this->jsonStats[$this->id]->toJson();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRelSpell($id)
|
|
||||||
{
|
|
||||||
if ($this->relSpells)
|
|
||||||
return $this->relSpells->getEntry($id);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = GLOBALINFO_ANY)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
if ($addMask & GLOBALINFO_SELF)
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
$data[Type::ENCHANTMENT][$this->id] = ['name' => $this->getField('name', true)];
|
|
||||||
|
|
||||||
if ($addMask & GLOBALINFO_RELATED)
|
|
||||||
{
|
|
||||||
if ($this->relSpells)
|
|
||||||
$data = $this->relSpells->getJSGlobals(GLOBALINFO_SELF);
|
|
||||||
|
|
||||||
foreach ($this->triggerIds as $tId)
|
|
||||||
if (empty($data[Type::SPELL][$tId]))
|
|
||||||
$data[Type::SPELL][$tId] = $tId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class EnchantmentListFilter extends Filter
|
|
||||||
{
|
|
||||||
protected string $type = 'enchantments';
|
|
||||||
protected array $enums = array(
|
|
||||||
3 => parent::ENUM_PROFESSION // requiresprof
|
|
||||||
);
|
|
||||||
|
|
||||||
protected array $genericFilter = array(
|
|
||||||
2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
|
||||||
3 => [parent::CR_ENUM, 'skillLine' ], // requiresprof
|
|
||||||
4 => [parent::CR_NUMERIC, 'skillLevel', NUM_CAST_INT ], // reqskillrank
|
|
||||||
5 => [parent::CR_BOOLEAN, 'conditionId' ], // hascondition
|
|
||||||
10 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
|
||||||
11 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
|
||||||
12 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
|
||||||
20 => [parent::CR_NUMERIC, 'is.str', NUM_CAST_INT, true], // str
|
|
||||||
21 => [parent::CR_NUMERIC, 'is.agi', NUM_CAST_INT, true], // agi
|
|
||||||
22 => [parent::CR_NUMERIC, 'is.sta', NUM_CAST_INT, true], // sta
|
|
||||||
23 => [parent::CR_NUMERIC, 'is.int', NUM_CAST_INT, true], // int
|
|
||||||
24 => [parent::CR_NUMERIC, 'is.spi', NUM_CAST_INT, true], // spi
|
|
||||||
25 => [parent::CR_NUMERIC, 'is.arcres', NUM_CAST_INT, true], // arcres
|
|
||||||
26 => [parent::CR_NUMERIC, 'is.firres', NUM_CAST_INT, true], // firres
|
|
||||||
27 => [parent::CR_NUMERIC, 'is.natres', NUM_CAST_INT, true], // natres
|
|
||||||
28 => [parent::CR_NUMERIC, 'is.frores', NUM_CAST_INT, true], // frores
|
|
||||||
29 => [parent::CR_NUMERIC, 'is.shares', NUM_CAST_INT, true], // shares
|
|
||||||
30 => [parent::CR_NUMERIC, 'is.holres', NUM_CAST_INT, true], // holres
|
|
||||||
32 => [parent::CR_NUMERIC, 'is.dps', NUM_CAST_FLOAT, true], // dps
|
|
||||||
34 => [parent::CR_NUMERIC, 'is.dmg', NUM_CAST_FLOAT, true], // dmg
|
|
||||||
37 => [parent::CR_NUMERIC, 'is.mleatkpwr', NUM_CAST_INT, true], // mleatkpwr
|
|
||||||
38 => [parent::CR_NUMERIC, 'is.rgdatkpwr', NUM_CAST_INT, true], // rgdatkpwr
|
|
||||||
39 => [parent::CR_NUMERIC, 'is.rgdhitrtng', NUM_CAST_INT, true], // rgdhitrtng
|
|
||||||
40 => [parent::CR_NUMERIC, 'is.rgdcritstrkrtng', NUM_CAST_INT, true], // rgdcritstrkrtng
|
|
||||||
41 => [parent::CR_NUMERIC, 'is.armor', NUM_CAST_INT, true], // armor
|
|
||||||
42 => [parent::CR_NUMERIC, 'is.defrtng', NUM_CAST_INT, true], // defrtng
|
|
||||||
43 => [parent::CR_NUMERIC, 'is.block', NUM_CAST_INT, true], // block
|
|
||||||
44 => [parent::CR_NUMERIC, 'is.blockrtng', NUM_CAST_INT, true], // blockrtng
|
|
||||||
45 => [parent::CR_NUMERIC, 'is.dodgertng', NUM_CAST_INT, true], // dodgertng
|
|
||||||
46 => [parent::CR_NUMERIC, 'is.parryrtng', NUM_CAST_INT, true], // parryrtng
|
|
||||||
48 => [parent::CR_NUMERIC, 'is.splhitrtng', NUM_CAST_INT, true], // splhitrtng
|
|
||||||
49 => [parent::CR_NUMERIC, 'is.splcritstrkrtng', NUM_CAST_INT, true], // splcritstrkrtng
|
|
||||||
50 => [parent::CR_NUMERIC, 'is.splheal', NUM_CAST_INT, true], // splheal
|
|
||||||
51 => [parent::CR_NUMERIC, 'is.spldmg', NUM_CAST_INT, true], // spldmg
|
|
||||||
52 => [parent::CR_NUMERIC, 'is.arcsplpwr', NUM_CAST_INT, true], // arcsplpwr
|
|
||||||
53 => [parent::CR_NUMERIC, 'is.firsplpwr', NUM_CAST_INT, true], // firsplpwr
|
|
||||||
54 => [parent::CR_NUMERIC, 'is.frosplpwr', NUM_CAST_INT, true], // frosplpwr
|
|
||||||
55 => [parent::CR_NUMERIC, 'is.holsplpwr', NUM_CAST_INT, true], // holsplpwr
|
|
||||||
56 => [parent::CR_NUMERIC, 'is.natsplpwr', NUM_CAST_INT, true], // natsplpwr
|
|
||||||
57 => [parent::CR_NUMERIC, 'is.shasplpwr', NUM_CAST_INT, true], // shasplpwr
|
|
||||||
60 => [parent::CR_NUMERIC, 'is.healthrgn', NUM_CAST_INT, true], // healthrgn
|
|
||||||
61 => [parent::CR_NUMERIC, 'is.manargn', NUM_CAST_INT, true], // manargn
|
|
||||||
77 => [parent::CR_NUMERIC, 'is.atkpwr', NUM_CAST_INT, true], // atkpwr
|
|
||||||
78 => [parent::CR_NUMERIC, 'is.mlehastertng', NUM_CAST_INT, true], // mlehastertng
|
|
||||||
79 => [parent::CR_NUMERIC, 'is.resirtng', NUM_CAST_INT, true], // resirtng
|
|
||||||
84 => [parent::CR_NUMERIC, 'is.mlecritstrkrtng', NUM_CAST_INT, true], // mlecritstrkrtng
|
|
||||||
94 => [parent::CR_NUMERIC, 'is.splpen', NUM_CAST_INT, true], // splpen
|
|
||||||
95 => [parent::CR_NUMERIC, 'is.mlehitrtng', NUM_CAST_INT, true], // mlehitrtng
|
|
||||||
96 => [parent::CR_NUMERIC, 'is.critstrkrtng', NUM_CAST_INT, true], // critstrkrtng
|
|
||||||
97 => [parent::CR_NUMERIC, 'is.feratkpwr', NUM_CAST_INT, true], // feratkpwr
|
|
||||||
101 => [parent::CR_NUMERIC, 'is.rgdhastertng', NUM_CAST_INT, true], // rgdhastertng
|
|
||||||
102 => [parent::CR_NUMERIC, 'is.splhastertng', NUM_CAST_INT, true], // splhastertng
|
|
||||||
103 => [parent::CR_NUMERIC, 'is.hastertng', NUM_CAST_INT, true], // hastertng
|
|
||||||
114 => [parent::CR_NUMERIC, 'is.armorpenrtng', NUM_CAST_INT, true], // armorpenrtng
|
|
||||||
115 => [parent::CR_NUMERIC, 'is.health', NUM_CAST_INT, true], // health
|
|
||||||
116 => [parent::CR_NUMERIC, 'is.mana', NUM_CAST_INT, true], // mana
|
|
||||||
117 => [parent::CR_NUMERIC, 'is.exprtng', NUM_CAST_INT, true], // exprtng
|
|
||||||
119 => [parent::CR_NUMERIC, 'is.hitrtng', NUM_CAST_INT, true], // hitrtng
|
|
||||||
123 => [parent::CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true] // splpwr
|
|
||||||
);
|
|
||||||
|
|
||||||
protected array $inputFields = array(
|
|
||||||
'cr' => [parent::V_RANGE, [2, 123], true ], // criteria ids
|
|
||||||
'crs' => [parent::V_RANGE, [1, 15], true ], // criteria operators
|
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numerals
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'ty' => [parent::V_RANGE, [1, 8], true ] // types
|
|
||||||
);
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = &$this->values;
|
|
||||||
|
|
||||||
//string
|
|
||||||
if ($_v['na'])
|
|
||||||
if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]))
|
|
||||||
$parts[] = $_;
|
|
||||||
|
|
||||||
// type
|
|
||||||
if ($_v['ty'])
|
|
||||||
$parts[] = ['OR', ['type1', $_v['ty']], ['type2', $_v['ty']], ['type3', $_v['ty']]];
|
|
||||||
|
|
||||||
return $parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,27 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class FactionList extends BaseType
|
class FactionList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::FACTION;
|
public static $type = TYPE_FACTION;
|
||||||
public static $brickFile = 'faction';
|
public static $brickFile = 'faction';
|
||||||
public static $dataTable = '?_factions';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT f.*, f.parentFactionId AS cat, f.id AS ARRAY_KEY FROM ?_factions f';
|
protected $queryBase = 'SELECT f.*, f.parentFactionId AS cat, f.id AS ARRAY_KEY FROM ?_factions f';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
'f' => [['f2']],
|
'f' => [['f2']],
|
||||||
'f2' => ['j' => ['?_factions f2 ON f.parentFactionId = f2.id', true], 's' => ', IFNULL(f2.parentFactionId, 0) AS cat2'],
|
'f2' => ['j' => ['?_factions f2 ON f.parentFactionId = f2.id', true], 's' => ', IFNULL(f2.parentFactionId, 0) AS cat2'],
|
||||||
'ft' => ['j' => '?_factiontemplate ft ON ft.factionId = f.id']
|
'ft' => ['j' => '?_factiontemplate ft ON ft.factionId = f.id']
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [])
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions);
|
||||||
|
|
||||||
if ($this->error)
|
if ($this->error)
|
||||||
return;
|
return;
|
||||||
@@ -39,7 +36,7 @@ class FactionList extends BaseType
|
|||||||
|
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_factions WHERE id = ?d', $id);
|
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_factions WHERE id = ?d', $id);
|
||||||
return Util::localizedString($n, 'name');
|
return Util::localizedString($n, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +74,7 @@ class FactionList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[Type::FACTION][$this->id] = ['name' => $this->getField('name', true)];
|
$data[TYPE_FACTION][$this->id] = ['name' => $this->getField('name', true)];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,12 +8,11 @@ class GameObjectList extends BaseType
|
|||||||
{
|
{
|
||||||
use listviewHelper, spawnHelper;
|
use listviewHelper, spawnHelper;
|
||||||
|
|
||||||
public static $type = Type::OBJECT;
|
public static $type = TYPE_OBJECT;
|
||||||
public static $brickFile = 'object';
|
public static $brickFile = 'object';
|
||||||
public static $dataTable = '?_objects';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT o.*, o.id AS ARRAY_KEY FROM ?_objects o';
|
protected $queryBase = 'SELECT o.*, o.id AS ARRAY_KEY FROM ?_objects o';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
'o' => [['ft', 'qse']],
|
'o' => [['ft', 'qse']],
|
||||||
'ft' => ['j' => ['?_factiontemplate ft ON ft.id = o.faction', true], 's' => ', ft.factionId, ft.A, ft.H'],
|
'ft' => ['j' => ['?_factiontemplate ft ON ft.id = o.faction', true], 's' => ', ft.factionId, ft.A, ft.H'],
|
||||||
'qse' => ['j' => ['?_quests_startend qse ON qse.type = 2 AND qse.typeId = o.id', true], 's' => ', IF(min(qse.method) = 1 OR max(qse.method) = 3, 1, 0) AS startsQuests, IF(min(qse.method) = 2 OR max(qse.method) = 3, 1, 0) AS endsQuests', 'g' => 'o.id'],
|
'qse' => ['j' => ['?_quests_startend qse ON qse.type = 2 AND qse.typeId = o.id', true], 's' => ', IF(min(qse.method) = 1 OR max(qse.method) = 3, 1, 0) AS startsQuests, IF(min(qse.method) = 2 OR max(qse.method) = 3, 1, 0) AS endsQuests', 'g' => 'o.id'],
|
||||||
@@ -23,7 +20,7 @@ class GameObjectList extends BaseType
|
|||||||
's' => ['j' => '?_spawns s ON s.type = 2 AND s.typeId = o.id']
|
's' => ['j' => '?_spawns s ON s.type = 2 AND s.typeId = o.id']
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [], $miscData = null)
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions, $miscData);
|
||||||
|
|
||||||
@@ -33,9 +30,6 @@ class GameObjectList extends BaseType
|
|||||||
// post processing
|
// post processing
|
||||||
foreach ($this->iterate() as $_id => &$curTpl)
|
foreach ($this->iterate() as $_id => &$curTpl)
|
||||||
{
|
{
|
||||||
if (!$curTpl['name_loc0'])
|
|
||||||
$curTpl['name_loc0'] = 'Unnamed Object #' . $_id;
|
|
||||||
|
|
||||||
// unpack miscInfo
|
// unpack miscInfo
|
||||||
$curTpl['lootStack'] = [];
|
$curTpl['lootStack'] = [];
|
||||||
$curTpl['spells'] = [];
|
$curTpl['spells'] = [];
|
||||||
@@ -64,7 +58,7 @@ class GameObjectList extends BaseType
|
|||||||
|
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_objects WHERE id = ?d', $id);
|
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_objects WHERE id = ?d', $id);
|
||||||
return Util::localizedString($n, 'name');
|
return Util::localizedString($n, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +69,7 @@ class GameObjectList extends BaseType
|
|||||||
{
|
{
|
||||||
$data[$this->id] = array(
|
$data[$this->id] = array(
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'name' => Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_RAW),
|
'name' => $this->getField('name', true),
|
||||||
'type' => $this->curTpl['typeCat'],
|
'type' => $this->curTpl['typeCat'],
|
||||||
'location' => $this->getSpawns(SPAWNINFO_ZONES)
|
'location' => $this->getSpawns(SPAWNINFO_ZONES)
|
||||||
);
|
);
|
||||||
@@ -97,15 +91,14 @@ class GameObjectList extends BaseType
|
|||||||
return array();
|
return array();
|
||||||
|
|
||||||
$x = '<table>';
|
$x = '<table>';
|
||||||
$x .= '<tr><td><b class="q">'.Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_HTML).'</b></td></tr>';
|
$x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>';
|
||||||
if ($this->curTpl['typeCat'])
|
if ($_ = Lang::gameObject('type', $this->curTpl['typeCat']))
|
||||||
if ($_ = Lang::gameObject('type', $this->curTpl['typeCat']))
|
$x .= '<tr><td>'.$_.'</td></tr>';
|
||||||
$x .= '<tr><td>'.$_.'</td></tr>';
|
|
||||||
|
|
||||||
if (isset($this->curTpl['lockId']))
|
if (isset($this->curTpl['lockId']))
|
||||||
if ($locks = Lang::getLocks($this->curTpl['lockId']))
|
if ($locks = Lang::getLocks($this->curTpl['lockId']))
|
||||||
foreach ($locks as $l)
|
foreach ($locks as $l)
|
||||||
$x .= '<tr><td>'.sprintf(Lang::game('requires'), $l).'</td></tr>';
|
$x .= '<tr><td>'.$l.'</td></tr>';
|
||||||
|
|
||||||
$x .= '</table>';
|
$x .= '</table>';
|
||||||
|
|
||||||
@@ -117,24 +110,23 @@ class GameObjectList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[Type::OBJECT][$this->id] = ['name' => Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_RAW)];
|
$data[TYPE_OBJECT][$this->id] = ['name' => $this->getField('name', true)];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSourceData(int $id = 0) : array
|
public function getSourceData()
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if ($id && $id != $this->id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$data[$this->id] = array(
|
$data[$this->id] = array(
|
||||||
'n' => $this->getField('name', true),
|
'n' => $this->getField('name', true),
|
||||||
't' => Type::OBJECT,
|
't' => TYPE_OBJECT,
|
||||||
'ti' => $this->id
|
'ti' => $this->id
|
||||||
|
// 'bd' => bossdrop
|
||||||
|
// 'dd' => dungeondifficulty
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,108 +137,116 @@ class GameObjectList extends BaseType
|
|||||||
|
|
||||||
class GameObjectListFilter extends Filter
|
class GameObjectListFilter extends Filter
|
||||||
{
|
{
|
||||||
protected string $type = 'objects';
|
public $extraOpts = [];
|
||||||
protected array $enums = array(
|
|
||||||
1 => parent::ENUM_ZONE,
|
protected $genericFilter = array(
|
||||||
16 => parent::ENUM_EVENT,
|
1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin
|
||||||
50 => [1, 2, 3, 4, 663, 883]
|
7 => [FILTER_CR_NUMERIC, 'reqSkill', null ], // requiredskilllevel
|
||||||
|
11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT], // hasscreenshots
|
||||||
|
13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||||
|
15 => [FILTER_CR_NUMERIC, 'id', null ], // id
|
||||||
|
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $genericFilter = array(
|
protected function createSQLForCriterium(&$cr)
|
||||||
1 => [parent::CR_ENUM, 's.areaId', false, true], // foundin
|
{
|
||||||
2 => [parent::CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [side]
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
||||||
3 => [parent::CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [side]
|
{
|
||||||
4 => [parent::CR_CALLBACK, 'cbOpenable', null, null], // openable [yn]
|
if ($genCR = $this->genericCriterion($cr))
|
||||||
5 => [parent::CR_NYI_PH, null, 0 ], // averagemoneycontained [op] [int] - GOs don't contain money, match against 0
|
return $genCR;
|
||||||
7 => [parent::CR_NUMERIC, 'reqSkill', NUM_CAST_INT ], // requiredskilllevel
|
|
||||||
11 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
|
||||||
13 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
|
||||||
15 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT ], // id
|
|
||||||
16 => [parent::CR_CALLBACK, 'cbRelEvent', null, null], // relatedevent (ignore removed by event)
|
|
||||||
18 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
|
||||||
50 => [parent::CR_ENUM, 'spellFocusId', true, true], // spellfocus
|
|
||||||
);
|
|
||||||
|
|
||||||
protected array $inputFields = array(
|
unset($cr);
|
||||||
'cr' => [parent::V_LIST, [[1, 5], 7, 11, 13, 15, 16, 18, 50], true ], // criteria ids
|
$this->error = true;
|
||||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 5000]], true ], // criteria operators
|
return [1];
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numeric input values expected
|
}
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false] // match any / all filter
|
|
||||||
);
|
|
||||||
|
|
||||||
public array $extraOpts = [];
|
switch ($cr[0])
|
||||||
|
{
|
||||||
|
case 4:
|
||||||
|
if (!$this->int2Bool($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
return $cr[1] ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']];
|
||||||
|
case 5: // averagemoneycontained [op] [int] GOs don't contain money .. eval to 0 == true
|
||||||
|
if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
return eval('return ('.$cr[2].' '.$cr[1].' 0)') ? [1] : [0];
|
||||||
|
case 2: // startsquest [side]
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case 1: // any
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!']];
|
||||||
|
case 2: // alliance only
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
|
||||||
|
case 3: // horde only
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
|
||||||
|
case 4: // both
|
||||||
|
return ['AND', ['qse.method', 0x1, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
||||||
|
case 5: // none
|
||||||
|
$this->extraOpts['o']['h'][] = 'startsQuests = 0';
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3: // endsquest [side]
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case 1: // any
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!']];
|
||||||
|
case 2: // alliance only
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
|
||||||
|
case 3: // horde only
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
|
||||||
|
case 4: // both
|
||||||
|
return ['AND', ['qse.method', 0x2, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
||||||
|
case 5: // none todo: broken, if entry starts and ends quests...
|
||||||
|
$this->extraOpts['o']['h'][] = 'endsQuests = 0';
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 16; // relatedevent (ignore removed by event)
|
||||||
|
if (!$this->isSaneNumeric($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ($cr[1] == FILTER_ENUM_ANY)
|
||||||
|
{
|
||||||
|
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
|
||||||
|
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
|
||||||
|
return ['s.guid', $goGuids];
|
||||||
|
}
|
||||||
|
else if ($cr[1] == FILTER_ENUM_NONE)
|
||||||
|
{
|
||||||
|
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId <> 0');
|
||||||
|
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
|
||||||
|
return ['s.guid', $goGuids, '!'];
|
||||||
|
}
|
||||||
|
else if ($cr[1])
|
||||||
|
{
|
||||||
|
$eventIds = DB::Aowow()->selectCol('SELECT id FROM ?_events WHERE holidayId = ?d', $cr[1]);
|
||||||
|
$goGuids = DB::World()->selectCol('SELECT DISTINCT guid FROM game_event_gameobject WHERE eventEntry IN (?a)', $eventIds);
|
||||||
|
return ['s.guid', $goGuids];
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($cr);
|
||||||
|
$this->error = 1;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createSQLForValues()
|
||||||
{
|
{
|
||||||
$parts = [];
|
$parts = [];
|
||||||
$_v = $this->values;
|
$_v = $this->fiData['v'];
|
||||||
|
|
||||||
// name
|
// name
|
||||||
if ($_v['na'])
|
if (isset($_v['na']))
|
||||||
if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]))
|
if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
|
||||||
$parts[] = $_;
|
$parts[] = $_;
|
||||||
|
|
||||||
return $parts;
|
return $parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cbOpenable(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if ($this->int2Bool($crs))
|
|
||||||
return $crs ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbQuestRelation(int $cr, int $crs, string $crv, $field, $value) : ?array
|
|
||||||
{
|
|
||||||
switch ($crs)
|
|
||||||
{
|
|
||||||
case 1: // any
|
|
||||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!']];
|
|
||||||
case 2: // alliance only
|
|
||||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_HORDE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&']];
|
|
||||||
case 3: // horde only
|
|
||||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']];
|
|
||||||
case 4: // both
|
|
||||||
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ['qt.reqRaceMask', ChrRace::MASK_HORDE, '&']], ['qt.reqRaceMask', 0]]];
|
|
||||||
case 5: // none todo (low): broken, if entry starts and ends quests...
|
|
||||||
$this->extraOpts['o']['h'][] = $field.' = 0';
|
|
||||||
return [1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbRelEvent(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if ($crs == parent::ENUM_ANY)
|
|
||||||
{
|
|
||||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
|
||||||
if ($goGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_gameobject WHERE `eventEntry` IN (?a)', $eventIds))
|
|
||||||
return ['s.guid', $goGuids];
|
|
||||||
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
else if ($crs == parent::ENUM_NONE)
|
|
||||||
{
|
|
||||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` <> 0'))
|
|
||||||
if ($goGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_gameobject WHERE `eventEntry` IN (?a)', $eventIds))
|
|
||||||
return ['s.guid', $goGuids, '!'];
|
|
||||||
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
else if (in_array($crs, $this->enums[$cr]))
|
|
||||||
{
|
|
||||||
if ($eventIds = DB::Aowow()->selectCol('SELECT `id` FROM ?_events WHERE `holidayId` = ?d', $crs))
|
|
||||||
if ($goGuids = DB::World()->selectCol('SELECT DISTINCT `guid` FROM game_event_gameobject WHERE `eventEntry` IN (?a)', $eventIds))
|
|
||||||
return ['s.guid', $goGuids];
|
|
||||||
|
|
||||||
return [0];
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,175 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class GuideList extends BaseType
|
|
||||||
{
|
|
||||||
use ListviewHelper;
|
|
||||||
|
|
||||||
public const STATUS_COLORS = array(
|
|
||||||
GUIDE_STATUS_DRAFT => '#71D5FF',
|
|
||||||
GUIDE_STATUS_REVIEW => '#FFFF00',
|
|
||||||
GUIDE_STATUS_APPROVED => '#1EFF00',
|
|
||||||
GUIDE_STATUS_REJECTED => '#FF4040',
|
|
||||||
GUIDE_STATUS_ARCHIVED => '#FFD100'
|
|
||||||
);
|
|
||||||
|
|
||||||
public static $type = Type::GUIDE;
|
|
||||||
public static $brickFile = 'guide';
|
|
||||||
public static $dataTable = '?_guides';
|
|
||||||
public static $contribute = CONTRIBUTE_CO;
|
|
||||||
|
|
||||||
private $article = [];
|
|
||||||
private $jsGlobals = [];
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT g.*, g.id AS ARRAY_KEY FROM ?_guides g';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'g' => [['a', 'c'], 'g' => 'g.`id`'],
|
|
||||||
'a' => ['j' => ['?_account a ON a.`id` = g.`userId`', true], 's' => ', IFNULL(a.`username`, "") AS "author"'],
|
|
||||||
'c' => ['j' => ['?_comments c ON c.`type` = '.Type::GUIDE.' AND c.`typeId` = g.`id` AND (c.`flags` & '.CC_FLAG_DELETED.') = 0', true], 's' => ', COUNT(c.`id`) AS "comments"']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$ratings = DB::Aowow()->select('SELECT `entry` AS ARRAY_KEY, IFNULL(SUM(`value`), 0) AS `t`, IFNULL(COUNT(*), 0) AS `n`, IFNULL(MAX(IF(`userId` = ?d, `value`, 0)), 0) AS `s` FROM ?_user_ratings WHERE `type` = ?d AND `entry` IN (?a)', User::$id, RATING_GUIDE, $this->getFoundIDs());
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as $id => &$_curTpl)
|
|
||||||
{
|
|
||||||
if (isset($ratings[$id]))
|
|
||||||
{
|
|
||||||
$_curTpl['nvotes'] = $ratings[$id]['n'];
|
|
||||||
$_curTpl['rating'] = $ratings[$id]['n'] < 5 ? -1 : $ratings[$id]['t'] / $ratings[$id]['n'];
|
|
||||||
$_curTpl['_self'] = $ratings[$id]['s'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$_curTpl['nvotes'] = 0;
|
|
||||||
$_curTpl['rating'] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getArticle(int $rev = -1) : string
|
|
||||||
{
|
|
||||||
if ($rev < -1)
|
|
||||||
$rev = -1;
|
|
||||||
|
|
||||||
if (empty($this->article[$rev]))
|
|
||||||
{
|
|
||||||
$a = DB::Aowow()->selectRow('SELECT `article`, `rev` FROM ?_articles WHERE ((`type` = ?d AND `typeId` = ?d){ OR `url` = ?}){ AND `rev`= ?d} ORDER BY `rev` DESC LIMIT 1',
|
|
||||||
Type::GUIDE, $this->id, $this->getField('url') ?: DBSIMPLE_SKIP, $rev < 0 ? DBSIMPLE_SKIP : $rev);
|
|
||||||
|
|
||||||
$this->article[$a['rev']] = $a['article'];
|
|
||||||
if ($this->article[$a['rev']])
|
|
||||||
{
|
|
||||||
Markup::parseTags($this->article[$a['rev']], $this->jsGlobals);
|
|
||||||
return $this->article[$a['rev']];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
trigger_error('GuideList::getArticle - linked article is missing');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->article[$rev] ?? '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getListviewData(bool $addDescription = false) : array
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->id,
|
|
||||||
'category' => $this->getField('category'),
|
|
||||||
'title' => $this->getField('title'),
|
|
||||||
'description' => $this->getField('description'),
|
|
||||||
'sticky' => !!($this->getField('cuFlags') & CC_FLAG_STICKY),
|
|
||||||
'nvotes' => $this->getField('nvotes'),
|
|
||||||
'url' => '?guide=' . ($this->getField('url') ?: $this->id),
|
|
||||||
'status' => $this->getField('status'),
|
|
||||||
'author' => $this->getField('author'),
|
|
||||||
'authorroles' => $this->getField('roles'),
|
|
||||||
'rating' => $this->getField('rating'),
|
|
||||||
'views' => $this->getField('views'),
|
|
||||||
'comments' => $this->getField('comments'),
|
|
||||||
// 'patch' => $this->getField(''), // 30305 - patch is pointless, use date instead
|
|
||||||
'date' => $this->getField('date'), // ok
|
|
||||||
'when' => date(Util::$dateFormatInternal, $this->getField('date'))
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($this->getField('category') == 1)
|
|
||||||
{
|
|
||||||
$data[$this->id]['classs'] = $this->getField('classId');
|
|
||||||
$data[$this->id]['spec'] = $this->getField('specId');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function userCanView() : bool
|
|
||||||
{
|
|
||||||
// is owner || is staff
|
|
||||||
return $this->getField('userId') == User::$id || User::isInGroup(U_GROUP_STAFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canBeViewed() : bool
|
|
||||||
{
|
|
||||||
// currently approved || has prev. approved version
|
|
||||||
return $this->getField('status') == GUIDE_STATUS_APPROVED || $this->getField('rev') > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canBeReported() : bool
|
|
||||||
{
|
|
||||||
// not own guide && is not archived
|
|
||||||
return $this->getField('userId') != User::$id && $this->getField('status') != GUIDE_STATUS_ARCHIVED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = GLOBALINFO_ANY) : array
|
|
||||||
{
|
|
||||||
return $this->jsGlobals;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() : string
|
|
||||||
{
|
|
||||||
$specStr = '';
|
|
||||||
|
|
||||||
if ($this->getField('classId') && $this->getField('category') == 1)
|
|
||||||
{
|
|
||||||
if ($c = $this->getField('classId'))
|
|
||||||
{
|
|
||||||
$n = Lang::game('cl', $c);
|
|
||||||
$specStr .= ' – <span class="icontiny c'.$c.'" style="background-image: url('.Cfg::get('STATIC_URL').'/images/wow/icons/tiny/class_'.ChrClass::tryFrom($c)->json().'.gif)">%s</span>';
|
|
||||||
|
|
||||||
if (($s = $this->getField('specId')) > -1)
|
|
||||||
{
|
|
||||||
$i = Game::$specIconStrings[$c][$s];
|
|
||||||
$n = '';
|
|
||||||
$specStr .= '<span class="icontiny c'.$c.'" style="background-image: url('.Cfg::get('STATIC_URL').'/images/wow/icons/tiny/'.$i.'.gif)">'.Lang::game('classSpecs', $c, $s).'</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
$specStr = sprintf($specStr, $n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$tt = '<table><tr><td><div style="max-width: 320px"><b class="q">'.$this->getField('title').'</b><br>';
|
|
||||||
$tt .= '<table width="100%"><tr><td>'.Lang::game('guide').'</td><th>'.Lang::guide('byAuthor', [$this->getField('author')]).'</th></tr></table>';
|
|
||||||
$tt .= '<table width="100%"><tr><td>'.Lang::guide('category', $this->getField('category')).$specStr.'</td><th>'.Lang::guide('patch').' 3.3.5</th></tr></table>';
|
|
||||||
$tt .= '<div class="q" style="margin: 0.25em 0">'.$this->getField('description').'</div>';
|
|
||||||
$tt .= '</div></td></tr></table>';
|
|
||||||
|
|
||||||
return $tt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class GuildList extends BaseType
|
|
||||||
{
|
|
||||||
use profilerHelper, listviewHelper;
|
|
||||||
|
|
||||||
public static $contribute = CONTRIBUTE_NONE;
|
|
||||||
|
|
||||||
public function getListviewData()
|
|
||||||
{
|
|
||||||
$this->getGuildScores();
|
|
||||||
|
|
||||||
$data = [];
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'name' => '$"'.str_replace ('"', '', $this->curTpl['name']).'"', // MUST be a string, omit any quotes in name
|
|
||||||
'members' => $this->curTpl['members'],
|
|
||||||
'faction' => $this->curTpl['faction'],
|
|
||||||
'achievementpoints' => $this->getField('achievementpoints'),
|
|
||||||
'gearscore' => $this->getField('gearscore'),
|
|
||||||
'realm' => Profiler::urlize($this->curTpl['realmName'], true),
|
|
||||||
'realmname' => $this->curTpl['realmName'],
|
|
||||||
// 'battlegroup' => Profiler::urlize($this->curTpl['battlegroup']), // was renamed to subregion somewhere around cata release
|
|
||||||
// 'battlegroupname' => $this->curTpl['battlegroup'],
|
|
||||||
'region' => Profiler::urlize($this->curTpl['region'])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getGuildScores()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Guild gear scores and achievement points are derived using a weighted average of all of the known characters in that guild.
|
|
||||||
Guilds with at least 25 level 80 players receive full benefit of the top 25 characters' gear scores, while guilds with at least 10 level 80 characters receive a slight penalty,
|
|
||||||
at least 1 level 80 a moderate penalty, and no level 80 characters a severe penalty. [...]
|
|
||||||
Instead of being based on level, achievement point averages are based around 1,500 points, but the same penalties apply.
|
|
||||||
*/
|
|
||||||
$guilds = array_column($this->templates, 'id');
|
|
||||||
if (!$guilds)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$stats = DB::Aowow()->select('SELECT `guild` AS ARRAY_KEY, `id` AS ARRAY_KEY2, `level`, `gearscore`, `achievementpoints`, IF(`cuFlags` & ?d, 0, 1) AS "synced" FROM ?_profiler_profiles WHERE `guild` IN (?a) ORDER BY `gearscore` DESC', PROFILER_CU_NEEDS_RESYNC, $guilds);
|
|
||||||
foreach ($this->iterate() as &$_curTpl)
|
|
||||||
{
|
|
||||||
$id = $_curTpl['id'];
|
|
||||||
if (empty($stats[$id]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$guildStats = array_filter($stats[$id], function ($x) { return $x['synced']; } );
|
|
||||||
if (!$guildStats)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$nMaxLevel = count(array_filter($stats[$id], function ($x) { return $x['level'] >= MAX_LEVEL; } ));
|
|
||||||
$levelMod = 1.0;
|
|
||||||
|
|
||||||
if ($nMaxLevel < 25)
|
|
||||||
$levelMod = 0.85;
|
|
||||||
if ($nMaxLevel < 10)
|
|
||||||
$levelMod = 0.66;
|
|
||||||
if ($nMaxLevel < 1)
|
|
||||||
$levelMod = 0.20;
|
|
||||||
|
|
||||||
$totalGS = $totalAP = $nMembers = 0;
|
|
||||||
foreach ($guildStats as $gs)
|
|
||||||
{
|
|
||||||
$totalGS += $gs['gearscore'] * $levelMod * min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
|
|
||||||
$totalAP += $gs['achievementpoints'] * $levelMod * min($gs['achievementpoints'], 1500) / 1500;
|
|
||||||
$nMembers += min($gs['level'], MAX_LEVEL) / MAX_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_curTpl['gearscore'] = intval($totalGS / $nMembers);
|
|
||||||
$_curTpl['achievementpoints'] = intval($totalAP / $nMembers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() {}
|
|
||||||
public function getJSGlobals($addMask = 0) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class GuildListFilter extends Filter
|
|
||||||
{
|
|
||||||
use TrProfilerFilter;
|
|
||||||
|
|
||||||
protected string $type = 'guilds';
|
|
||||||
protected array $genericFilter = [];
|
|
||||||
protected array $inputFields = array(
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'ex' => [parent::V_EQUAL, 'on', false], // only match exact
|
|
||||||
'si' => [parent::V_LIST, [SIDE_ALLIANCE, SIDE_HORDE], false], // side
|
|
||||||
'rg' => [parent::V_CALLBACK, 'cbRegionCheck', false], // region
|
|
||||||
'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server
|
|
||||||
);
|
|
||||||
|
|
||||||
public array $extraOpts = [];
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = $this->values;
|
|
||||||
|
|
||||||
// region (rg), battlegroup (bg) and server (sv) are passed to GuildList as miscData and handled there
|
|
||||||
|
|
||||||
// name [str]
|
|
||||||
if ($_v['na'])
|
|
||||||
if ($_ = $this->tokenizeString(['g.name'], $_v['na'], $_v['ex'] == 'on'))
|
|
||||||
$parts[] = $_;
|
|
||||||
|
|
||||||
// side [list]
|
|
||||||
if ($_v['si'] == SIDE_ALLIANCE)
|
|
||||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)];
|
|
||||||
else if ($_v['si'] == SIDE_HORDE)
|
|
||||||
$parts[] = ['c.race', ChrRace::fromMask(ChrRace::MASK_HORDE)];
|
|
||||||
|
|
||||||
return $parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RemoteGuildList extends GuildList
|
|
||||||
{
|
|
||||||
protected $queryBase = 'SELECT `g`.*, `g`.`guildid` AS ARRAY_KEY FROM guild g';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'g' => [['gm', 'c'], 'g' => 'ARRAY_KEY'],
|
|
||||||
'gm' => ['j' => 'guild_member gm ON gm.guildid = g.guildid', 's' => ', COUNT(1) AS members'],
|
|
||||||
'c' => ['j' => 'characters c ON c.guid = gm.guid', 's' => ', BIT_OR(IF(c.race IN (1, 3, 4, 7, 11), 1, 2)) - 1 AS faction']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
// select DB by realm
|
|
||||||
if (!$this->selectRealms($miscData))
|
|
||||||
{
|
|
||||||
trigger_error('RemoteGuildList::__construct - cannot access any realm.', E_USER_WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
reset($this->dbNames); // only use when querying single realm
|
|
||||||
$realmId = key($this->dbNames);
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
$distrib = [];
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as $guid => &$curTpl)
|
|
||||||
{
|
|
||||||
// battlegroup
|
|
||||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
|
||||||
|
|
||||||
$r = explode(':', $guid)[0];
|
|
||||||
if (!empty($realms[$r]))
|
|
||||||
{
|
|
||||||
$curTpl['realm'] = $r;
|
|
||||||
$curTpl['realmName'] = $realms[$r]['name'];
|
|
||||||
$curTpl['region'] = $realms[$r]['region'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('guild #'.$guid.' belongs to nonexistant realm #'.$r, E_USER_WARNING);
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty name
|
|
||||||
if (!$curTpl['name'])
|
|
||||||
{
|
|
||||||
trigger_error('guild #'.$guid.' on realm #'.$r.' has empty name.', E_USER_WARNING);
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// equalize distribution
|
|
||||||
if (empty($distrib[$curTpl['realm']]))
|
|
||||||
$distrib[$curTpl['realm']] = 1;
|
|
||||||
else
|
|
||||||
$distrib[$curTpl['realm']]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($conditions as $c)
|
|
||||||
if (is_int($c))
|
|
||||||
$limit = $c;
|
|
||||||
|
|
||||||
$limit ??= Cfg::get('SQL_LIMIT_DEFAULT');
|
|
||||||
if (!$limit) // int:0 means unlimited, so skip early
|
|
||||||
return;
|
|
||||||
|
|
||||||
$total = array_sum($distrib);
|
|
||||||
foreach ($distrib as &$d)
|
|
||||||
$d = ceil($limit * $d / $total);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $guid => &$curTpl)
|
|
||||||
{
|
|
||||||
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
|
|
||||||
{
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$distrib[$curTpl['realm']]--;
|
|
||||||
$limit--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function initializeLocalEntries()
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
foreach ($this->iterate() as $guid => $__)
|
|
||||||
{
|
|
||||||
$data[$guid] = array(
|
|
||||||
'realm' => $this->getField('realm'),
|
|
||||||
'realmGUID' => $this->getField('guildid'),
|
|
||||||
'name' => $this->getField('name'),
|
|
||||||
'nameUrl' => Profiler::urlize($this->getField('name')),
|
|
||||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// basic guild data
|
|
||||||
foreach (Util::createSqlBatchInsert($data) as $ins)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_profiler_guild (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `id` = `id`', array_keys(reset($data)));
|
|
||||||
|
|
||||||
// merge back local ids
|
|
||||||
$localIds = DB::Aowow()->selectCol(
|
|
||||||
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id FROM ?_profiler_guild WHERE realm IN (?a) AND realmGUID IN (?a)',
|
|
||||||
array_column($data, 'realm'),
|
|
||||||
array_column($data, 'realmGUID')
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $guid => &$_curTpl)
|
|
||||||
if (isset($localIds[$guid]))
|
|
||||||
$_curTpl['id'] = $localIds[$guid];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LocalGuildList extends GuildList
|
|
||||||
{
|
|
||||||
protected $queryBase = 'SELECT g.*, g.id AS ARRAY_KEY FROM ?_profiler_guild g';
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
|
|
||||||
// graft realm selection from miscData onto conditions
|
|
||||||
if (isset($miscData['sv']))
|
|
||||||
$realms = array_filter($realms, fn($x) => Profiler::urlize($x['name']) == Profiler::urlize($miscData['sv']));
|
|
||||||
|
|
||||||
if (isset($miscData['rg']))
|
|
||||||
$realms = array_filter($realms, fn($x) => $x['region'] == $miscData['rg']);
|
|
||||||
|
|
||||||
if (!$realms)
|
|
||||||
{
|
|
||||||
trigger_error('LocalGuildList::__construct - cannot access any realm.', E_USER_WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($conditions)
|
|
||||||
{
|
|
||||||
array_unshift($conditions, 'AND');
|
|
||||||
$conditions = ['AND', ['realm', array_keys($realms)], $conditions];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$conditions = [['realm', array_keys($realms)]];
|
|
||||||
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $id => &$curTpl)
|
|
||||||
{
|
|
||||||
if ($curTpl['realm'] && !isset($realms[$curTpl['realm']]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isset($realms[$curTpl['realm']]))
|
|
||||||
{
|
|
||||||
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
|
|
||||||
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// battlegroup
|
|
||||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getProfileUrl()
|
|
||||||
{
|
|
||||||
$url = '?guild=';
|
|
||||||
|
|
||||||
return $url.implode('.', array(
|
|
||||||
Profiler::urlize($this->getField('region')),
|
|
||||||
Profiler::urlize($this->getField('realmName')),
|
|
||||||
Profiler::urlize($this->getField('name'))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class IconList extends BaseType
|
|
||||||
{
|
|
||||||
use listviewHelper;
|
|
||||||
|
|
||||||
public static $type = Type::ICON;
|
|
||||||
public static $brickFile = 'icongallery';
|
|
||||||
public static $dataTable = '?_icons';
|
|
||||||
public static $contribute = CONTRIBUTE_CO;
|
|
||||||
|
|
||||||
private $pseudoQry = 'SELECT `iconId` AS ARRAY_KEY, COUNT(*) FROM ?# WHERE `iconId` IN (?a) GROUP BY `iconId`';
|
|
||||||
private $pseudoJoin = array(
|
|
||||||
'nItems' => '?_items',
|
|
||||||
'nSpells' => '?_spell',
|
|
||||||
'nAchievements' => '?_achievement',
|
|
||||||
'nCurrencies' => '?_currencies',
|
|
||||||
'nPets' => '?_pet'
|
|
||||||
);
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT ic.*, ic.id AS ARRAY_KEY FROM ?_icons ic';
|
|
||||||
/* this works, but takes ~100x more time than i'm comfortable with .. kept as reference
|
|
||||||
protected $queryOpts = array( // 29 => Type::ICON
|
|
||||||
'ic' => [['s', 'i', 'a', 'c', 'p'], 'g' => 'ic.id'],
|
|
||||||
'i' => ['j' => ['?_items `i` ON `i`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `i`.`id`) AS nItems'],
|
|
||||||
's' => ['j' => ['?_spell `s` ON `s`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `s`.`id`) AS nSpells'],
|
|
||||||
'a' => ['j' => ['?_achievement `a` ON `a`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `a`.`id`) AS nAchievements'],
|
|
||||||
'c' => ['j' => ['?_currencies `c` ON `c`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `c`.`id`) AS nCurrencies'],
|
|
||||||
'p' => ['j' => ['?_pet `p` ON `p`.`iconId` = `ic`.`id`', true], 's' => ', COUNT(DISTINCT `p`.`id`) AS nPets']
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if (!$this->getFoundIDs())
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach ($this->pseudoJoin as $var => $tbl)
|
|
||||||
{
|
|
||||||
$res = DB::Aowow()->selectCol($this->pseudoQry, $tbl, $this->getFoundIDs());
|
|
||||||
foreach ($res as $icon => $qty)
|
|
||||||
$this->templates[$icon][$var] = $qty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// use if you JUST need the name
|
|
||||||
public static function getName($id)
|
|
||||||
{
|
|
||||||
$n = DB::Aowow()->SelectRow('SELECT `name` FROM ?_icons WHERE `id` = ?d', $id );
|
|
||||||
return Util::localizedString($n, 'name');
|
|
||||||
}
|
|
||||||
// end static use
|
|
||||||
|
|
||||||
public function getListviewData($addInfoMask = 0x0)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->id,
|
|
||||||
'name' => $this->getField('name', true, true),
|
|
||||||
'icon' => $this->getField('name', true, true),
|
|
||||||
'itemcount' => (int)$this->getField('nItems'),
|
|
||||||
'spellcount' => (int)$this->getField('nSpells'),
|
|
||||||
'achievementcount' => (int)$this->getField('nAchievements'),
|
|
||||||
'npccount' => 0, // UNUSED
|
|
||||||
'petabilitycount' => 0, // UNUSED
|
|
||||||
'currencycount' => (int)$this->getField('nCurrencies'),
|
|
||||||
'missionabilitycount' => 0, // UNUSED
|
|
||||||
'buildingcount' => 0, // UNUSED
|
|
||||||
'petcount' => (int)$this->getField('nPets'),
|
|
||||||
'threatcount' => 0, // UNUSED
|
|
||||||
'classcount' => 0 // class icons are hardcoded and not referenced in dbc
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = GLOBALINFO_ANY)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
$data[Type::ICON][$this->id] = ['name' => $this->getField('name', true, true), 'icon' => $this->getField('name', true, true)];
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class IconListFilter extends Filter
|
|
||||||
{
|
|
||||||
private array $totalUses = [];
|
|
||||||
private array $criterion2field = array(
|
|
||||||
1 => '?_items', // items [num]
|
|
||||||
2 => '?_spell', // spells [num]
|
|
||||||
3 => '?_achievement', // achievements [num]
|
|
||||||
// 4 => '', // battlepets [num]
|
|
||||||
// 5 => '', // battlepetabilities [num]
|
|
||||||
6 => '?_currencies', // currencies [num]
|
|
||||||
// 7 => '', // garrisonabilities [num]
|
|
||||||
// 8 => '', // garrisonbuildings [num]
|
|
||||||
9 => '?_pet', // hunterpets [num]
|
|
||||||
// 10 => '', // garrisonmissionthreats [num]
|
|
||||||
11 => '', // classes [num]
|
|
||||||
13 => '' // used [num]
|
|
||||||
);
|
|
||||||
|
|
||||||
protected string $type = 'icons';
|
|
||||||
protected array $genericFilter = array(
|
|
||||||
1 => [parent::CR_CALLBACK, 'cbUseAny' ], // items [num]
|
|
||||||
2 => [parent::CR_CALLBACK, 'cbUseAny' ], // spells [num]
|
|
||||||
3 => [parent::CR_CALLBACK, 'cbUseAny' ], // achievements [num]
|
|
||||||
6 => [parent::CR_CALLBACK, 'cbUseAny' ], // currencies [num]
|
|
||||||
9 => [parent::CR_CALLBACK, 'cbUseAny' ], // hunterpets [num]
|
|
||||||
11 => [parent::CR_NYI_PH, null, 0], // classes [num]
|
|
||||||
13 => [parent::CR_CALLBACK, 'cbUseAll' ] // used [num]
|
|
||||||
);
|
|
||||||
|
|
||||||
protected array $inputFields = array(
|
|
||||||
'cr' => [parent::V_LIST, [1, 2, 3, 6, 9, 11, 13], true ], // criteria ids
|
|
||||||
'crs' => [parent::V_RANGE, [1, 6], true ], // criteria operators
|
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - all criteria are numeric here
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false] // match any / all filter
|
|
||||||
);
|
|
||||||
|
|
||||||
public array $extraOpts = [];
|
|
||||||
|
|
||||||
private function _getCnd(string $op, int $val, string $tbl) : ?array
|
|
||||||
{
|
|
||||||
switch ($op)
|
|
||||||
{
|
|
||||||
case '>':
|
|
||||||
case '>=':
|
|
||||||
case '=':
|
|
||||||
$ids = DB::Aowow()->selectCol('SELECT `iconId` AS ARRAY_KEY, COUNT(*) AS "n" FROM ?# GROUP BY `iconId` HAVING n '.$op.' '.$val, $tbl);
|
|
||||||
return $ids ? ['id', array_keys($ids)] : [1];
|
|
||||||
case '<=':
|
|
||||||
if ($val)
|
|
||||||
$op = '>';
|
|
||||||
break;
|
|
||||||
case '<':
|
|
||||||
if ($val)
|
|
||||||
$op = '>=';
|
|
||||||
break;
|
|
||||||
case '!=':
|
|
||||||
if ($val)
|
|
||||||
$op = '=';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ids = DB::Aowow()->selectCol('SELECT `iconId` AS ARRAY_KEY, COUNT(*) AS "n" FROM ?# GROUP BY `iconId` HAVING n '.$op.' '.$val, $tbl);
|
|
||||||
return $ids ? ['id', array_keys($ids), '!'] : [1];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = &$this->values;
|
|
||||||
|
|
||||||
//string
|
|
||||||
if ($_v['na'])
|
|
||||||
if ($_ = $this->tokenizeString(['name']))
|
|
||||||
$parts[] = $_;
|
|
||||||
|
|
||||||
return $parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbUseAny(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (Util::checkNumeric($crv, NUM_CAST_INT) && $this->int2Op($crs))
|
|
||||||
return $this->_getCnd($crs, $crv, $this->criterion2field[$cr]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbUseAll(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!$this->totalUses)
|
|
||||||
{
|
|
||||||
foreach ($this->criterion2field as $tbl)
|
|
||||||
{
|
|
||||||
if (!$tbl)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$res = DB::Aowow()->selectCol('SELECT `iconId` AS ARRAY_KEY, COUNT(*) AS "n" FROM ?# GROUP BY `iconId`', $tbl);
|
|
||||||
Util::arraySumByKey($this->totalUses, $res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($crs == '=')
|
|
||||||
$crs = '==';
|
|
||||||
|
|
||||||
$op = $crs;
|
|
||||||
if ($crs == '<=' && $crv)
|
|
||||||
$op = '>';
|
|
||||||
else if ($crs == '<' && $crv)
|
|
||||||
$op = '>=';
|
|
||||||
else if ($crs == '!=' && $crv)
|
|
||||||
$op = '==';
|
|
||||||
$ids = array_filter($this->totalUses, fn($x) => eval('return '.$x.' '.$op.' '.$crv.';'));
|
|
||||||
|
|
||||||
if ($crs != $op)
|
|
||||||
return $ids ? ['id', array_keys($ids), '!'] : [1];
|
|
||||||
else
|
|
||||||
return $ids ? ['id', array_keys($ids)] : ['id', array_keys($this->totalUses), '!'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,31 +8,33 @@ class ItemsetList extends BaseType
|
|||||||
{
|
{
|
||||||
use ListviewHelper;
|
use ListviewHelper;
|
||||||
|
|
||||||
public static $type = Type::ITEMSET;
|
public static $type = TYPE_ITEMSET;
|
||||||
public static $brickFile = 'itemset';
|
public static $brickFile = 'itemset';
|
||||||
public static $dataTable = '?_itemset';
|
|
||||||
|
|
||||||
public $pieceToSet = []; // used to build g_items and search
|
public $pieceToSet = []; // used to build g_items and search
|
||||||
private $classes = []; // used to build g_classes
|
private $classes = []; // used to build g_classes
|
||||||
|
|
||||||
protected $queryBase = 'SELECT `set`.*, `set`.id AS ARRAY_KEY FROM ?_itemset `set`';
|
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_itemset `set`';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = ['set' => ['o' => 'maxlevel DESC']];
|
||||||
'set' => ['o' => 'maxlevel DESC'],
|
|
||||||
'e' => ['j' => ['?_events e ON `e`.`id` = `set`.`eventId`', true], 's' => ', e.holidayId'],
|
|
||||||
'src' => ['j' => ['?_source src ON `src`.`typeId` = `set`.`id` AND `src`.`type` = 4', true], 's' => ', src1, src2, src3, src4, src5, src6, src7, src8, src9, src10, src11, src12, src13, src14, src15, src16, src17, src18, src19, src20, src21, src22, src23, src24']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [])
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions);
|
||||||
|
|
||||||
// post processing
|
// post processing
|
||||||
foreach ($this->iterate() as &$_curTpl)
|
foreach ($this->iterate() as &$_curTpl)
|
||||||
{
|
{
|
||||||
$_curTpl['classes'] = ChrClass::fromMask($_curTpl['classMask']);
|
$_curTpl['classes'] = [];
|
||||||
$this->classes = array_merge($this->classes, $_curTpl['classes']);
|
|
||||||
|
|
||||||
$_curTpl['pieces'] = [];
|
$_curTpl['pieces'] = [];
|
||||||
|
for ($i = 1; $i < 12; $i++)
|
||||||
|
{
|
||||||
|
if ($_curTpl['classMask'] & (1 << ($i - 1)))
|
||||||
|
{
|
||||||
|
$this->classes[] = $i;
|
||||||
|
$_curTpl['classes'][] = $i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ($i = 1; $i < 10; $i++)
|
for ($i = 1; $i < 10; $i++)
|
||||||
{
|
{
|
||||||
if ($piece = $_curTpl['item'.$i])
|
if ($piece = $_curTpl['item'.$i])
|
||||||
@@ -76,184 +76,134 @@ class ItemsetList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
if ($this->classes && ($addMask & GLOBALINFO_RELATED))
|
if ($this->classes && ($addMask & GLOBALINFO_RELATED))
|
||||||
$data[Type::CHR_CLASS] = array_combine($this->classes, $this->classes);
|
$data[TYPE_CLASS] = array_combine($this->classes, $this->classes);
|
||||||
|
|
||||||
if ($this->pieceToSet && ($addMask & GLOBALINFO_SELF))
|
if ($this->pieceToSet && ($addMask & GLOBALINFO_SELF))
|
||||||
$data[Type::ITEM] = array_combine(array_keys($this->pieceToSet), array_keys($this->pieceToSet));
|
$data[TYPE_ITEM] = array_combine(array_keys($this->pieceToSet), array_keys($this->pieceToSet));
|
||||||
|
|
||||||
if ($addMask & GLOBALINFO_SELF)
|
if ($addMask & GLOBALINFO_SELF)
|
||||||
foreach ($this->iterate() as $id => $__)
|
foreach ($this->iterate() as $id => $__)
|
||||||
$data[Type::ITEMSET][$id] = ['name' => $this->getField('name', true)];
|
$data[TYPE_ITEMSET][$id] = ['name' => $this->getField('name', true)];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderTooltip()
|
public function renderTooltip() { }
|
||||||
{
|
|
||||||
if (!$this->curTpl)
|
|
||||||
return array();
|
|
||||||
|
|
||||||
$x = '<table><tr><td>';
|
|
||||||
$x .= '<span class="q'.$this->getField('quality').'">'.$this->getField('name', true).'</span><br />';
|
|
||||||
|
|
||||||
$nCl = 0;
|
|
||||||
if ($_ = $this->getField('classMask'))
|
|
||||||
{
|
|
||||||
$jsg = [];
|
|
||||||
$cl = Lang::getClassString($_, $jsg);
|
|
||||||
$nCl = count($jsg);
|
|
||||||
$x .= Util::ucFirst($nCl > 1 ? Lang::game('classes') : Lang::game('class')).Lang::main('colon').$cl.'<br />';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_ = $this->getField('contentGroup'))
|
|
||||||
$x .= Lang::itemset('notes', $_).($this->getField('heroic') ? ' <i class="q2">('.Lang::item('heroic').')</i>' : '').'<br />';
|
|
||||||
|
|
||||||
if (!$nCl || !$this->getField('type'))
|
|
||||||
$x.= Lang::itemset('types', $this->getField('type')).'<br />';
|
|
||||||
|
|
||||||
if ($bonuses = $this->getBonuses())
|
|
||||||
{
|
|
||||||
$x .= '<span>';
|
|
||||||
|
|
||||||
foreach ($bonuses as $b)
|
|
||||||
$x .= '<br /><span class="q13">'.$b['bonus'].' '.Lang::itemset('_pieces').Lang::main('colon').'</span>'.$b['desc'];
|
|
||||||
|
|
||||||
$x .= '</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
$x .= '</td></tr></table>';
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBonuses()
|
|
||||||
{
|
|
||||||
$spells = [];
|
|
||||||
for ($i = 1; $i < 9; $i++)
|
|
||||||
{
|
|
||||||
$spl = $this->getField('spell'.$i);
|
|
||||||
$qty = $this->getField('bonus'.$i);
|
|
||||||
|
|
||||||
// cant use spell as index, would change order
|
|
||||||
if ($spl && $qty)
|
|
||||||
$spells[] = ['id' => $spl, 'bonus' => $qty];
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort by required pieces ASC
|
|
||||||
usort($spells, function($a, $b) {
|
|
||||||
if ($a['bonus'] == $b['bonus'])
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return ($a['bonus'] > $b['bonus']) ? 1 : -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
$setSpells = new SpellList(array(['s.id', array_column($spells, 'id')]));
|
|
||||||
foreach ($setSpells->iterate() as $spellId => $__)
|
|
||||||
{
|
|
||||||
foreach ($spells as &$s)
|
|
||||||
{
|
|
||||||
if ($spellId != $s['id'])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$s['desc'] = $setSpells->parseText('description', $this->getField('reqLevel') ?: MAX_LEVEL)[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $spells;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// missing filter: "Available to Players"
|
// missing filter: "Available to Players"
|
||||||
class ItemsetListFilter extends Filter
|
class ItemsetListFilter extends Filter
|
||||||
{
|
{
|
||||||
protected string $type = 'itemsets';
|
// cr => [type, field, misc, extraCol]
|
||||||
protected array $enums = array(
|
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
|
||||||
6 => parent::ENUM_EVENT
|
2 => [FILTER_CR_NUMERIC, 'id', null, true], // id
|
||||||
|
3 => [FILTER_CR_NUMERIC, 'npieces', ], // pieces
|
||||||
|
4 => [FILTER_CR_STRING, 'bonusText', true ], // bonustext
|
||||||
|
5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic
|
||||||
|
6 => [FILTER_CR_ENUM, 'holidayId', ], // relatedevent
|
||||||
|
8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||||
|
9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||||
|
10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $genericFilter = array(
|
protected function createSQLForCriterium(&$cr)
|
||||||
2 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
{
|
||||||
3 => [parent::CR_NUMERIC, 'npieces', NUM_CAST_INT ], // pieces
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
||||||
4 => [parent::CR_STRING, 'bonusText', STR_LOCALIZED ], // bonustext
|
{
|
||||||
5 => [parent::CR_BOOLEAN, 'heroic' ], // heroic
|
if ($genCR = $this->genericCriterion($cr))
|
||||||
6 => [parent::CR_ENUM, 'e.holidayId', true, true], // relatedevent
|
return $genCR;
|
||||||
8 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
|
||||||
9 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
|
||||||
10 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
|
||||||
12 => [parent::CR_CALLBACK, 'cbAvaliable', ] // available to players [yn]
|
|
||||||
);
|
|
||||||
|
|
||||||
protected array $inputFields = array(
|
unset($cr);
|
||||||
'cr' => [parent::V_RANGE, [2, 12], true ], // criteria ids
|
$this->error = true;
|
||||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 424]], true ], // criteria operators
|
return [1];
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values - only printable chars, no delimiters
|
}
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / description - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'qu' => [parent::V_RANGE, [0, 7], true ], // quality
|
|
||||||
'ty' => [parent::V_RANGE, [1, 12], true ], // set type
|
|
||||||
'minle' => [parent::V_RANGE, [1, 999], false], // min item level
|
|
||||||
'maxle' => [parent::V_RANGE, [1, 999], false], // max itemlevel
|
|
||||||
'minrl' => [parent::V_RANGE, [1, MAX_LEVEL], false], // min required level
|
|
||||||
'maxrl' => [parent::V_RANGE, [1, MAX_LEVEL], false], // max required level
|
|
||||||
'cl' => [parent::V_LIST, [[1, 9], 11], false], // class
|
|
||||||
'ta' => [parent::V_RANGE, [1, 30], false] // tag / content group
|
|
||||||
);
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
switch ($cr[0])
|
||||||
|
{
|
||||||
|
case 12: // available to players [yn] ugh .. scan loot, quest and vendor templates and write to ?_itemset
|
||||||
|
/* todo */ return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($cr);
|
||||||
|
$this->error = true;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createSQLForValues()
|
||||||
{
|
{
|
||||||
$parts = [];
|
$parts = [];
|
||||||
$_v = &$this->values;
|
$_v = &$this->fiData['v'];
|
||||||
|
|
||||||
// name [str]
|
// name [str]
|
||||||
if ($_v['na'])
|
if (isset($_v['na']))
|
||||||
if ($_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]))
|
if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
|
||||||
$parts[] = $_;
|
$parts[] = $_;
|
||||||
|
|
||||||
// quality [enum]
|
// quality [enum]
|
||||||
if ($_v['qu'])
|
if (isset($_v['qu']))
|
||||||
$parts[] = ['quality', $_v['qu']];
|
$parts[] = ['quality', (array)$_v['qu']];
|
||||||
|
|
||||||
// type [enum]
|
// type [enum]
|
||||||
if ($_v['ty'])
|
if (isset($_v['ty']))
|
||||||
$parts[] = ['type', $_v['ty']];
|
$parts[] = ['type', (array)$_v['ty']];
|
||||||
|
|
||||||
// itemLevel min [int]
|
// itemLevel min [int]
|
||||||
if ($_v['minle'])
|
if (isset($_v['minle']))
|
||||||
$parts[] = ['minLevel', $_v['minle'], '>='];
|
{
|
||||||
|
if (is_int($_v['minle']) && $_v['minle'] > 0)
|
||||||
|
$parts[] = ['minLevel', $_v['minle'], '>='];
|
||||||
|
else
|
||||||
|
unset($_v['minle']);
|
||||||
|
}
|
||||||
|
|
||||||
// itemLevel max [int]
|
// itemLevel max [int]
|
||||||
if ($_v['maxle'])
|
if (isset($_v['maxle']))
|
||||||
$parts[] = ['maxLevel', $_v['maxle'], '<='];
|
{
|
||||||
|
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
|
||||||
|
$parts[] = ['maxLevel', $_v['maxle'], '<='];
|
||||||
|
else
|
||||||
|
unset($_v['maxle']);
|
||||||
|
}
|
||||||
|
|
||||||
// reqLevel min [int]
|
// reqLevel min [int]
|
||||||
if ($_v['minrl'])
|
if (isset($_v['minrl']))
|
||||||
$parts[] = ['reqLevel', $_v['minrl'], '>='];
|
{
|
||||||
|
if (is_int($_v['minrl']) && $_v['minrl'] > 0)
|
||||||
|
$parts[] = ['reqLevel', $_v['minrl'], '>='];
|
||||||
|
else
|
||||||
|
unset($_v['minrl']);
|
||||||
|
}
|
||||||
|
|
||||||
// reqLevel max [int]
|
// reqLevel max [int]
|
||||||
if ($_v['maxrl'])
|
if (isset($_v['maxrl']))
|
||||||
$parts[] = ['reqLevel', $_v['maxrl'], '<='];
|
{
|
||||||
|
if (is_int($_v['maxrl']) && $_v['maxrl'] > 0)
|
||||||
|
$parts[] = ['reqLevel', $_v['maxrl'], '<='];
|
||||||
|
else
|
||||||
|
unset($_v['maxrl']);
|
||||||
|
}
|
||||||
|
|
||||||
// class [enum]
|
// class [enum]
|
||||||
if ($_v['cl'])
|
if (isset($_v['cl']))
|
||||||
$parts[] = ['classMask', $this->list2Mask([$_v['cl']]), '&'];
|
{
|
||||||
|
if (in_array($_v['cl'], [1, 2, 3, 4, 5, 6, 7, 8, 9, 11]))
|
||||||
|
$parts[] = ['classMask', $this->list2Mask($_v['cl']), '&'];
|
||||||
|
else
|
||||||
|
unset($_v['cl']);
|
||||||
|
}
|
||||||
|
|
||||||
// tag [enum]
|
// tag [enum]
|
||||||
if ($_v['ta'])
|
if (isset($_v['ta']))
|
||||||
$parts[] = ['contentGroup', intVal($_v['ta'])];
|
{
|
||||||
|
if ($_v['ta'] > 0 && $_v['ta'] < 31)
|
||||||
|
$parts[] = ['contentGroup', intVal($_v['ta'])];
|
||||||
|
else
|
||||||
|
unset($_v['ta']);
|
||||||
|
}
|
||||||
|
|
||||||
return $parts;
|
return $parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cbAvaliable(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
return match ($crs)
|
|
||||||
{
|
|
||||||
1 => ['src.typeId', null, '!'], // Yes
|
|
||||||
2 => ['src.typeId', null], // No
|
|
||||||
default => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class MailList extends BaseType
|
|
||||||
{
|
|
||||||
public static $type = Type::MAIL;
|
|
||||||
public static $brickFile = 'mail';
|
|
||||||
public static $dataTable = '?_mails';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT m.*, m.id AS ARRAY_KEY FROM ?_mails m';
|
|
||||||
protected $queryOpts = [];
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as $_id => &$_curTpl)
|
|
||||||
{
|
|
||||||
$_curTpl['name'] = Util::localizedString($_curTpl, 'subject', true);
|
|
||||||
if (!$_curTpl['name'])
|
|
||||||
{
|
|
||||||
$_curTpl['name'] = sprintf(Lang::mail('untitled'), $_id);
|
|
||||||
$_curTpl['subject_loc0'] = $_curTpl['name'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getName($id)
|
|
||||||
{
|
|
||||||
$n = DB::Aowow()->SelectRow('SELECT subject_loc0, subject_loc2, subject_loc3, subject_loc4, subject_loc6, subject_loc8 FROM ?_mails WHERE id = ?d', $id);
|
|
||||||
return Util::localizedString($n, 'subject');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getListviewData()
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$body = str_replace('[br]', ' ', Util::parseHtmlText($this->getField('text', true), true));
|
|
||||||
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->id,
|
|
||||||
'subject' => $this->getField('subject', true),
|
|
||||||
'body' => Lang::trimTextClean($body),
|
|
||||||
'attachments' => [$this->getField('attachment')]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = 0)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
if ($a = $this->curTpl['attachment'])
|
|
||||||
$data[Type::ITEM][$a] = $a;
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,15 +8,10 @@ class PetList extends BaseType
|
|||||||
{
|
{
|
||||||
use ListviewHelper;
|
use ListviewHelper;
|
||||||
|
|
||||||
public static $type = Type::PET;
|
public static $type = TYPE_PET;
|
||||||
public static $brickFile = 'pet';
|
public static $brickFile = 'pet';
|
||||||
public static $dataTable = '?_pet';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT p.*, p.id AS ARRAY_KEY FROM ?_pet p';
|
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_pet p';
|
||||||
protected $queryOpts = array(
|
|
||||||
'p' => [['ic']],
|
|
||||||
'ic' => ['j' => ['?_icons ic ON p.iconId = ic.id', true], 's' => ', ic.name AS iconString'],
|
|
||||||
);
|
|
||||||
|
|
||||||
public function getListviewData()
|
public function getListviewData()
|
||||||
{
|
{
|
||||||
@@ -61,10 +54,10 @@ class PetList extends BaseType
|
|||||||
if ($addMask & GLOBALINFO_RELATED)
|
if ($addMask & GLOBALINFO_RELATED)
|
||||||
for ($i = 1; $i <= 4; $i++)
|
for ($i = 1; $i <= 4; $i++)
|
||||||
if ($this->curTpl['spellId'.$i] > 0)
|
if ($this->curTpl['spellId'.$i] > 0)
|
||||||
$data[Type::SPELL][$this->curTpl['spellId'.$i]] = $this->curTpl['spellId'.$i];
|
$data[TYPE_SPELL][$this->curTpl['spellId'.$i]] = $this->curTpl['spellId'.$i];
|
||||||
|
|
||||||
if ($addMask & GLOBALINFO_SELF)
|
if ($addMask & GLOBALINFO_SELF)
|
||||||
$data[Type::PET][$this->id] = ['icon' => $this->curTpl['iconString']];
|
$data[TYPE_PET][$this->id] = ['icon' => $this->curTpl['iconString']];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
|
|||||||
@@ -1,763 +1,228 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
|
// class CharacterList extends BaseType // new profiler-related parent: ProfilerType?; maybe a trait is enough => use ProfileHelper;
|
||||||
|
// class GuildList extends BaseType
|
||||||
|
// class ArenaTeamList extends BaseType
|
||||||
class ProfileList extends BaseType
|
class ProfileList extends BaseType
|
||||||
{
|
{
|
||||||
use profilerHelper, listviewHelper;
|
public static $type = 0; // profiles dont actually have one
|
||||||
|
public static $brickFile = 'profile';
|
||||||
|
|
||||||
public static $contribute = CONTRIBUTE_NONE;
|
protected $queryBase = ''; // SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiles p';
|
||||||
|
protected $queryOpts = array(
|
||||||
|
'p' => [['pa', 'pg']],
|
||||||
|
'pam' => [['?_profiles_arenateam_member pam ON pam.memberId = p.id', true], 's' => ', pam.status'],
|
||||||
|
'pa' => ['?_profiles_arenateam pa ON pa.id = pam.teamId', 's' => ', pa.mode, pa.name'],
|
||||||
|
'pgm' => [['?_profiles_guid_member pgm ON pgm.memberId = p.Id', true], 's' => ', pgm.rankId'],
|
||||||
|
'pg' => ['?_profiles_guild pg ON pg.if = pgm.guildId', 's' => ', pg.name']
|
||||||
|
);
|
||||||
|
|
||||||
public function getListviewData($addInfo = 0, array $reqCols = [])
|
public function __construct($conditions = [], $miscData = null)
|
||||||
|
{
|
||||||
|
$character = array(
|
||||||
|
'id' => 2,
|
||||||
|
'name' => 'CharName',
|
||||||
|
'region' => ['eu', 'Europe'],
|
||||||
|
'battlegroup' => ['pure-pwnage', 'Pure Pwnage'],
|
||||||
|
'realm' => ['dafuque', 'da\'Fuqúe'],
|
||||||
|
'level' => 80,
|
||||||
|
'classs' => 11,
|
||||||
|
'race' => 6,
|
||||||
|
'faction' => 1, // 0:alliance; 1:horde
|
||||||
|
'gender' => 1, // 0:male, 1:female
|
||||||
|
'skincolor' => 0, // playerbytes % 256
|
||||||
|
'hairstyle' => 0, // (playerbytes >> 16) % 256
|
||||||
|
'haircolor' => 0, // (playerbytes >> 24) % 256
|
||||||
|
'facetype' => 0, // (playerbytes >> 8) % 256 [maybe features]
|
||||||
|
'features' => 0, // playerBytes2 % 256 [maybe facetype]
|
||||||
|
'source' => 2, // source: used if you create a profile from a genuine character. It inherites region, realm and bGroup
|
||||||
|
'sourcename' => 'SourceCharName', // > if these three are false we get a 'genuine' profile [0 for genuine characters..?]
|
||||||
|
'user' => 1, // > 'genuine' is the parameter for _isArmoryProfile(allowCustoms) ['' for genuine characters..?]
|
||||||
|
'username' => 'TestUser', // > also, if 'source' <> 0, the char-icon is requestet via profile.php?avatar
|
||||||
|
'published' => 1, // public / private
|
||||||
|
'pinned' => 1, // usable for some utility funcs on site
|
||||||
|
'nomodel' => 0x0, // unchecks DisplayOnCharacter by (1 << slotId - 1)
|
||||||
|
'title' => 0, // titleId currently in use or null
|
||||||
|
'guild' => 'GuildName', // only on chars; id or null
|
||||||
|
'description' => 'this is a profile', // only on custom profiles
|
||||||
|
'arenateams' => [], // [size(2|3|5) => DisplayName]; DisplayName gets urlized to use as link
|
||||||
|
'playedtime' => 0, // exact to the day
|
||||||
|
'lastupdated' => 0, // timestamp in ms
|
||||||
|
'achievementpoints' => 0, // max you have
|
||||||
|
'talents' => array(
|
||||||
|
'builds' => array(
|
||||||
|
['talents' => '', 'glyphs' => ''], // talents:string of 0-5 points; glyphs: itemIds.join(':')
|
||||||
|
),
|
||||||
|
'active' => 1 // 1|2
|
||||||
|
),
|
||||||
|
'customs' => [], // custom profiles created from this char; profileId => [name, ownerId, iconString(optional)]
|
||||||
|
'skills' => [], // skillId => [curVal, maxVal]; can contain anything, should be limited to prim/sec professions
|
||||||
|
'inventory' => [], // slotId => [itemId, subItemId, permEnchantId, tempEnchantId, gemItemId1, gemItemId2, gemItemId3, gemItemId4]
|
||||||
|
'auras' => [], // custom list of buffs, debuffs [spellId]
|
||||||
|
|
||||||
|
// completion lists: [subjectId => amount/timestamp/1]
|
||||||
|
'reputation' => [], // factionId => amount
|
||||||
|
'titles' => [], // titleId => 1
|
||||||
|
'spells' => [], // spellId => 1; recipes, pets, mounts
|
||||||
|
'achievements' => [], // achievementId => timestamp
|
||||||
|
'quests' => [], // questId => 1
|
||||||
|
|
||||||
|
// UNKNOWN
|
||||||
|
'bookmarks' => [2], // UNK pinned or claimed userId => profileIds..?
|
||||||
|
'statistics' => [], // UNK all statistics? [achievementId => killCount]
|
||||||
|
'activity' => [], // UNK recent achievements? [achievementId => killCount]
|
||||||
|
'glyphs' => [], // not really used .. i guess..?
|
||||||
|
'pets' => array( // UNK
|
||||||
|
[], // one array per pet, structure UNK
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// parent::__construct($conditions, $miscData);
|
||||||
|
@include('datasets/ProfilerExampleChar'); // tmp char data
|
||||||
|
|
||||||
|
$this->templates[2] = $character;
|
||||||
|
$this->curTpl = $character;
|
||||||
|
|
||||||
|
if ($this->error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// post processing
|
||||||
|
// foreach ($this->iterate() as $_id => &$curTpl)
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getListviewData()
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if (!$this->isVisibleToUser())
|
$tDistrib = $this->getTalentDistribution();
|
||||||
continue;
|
|
||||||
|
|
||||||
if (($addInfo & PROFILEINFO_PROFILE) && !$this->isCustom())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (($addInfo & PROFILEINFO_CHARACTER) && $this->isCustom())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$data[$this->id] = array(
|
$data[$this->id] = array(
|
||||||
'id' => $this->getField('id'),
|
'id' => 0,
|
||||||
'name' => $this->getField('name'),
|
'name' => $this->curTpl['name'],
|
||||||
'race' => $this->getField('race'),
|
'achievementpoints' => $this->curTpl['achievementpoints'],
|
||||||
'classs' => $this->getField('class'),
|
'guild' => $this->curTpl['guild'], // 0 if none
|
||||||
'gender' => $this->getField('gender'),
|
'guildRank' => -1,
|
||||||
'level' => $this->getField('level'),
|
'realm' => $this->curTpl['realm'][0],
|
||||||
'faction' => ChrRace::tryFrom($this->getField('race'))?->isAlliance() ? 0 : 1,
|
'realmname' => $this->curTpl['realm'][1],
|
||||||
'talenttree1' => $this->getField('talenttree1'),
|
'battlegroup' => $this->curTpl['battlegroup'][0],
|
||||||
'talenttree2' => $this->getField('talenttree2'),
|
'battlegroupname' => $this->curTpl['battlegroup'][0],
|
||||||
'talenttree3' => $this->getField('talenttree3'),
|
'region' => $this->curTpl['region'][0],
|
||||||
'talentspec' => $this->getField('activespec') + 1, // 0 => 1; 1 => 2
|
'level' => $this->curTpl['level'],
|
||||||
'achievementpoints' => $this->getField('achievementpoints'),
|
'race' => $this->curTpl['race'],
|
||||||
'guild' => $this->curTpl['guildname'] ? '$"'.str_replace ('"', '', $this->curTpl['guildname']).'"' : '', // force this to be a string
|
'gender' => $this->curTpl['gender'],
|
||||||
'guildrank' => $this->getField('guildrank'),
|
'classs' => $this->curTpl['classs'],
|
||||||
'realm' => Profiler::urlize($this->getField('realmName'), true),
|
'faction' => $this->curTpl['faction'],
|
||||||
'realmname' => $this->getField('realmName'),
|
'talenttree1' => $tDistrib[0],
|
||||||
// 'battlegroup' => Profiler::urlize($this->getField('battlegroup')), // was renamed to subregion somewhere around cata release
|
'talenttree2' => $tDistrib[1],
|
||||||
// 'battlegroupname' => $this->getField('battlegroup'),
|
'talenttree3' => $tDistrib[2],
|
||||||
'gearscore' => $this->getField('gearscore')
|
'talentspec' => $this->curTpl['talents']['active']
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($addInfo & PROFILEINFO_USER)
|
if (!empty($this->curTpl['description']))
|
||||||
$data[$this->id]['published'] = (int)!!($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
|
$data[$this->id]['description'] = $this->curTpl['description'];
|
||||||
|
|
||||||
// for the lv this determins if the link is profile=<id> or profile=<region>.<realm>.<name>
|
if (!empty($this->curTpl['icon']))
|
||||||
if (!$this->isCustom())
|
$data[$this->id]['icon'] = $this->curTpl['icon'];
|
||||||
$data[$this->id]['region'] = Profiler::urlize($this->getField('region'));
|
|
||||||
|
|
||||||
if ($addInfo & PROFILEINFO_ARENA)
|
if ($this->curTpl['cuFlags'] & PROFILE_CU_PUBLISHED)
|
||||||
{
|
$data[$this->id]['published'] = 1;
|
||||||
$data[$this->id]['rating'] = $this->getField('rating');
|
|
||||||
$data[$this->id]['captain'] = $this->getField('captain');
|
|
||||||
$data[$this->id]['games'] = $this->getField('seasonGames');
|
|
||||||
$data[$this->id]['wins'] = $this->getField('seasonWins');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter asked for skills - add them
|
if ($this->curTpl['cuFlags'] & PROFILE_CU_PINNED)
|
||||||
foreach ($reqCols as $col)
|
|
||||||
$data[$this->id][$col] = $this->getField($col);
|
|
||||||
|
|
||||||
if ($addInfo & PROFILEINFO_PROFILE)
|
|
||||||
{
|
|
||||||
if ($_ = $this->getField('description'))
|
|
||||||
$data[$this->id]['description'] = $_;
|
|
||||||
|
|
||||||
if ($_ = $this->getField('icon'))
|
|
||||||
$data[$this->id]['icon'] = $_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($addInfo & PROFILEINFO_CHARACTER)
|
|
||||||
if ($_ = $this->getField('renameItr'))
|
|
||||||
$data[$this->id]['renameItr'] = $_;
|
|
||||||
|
|
||||||
if ($this->getField('cuFlags') & PROFILER_CU_PINNED)
|
|
||||||
$data[$this->id]['pinned'] = 1;
|
$data[$this->id]['pinned'] = 1;
|
||||||
|
|
||||||
if ($this->getField('cuFlags') & PROFILER_CU_DELETED)
|
if ($this->curTpl['cuFlags'] & PROFILE_CU_DELETED)
|
||||||
$data[$this->id]['deleted'] = 1;
|
$data[$this->id]['deleted'] = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_values($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip()
|
|
||||||
{
|
|
||||||
if (!$this->curTpl)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
$title = '';
|
|
||||||
$name = $this->getField('name');
|
|
||||||
if ($_ = $this->getField('title'))
|
|
||||||
$title = (new TitleList(array(['id', $_])))->getField($this->getField('gender') ? 'female' : 'male', true);
|
|
||||||
|
|
||||||
if ($this->isCustom())
|
|
||||||
$name .= Lang::profiler('customProfile');
|
|
||||||
else if ($title)
|
|
||||||
$name = sprintf($title, $name);
|
|
||||||
|
|
||||||
$x = '<table>';
|
|
||||||
$x .= '<tr><td><b class="q">'.$name.'</b></td></tr>';
|
|
||||||
if ($g = $this->getField('guildname'))
|
|
||||||
$x .= '<tr><td><'.$g.'></td></tr>';
|
|
||||||
else if ($d = $this->getField('description'))
|
|
||||||
$x .= '<tr><td>'.$d.'</td></tr>';
|
|
||||||
$x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->getField('race')).' '.Lang::game('cl', $this->getField('class')).'</td></tr>';
|
|
||||||
$x .= '</table>';
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = 0)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $id => $__)
|
|
||||||
{
|
|
||||||
if (($addMask & PROFILEINFO_PROFILE) && $this->isCustom())
|
|
||||||
{
|
|
||||||
$profile = array(
|
|
||||||
'id' => $this->getField('id'),
|
|
||||||
'name' => $this->getField('name'),
|
|
||||||
'race' => $this->getField('race'),
|
|
||||||
'classs' => $this->getField('class'),
|
|
||||||
'level' => $this->getField('level'),
|
|
||||||
'gender' => $this->getField('gender')
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($_ = $this->getField('icon'))
|
|
||||||
$profile['icon'] = $_;
|
|
||||||
|
|
||||||
$data[] = $profile;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($addMask & PROFILEINFO_CHARACTER && !$this->isCustom())
|
|
||||||
{
|
|
||||||
if (!isset($realms[$this->getField('realm')]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$data[] = array(
|
|
||||||
'id' => $this->getField('id'),
|
|
||||||
'name' => $this->getField('name'),
|
|
||||||
'realmname' => $realms[$this->getField('realm')]['name'],
|
|
||||||
'region' => $realms[$this->getField('realm')]['region'],
|
|
||||||
'realm' => Profiler::urlize($realms[$this->getField('realm')]['name']),
|
|
||||||
'race' => $this->getField('race'),
|
|
||||||
'classs' => $this->getField('class'),
|
|
||||||
'level' => $this->getField('level'),
|
|
||||||
'gender' => $this->getField('gender'),
|
|
||||||
'pinned' => $this->getField('cuFlags') & PROFILER_CU_PINNED ? 1 : 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isCustom()
|
public function renderTooltip($interactive = false)
|
||||||
{
|
{
|
||||||
return $this->getField('cuFlags') & PROFILER_CU_PROFILE;
|
if (!$this->curTpl)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
$x = '<table>';
|
||||||
|
$x .= '<tr><td><b class="q">'.$this->getField('name').'</b></td></tr>';
|
||||||
|
if ($g = $this->getField('name'))
|
||||||
|
$x .= '<tr><td><'.$g.'> ('.$this->getField('guildrank').')</td></tr>';
|
||||||
|
else if ($d = $this->getField('description'))
|
||||||
|
$x .= '<tr><td>'.$d.'</td></tr>';
|
||||||
|
$x .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->curTpl['race']).' '.Lang::game('cl', $this->curTpl['classs']).'</td></tr>';
|
||||||
|
$x .= '</table>';
|
||||||
|
|
||||||
|
return $x;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isVisibleToUser()
|
public function getJSGlobals($addMask = 0) {}
|
||||||
|
|
||||||
|
private function getTalentDistribution()
|
||||||
{
|
{
|
||||||
if (!$this->isCustom() || User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
|
if (!empty($this->tDistribution))
|
||||||
return true;
|
$this->tDistribution[$this->curTpl['classId']] = DB::Aowow()->selectCol('SELECT COUNT(t.id) FROM dbc_talent t JOIN dbc_talenttab tt ON t.tabId = tt.id WHERE tt.classMask & ?d GROUP BY tt.id ORDER BY tt.tabNumber ASC', 1 << ($this->curTpl['classId'] - 1));
|
||||||
|
|
||||||
if ($this->getField('cuFlags') & PROFILER_CU_DELETED)
|
$result = [];
|
||||||
return false;
|
$start = 0;
|
||||||
|
foreach ($this->tDistribution[$this->curTpl['classId']] as $len)
|
||||||
if (User::$id == $this->getField('user'))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return (bool)($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIcon()
|
|
||||||
{
|
|
||||||
if ($_ = $this->getField('icon'))
|
|
||||||
return $_;
|
|
||||||
|
|
||||||
$str = 'chr_';
|
|
||||||
|
|
||||||
switch ($this->getField('race'))
|
|
||||||
{
|
{
|
||||||
case 1: $str .= 'human_'; break;
|
$result[] = array_sum(str_split(substr($this->curTpl['talentString'], $start, $len)));
|
||||||
case 2: $str .= 'orc_'; break;
|
$start += $len;
|
||||||
case 3: $str .= 'dwarf_'; break;
|
|
||||||
case 4: $str .= 'nightelf_'; break;
|
|
||||||
case 5: $str .= 'scourge_'; break;
|
|
||||||
case 6: $str .= 'tauren_'; break;
|
|
||||||
case 7: $str .= 'gnome_'; break;
|
|
||||||
case 8: $str .= 'troll_'; break;
|
|
||||||
case 10: $str .= 'bloodelf_'; break;
|
|
||||||
case 11: $str .= 'draenei_'; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->getField('gender'))
|
return $result;
|
||||||
{
|
|
||||||
case 0: $str .= 'male_'; break;
|
|
||||||
case 1: $str .= 'female_'; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($this->getField('class'))
|
|
||||||
{
|
|
||||||
case 1: $str .= 'warrior0'; break;
|
|
||||||
case 2: $str .= 'paladin0'; break;
|
|
||||||
case 3: $str .= 'hunter0'; break;
|
|
||||||
case 4: $str .= 'rogue0'; break;
|
|
||||||
case 5: $str .= 'priest0'; break;
|
|
||||||
case 6: $str .= 'deathknight0'; break;
|
|
||||||
case 7: $str .= 'shaman0'; break;
|
|
||||||
case 8: $str .= 'mage0'; break;
|
|
||||||
case 9: $str .= 'warlock0'; break;
|
|
||||||
case 11: $str .= 'druid0'; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$level = $this->getField('level');
|
|
||||||
if ($level > 59)
|
|
||||||
$str .= floor(($level - 60) / 10) + 2;
|
|
||||||
else
|
|
||||||
$str .= 1;
|
|
||||||
|
|
||||||
return $str;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ProfileListFilter extends Filter
|
class ProfileListFilter extends Filter
|
||||||
{
|
{
|
||||||
use TrProfilerFilter;
|
public $extraOpts = [];
|
||||||
|
|
||||||
protected string $type = 'profiles';
|
protected $genericFilter = array(
|
||||||
protected array $genericFilter = array(
|
|
||||||
2 => [parent::CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
|
|
||||||
3 => [parent::CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num]
|
|
||||||
5 => [parent::CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
|
|
||||||
6 => [parent::CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
|
|
||||||
7 => [parent::CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
|
|
||||||
9 => [parent::CR_STRING, 'g.name' ], // guildname
|
|
||||||
10 => [parent::CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
|
|
||||||
12 => [parent::CR_CALLBACK, 'cbTeamName', 2, null], // teamname2v2
|
|
||||||
15 => [parent::CR_CALLBACK, 'cbTeamName', 3, null], // teamname3v3
|
|
||||||
18 => [parent::CR_CALLBACK, 'cbTeamName', 5, null], // teamname5v5
|
|
||||||
13 => [parent::CR_CALLBACK, 'cbTeamRating', 2, null], // teamrtng2v2
|
|
||||||
16 => [parent::CR_CALLBACK, 'cbTeamRating', 3, null], // teamrtng3v3
|
|
||||||
19 => [parent::CR_CALLBACK, 'cbTeamRating', 5, null], // teamrtng5v5
|
|
||||||
14 => [parent::CR_NYI_PH, null, 0 /* 2 */ ], // teamcontrib2v2 [num]
|
|
||||||
17 => [parent::CR_NYI_PH, null, 0 /* 3 */ ], // teamcontrib3v3 [num]
|
|
||||||
20 => [parent::CR_NYI_PH, null, 0 /* 5 */ ], // teamcontrib5v5 [num]
|
|
||||||
21 => [parent::CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
|
|
||||||
23 => [parent::CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
|
|
||||||
25 => [parent::CR_CALLBACK, 'cbProfession', SKILL_ALCHEMY, null], // alchemy [num]
|
|
||||||
26 => [parent::CR_CALLBACK, 'cbProfession', SKILL_BLACKSMITHING, null], // blacksmithing [num]
|
|
||||||
27 => [parent::CR_CALLBACK, 'cbProfession', SKILL_ENCHANTING, null], // enchanting [num]
|
|
||||||
28 => [parent::CR_CALLBACK, 'cbProfession', SKILL_ENGINEERING, null], // engineering [num]
|
|
||||||
29 => [parent::CR_CALLBACK, 'cbProfession', SKILL_HERBALISM, null], // herbalism [num]
|
|
||||||
30 => [parent::CR_CALLBACK, 'cbProfession', SKILL_INSCRIPTION, null], // inscription [num]
|
|
||||||
31 => [parent::CR_CALLBACK, 'cbProfession', SKILL_JEWELCRAFTING, null], // jewelcrafting [num]
|
|
||||||
32 => [parent::CR_CALLBACK, 'cbProfession', SKILL_LEATHERWORKING, null], // leatherworking [num]
|
|
||||||
33 => [parent::CR_CALLBACK, 'cbProfession', SKILL_MINING, null], // mining [num]
|
|
||||||
34 => [parent::CR_CALLBACK, 'cbProfession', SKILL_SKINNING, null], // skinning [num]
|
|
||||||
35 => [parent::CR_CALLBACK, 'cbProfession', SKILL_TAILORING, null], // tailoring [num]
|
|
||||||
36 => [parent::CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $inputFields = array(
|
protected function createSQLForCriterium(&$cr)
|
||||||
'cr' => [parent::V_RANGE, [1, 36], true ], // criteria ids
|
|
||||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 5000]], true ], // criteria operators
|
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_CRV, true ], // criteria values
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'ex' => [parent::V_EQUAL, 'on', false], // only match exact
|
|
||||||
'si' => [parent::V_LIST, [SIDE_ALLIANCE, SIDE_HORDE], false], // side
|
|
||||||
'ra' => [parent::V_LIST, [[1, 8], 10, 11], true ], // race
|
|
||||||
'cl' => [parent::V_LIST, [[1, 9], 11], true ], // class
|
|
||||||
'minle' => [parent::V_RANGE, [1, MAX_LEVEL], false], // min level
|
|
||||||
'maxle' => [parent::V_RANGE, [1, MAX_LEVEL], false], // max level
|
|
||||||
'rg' => [parent::V_CALLBACK, 'cbRegionCheck', false], // region
|
|
||||||
'sv' => [parent::V_CALLBACK, 'cbServerCheck', false], // server
|
|
||||||
);
|
|
||||||
|
|
||||||
public bool $useLocalList = false;
|
|
||||||
public array $extraOpts = [];
|
|
||||||
|
|
||||||
/* heads up!
|
|
||||||
a couple of filters are too complex to be run against the characters database
|
|
||||||
if they are selected, force useage of LocalProfileList
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function __construct(string|array $data, array $opts = [])
|
|
||||||
{
|
{
|
||||||
parent::__construct($data, $opts);
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
||||||
|
|
||||||
if (!empty($this->criteria['cr']))
|
|
||||||
if (array_intersect($this->criteria['cr'], [2, 5, 6, 7, 21]))
|
|
||||||
$this->useLocalList = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = $this->values;
|
|
||||||
|
|
||||||
// region (rg), battlegroup (bg) and server (sv) are passed to ProflieList as miscData and handled there
|
|
||||||
|
|
||||||
// table key differs between remote and local :<
|
|
||||||
$k = $this->useLocalList ? 'p' : 'c';
|
|
||||||
|
|
||||||
// name [str] - the table is case sensitive. Since i don't want to destroy indizes, lets alter the search terms
|
|
||||||
if ($_v['na'])
|
|
||||||
{
|
{
|
||||||
$lower = $this->tokenizeString([$k.'.name'], Util::lower($_v['na']), $_v['ex'] == 'on', true);
|
if ($genCR = $this->genericCriterion($cr))
|
||||||
$proper = $this->tokenizeString([$k.'.name'], Util::ucWords($_v['na']), $_v['ex'] == 'on', true);
|
return $genCR;
|
||||||
|
|
||||||
$parts[] = ['OR', $lower, $proper];
|
unset($cr);
|
||||||
|
$this->error = true;
|
||||||
|
return [1];
|
||||||
}
|
}
|
||||||
|
|
||||||
// side [list]
|
switch ($cr[0])
|
||||||
if ($_v['si'] == SIDE_ALLIANCE)
|
{
|
||||||
$parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_ALLIANCE)];
|
default:
|
||||||
else if ($_v['si'] == SIDE_HORDE)
|
break;
|
||||||
$parts[] = [$k.'.race', ChrRace::fromMask(ChrRace::MASK_HORDE)];
|
}
|
||||||
|
|
||||||
// race [list]
|
unset($cr);
|
||||||
if ($_v['ra'])
|
$this->error = 1;
|
||||||
$parts[] = [$k.'.race', $_v['ra']];
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
// class [list]
|
protected function createSQLForValues()
|
||||||
if ($_v['cl'])
|
{
|
||||||
$parts[] = [$k.'.class', $_v['cl']];
|
$parts = [];
|
||||||
|
$_v = $this->fiData['v'];
|
||||||
|
|
||||||
// min level [int]
|
// name
|
||||||
if ($_v['minle'])
|
if (isset($_v['na']))
|
||||||
$parts[] = [$k.'.level', $_v['minle'], '>='];
|
if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
|
||||||
|
$parts[] = $_;
|
||||||
// max level [int]
|
|
||||||
if ($_v['maxle'])
|
|
||||||
$parts[] = [$k.'.level', $_v['maxle'], '<='];
|
|
||||||
|
|
||||||
return $parts;
|
return $parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cbProfession(int $cr, int $crs, string $crv, $skillId) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$k = 'sk_'.Util::createHash(12);
|
|
||||||
$col = 'skill-'.$skillId;
|
|
||||||
|
|
||||||
$this->fiExtraCols[$skillId] = $col;
|
|
||||||
|
|
||||||
if ($this->useLocalList)
|
|
||||||
{
|
|
||||||
$this->extraOpts[$k] = array(
|
|
||||||
'j' => [sprintf('?_profiler_completion_skills %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`skillId` = %2$d AND `%1$s`.`value` %3$s %4$d', $k, $skillId, $crs, $crv), true],
|
|
||||||
's' => [', '.$k.'.`value` AS "'.$col.'"']
|
|
||||||
);
|
|
||||||
return [$k.'.skillId', null, '!'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->extraOpts[$k] = array(
|
|
||||||
'j' => [sprintf('character_skills %1$s ON `%1$s`.`guid` = c.`guid` AND `%1$s`.`skill` = %2$d AND `%1$s`.`value` %3$s %4$d', $k, $skillId, $crs, $crv), true],
|
|
||||||
's' => [', '.$k.'.`value` AS "'.$col.'"']
|
|
||||||
);
|
|
||||||
return [$k.'.skill', null, '!'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbCompletedAcv(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!Type::validateIds(Type::ACHIEVEMENT, $crv))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$k = 'acv_'.Util::createHash(12);
|
|
||||||
|
|
||||||
if ($this->useLocalList)
|
|
||||||
{
|
|
||||||
$this->extraOpts[$k] = ['j' => [sprintf('?_profiler_completion_achievements %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`achievementId` = %2$d', $k, $crv), true]];
|
|
||||||
return [$k.'.achievementId', null, '!'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->extraOpts[$k] = ['j' => [sprintf('character_achievement %1$s ON `%1$s`.`guid` = c.`guid` AND `%1$s`.`achievement` = %2$d', $k, $crv), true]];
|
|
||||||
return [$k.'.achievement', null, '!'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbWearsItems(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!Type::validateIds(Type::ITEM, $crv))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$k = 'i_'.Util::createHash(12);
|
|
||||||
|
|
||||||
$this->extraOpts[$k] = ['j' => [sprintf('?_profiler_items %1$s ON `%1$s`.`id` = p.`id` AND `%1$s`.`item` = %2$d', $k, $crv), true]];
|
|
||||||
return [$k.'.item', null, '!'];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbHasGuild(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($this->useLocalList)
|
|
||||||
return ['p.guild', null, $crs ? '!' : null];
|
|
||||||
else
|
|
||||||
return ['gm.guildId', null, $crs ? '!' : null];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbHasGuildRank(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($this->useLocalList)
|
|
||||||
return ['p.guildrank', $crv, $crs];
|
|
||||||
else
|
|
||||||
return ['gm.rank', $crv, $crs];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbTeamName(int $cr, int $crs, string $crv, $size) : ?array
|
|
||||||
{
|
|
||||||
if ($_ = $this->tokenizeString(['at.name'], $crv))
|
|
||||||
return ['AND', ['at.type', $size], $_];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbTeamRating(int $cr, int $crs, string $crv, $size) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return ['AND', ['at.type', $size], ['at.rating', $crv, $crs]];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbAchievs(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($this->useLocalList)
|
|
||||||
return ['p.achievementpoints', $crv, $crs];
|
|
||||||
else
|
|
||||||
return ['cap.counter', $crv, $crs];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class RemoteProfileList extends ProfileList
|
|
||||||
{
|
|
||||||
protected $queryBase = 'SELECT `c`.*, `c`.`guid` AS ARRAY_KEY FROM characters c';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'c' => [['gm', 'g', 'cap']], // 12698: use criteria of Achievement 4496 as shortcut to get total achievement points
|
|
||||||
'cap' => ['j' => ['character_achievement_progress cap ON cap.guid = c.guid AND cap.criteria = 12698', true], 's' => ', IFNULL(cap.counter, 0) AS achievementpoints'],
|
|
||||||
'gm' => ['j' => ['guild_member gm ON gm.guid = c.guid', true], 's' => ', gm.rank AS guildrank'],
|
|
||||||
'g' => ['j' => ['guild g ON g.guildid = gm.guildid', true], 's' => ', g.guildid AS guild, g.name AS guildname'],
|
|
||||||
'atm' => ['j' => ['arena_team_member atm ON atm.guid = c.guid', true], 's' => ', atm.personalRating AS rating'],
|
|
||||||
'at' => [['atm'], 'j' => 'arena_team at ON atm.arenaTeamId = at.arenaTeamId', 's' => ', at.name AS arenateam, IF(at.captainGuid = c.guid, 1, 0) AS captain']
|
|
||||||
);
|
|
||||||
|
|
||||||
private $rnItr = []; // rename iterator [name => nCharsWithThisName]
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
// select DB by realm
|
|
||||||
if (!$this->selectRealms($miscData))
|
|
||||||
{
|
|
||||||
trigger_error('RemoteProfileList::__construct - cannot access any realm.', E_USER_WARNING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
reset($this->dbNames); // only use when querying single realm
|
|
||||||
$realmId = key($this->dbNames);
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
$talentSpells = [];
|
|
||||||
$talentLookup = [];
|
|
||||||
$distrib = [];
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as $guid => &$curTpl)
|
|
||||||
{
|
|
||||||
// battlegroup
|
|
||||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
|
||||||
|
|
||||||
// realm
|
|
||||||
[$r, $g] = explode(':', $guid);
|
|
||||||
if (!empty($realms[$r]))
|
|
||||||
{
|
|
||||||
$curTpl['realm'] = $r;
|
|
||||||
$curTpl['realmName'] = $realms[$r]['name'];
|
|
||||||
$curTpl['region'] = $realms[$r]['region'];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trigger_error('char #'.$guid.' belongs to nonexistant realm #'.$r, E_USER_WARNING);
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty name
|
|
||||||
if (!$curTpl['name'])
|
|
||||||
{
|
|
||||||
trigger_error('char #'.$guid.' on realm #'.$r.' has empty name.', E_USER_WARNING);
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// temp id
|
|
||||||
$curTpl['id'] = 0;
|
|
||||||
|
|
||||||
// talent points pre
|
|
||||||
$talentLookup[$r][$g] = [];
|
|
||||||
$talentSpells[] = $curTpl['class'];
|
|
||||||
$curTpl['activespec'] = $curTpl['activeTalentGroup'];
|
|
||||||
|
|
||||||
// equalize distribution
|
|
||||||
if (empty($distrib[$curTpl['realm']]))
|
|
||||||
$distrib[$curTpl['realm']] = 1;
|
|
||||||
else
|
|
||||||
$distrib[$curTpl['realm']]++;
|
|
||||||
|
|
||||||
// char is pending rename
|
|
||||||
if ($curTpl['at_login'] & 0x1)
|
|
||||||
{
|
|
||||||
if (!isset($this->rnItr[$curTpl['name']]))
|
|
||||||
$this->rnItr[$curTpl['name']] = DB::Aowow()->selectCell('SELECT MAX(renameItr) FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID IS NOT NULL AND name = ?', $r, $curTpl['name']) ?: 0;
|
|
||||||
|
|
||||||
// already saved as "pending rename"
|
|
||||||
if ($rnItr = DB::Aowow()->selectCell('SELECT renameItr FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $r, $g))
|
|
||||||
$curTpl['renameItr'] = $rnItr;
|
|
||||||
// not yet recognized: get max itr
|
|
||||||
else
|
|
||||||
$curTpl['renameItr'] = ++$this->rnItr[$curTpl['name']];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
$curTpl['renameItr'] = 0;
|
|
||||||
|
|
||||||
$curTpl['cuFlags'] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($talentLookup as $realm => $chars)
|
|
||||||
$talentLookup[$realm] = DB::Characters($realm)->selectCol('SELECT guid AS ARRAY_KEY, spell AS ARRAY_KEY2, talentGroup FROM character_talent ct WHERE guid IN (?a)', array_keys($chars));
|
|
||||||
|
|
||||||
$talentSpells = DB::Aowow()->select('SELECT spell AS ARRAY_KEY, tab, `rank` FROM ?_talents WHERE class IN (?a)', array_unique($talentSpells));
|
|
||||||
|
|
||||||
foreach ($conditions as $c)
|
|
||||||
if (is_int($c))
|
|
||||||
$limit = $c;
|
|
||||||
|
|
||||||
$limit ??= Cfg::get('SQL_LIMIT_DEFAULT');
|
|
||||||
if (!$limit) // int:0 means unlimited, so skip process
|
|
||||||
$distrib = [];
|
|
||||||
|
|
||||||
$total = array_sum($distrib);
|
|
||||||
foreach ($distrib as &$d)
|
|
||||||
$d = ceil($limit * $d / $total);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $guid => &$curTpl)
|
|
||||||
{
|
|
||||||
if ($distrib)
|
|
||||||
{
|
|
||||||
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
|
|
||||||
{
|
|
||||||
unset($this->templates[$guid]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$distrib[$curTpl['realm']]--;
|
|
||||||
$limit--;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$r, $g] = explode(':', $guid);
|
|
||||||
|
|
||||||
// talent points post
|
|
||||||
$curTpl['talenttree1'] = 0;
|
|
||||||
$curTpl['talenttree2'] = 0;
|
|
||||||
$curTpl['talenttree3'] = 0;
|
|
||||||
if (!empty($talentLookup[$r][$g]))
|
|
||||||
{
|
|
||||||
$talents = array_filter($talentLookup[$r][$g], function($v) use ($curTpl) { return $curTpl['activespec'] == $v; } );
|
|
||||||
foreach (array_intersect_key($talentSpells, $talents) as $spell => $data)
|
|
||||||
$curTpl['talenttree'.($data['tab'] + 1)] += $data['rank'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getListviewData($addInfoMask = 0, array $reqCols = [])
|
|
||||||
{
|
|
||||||
$data = parent::getListviewData($addInfoMask, $reqCols);
|
|
||||||
|
|
||||||
// not wanted on server list
|
|
||||||
foreach ($data as &$d)
|
|
||||||
unset($d['published']);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function initializeLocalEntries()
|
|
||||||
{
|
|
||||||
$baseData = $guildData = [];
|
|
||||||
foreach ($this->iterate() as $guid => $__)
|
|
||||||
{
|
|
||||||
$realmId = $this->getField('realm');
|
|
||||||
$guildGUID = $this->getField('guild');
|
|
||||||
|
|
||||||
$baseData[$guid] = array(
|
|
||||||
'realm' => $realmId,
|
|
||||||
'realmGUID' => $this->getField('guid'),
|
|
||||||
'name' => $this->getField('name'),
|
|
||||||
'renameItr' => $this->getField('renameItr'),
|
|
||||||
'race' => $this->getField('race'),
|
|
||||||
'class' => $this->getField('class'),
|
|
||||||
'level' => $this->getField('level'),
|
|
||||||
'gender' => $this->getField('gender'),
|
|
||||||
'guild' => $guildGUID ?: null,
|
|
||||||
'guildrank' => $guildGUID ? $this->getField('guildrank') : null,
|
|
||||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($guildGUID && empty($guildData[$realmId.'-'.$guildGUID]))
|
|
||||||
$guildData[$realmId.'-'.$guildGUID] = array(
|
|
||||||
'realm' => $realmId,
|
|
||||||
'realmGUID' => $guildGUID,
|
|
||||||
'name' => $this->getField('guildname'),
|
|
||||||
'nameUrl' => Profiler::urlize($this->getField('guildname')),
|
|
||||||
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// basic guild data (satisfying table constraints)
|
|
||||||
if ($guildData)
|
|
||||||
{
|
|
||||||
foreach (Util::createSqlBatchInsert($guildData) as $ins)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_profiler_guild (?#) VALUES '.$ins.' ON DUPLICATE KEY UPDATE `id` = `id`', array_keys(reset($guildData)));
|
|
||||||
|
|
||||||
// merge back local ids
|
|
||||||
$localGuilds = DB::Aowow()->selectCol('SELECT realm AS ARRAY_KEY, realmGUID AS ARRAY_KEY2, id FROM ?_profiler_guild WHERE realm IN (?a) AND realmGUID IN (?a)',
|
|
||||||
array_column($guildData, 'realm'), array_column($guildData, 'realmGUID')
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($baseData as &$bd)
|
|
||||||
if ($bd['guild'])
|
|
||||||
$bd['guild'] = $localGuilds[$bd['realm']][$bd['guild']];
|
|
||||||
}
|
|
||||||
|
|
||||||
// basic char data (enough for tooltips)
|
|
||||||
if ($baseData)
|
|
||||||
{
|
|
||||||
foreach ($baseData as $ins)
|
|
||||||
DB::Aowow()->query('INSERT INTO ?_profiler_profiles (?#) VALUES (?a) ON DUPLICATE KEY UPDATE `name` = ?, `renameItr` = ?d', array_keys($ins), array_values($ins), $ins['name'], $ins['renameItr']);
|
|
||||||
|
|
||||||
// merge back local ids
|
|
||||||
$localIds = DB::Aowow()->select(
|
|
||||||
'SELECT CONCAT(realm, ":", realmGUID) AS ARRAY_KEY, id, gearscore FROM ?_profiler_profiles WHERE (cuFlags & ?d) = 0 AND realm IN (?a) AND realmGUID IN (?a)',
|
|
||||||
PROFILER_CU_PROFILE,
|
|
||||||
array_column($baseData, 'realm'),
|
|
||||||
array_column($baseData, 'realmGUID')
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $guid => &$_curTpl)
|
|
||||||
if (isset($localIds[$guid]))
|
|
||||||
$_curTpl = array_merge($_curTpl, $localIds[$guid]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LocalProfileList extends ProfileList
|
|
||||||
{
|
|
||||||
protected $queryBase = 'SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiler_profiles p';
|
|
||||||
protected $queryOpts = array(
|
|
||||||
'p' => [['g'], 'g' => 'p.id'],
|
|
||||||
'ap' => ['j' => ['?_account_profiles ap ON ap.profileId = p.id', true], 's' => ', (IFNULL(ap.ExtraFlags, 0) | p.cuFlags) AS cuFlags'],
|
|
||||||
'atm' => ['j' => ['?_profiler_arena_team_member atm ON atm.profileId = p.id', true], 's' => ', atm.captain, atm.personalRating AS rating, atm.seasonGames, atm.seasonWins'],
|
|
||||||
'at' => [['atm'], 'j' => ['?_profiler_arena_team at ON at.id = atm.arenaTeamId', true], 's' => ', at.type'],
|
|
||||||
'g' => ['j' => ['?_profiler_guild g ON g.id = p.guild', true], 's' => ', g.name AS guildname']
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
$realms = Profiler::getRealms();
|
|
||||||
|
|
||||||
// graft realm selection from miscData onto conditions
|
|
||||||
$realmIds = [];
|
|
||||||
if (isset($miscData['sv']))
|
|
||||||
$realmIds = array_keys(array_filter($realms, fn($x) => Profiler::urlize($x['name']) == Profiler::urlize($miscData['sv'])));
|
|
||||||
|
|
||||||
if (isset($miscData['rg']))
|
|
||||||
$realmIds = array_merge($realmIds, array_keys(array_filter($realms, fn($x) => $x['region'] == $miscData['rg'])));
|
|
||||||
|
|
||||||
if ($conditions && $realmIds)
|
|
||||||
{
|
|
||||||
array_unshift($conditions, 'AND');
|
|
||||||
$conditions = ['AND', ['realm', $realmIds], $conditions];
|
|
||||||
}
|
|
||||||
else if ($realmIds)
|
|
||||||
$conditions = [['realm', $realmIds]];
|
|
||||||
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
if ($this->error)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $id => &$curTpl)
|
|
||||||
{
|
|
||||||
if (!$curTpl['realm']) // custom profile w/o realminfo
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!isset($realms[$curTpl['realm']]))
|
|
||||||
{
|
|
||||||
unset($this->templates[$id]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$curTpl['realmName'] = $realms[$curTpl['realm']]['name'];
|
|
||||||
$curTpl['region'] = $realms[$curTpl['realm']]['region'];
|
|
||||||
$curTpl['battlegroup'] = Cfg::get('BATTLEGROUP');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getProfileUrl()
|
|
||||||
{
|
|
||||||
$url = '?profile=';
|
|
||||||
|
|
||||||
if ($this->isCustom())
|
|
||||||
return $url.$this->getField('id');
|
|
||||||
|
|
||||||
return $url.implode('.', array(
|
|
||||||
Profiler::urlize($this->getField('region')),
|
|
||||||
Profiler::urlize($this->getField('realmName')),
|
|
||||||
urlencode($this->getField('name'))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class QuestList extends BaseType
|
class QuestList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::QUEST;
|
public static $type = TYPE_QUEST;
|
||||||
public static $brickFile = 'quest';
|
public static $brickFile = 'quest';
|
||||||
public static $dataTable = '?_quests';
|
|
||||||
|
|
||||||
public $requires = [];
|
public $requires = [];
|
||||||
public $rewards = [];
|
public $rewards = [];
|
||||||
@@ -21,10 +18,9 @@ class QuestList extends BaseType
|
|||||||
'q' => [],
|
'q' => [],
|
||||||
'rsc' => ['j' => '?_spell rsc ON q.rewardSpellCast = rsc.id'], // limit rewardSpellCasts
|
'rsc' => ['j' => '?_spell rsc ON q.rewardSpellCast = rsc.id'], // limit rewardSpellCasts
|
||||||
'qse' => ['j' => '?_quests_startend qse ON q.id = qse.questId', 's' => ', qse.method'], // groupConcat..?
|
'qse' => ['j' => '?_quests_startend qse ON q.id = qse.questId', 's' => ', qse.method'], // groupConcat..?
|
||||||
'e' => ['j' => ['?_events e ON e.id = `q`.eventId', true], 's' => ', e.holidayId']
|
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [], $miscData = null)
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions, $miscData);
|
||||||
|
|
||||||
@@ -37,7 +33,7 @@ class QuestList extends BaseType
|
|||||||
$_curTpl['cat1'] = $_curTpl['zoneOrSort']; // should probably be in a method...
|
$_curTpl['cat1'] = $_curTpl['zoneOrSort']; // should probably be in a method...
|
||||||
$_curTpl['cat2'] = 0;
|
$_curTpl['cat2'] = 0;
|
||||||
|
|
||||||
foreach (Game::$questClasses as $k => $arr)
|
foreach (Util::$questClasses as $k => $arr)
|
||||||
{
|
{
|
||||||
if (in_array($_curTpl['cat1'], $arr))
|
if (in_array($_curTpl['cat1'], $arr))
|
||||||
{
|
{
|
||||||
@@ -51,18 +47,18 @@ class QuestList extends BaseType
|
|||||||
for ($i = 1; $i < 7; $i++)
|
for ($i = 1; $i < 7; $i++)
|
||||||
{
|
{
|
||||||
if ($_ = $_curTpl['reqItemId'.$i])
|
if ($_ = $_curTpl['reqItemId'.$i])
|
||||||
$requires[Type::ITEM][] = $_;
|
$requires[TYPE_ITEM][] = $_;
|
||||||
|
|
||||||
if ($i > 4)
|
if ($i > 4)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ($_curTpl['reqNpcOrGo'.$i] > 0)
|
if ($_curTpl['reqNpcOrGo'.$i] > 0)
|
||||||
$requires[Type::NPC][] = $_curTpl['reqNpcOrGo'.$i];
|
$requires[TYPE_NPC][] = $_curTpl['reqNpcOrGo'.$i];
|
||||||
else if ($_curTpl['reqNpcOrGo'.$i] < 0)
|
else if ($_curTpl['reqNpcOrGo'.$i] < 0)
|
||||||
$requires[Type::OBJECT][] = -$_curTpl['reqNpcOrGo'.$i];
|
$requires[TYPE_OBJECT][] = -$_curTpl['reqNpcOrGo'.$i];
|
||||||
|
|
||||||
if ($_ = $_curTpl['reqSourceItemId'.$i])
|
if ($_ = $_curTpl['reqSourceItemId'.$i])
|
||||||
$requires[Type::ITEM][] = $_;
|
$requires[TYPE_ITEM][] = $_;
|
||||||
}
|
}
|
||||||
if ($requires)
|
if ($requires)
|
||||||
$this->requires[$id] = $requires;
|
$this->requires[$id] = $requires;
|
||||||
@@ -72,24 +68,24 @@ class QuestList extends BaseType
|
|||||||
$choices = [];
|
$choices = [];
|
||||||
|
|
||||||
if ($_ = $_curTpl['rewardTitleId'])
|
if ($_ = $_curTpl['rewardTitleId'])
|
||||||
$rewards[Type::TITLE][] = $_;
|
$rewards[TYPE_TITLE][] = $_;
|
||||||
|
|
||||||
if ($_ = $_curTpl['rewardHonorPoints'])
|
if ($_ = $_curTpl['rewardHonorPoints'])
|
||||||
$rewards[Type::CURRENCY][104] = $_;
|
$rewards[TYPE_CURRENCY][104] = $_;
|
||||||
|
|
||||||
if ($_ = $_curTpl['rewardArenaPoints'])
|
if ($_ = $_curTpl['rewardArenaPoints'])
|
||||||
$rewards[Type::CURRENCY][103] = $_;
|
$rewards[TYPE_CURRENCY][103] = $_;
|
||||||
|
|
||||||
for ($i = 1; $i < 7; $i++)
|
for ($i = 1; $i < 7; $i++)
|
||||||
{
|
{
|
||||||
if ($_ = $_curTpl['rewardChoiceItemId'.$i])
|
if ($_ = $_curTpl['rewardChoiceItemId'.$i])
|
||||||
$choices[Type::ITEM][$_] = $_curTpl['rewardChoiceItemCount'.$i];
|
$choices[TYPE_ITEM][$_] = $_curTpl['rewardChoiceItemCount'.$i];
|
||||||
|
|
||||||
if ($i > 5)
|
if ($i > 5)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ($_ = $_curTpl['rewardFactionId'.$i])
|
if ($_ = $_curTpl['rewardFactionId'.$i])
|
||||||
$rewards[Type::FACTION][$_] = $_curTpl['rewardFactionValue'.$i];
|
$rewards[TYPE_FACTION][$_] = $_curTpl['rewardFactionValue'.$i];
|
||||||
|
|
||||||
if ($i > 4)
|
if ($i > 4)
|
||||||
continue;
|
continue;
|
||||||
@@ -98,9 +94,9 @@ class QuestList extends BaseType
|
|||||||
{
|
{
|
||||||
$qty = $_curTpl['rewardItemCount'.$i];
|
$qty = $_curTpl['rewardItemCount'.$i];
|
||||||
if (in_array($_, $currencies))
|
if (in_array($_, $currencies))
|
||||||
$rewards[Type::CURRENCY][array_search($_, $currencies)] = $qty;
|
$rewards[TYPE_CURRENCY][array_search($_, $currencies)] = $qty;
|
||||||
else
|
else
|
||||||
$rewards[Type::ITEM][$_] = $qty;
|
$rewards[TYPE_ITEM][$_] = $qty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($rewards)
|
if ($rewards)
|
||||||
@@ -114,7 +110,7 @@ class QuestList extends BaseType
|
|||||||
// static use START
|
// static use START
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_quests WHERE id = ?d', $id);
|
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_quests WHERE id = ?d', $id);
|
||||||
return Util::localizedString($n, 'name');
|
return Util::localizedString($n, 'name');
|
||||||
}
|
}
|
||||||
// static use END
|
// static use END
|
||||||
@@ -124,18 +120,12 @@ class QuestList extends BaseType
|
|||||||
return $this->curTpl['flags'] & QUEST_FLAG_REPEATABLE || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_REPEATABLE;
|
return $this->curTpl['flags'] & QUEST_FLAG_REPEATABLE || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_REPEATABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isDaily()
|
public function isDaily($strict = false)
|
||||||
{
|
{
|
||||||
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
|
if ($strict)
|
||||||
return 1;
|
return $this->curTpl['flags'] & QUEST_FLAG_DAILY;
|
||||||
|
else
|
||||||
if ($this->curTpl['flags'] & QUEST_FLAG_WEEKLY)
|
return $this->curTpl['flags'] & (QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY) || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_MONTHLY;
|
||||||
return 2;
|
|
||||||
|
|
||||||
if ($this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_MONTHLY)
|
|
||||||
return 3;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// using reqPlayerKills and rewardHonor as a crutch .. has TC this even implemented..?
|
// using reqPlayerKills and rewardHonor as a crutch .. has TC this even implemented..?
|
||||||
@@ -150,18 +140,15 @@ class QuestList extends BaseType
|
|||||||
return in_array($this->getField('zoneOrSortBak'), [-22, -284, -366, -369, -370, -376, -374]) && !$this->isRepeatable();
|
return in_array($this->getField('zoneOrSortBak'), [-22, -284, -366, -369, -370, -376, -374]) && !$this->isRepeatable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSourceData(int $id = 0) : array
|
public function getSourceData()
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if ($id && $id != $this->id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$data[$this->id] = array(
|
$data[$this->id] = array(
|
||||||
"n" => $this->getField('name', true),
|
"n" => $this->getField('name', true),
|
||||||
"t" => Type::QUEST,
|
"t" => TYPE_QUEST,
|
||||||
"ti" => $this->id,
|
"ti" => $this->id,
|
||||||
"c" => $this->curTpl['cat1'],
|
"c" => $this->curTpl['cat1'],
|
||||||
"c2" => $this->curTpl['cat2']
|
"c2" => $this->curTpl['cat2']
|
||||||
@@ -177,10 +164,10 @@ class QuestList extends BaseType
|
|||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
if (!(ChrRace::sideFromMask($this->curTpl['reqRaceMask']) & $side))
|
if (!(Util::sideByRaceMask($this->curTpl['reqRaceMask']) & $side))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
[$series, $first] = DB::Aowow()->SelectRow(
|
list($series, $first) = DB::Aowow()->SelectRow(
|
||||||
'SELECT IF(prev.id OR cur.nextQuestIdChain, 1, 0) AS "0", IF(prev.id IS NULL AND cur.nextQuestIdChain, 1, 0) AS "1" FROM ?_quests cur LEFT JOIN ?_quests prev ON prev.nextQuestIdChain = cur.id WHERE cur.id = ?d',
|
'SELECT IF(prev.id OR cur.nextQuestIdChain, 1, 0) AS "0", IF(prev.id IS NULL AND cur.nextQuestIdChain, 1, 0) AS "1" FROM ?_quests cur LEFT JOIN ?_quests prev ON prev.nextQuestIdChain = cur.id WHERE cur.id = ?d',
|
||||||
$this->id
|
$this->id
|
||||||
);
|
);
|
||||||
@@ -213,22 +200,22 @@ class QuestList extends BaseType
|
|||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'level' => $this->curTpl['level'],
|
'level' => $this->curTpl['level'],
|
||||||
'reqlevel' => $this->curTpl['minLevel'],
|
'reqlevel' => $this->curTpl['minLevel'],
|
||||||
'name' => Lang::unescapeUISequences($this->getField('name', true), Lang::FMT_RAW),
|
'name' => $this->getField('name', true),
|
||||||
'side' => ChrRace::sideFromMask($this->curTpl['reqRaceMask']),
|
'side' => Util::sideByRaceMask($this->curTpl['reqRaceMask']),
|
||||||
'wflags' => 0x0,
|
'wflags' => 0x0,
|
||||||
'xp' => $this->curTpl['rewardXP']
|
'xp' => $this->curTpl['rewardXP']
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!empty($this->rewards[$this->id][Type::CURRENCY]))
|
if (!empty($this->rewards[$this->id][TYPE_CURRENCY]))
|
||||||
foreach ($this->rewards[$this->id][Type::CURRENCY] as $iId => $qty)
|
foreach ($this->rewards[$this->id][TYPE_CURRENCY] as $iId => $qty)
|
||||||
$data[$this->id]['currencyrewards'][] = [$iId, $qty];
|
$data[$this->id]['currencyrewards'][] = [$iId, $qty];
|
||||||
|
|
||||||
if (!empty($this->rewards[$this->id][Type::ITEM]))
|
if (!empty($this->rewards[$this->id][TYPE_ITEM]))
|
||||||
foreach ($this->rewards[$this->id][Type::ITEM] as $iId => $qty)
|
foreach ($this->rewards[$this->id][TYPE_ITEM] as $iId => $qty)
|
||||||
$data[$this->id]['itemrewards'][] = [$iId, $qty];
|
$data[$this->id]['itemrewards'][] = [$iId, $qty];
|
||||||
|
|
||||||
if (!empty($this->choices[$this->id][Type::ITEM]))
|
if (!empty($this->choices[$this->id][TYPE_ITEM]))
|
||||||
foreach ($this->choices[$this->id][Type::ITEM] as $iId => $qty)
|
foreach ($this->choices[$this->id][TYPE_ITEM] as $iId => $qty)
|
||||||
$data[$this->id]['itemchoices'][] = [$iId, $qty];
|
$data[$this->id]['itemchoices'][] = [$iId, $qty];
|
||||||
|
|
||||||
if ($_ = $this->curTpl['rewardTitleId'])
|
if ($_ = $this->curTpl['rewardTitleId'])
|
||||||
@@ -240,8 +227,8 @@ class QuestList extends BaseType
|
|||||||
if ($_ = $this->curTpl['reqClassMask'])
|
if ($_ = $this->curTpl['reqClassMask'])
|
||||||
$data[$this->id]['reqclass'] = $_;
|
$data[$this->id]['reqclass'] = $_;
|
||||||
|
|
||||||
if ($_ = ($this->curTpl['reqRaceMask'] & ChrRace::MASK_ALL))
|
if ($_ = ($this->curTpl['reqRaceMask'] & RACE_MASK_ALL))
|
||||||
if ((($_ & ChrRace::MASK_ALLIANCE) != ChrRace::MASK_ALLIANCE) && (($_ & ChrRace::MASK_HORDE) != ChrRace::MASK_HORDE))
|
if ((($_ & RACE_MASK_ALLIANCE) != RACE_MASK_ALLIANCE) && (($_ & RACE_MASK_HORDE) != RACE_MASK_HORDE))
|
||||||
$data[$this->id]['reqrace'] = $_;
|
$data[$this->id]['reqrace'] = $_;
|
||||||
|
|
||||||
if ($_ = $this->curTpl['rewardOrReqMoney'])
|
if ($_ = $this->curTpl['rewardOrReqMoney'])
|
||||||
@@ -254,8 +241,6 @@ class QuestList extends BaseType
|
|||||||
|
|
||||||
// if ($this->isRepeatable()) // dafuque..? says repeatable and is used as 'disabled'..?
|
// if ($this->isRepeatable()) // dafuque..? says repeatable and is used as 'disabled'..?
|
||||||
// $data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
|
// $data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
|
||||||
if ($this->curTpl['cuFlags'] & (CUSTOM_UNAVAILABLE | CUSTOM_DISABLED))
|
|
||||||
$data[$this->id]['wflags'] |= QUEST_CU_REPEATABLE;
|
|
||||||
|
|
||||||
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
|
if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
|
||||||
{
|
{
|
||||||
@@ -318,7 +303,7 @@ class QuestList extends BaseType
|
|||||||
if (!$this->curTpl)
|
if (!$this->curTpl)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
$title = Lang::unescapeUISequences(Util::htmlEscape($this->getField('name', true)), Lang::FMT_HTML);
|
$title = Util::jsEscape($this->getField('name', true));
|
||||||
$level = $this->curTpl['level'];
|
$level = $this->curTpl['level'];
|
||||||
if ($level < 0)
|
if ($level < 0)
|
||||||
$level = 0;
|
$level = 0;
|
||||||
@@ -337,7 +322,7 @@ class QuestList extends BaseType
|
|||||||
$x .= '<table><tr><td><b class="q">'.$title.'</b></td></tr></table>';
|
$x .= '<table><tr><td><b class="q">'.$title.'</b></td></tr></table>';
|
||||||
|
|
||||||
|
|
||||||
$x .= '<table><tr><td><br />'.$this->parseText('objectives', false);
|
$x .= '<table><tr><td><br />'.$this->parseText('objectives');
|
||||||
|
|
||||||
|
|
||||||
$xReq = '';
|
$xReq = '';
|
||||||
@@ -353,9 +338,9 @@ class QuestList extends BaseType
|
|||||||
if ($ot)
|
if ($ot)
|
||||||
$name = $ot;
|
$name = $ot;
|
||||||
else
|
else
|
||||||
$name = $rng > 0 ? CreatureList::getName($rng) : Lang::unescapeUISequences(GameObjectList::getName(-$rng), Lang::FMT_HTML);
|
$name = $rng > 0 ? CreatureList::getName($rng) : GameObjectList::getName(-$rng);
|
||||||
|
|
||||||
$xReq .= '<br /> - '.$name.($rngQty > 1 ? ' x '.$rngQty : null);
|
$xReq .= '<br /> - '.Util::jsEscape($name).($rngQty > 1 ? ' x '.$rngQty : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ($i = 1; $i < 7; $i++)
|
for ($i = 1; $i < 7; $i++)
|
||||||
@@ -366,11 +351,11 @@ class QuestList extends BaseType
|
|||||||
if (!$ri || $riQty < 1)
|
if (!$ri || $riQty < 1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
$xReq .= '<br /> - '.Lang::unescapeUISequences(ItemList::getName($ri), Lang::FMT_HTML).($riQty > 1 ? ' x '.$riQty : null);
|
$xReq .= '<br /> - '.Util::jsEscape(ItemList::getName($ri)).($riQty > 1 ? ' x '.$riQty : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($et = $this->getField('end', true))
|
if ($et = $this->getField('end', true))
|
||||||
$xReq .= '<br /> - '.$et;
|
$xReq .= '<br /> - '.Util::jsEscape($et);
|
||||||
|
|
||||||
if ($_ = $this->getField('rewardOrReqMoney'))
|
if ($_ = $this->getField('rewardOrReqMoney'))
|
||||||
if ($_ < 0)
|
if ($_ < 0)
|
||||||
@@ -395,31 +380,33 @@ class QuestList extends BaseType
|
|||||||
// items
|
// items
|
||||||
for ($i = 1; $i < 5; $i++)
|
for ($i = 1; $i < 5; $i++)
|
||||||
if ($this->curTpl['rewardItemId'.$i] > 0)
|
if ($this->curTpl['rewardItemId'.$i] > 0)
|
||||||
$data[Type::ITEM][$this->curTpl['rewardItemId'.$i]] = $this->curTpl['rewardItemId'.$i];
|
$data[TYPE_ITEM][$this->curTpl['rewardItemId'.$i]] = $this->curTpl['rewardItemId'.$i];
|
||||||
|
|
||||||
for ($i = 1; $i < 7; $i++)
|
for ($i = 1; $i < 7; $i++)
|
||||||
if ($this->curTpl['rewardChoiceItemId'.$i] > 0)
|
if ($this->curTpl['rewardChoiceItemId'.$i] > 0)
|
||||||
$data[Type::ITEM][$this->curTpl['rewardChoiceItemId'.$i]] = $this->curTpl['rewardChoiceItemId'.$i];
|
$data[TYPE_ITEM][$this->curTpl['rewardChoiceItemId'.$i]] = $this->curTpl['rewardChoiceItemId'.$i];
|
||||||
|
|
||||||
// spells
|
// spells
|
||||||
if ($this->curTpl['rewardSpell'] > 0)
|
if ($this->curTpl['rewardSpell'] > 0)
|
||||||
$data[Type::SPELL][$this->curTpl['rewardSpell']] = $this->curTpl['rewardSpell'];
|
$data[TYPE_SPELL][$this->curTpl['rewardSpell']] = $this->curTpl['rewardSpell'];
|
||||||
|
|
||||||
if ($this->curTpl['rewardSpellCast'] > 0)
|
if ($this->curTpl['rewardSpellCast'] > 0)
|
||||||
$data[Type::SPELL][$this->curTpl['rewardSpellCast']] = $this->curTpl['rewardSpellCast'];
|
$data[TYPE_SPELL][$this->curTpl['rewardSpellCast']] = $this->curTpl['rewardSpellCast'];
|
||||||
|
|
||||||
// titles
|
// titles
|
||||||
if ($this->curTpl['rewardTitleId'] > 0)
|
if ($this->curTpl['rewardTitleId'] > 0)
|
||||||
$data[Type::TITLE][$this->curTpl['rewardTitleId']] = $this->curTpl['rewardTitleId'];
|
$data[TYPE_TITLE][$this->curTpl['rewardTitleId']] = $this->curTpl['rewardTitleId'];
|
||||||
|
|
||||||
// currencies
|
// currencies
|
||||||
if (!empty($this->rewards[$this->id][Type::CURRENCY]))
|
if (!empty($this->rewards[$this->id][TYPE_CURRENCY]))
|
||||||
foreach ($this->rewards[$this->id][Type::CURRENCY] as $id => $__)
|
{
|
||||||
$data[Type::CURRENCY][$id] = $id;
|
$_ = $this->rewards[$this->id][TYPE_CURRENCY];
|
||||||
|
$data[TYPE_CURRENCY] = array_combine(array_keys($_), array_keys($_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($addMask & GLOBALINFO_SELF)
|
if ($addMask & GLOBALINFO_SELF)
|
||||||
$data[Type::QUEST][$this->id] = ['name' => $this->getField('name', true)];
|
$data[TYPE_QUEST][$this->id] = ['name' => $this->getField('name', true)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@@ -429,295 +416,292 @@ class QuestList extends BaseType
|
|||||||
|
|
||||||
class QuestListFilter extends Filter
|
class QuestListFilter extends Filter
|
||||||
{
|
{
|
||||||
protected string $type = 'quests';
|
public $extraOpts = [];
|
||||||
protected array $enums = array(
|
protected $enums = array( // massive enums could be put here, if you want to restrict inputs further to be valid IDs instead of just integers
|
||||||
37 => parent::ENUM_CLASSS, // classspecific
|
37 => [null, 1, 2, 3, 4, 5, 6, 7, 8, 9, null, 11, true, false],
|
||||||
38 => parent::ENUM_RACE, // racespecific
|
38 => [null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false],
|
||||||
9 => parent::ENUM_FACTION, // objectiveearnrepwith
|
);
|
||||||
33 => parent::ENUM_EVENT, // relatedevent
|
protected $genericFilter = array(
|
||||||
43 => parent::ENUM_CURRENCY, // currencyrewarded
|
27 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily
|
||||||
1 => parent::ENUM_FACTION, // increasesrepwith
|
28 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly
|
||||||
10 => parent::ENUM_FACTION // decreasesrepwith
|
29 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_REPEATABLE ], // repeatable
|
||||||
|
30 => [FILTER_CR_NUMERIC, 'id', null, true], // id
|
||||||
|
5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable
|
||||||
|
11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', ], // suggestedplayers
|
||||||
|
6 => [FILTER_CR_NUMERIC, 'timeLimit', ], // timer
|
||||||
|
42 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags
|
||||||
|
45 => [FILTER_CR_BOOLEAN, 'rewardTitleId', ], // titlerewarded
|
||||||
|
2 => [FILTER_CR_NUMERIC, 'rewardXP', ], // experiencegained
|
||||||
|
3 => [FILTER_CR_NUMERIC, 'rewardOrReqMoney', ], // moneyrewarded
|
||||||
|
33 => [FILTER_CR_ENUM, 'holidayId', ], // relatedevent
|
||||||
|
25 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
||||||
|
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
||||||
|
36 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
||||||
);
|
);
|
||||||
|
|
||||||
protected array $genericFilter = array(
|
protected function createSQLForCriterium(&$cr)
|
||||||
1 => [parent::CR_CALLBACK, 'cbReputation', '>', null], // increasesrepwith
|
{
|
||||||
2 => [parent::CR_NUMERIC, 'rewardXP', NUM_CAST_INT ], // experiencegained
|
if (in_array($cr[0], array_keys($this->genericFilter)))
|
||||||
3 => [parent::CR_NUMERIC, 'rewardOrReqMoney', NUM_CAST_INT ], // moneyrewarded
|
{
|
||||||
4 => [parent::CR_CALLBACK, 'cbSpellRewards', null, null], // spellrewarded [yn]
|
if ($genCr = $this->genericCriterion($cr))
|
||||||
5 => [parent::CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable
|
return $genCr;
|
||||||
6 => [parent::CR_NUMERIC, 'timeLimit', NUM_CAST_INT ], // timer
|
|
||||||
7 => [parent::CR_FLAG, 'cuFlags', QUEST_CU_FIRST_SERIES ], // firstquestseries
|
|
||||||
9 => [parent::CR_CALLBACK, 'cbEarnReputation', null, null], // objectiveearnrepwith [enum]
|
|
||||||
10 => [parent::CR_CALLBACK, 'cbReputation', '<', null], // decreasesrepwith
|
|
||||||
11 => [parent::CR_NUMERIC, 'suggestedPlayers', NUM_CAST_INT ], // suggestedplayers
|
|
||||||
15 => [parent::CR_FLAG, 'cuFlags', QUEST_CU_LAST_SERIES ], // lastquestseries
|
|
||||||
16 => [parent::CR_FLAG, 'cuFlags', QUEST_CU_PART_OF_SERIES ], // partseries
|
|
||||||
18 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
|
|
||||||
19 => [parent::CR_CALLBACK, 'cbQuestRelation', 0x1, null], // startsfrom [enum]
|
|
||||||
21 => [parent::CR_CALLBACK, 'cbQuestRelation', 0x2, null], // endsat [enum]
|
|
||||||
22 => [parent::CR_CALLBACK, 'cbItemRewards', null, null], // itemrewards [op] [int]
|
|
||||||
23 => [parent::CR_CALLBACK, 'cbItemChoices', null, null], // itemchoices [op] [int]
|
|
||||||
24 => [parent::CR_CALLBACK, 'cbLacksStartEnd', null, null], // lacksstartend [yn]
|
|
||||||
25 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
|
|
||||||
27 => [parent::CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily
|
|
||||||
28 => [parent::CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly
|
|
||||||
29 => [parent::CR_CALLBACK, 'cbRepeatable', null ], // repeatable
|
|
||||||
30 => [parent::CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
|
|
||||||
33 => [parent::CR_ENUM, 'e.holidayId', true, true], // relatedevent
|
|
||||||
34 => [parent::CR_CALLBACK, 'cbAvailable', null, null], // availabletoplayers [yn]
|
|
||||||
36 => [parent::CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
|
|
||||||
37 => [parent::CR_CALLBACK, 'cbClassSpec', null, null], // classspecific [enum]
|
|
||||||
38 => [parent::CR_CALLBACK, 'cbRaceSpec', null, null], // racespecific [enum]
|
|
||||||
42 => [parent::CR_STAFFFLAG, 'flags' ], // flags
|
|
||||||
43 => [parent::CR_CALLBACK, 'cbCurrencyReward', null, null], // currencyrewarded [enum]
|
|
||||||
44 => [parent::CR_CALLBACK, 'cbLoremaster', null, null], // countsforloremaster_stc [yn]
|
|
||||||
45 => [parent::CR_BOOLEAN, 'rewardTitleId' ] // titlerewarded
|
|
||||||
);
|
|
||||||
|
|
||||||
protected array $inputFields = array(
|
unset($cr);
|
||||||
'cr' => [parent::V_RANGE, [1, 45], true ], // criteria ids
|
$this->error = true;
|
||||||
'crs' => [parent::V_LIST, [parent::ENUM_NONE, parent::ENUM_ANY, [0, 99999]], true ], // criteria operators
|
return [1];
|
||||||
'crv' => [parent::V_REGEX, parent::PATTERN_INT, true ], // criteria values - only numerals
|
}
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name / text - only printable chars, no delimiter
|
|
||||||
'ex' => [parent::V_EQUAL, 'on', false], // also match subname
|
|
||||||
'ma' => [parent::V_EQUAL, 1, false], // match any / all filter
|
|
||||||
'minle' => [parent::V_RANGE, [1, 99], false], // min quest level
|
|
||||||
'maxle' => [parent::V_RANGE, [1, 99], false], // max quest level
|
|
||||||
'minrl' => [parent::V_RANGE, [1, 99], false], // min required level
|
|
||||||
'maxrl' => [parent::V_RANGE, [1, 99], false], // max required level
|
|
||||||
'si' => [parent::V_LIST, [-SIDE_HORDE, -SIDE_ALLIANCE, SIDE_ALLIANCE, SIDE_HORDE, SIDE_BOTH], false], // side
|
|
||||||
'ty' => [parent::V_LIST, [0, 1, 21, 41, 62, [81, 85], 88, 89], true ] // type
|
|
||||||
);
|
|
||||||
|
|
||||||
public array $extraOpts = [];
|
switch ($cr[0])
|
||||||
|
{
|
||||||
|
case 1: // increasesrepwith
|
||||||
|
if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'OR',
|
||||||
|
['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, '>']],
|
||||||
|
['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, '>']],
|
||||||
|
['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, '>']],
|
||||||
|
['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, '>']],
|
||||||
|
['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, '>']]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 10: // decreasesrepwith
|
||||||
|
if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'OR',
|
||||||
|
['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, '<']],
|
||||||
|
['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, '<']],
|
||||||
|
['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, '<']],
|
||||||
|
['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, '<']],
|
||||||
|
['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, '<']]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 43: // currencyrewarded
|
||||||
|
if ($this->isSaneNumeric($cr[1]) && $cr[1] > 0)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'OR',
|
||||||
|
['rewardItemId1', $cr[1]], ['rewardItemId2', $cr[1]], ['rewardItemId3', $cr[1]], ['rewardItemId4', $cr[1]],
|
||||||
|
['rewardChoiceItemId1', $cr[1]], ['rewardChoiceItemId2', $cr[1]], ['rewardChoiceItemId3', $cr[1]], ['rewardChoiceItemId4', $cr[1]], ['rewardChoiceItemId5', $cr[1]], ['rewardChoiceItemId6', $cr[1]]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 34: // availabletoplayers
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['AND', [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0], [['flags', QUEST_FLAG_UNAVAILABLE, '&'], 0]];
|
||||||
|
else
|
||||||
|
return ['OR', ['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], ['flags', QUEST_FLAG_UNAVAILABLE, '&']];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 23: // itemchoices [op] [int]
|
||||||
|
if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
$this->extraOpts['q']['s'][] = ', (IF(rewardChoiceItemId1, 1, 0) + IF(rewardChoiceItemId2, 1, 0) + IF(rewardChoiceItemId3, 1, 0) + IF(rewardChoiceItemId4, 1, 0) + IF(rewardChoiceItemId5, 1, 0) + IF(rewardChoiceItemId6, 1, 0)) as numChoices';
|
||||||
|
$this->extraOpts['q']['h'][] = 'numChoices '.$cr[1].' '.$cr[2];
|
||||||
|
return [1];
|
||||||
|
case 22: // itemrewards [op] [int]
|
||||||
|
if (!$this->isSaneNumeric($cr[2], false) || !$this->int2Op($cr[1]))
|
||||||
|
break;
|
||||||
|
|
||||||
|
$this->extraOpts['q']['s'][] = ', (IF(rewardItemId1, 1, 0) + IF(rewardItemId2, 1, 0) + IF(rewardItemId3, 1, 0) + IF(rewardItemId4, 1, 0)) as numRewards';
|
||||||
|
$this->extraOpts['q']['h'][] = 'numRewards '.$cr[1].' '.$cr[2];
|
||||||
|
return [1];
|
||||||
|
case 44: // countsforloremaster_stc [bool]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&'], 0]];
|
||||||
|
else
|
||||||
|
return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE , '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY , '&']];;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 4: // spellrewarded [bool]
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::$effects['teach']], ['rsc.effect2Id', SpellList::$effects['teach']], ['rsc.effect3Id', SpellList::$effects['teach']]];
|
||||||
|
else
|
||||||
|
return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 9: // objectiveearnrepwith [enum]
|
||||||
|
$_ = intVal($cr[1]);
|
||||||
|
if ($_ > 0)
|
||||||
|
return ['OR', ['reqFactionId1', $_], ['reqFactionId2', $_]];
|
||||||
|
else if ($cr[1] == FILTER_ENUM_ANY) // any
|
||||||
|
return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']];
|
||||||
|
else if ($cr[1] == FILTER_ENUM_NONE) // none
|
||||||
|
return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]];
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 37: // classspecific [enum]
|
||||||
|
$_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null;
|
||||||
|
if ($_ !== null)
|
||||||
|
{
|
||||||
|
if ($_ === true)
|
||||||
|
return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']];
|
||||||
|
else if ($_ === false)
|
||||||
|
return ['OR', ['reqClassMask', 0], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL]];
|
||||||
|
else if (is_int($_))
|
||||||
|
return ['AND', ['reqClassMask', (1 << ($_ - 1)), '&'], [['reqClassMask', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!']];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 38: // racespecific [enum]
|
||||||
|
$_ = isset($this->enums[$cr[0]][$cr[1]]) ? $this->enums[$cr[0]][$cr[1]] : null;
|
||||||
|
if ($_ !== null)
|
||||||
|
{
|
||||||
|
if ($_ === true)
|
||||||
|
return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']];
|
||||||
|
else if ($_ === false)
|
||||||
|
return ['OR', ['reqRaceMask', 0], ['reqRaceMask', RACE_MASK_ALL], ['reqRaceMask', RACE_MASK_ALLIANCE], ['reqRaceMask', RACE_MASK_HORDE]];
|
||||||
|
else if (is_int($_))
|
||||||
|
return ['AND', ['reqRaceMask', (1 << ($_ - 1)), '&'], [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!'], [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!']];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 19: // startsfrom [enum]
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case 1: // npc
|
||||||
|
return ['AND', ['qse.type', TYPE_NPC], ['qse.method', 0x1, '&']];
|
||||||
|
case 2: // object
|
||||||
|
return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', 0x1, '&']];
|
||||||
|
case 3: // item
|
||||||
|
return ['AND', ['qse.type', TYPE_ITEM], ['qse.method', 0x1, '&']];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 21: // endsat [enum]
|
||||||
|
switch ($cr[1])
|
||||||
|
{
|
||||||
|
case 1: // npc
|
||||||
|
return ['AND', ['qse.type', TYPE_NPC], ['qse.method', 0x2, '&']];
|
||||||
|
case 2: // object
|
||||||
|
return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', 0x2, '&']];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 24: // lacksstartend [bool]
|
||||||
|
$missing = DB::Aowow()->selectCol('SELECT questId, max(method) a, min(method) b FROM ?_quests_startend GROUP BY questId HAVING (a | b) <> 3');
|
||||||
|
if ($this->int2Bool($cr[1]))
|
||||||
|
{
|
||||||
|
if ($cr[1])
|
||||||
|
return ['id', $missing];
|
||||||
|
else
|
||||||
|
return ['id', $missing, '!'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 7: // firstquestseries
|
||||||
|
case 15: // lastquestseries
|
||||||
|
case 16: // partseries
|
||||||
|
/* todo */ return [1]; // self-joining eats substential amounts of time: should restructure that and also incorporate reqQ and openQ cases from infobox
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($cr);
|
||||||
|
$this->error = 1;
|
||||||
|
return [1];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createSQLForValues()
|
||||||
{
|
{
|
||||||
$parts = [];
|
$parts = [];
|
||||||
$_v = $this->values;
|
$_v = $this->fiData['v'];
|
||||||
|
|
||||||
// name
|
// name
|
||||||
if ($_v['na'])
|
if (isset($_v['na']))
|
||||||
{
|
{
|
||||||
$_ = [];
|
$_ = [];
|
||||||
if ($_v['ex'] == 'on')
|
if (isset($_v['ex']) && $_v['ex'] == 'on')
|
||||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value, 'objectives_loc'.Lang::getLocale()->value, 'details_loc'.Lang::getLocale()->value]);
|
$_ = $this->modularizeString(['name_loc'.User::$localeId, 'objectives_loc'.User::$localeId, 'details_loc'.User::$localeId]);
|
||||||
else
|
else
|
||||||
$_ = $this->tokenizeString(['name_loc'.Lang::getLocale()->value]);
|
$_ = $this->modularizeString(['name_loc'.User::$localeId]);
|
||||||
|
|
||||||
if ($_)
|
if ($_)
|
||||||
$parts[] = $_;
|
$parts[] = $_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// level min
|
// level min
|
||||||
if ($_v['minle'])
|
if (isset($_v['minle']))
|
||||||
$parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1)
|
{
|
||||||
|
if (is_int($_v['minle']) && $_v['minle'] > 0)
|
||||||
|
$parts[] = ['level', $_v['minle'], '>=']; // not considering quests that are always at player level (-1)
|
||||||
|
else
|
||||||
|
unset($_v['minle']);
|
||||||
|
}
|
||||||
|
|
||||||
// level max
|
// level max
|
||||||
if ($_v['maxle'])
|
if (isset($_v['maxle']))
|
||||||
$parts[] = ['level', $_v['maxle'], '<='];
|
{
|
||||||
|
if (is_int($_v['maxle']) && $_v['maxle'] > 0)
|
||||||
|
$parts[] = ['level', $_v['maxle'], '<='];
|
||||||
|
else
|
||||||
|
unset($_v['maxle']);
|
||||||
|
}
|
||||||
|
|
||||||
// reqLevel min
|
// reqLevel min
|
||||||
if ($_v['minrl'])
|
if (isset($_v['minrl']))
|
||||||
$parts[] = ['minLevel', $_v['minrl'], '>=']; // ignoring maxLevel
|
{
|
||||||
|
if (is_int($_v['minrl']) && $_v['minrl'] > 0)
|
||||||
|
$parts[] = ['minLevel', $_v['minrl'], '>='];// ignoring maxLevel
|
||||||
|
else
|
||||||
|
unset($_v['minrl']);
|
||||||
|
}
|
||||||
|
|
||||||
// reqLevel max
|
// reqLevel max
|
||||||
if ($_v['maxrl'])
|
if (isset($_v['maxrl']))
|
||||||
$parts[] = ['minLevel', $_v['maxrl'], '<=']; // ignoring maxLevel
|
{
|
||||||
|
if (is_int($_v['maxrl']) && $_v['maxrl'] > 0)
|
||||||
|
$parts[] = ['minLevel', $_v['maxrl'], '<='];// ignoring maxLevel
|
||||||
|
else
|
||||||
|
unset($_v['maxrl']);
|
||||||
|
}
|
||||||
|
|
||||||
// side
|
// side
|
||||||
if ($_v['si'])
|
if (isset($_v['si']))
|
||||||
{
|
{
|
||||||
$excl = [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!'];
|
$ex = [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL, '!'];
|
||||||
$incl = ['OR', ['reqRaceMask', 0], [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL]];
|
$notEx = ['OR', ['reqRaceMask', 0], [['reqRaceMask', RACE_MASK_ALL, '&'], RACE_MASK_ALL]];
|
||||||
|
|
||||||
$parts[] = match ($_v['si'])
|
switch ($_v['si'])
|
||||||
{
|
{
|
||||||
SIDE_BOTH => $incl,
|
case 3:
|
||||||
SIDE_HORDE => ['OR', $incl, ['reqRaceMask', ChrRace::MASK_HORDE, '&']],
|
$parts[] = $notEx;
|
||||||
-SIDE_HORDE => ['AND', $excl, ['reqRaceMask', ChrRace::MASK_HORDE, '&']],
|
break;
|
||||||
SIDE_ALLIANCE => ['OR', $incl, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']],
|
case 2:
|
||||||
-SIDE_ALLIANCE => ['AND', $excl, ['reqRaceMask', ChrRace::MASK_ALLIANCE, '&']]
|
$parts[] = ['OR', $notEx, ['reqRaceMask', RACE_MASK_HORDE, '&']];
|
||||||
};
|
break;
|
||||||
|
case -2:
|
||||||
|
$parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_HORDE, '&']];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
$parts[] = ['OR', $notEx, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']];
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
$parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
unset($_v['si']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type [list]
|
// type [list]
|
||||||
if ($_v['ty'] !== null)
|
if (isset($_v['ty']))
|
||||||
$parts[] = ['type', $_v['ty']];
|
{
|
||||||
|
$_ = (array)$_v['ty'];
|
||||||
|
if (!array_diff($_, [0, 1, 21, 41, 62, 81, 82, 83, 84, 85, 88, 89]))
|
||||||
|
$parts[] = ['type', $_];
|
||||||
|
else
|
||||||
|
unset($_v['ty']);
|
||||||
|
}
|
||||||
|
|
||||||
return $parts;
|
return $parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cbReputation(int $cr, int $crs, string $crv, string $sign) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!in_array($crs, $this->enums[$cr]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE `id` = ?d', $crs))
|
|
||||||
$this->fiReputationCols[] = [$crs, Util::localizedString($_, 'name')];
|
|
||||||
|
|
||||||
return [
|
|
||||||
'OR',
|
|
||||||
['AND', ['rewardFactionId1', $crs], ['rewardFactionValue1', 0, $sign]],
|
|
||||||
['AND', ['rewardFactionId2', $crs], ['rewardFactionValue2', 0, $sign]],
|
|
||||||
['AND', ['rewardFactionId3', $crs], ['rewardFactionValue3', 0, $sign]],
|
|
||||||
['AND', ['rewardFactionId4', $crs], ['rewardFactionValue4', 0, $sign]],
|
|
||||||
['AND', ['rewardFactionId5', $crs], ['rewardFactionValue5', 0, $sign]]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbQuestRelation(int $cr, int $crs, string $crv, $flags) : ?array
|
|
||||||
{
|
|
||||||
return match ($crs)
|
|
||||||
{
|
|
||||||
Type::NPC,
|
|
||||||
Type::OBJECT,
|
|
||||||
Type::ITEM => ['AND', ['qse.type', $crs], ['qse.method', $flags, '&']],
|
|
||||||
default => null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbCurrencyReward(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if (!in_array($crs, $this->enums[$cr]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'OR',
|
|
||||||
['rewardItemId1', $crs], ['rewardItemId2', $crs], ['rewardItemId3', $crs], ['rewardItemId4', $crs],
|
|
||||||
['rewardChoiceItemId1', $crs], ['rewardChoiceItemId2', $crs], ['rewardChoiceItemId3', $crs], ['rewardChoiceItemId4', $crs], ['rewardChoiceItemId5', $crs], ['rewardChoiceItemId6', $crs]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbAvailable(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($crs)
|
|
||||||
return [['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0];
|
|
||||||
else
|
|
||||||
return ['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbRepeatable(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($crs)
|
|
||||||
return ['OR', ['flags', QUEST_FLAG_REPEATABLE, '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE, '&']];
|
|
||||||
else
|
|
||||||
return ['AND', [['flags', QUEST_FLAG_REPEATABLE, '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE, '&'], 0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbItemChoices(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$this->extraOpts['q']['s'][] = ', (IF(`rewardChoiceItemId1`, 1, 0) + IF(`rewardChoiceItemId2`, 1, 0) + IF(`rewardChoiceItemId3`, 1, 0) + IF(`rewardChoiceItemId4`, 1, 0) + IF(`rewardChoiceItemId5`, 1, 0) + IF(`rewardChoiceItemId6`, 1, 0)) AS "numChoices"';
|
|
||||||
$this->extraOpts['q']['h'][] = '`numChoices` '.$crs.' '.$crv;
|
|
||||||
return [1];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbItemRewards(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crv, NUM_CAST_INT) || !$this->int2Op($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$this->extraOpts['q']['s'][] = ', (IF(`rewardItemId1`, 1, 0) + IF(`rewardItemId2`, 1, 0) + IF(`rewardItemId3`, 1, 0) + IF(`rewardItemId4`, 1, 0)) AS "numRewards"';
|
|
||||||
$this->extraOpts['q']['h'][] = '`numRewards` '.$crs.' '.$crv;
|
|
||||||
return [1];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbLoremaster(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($crs)
|
|
||||||
return ['AND', ['zoneOrSort', 0, '>'], [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE, '&'], 0], [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY, '&'], 0]];
|
|
||||||
else
|
|
||||||
return ['OR', ['zoneOrSort', 0, '<'], ['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE, '&'], ['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_MONTHLY, '&']];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbSpellRewards(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($crs)
|
|
||||||
return ['OR', ['sourceSpellId', 0, '>'], ['rewardSpell', 0, '>'], ['rsc.effect1Id', SpellList::EFFECTS_TEACH], ['rsc.effect2Id', SpellList::EFFECTS_TEACH], ['rsc.effect3Id', SpellList::EFFECTS_TEACH]];
|
|
||||||
else
|
|
||||||
return ['AND', ['sourceSpellId', 0], ['rewardSpell', 0], ['rewardSpellCast', 0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbEarnReputation(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!Util::checkNumeric($crs, NUM_CAST_INT))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
if ($crs == parent::ENUM_ANY)
|
|
||||||
return ['OR', ['reqFactionId1', 0, '>'], ['reqFactionId2', 0, '>']];
|
|
||||||
else if ($crs == parent::ENUM_NONE)
|
|
||||||
return ['AND', ['reqFactionId1', 0], ['reqFactionId2', 0]];
|
|
||||||
else if (in_array($crs, $this->enums[$cr]))
|
|
||||||
return ['OR', ['reqFactionId1', $crs], ['reqFactionId2', $crs]];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbClassSpec(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!isset($this->enums[$cr][$crs]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$_ = $this->enums[$cr][$crs];
|
|
||||||
if ($_ === true)
|
|
||||||
return ['AND', ['reqClassMask', 0, '!'], [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']];
|
|
||||||
else if ($_ === false)
|
|
||||||
return ['OR', ['reqClassMask', 0], [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL]];
|
|
||||||
else if (is_int($_))
|
|
||||||
return ['AND', ['reqClassMask', ChrClass::from($_)->toMask(), '&'], [['reqClassMask', ChrClass::MASK_ALL, '&'], ChrClass::MASK_ALL, '!']];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbRaceSpec(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!isset($this->enums[$cr][$crs]))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$_ = $this->enums[$cr][$crs];
|
|
||||||
if ($_ === true)
|
|
||||||
return ['AND', ['reqRaceMask', 0, '!'], [['reqRaceMask', ChrRace::MASK_ALL, '&'], ChrRace::MASK_ALL, '!'], [['reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ChrRace::MASK_ALLIANCE, '!'], [['reqRaceMask', ChrRace::MASK_HORDE, '&'], ChrRace::MASK_HORDE, '!']];
|
|
||||||
else if ($_ === false)
|
|
||||||
return ['OR', ['reqRaceMask', 0], ['reqRaceMask', ChrRace::MASK_ALL], ['reqRaceMask', ChrRace::MASK_ALLIANCE], ['reqRaceMask', ChrRace::MASK_HORDE]];
|
|
||||||
else if (is_int($_))
|
|
||||||
return ['AND', ['reqRaceMask', ChrRace::from($_)->toMask(), '&'], [['reqRaceMask', ChrRace::MASK_ALLIANCE, '&'], ChrRace::MASK_ALLIANCE, '!'], [['reqRaceMask', ChrRace::MASK_HORDE, '&'], ChrRace::MASK_HORDE, '!']];
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function cbLacksStartEnd(int $cr, int $crs, string $crv) : ?array
|
|
||||||
{
|
|
||||||
if (!$this->int2Bool($crs))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$missing = DB::Aowow()->selectCol('SELECT `questId`, BIT_OR(`method`) AS "se" FROM ?_quests_startend GROUP BY `questId` HAVING "se" <> 3');
|
|
||||||
if ($crs)
|
|
||||||
return ['id', $missing];
|
|
||||||
else
|
|
||||||
return ['id', $missing, '!'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class SkillList extends BaseType
|
class SkillList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::SKILL;
|
public static $type = TYPE_SKILL;
|
||||||
public static $brickFile = 'skill';
|
public static $brickFile = 'skill';
|
||||||
public static $dataTable = '?_skillline';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT sl.*, sl.id AS ARRAY_KEY FROM ?_skillline sl';
|
protected $queryBase = 'SELECT *, sl.id AS ARRAY_KEY FROM ?_skillline sl';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
'sl' => [['ic']],
|
'sl' => [['si']],
|
||||||
'ic' => ['j' => ['?_icons ic ON ic.id = sl.iconId', true], 's' => ', ic.name AS iconString'],
|
'si' => ['j' => '?_icons si ON si.id = sl.iconId', 's' => ', si.iconString'],
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [])
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions);
|
||||||
|
|
||||||
// post processing
|
// post processing
|
||||||
foreach ($this->iterate() as &$_curTpl)
|
foreach ($this->iterate() as &$_curTpl)
|
||||||
@@ -34,15 +31,12 @@ class SkillList extends BaseType
|
|||||||
while (count($_) < 5)
|
while (count($_) < 5)
|
||||||
$_[] = 0;
|
$_[] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$_curTpl['iconId'])
|
|
||||||
$_curTpl['iconString'] = DEFAULT_ICON;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_skillline WHERE id = ?d', $id);
|
$n = DB::Aowow()->SelectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_skillline WHERE id = ?d', $id);
|
||||||
return Util::localizedString($n, 'name');
|
return Util::localizedString($n, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,11 +50,11 @@ class SkillList extends BaseType
|
|||||||
'category' => $this->curTpl['typeCat'],
|
'category' => $this->curTpl['typeCat'],
|
||||||
'categorybak' => $this->curTpl['categoryId'],
|
'categorybak' => $this->curTpl['categoryId'],
|
||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'name' => $this->getField('name', true),
|
'name' => Util::jsEscape($this->getField('name', true)),
|
||||||
'profession' => $this->curTpl['professionMask'],
|
'profession' => $this->curTpl['professionMask'],
|
||||||
'recipeSubclass' => $this->curTpl['recipeSubClass'],
|
'recipeSubclass' => $this->curTpl['recipeSubClass'],
|
||||||
'specializations' => Util::toJSON($this->curTpl['specializations'], JSON_NUMERIC_CHECK),
|
'specializations' => Util::toJSON($this->curTpl['specializations']),
|
||||||
'icon' => $this->curTpl['iconString']
|
'icon' => Util::jsEscape($this->curTpl['iconString'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +66,7 @@ class SkillList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[self::$type][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']];
|
$data[self::$type][$this->id] = ['name' => Util::jsEscape($this->getField('name', true)), 'icon' => Util::jsEscape($this->curTpl['iconString'])];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
|
||||||
die('illegal access');
|
|
||||||
|
|
||||||
|
|
||||||
class SoundList extends BaseType
|
|
||||||
{
|
|
||||||
use spawnHelper;
|
|
||||||
|
|
||||||
public static $type = Type::SOUND;
|
|
||||||
public static $brickFile = 'sound';
|
|
||||||
public static $dataTable = '?_sounds';
|
|
||||||
public static $contribute = CONTRIBUTE_CO;
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT s.*, s.id AS ARRAY_KEY FROM ?_sounds s';
|
|
||||||
|
|
||||||
private $fileBuffer = [];
|
|
||||||
private static $fileTypes = array(
|
|
||||||
SOUND_TYPE_OGG => 'audio/ogg; codecs="vorbis"',
|
|
||||||
SOUND_TYPE_MP3 => 'audio/mpeg'
|
|
||||||
);
|
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
|
||||||
{
|
|
||||||
parent::__construct($conditions, $miscData);
|
|
||||||
|
|
||||||
// post processing
|
|
||||||
foreach ($this->iterate() as $id => &$_curTpl)
|
|
||||||
{
|
|
||||||
$_curTpl['files'] = [];
|
|
||||||
for ($i = 1; $i < 11; $i++)
|
|
||||||
{
|
|
||||||
if ($_curTpl['soundFile'.$i])
|
|
||||||
{
|
|
||||||
$this->fileBuffer[$_curTpl['soundFile'.$i]] = null;
|
|
||||||
$_curTpl['files'][] = &$this->fileBuffer[$_curTpl['soundFile'.$i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($_curTpl['soundFile'.$i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->fileBuffer)
|
|
||||||
{
|
|
||||||
$files = DB::Aowow()->select('SELECT `id` AS ARRAY_KEY, `id`, `file` AS "title", CAST(`type` AS UNSIGNED) AS "type", `path` FROM ?_sounds_files sf WHERE `id` IN (?a)', array_keys($this->fileBuffer));
|
|
||||||
foreach ($files as $id => $data)
|
|
||||||
{
|
|
||||||
// 3.3.5 bandaid - need fullpath to play via wow API, remove for cata and later
|
|
||||||
$data['path'] = str_replace('\\', '\\\\', $data['path'] ? $data['path'] . '\\' . $data['title'] : $data['title']);
|
|
||||||
// skip file extension
|
|
||||||
$data['title'] = substr($data['title'], 0, -4);
|
|
||||||
// enum to string
|
|
||||||
$data['type'] = self::$fileTypes[$data['type']];
|
|
||||||
// get real url
|
|
||||||
$data['url'] = Cfg::get('STATIC_URL') . '/wowsounds/' . $data['id'];
|
|
||||||
// v push v
|
|
||||||
$this->fileBuffer[$id] = $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getListviewData()
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
{
|
|
||||||
$data[$this->id] = array(
|
|
||||||
'id' => $this->id,
|
|
||||||
'type' => $this->getField('cat'),
|
|
||||||
'name' => $this->getField('name'),
|
|
||||||
'files' => array_values(array_filter($this->getField('files')))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getJSGlobals($addMask = 0)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
|
||||||
$data[self::$type][$this->id] = array(
|
|
||||||
'name' => $this->getField('name', true),
|
|
||||||
'type' => $this->getField('cat'),
|
|
||||||
'files' => array_values(array_filter($this->getField('files')))
|
|
||||||
);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderTooltip() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
class SoundListFilter extends Filter
|
|
||||||
{
|
|
||||||
protected string $type = 'sounds';
|
|
||||||
protected array $inputFields = array(
|
|
||||||
'na' => [parent::V_REGEX, parent::PATTERN_NAME, false], // name - only printable chars, no delimiter
|
|
||||||
'ty' => [parent::V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 31], 50, 52, 53], true ] // type
|
|
||||||
);
|
|
||||||
|
|
||||||
protected function createSQLForValues() : array
|
|
||||||
{
|
|
||||||
$parts = [];
|
|
||||||
$_v = &$this->values;
|
|
||||||
|
|
||||||
// name [str]
|
|
||||||
if ($_v['na'])
|
|
||||||
if ($_ = $this->tokenizeString(['name']))
|
|
||||||
$parts[] = $_;
|
|
||||||
|
|
||||||
// type [list]
|
|
||||||
if ($_v['ty'])
|
|
||||||
$parts[] = ['cat', $_v['ty']];
|
|
||||||
|
|
||||||
return $parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,47 +8,45 @@ class TitleList extends BaseType
|
|||||||
{
|
{
|
||||||
use listviewHelper;
|
use listviewHelper;
|
||||||
|
|
||||||
public static $type = Type::TITLE;
|
public static $type = TYPE_TITLE;
|
||||||
public static $brickFile = 'title';
|
public static $brickFile = 'title';
|
||||||
public static $dataTable = '?_titles';
|
|
||||||
|
|
||||||
public $sources = [];
|
public $sources = [];
|
||||||
|
|
||||||
protected $queryBase = 'SELECT t.*, t.id AS ARRAY_KEY FROM ?_titles t';
|
protected $queryBase = 'SELECT t.*, id AS ARRAY_KEY FROM ?_titles t';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
't' => [['src']], // 11: Type::TITLE
|
't' => [['src']], // 11: TYPE_TITLE
|
||||||
'src' => ['j' => ['?_source src ON type = 11 AND typeId = t.id', true], 's' => ', src13, moreType, moreTypeId']
|
'src' => ['j' => ['?_source src ON type = 11 AND typeId = t.id', true], 's' => ', src13, moreType, moreTypeId']
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [])
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions);
|
||||||
|
|
||||||
// post processing
|
// post processing
|
||||||
foreach ($this->iterate() as $id => &$_curTpl)
|
foreach ($this->iterate() as $id => &$_curTpl)
|
||||||
{
|
{
|
||||||
// preparse sources - notice: under this system titles can't have more than one source (or two for achivements), which is enough for standard TC cases but may break custom cases
|
// preparse sources - notice: under this system titles can't have more than one source (or two for achivements), which is enough for standard TC cases but may break custom cases
|
||||||
if ($_curTpl['moreType'] == Type::ACHIEVEMENT)
|
if ($_curTpl['moreType'] == TYPE_ACHIEVEMENT)
|
||||||
$this->sources[$this->id][SRC_ACHIEVEMENT][] = $_curTpl['moreTypeId'];
|
$this->sources[$this->id][12][] = $_curTpl['moreTypeId'];
|
||||||
else if ($_curTpl['moreType'] == Type::QUEST)
|
else if ($_curTpl['moreType'] == TYPE_QUEST)
|
||||||
$this->sources[$this->id][SRC_QUEST][] = $_curTpl['moreTypeId'];
|
$this->sources[$this->id][4][] = $_curTpl['moreTypeId'];
|
||||||
else if ($_curTpl['src13'])
|
else if ($_curTpl['src13'])
|
||||||
$this->sources[$this->id][SRC_CUSTOM_STRING][] = $_curTpl['src13'];
|
$this->sources[$this->id][13][] = $_curTpl['src13'];
|
||||||
|
|
||||||
// titles display up to two achievements at once
|
// titles display up to two achievements at once
|
||||||
if ($_curTpl['src12Ext'])
|
if ($_curTpl['src12Ext'])
|
||||||
$this->sources[$this->id][SRC_ACHIEVEMENT][] = $_curTpl['src12Ext'];
|
$this->sources[$this->id][12][] = $_curTpl['src12Ext'];
|
||||||
|
|
||||||
unset($_curTpl['src12Ext']);
|
unset($_curTpl['src12Ext']);
|
||||||
unset($_curTpl['moreType']);
|
unset($_curTpl['moreType']);
|
||||||
unset($_curTpl['moreTypeId']);
|
unset($_curTpl['moreTypeId']);
|
||||||
unset($_curTpl['src3']);
|
unset($_curTpl['src3']);
|
||||||
|
|
||||||
// shorthand for more generic access; required by CommunityContent to determine subject
|
// shorthand for more generic access
|
||||||
foreach (Locale::cases() as $loc)
|
foreach (Util::$localeStrings as $i => $str)
|
||||||
if ($loc->validate())
|
if ($str)
|
||||||
$_curTpl['name'] = new LocString($_curTpl, 'male', fn($x) => trim(str_replace('%s', '', $x)));
|
$_curTpl['name_loc'.$i] = trim(str_replace('%s', '', $_curTpl['male_loc'.$i]));
|
||||||
// $_curTpl['name_loc'.$loc->value] = trim(str_replace('%s', '', $_curTpl['male_loc'.$loc->value]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +80,10 @@ class TitleList extends BaseType
|
|||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
$data[Type::TITLE][$this->id]['name'] = $this->getField('male', true);
|
$data[TYPE_TITLE][$this->id]['name'] = $this->getField('male', true);
|
||||||
|
|
||||||
if ($_ = $this->getField('female', true))
|
if ($_ = $this->getField('female', true))
|
||||||
$data[Type::TITLE][$this->id]['namefemale'] = $_;
|
$data[TYPE_TITLE][$this->id]['namefemale'] = $_;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@@ -96,9 +92,9 @@ class TitleList extends BaseType
|
|||||||
private function createSource()
|
private function createSource()
|
||||||
{
|
{
|
||||||
$sources = array(
|
$sources = array(
|
||||||
SRC_QUEST => [],
|
4 => [], // Quest
|
||||||
SRC_ACHIEVEMENT => [],
|
12 => [], // Achievements
|
||||||
SRC_CUSTOM_STRING => []
|
13 => [] // simple text
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
@@ -112,43 +108,46 @@ class TitleList extends BaseType
|
|||||||
}
|
}
|
||||||
|
|
||||||
// fill in the details
|
// fill in the details
|
||||||
if (!empty($sources[SRC_QUEST]))
|
if (!empty($sources[4]))
|
||||||
$sources[SRC_QUEST] = (new QuestList(array(['id', $sources[SRC_QUEST]])))->getSourceData();
|
$sources[4] = (new QuestList(array(['id', $sources[4]])))->getSourceData();
|
||||||
|
|
||||||
if (!empty($sources[SRC_ACHIEVEMENT]))
|
if (!empty($sources[12]))
|
||||||
$sources[SRC_ACHIEVEMENT] = (new AchievementList(array(['id', $sources[SRC_ACHIEVEMENT]])))->getSourceData();
|
$sources[12] = (new AchievementList(array(['id', $sources[12]])))->getSourceData();
|
||||||
|
|
||||||
|
if (!empty($sources[13]))
|
||||||
|
$sources[13] = DB::Aowow()->SELECT('SELECT *, Id AS ARRAY_KEY FROM ?_sourcestrings WHERE Id IN (?a)', $sources[13]);
|
||||||
|
|
||||||
foreach ($this->sources as $Id => $src)
|
foreach ($this->sources as $Id => $src)
|
||||||
{
|
{
|
||||||
$tmp = [];
|
$tmp = [];
|
||||||
|
|
||||||
// Quest-source
|
// Quest-source
|
||||||
if (isset($src[SRC_QUEST]))
|
if (isset($src[4]))
|
||||||
{
|
{
|
||||||
foreach ($src[SRC_QUEST] as $s)
|
foreach ($src[4] as $s)
|
||||||
{
|
{
|
||||||
if (isset($sources[SRC_QUEST][$s]['s']))
|
if (isset($sources[4][$s]['s']))
|
||||||
$this->faction2Side($sources[SRC_QUEST][$s]['s']);
|
$this->faction2Side($sources[4][$s]['s']);
|
||||||
|
|
||||||
$tmp[SRC_QUEST][] = $sources[SRC_QUEST][$s];
|
$tmp[4][] = $sources[4][$s];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Achievement-source
|
// Achievement-source
|
||||||
if (isset($src[SRC_ACHIEVEMENT]))
|
if (isset($src[12]))
|
||||||
{
|
{
|
||||||
foreach ($src[SRC_ACHIEVEMENT] as $s)
|
foreach ($src[12] as $s)
|
||||||
{
|
{
|
||||||
if (isset($sources[SRC_ACHIEVEMENT][$s]['s']))
|
if (isset($sources[12][$s]['s']))
|
||||||
$this->faction2Side($sources[SRC_ACHIEVEMENT][$s]['s']);
|
$this->faction2Side($sources[12][$s]['s']);
|
||||||
|
|
||||||
$tmp[SRC_ACHIEVEMENT][] = $sources[SRC_ACHIEVEMENT][$s];
|
$tmp[12][] = $sources[12][$s];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// other source (only one item possible, so no iteration needed)
|
// other source (only one item possible, so no iteration needed)
|
||||||
if (isset($src[SRC_CUSTOM_STRING]))
|
if (isset($src[13]))
|
||||||
$tmp[SRC_CUSTOM_STRING] = [Lang::game('pvpSources', $Id)];
|
$tmp[13] = [Util::localizedString($sources[13][$this->sources[$Id][13][0]], 'source')];
|
||||||
|
|
||||||
$this->templates[$Id]['source'] = $tmp;
|
$this->templates[$Id]['source'] = $tmp;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class UserList extends BaseType
|
class UserList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::USER;
|
public static $type = TYPE_USER;
|
||||||
public static $brickFile = 'user';
|
public static $brickFile = 'user';
|
||||||
public static $dataTable = ''; // doesn't have community content
|
|
||||||
public static $contribute = CONTRIBUTE_NONE;
|
|
||||||
|
|
||||||
public $sources = [];
|
public $sources = [];
|
||||||
|
|
||||||
protected $queryBase = 'SELECT *, a.id AS ARRAY_KEY FROM ?_account a';
|
protected $queryBase = 'SELECT *, a.id AS ARRAY_KEY FROM ?_account a';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
'a' => [['r']],
|
'a' => [['r']],
|
||||||
'r' => ['j' => ['?_account_reputation r ON r.`userId` = a.`id`', true], 's' => ', IFNULL(SUM(r.`amount`), 0) AS "reputation"', 'g' => 'a.`id`']
|
'r' => ['j' => ['?_account_reputation r ON r.userId = a.id', true], 's' => ', IFNULL(SUM(r.amount), 0) AS reputation', 'g' => 'a.id']
|
||||||
);
|
);
|
||||||
|
|
||||||
public function getListviewData() { }
|
public function getListviewData() { }
|
||||||
|
|
||||||
@@ -29,7 +25,7 @@ class UserList extends BaseType
|
|||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
{
|
{
|
||||||
$data[$this->curTpl['username']] = array(
|
$data[$this->curTpl['displayName']] = array(
|
||||||
'border' => 0, // border around avatar (rarityColors)
|
'border' => 0, // border around avatar (rarityColors)
|
||||||
'roles' => $this->curTpl['userGroups'],
|
'roles' => $this->curTpl['userGroups'],
|
||||||
'joined' => date(Util::$dateFormatInternal, $this->curTpl['joinDate']),
|
'joined' => date(Util::$dateFormatInternal, $this->curTpl['joinDate']),
|
||||||
@@ -40,22 +36,22 @@ class UserList extends BaseType
|
|||||||
'reputation' => $this->curTpl['reputation']
|
'reputation' => $this->curTpl['reputation']
|
||||||
);
|
);
|
||||||
|
|
||||||
// custom titles (only seen on user page..?)
|
// custom titles (only ssen on user page..?)
|
||||||
if ($_ = $this->curTpl['title'])
|
if ($_ = $this->curTpl['title'])
|
||||||
$data[$this->curTpl['username']]['title'] = $_;
|
$data[$this->curTpl['displayName']]['title'] = $_;
|
||||||
|
|
||||||
if ($_ = $this->curTpl['avatar'])
|
if ($_ = $this->curTpl['avatar'])
|
||||||
{
|
{
|
||||||
$data[$this->curTpl['username']]['avatar'] = is_numeric($_) ? 2 : 1;
|
$data[$this->curTpl['displayName']]['avatar'] = is_numeric($_) ? 2 : 1;
|
||||||
$data[$this->curTpl['username']]['avatarmore'] = $_;
|
$data[$this->curTpl['displayName']]['avatarmore'] = $_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// more optional data
|
// more optional data
|
||||||
// sig: markdown formated string (only used in forum?)
|
// sig: markdown formated string (only used in forum?)
|
||||||
// border: seen as null|1|3 .. changes the border around the avatar (i suspect its meaning changed and got decupled from premium-status with the introduction of patreon-status)
|
// border: seen as null|1|3 .. changes the border around the avatar (i suspect its meaning changed and got decupled from premium-status with the introduction of patron-status)
|
||||||
}
|
}
|
||||||
|
|
||||||
return [Type::USER => $data];
|
return [TYPE_USER => $data];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderTooltip() { }
|
public function renderTooltip() { }
|
||||||
|
|||||||
@@ -1,26 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
|
|
||||||
class WorldEventList extends BaseType
|
class WorldEventList extends BaseType
|
||||||
{
|
{
|
||||||
public static $type = Type::WORLDEVENT;
|
public static $type = TYPE_WORLDEVENT;
|
||||||
public static $brickFile = 'event';
|
public static $brickFile = 'event';
|
||||||
public static $dataTable = '?_events';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT e.holidayId, e.cuFlags, e.startTime, e.endTime, e.occurence, e.length, e.requires, e.description AS nameINT, e.id AS eventId, e.id AS ARRAY_KEY, h.* FROM ?_events e';
|
protected $queryBase = 'SELECT *, -e.id as id, -e.id AS ARRAY_KEY FROM ?_events e';
|
||||||
protected $queryOpts = array(
|
protected $queryOpts = array(
|
||||||
'e' => [['h']],
|
'e' => [['h']],
|
||||||
'h' => ['j' => ['?_holidays h ON e.holidayId = h.id', true], 'o' => '-e.id ASC']
|
'h' => ['j' => ['?_holidays h ON e.holidayId = h.id', true], 'o' => '-e.id ASC']
|
||||||
);
|
);
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [])
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions);
|
||||||
|
|
||||||
// unseting elements while we iterate over the array will cause the pointer to reset
|
// unseting elements while we iterate over the array will cause the pointer to reset
|
||||||
$replace = [];
|
$replace = [];
|
||||||
@@ -43,18 +40,22 @@ class WorldEventList extends BaseType
|
|||||||
if ($this->curTpl['requires'])
|
if ($this->curTpl['requires'])
|
||||||
$this->curTpl['requires'] = explode(' ', $this->curTpl['requires']);
|
$this->curTpl['requires'] = explode(' ', $this->curTpl['requires']);
|
||||||
|
|
||||||
|
$this->curTpl['eventBak'] = -$this->curTpl['id'];
|
||||||
|
|
||||||
// change Ids if holiday is set
|
// change Ids if holiday is set
|
||||||
if ($this->curTpl['holidayId'] > 0)
|
if ($this->curTpl['holidayId'] > 0)
|
||||||
{
|
{
|
||||||
|
$this->curTpl['id'] = $this->curTpl['holidayId'];
|
||||||
$this->curTpl['name'] = $this->getField('name', true);
|
$this->curTpl['name'] = $this->getField('name', true);
|
||||||
$replace[$this->id] = $this->curTpl;
|
$replace[$this->id] = $this->curTpl;
|
||||||
|
unset($this->curTpl['description']);
|
||||||
}
|
}
|
||||||
else // set a name if holiday is missing
|
else // set a name if holiday is missing
|
||||||
{
|
{
|
||||||
// template
|
// template
|
||||||
$this->curTpl['name_loc0'] = $this->curTpl['nameINT'];
|
$this->curTpl['name_loc0'] = $this->curTpl['description'];
|
||||||
$this->curTpl['iconString'] = 'trade_engineering';
|
$this->curTpl['iconString'] = 'trade_engineering';
|
||||||
$this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('nameINT', true);
|
$this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('description', true);
|
||||||
$replace[$this->id] = $this->curTpl;
|
$replace[$this->id] = $this->curTpl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,28 +63,16 @@ class WorldEventList extends BaseType
|
|||||||
foreach ($replace as $old => $data)
|
foreach ($replace as $old => $data)
|
||||||
{
|
{
|
||||||
unset($this->templates[$old]);
|
unset($this->templates[$old]);
|
||||||
$this->templates[$data['eventId']] = $data;
|
$this->templates[$data['id']] = $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$row = DB::Aowow()->SelectRow('
|
if ($id > 0)
|
||||||
SELECT
|
$row = DB::Aowow()->SelectRow('SELECT * FROM ?_holidays WHERE Id = ?d', intVal($id));
|
||||||
IFNULL(h.name_loc0, e.description) AS name_loc0,
|
else
|
||||||
h.name_loc2,
|
$row = DB::Aowow()->SelectRow('SELECT description as name FROM ?_events WHERE Id = ?d', intVal(-$id));
|
||||||
h.name_loc3,
|
|
||||||
h.name_loc4,
|
|
||||||
h.name_loc6,
|
|
||||||
h.name_loc8
|
|
||||||
FROM
|
|
||||||
?_events e
|
|
||||||
LEFT JOIN
|
|
||||||
?_holidays h ON e.holidayId = h.id
|
|
||||||
WHERE
|
|
||||||
e.id = ?d',
|
|
||||||
$id
|
|
||||||
);
|
|
||||||
|
|
||||||
return Util::localizedString($row, 'name');
|
return Util::localizedString($row, 'name');
|
||||||
}
|
}
|
||||||
@@ -164,36 +153,12 @@ class WorldEventList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[Type::WORLDEVENT][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']];
|
$data[TYPE_WORLDEVENT][$this->id] = ['name' => $this->getField('name', true), 'icon' => $this->curTpl['iconString']];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderTooltip()
|
public function renderTooltip() { }
|
||||||
{
|
|
||||||
if (!$this->curTpl)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
$x = '<table><tr><td>';
|
|
||||||
|
|
||||||
// head v that extra % is nesecary because we are using sprintf later on
|
|
||||||
$x .= '<table width="100%%"><tr><td><b>'.$this->getField('name', true).'</b></td><th><b class="q0">'.Lang::event('category', $this->getField('category')).'</b></th></tr></table>';
|
|
||||||
|
|
||||||
// use string-placeholder for dates
|
|
||||||
// start
|
|
||||||
$x .= Lang::event('start').Lang::main('colon').'%s<br>';
|
|
||||||
// end
|
|
||||||
$x .= Lang::event('end').Lang::main('colon').'%s';
|
|
||||||
|
|
||||||
$x .= '</td></tr></table>';
|
|
||||||
|
|
||||||
// desc
|
|
||||||
if ($this->getField('holidayId'))
|
|
||||||
if ($_ = $this->getField('description', true))
|
|
||||||
$x .= '<table><tr><td><span class="q">'.$_.'</span></td></tr></table>';
|
|
||||||
|
|
||||||
return $x;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Aowow;
|
|
||||||
|
|
||||||
if (!defined('AOWOW_REVISION'))
|
if (!defined('AOWOW_REVISION'))
|
||||||
die('illegal access');
|
die('illegal access');
|
||||||
|
|
||||||
@@ -10,13 +8,12 @@ class ZoneList extends BaseType
|
|||||||
{
|
{
|
||||||
use listviewHelper;
|
use listviewHelper;
|
||||||
|
|
||||||
public static $type = Type::ZONE;
|
public static $type = TYPE_ZONE;
|
||||||
public static $brickFile = 'zone';
|
public static $brickFile = 'zone';
|
||||||
public static $dataTable = '?_zones';
|
|
||||||
|
|
||||||
protected $queryBase = 'SELECT z.*, z.id AS ARRAY_KEY FROM ?_zones z';
|
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_zones z';
|
||||||
|
|
||||||
public function __construct(array $conditions = [], array $miscData = [])
|
public function __construct($conditions = [], $miscData = null)
|
||||||
{
|
{
|
||||||
parent::__construct($conditions, $miscData);
|
parent::__construct($conditions, $miscData);
|
||||||
|
|
||||||
@@ -56,7 +53,7 @@ class ZoneList extends BaseType
|
|||||||
// use if you JUST need the name
|
// use if you JUST need the name
|
||||||
public static function getName($id)
|
public static function getName($id)
|
||||||
{
|
{
|
||||||
$n = DB::Aowow()->selectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8 FROM ?_zones WHERE id = ?d', $id );
|
$n = DB::Aowow()->selectRow('SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_zones WHERE id = ?d', $id );
|
||||||
return Util::localizedString($n, 'name');
|
return Util::localizedString($n, 'name');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +99,7 @@ class ZoneList extends BaseType
|
|||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
foreach ($this->iterate() as $__)
|
foreach ($this->iterate() as $__)
|
||||||
$data[Type::ZONE][$this->id] = ['name' => $this->getField('name', true)];
|
$data[TYPE_ZONE][$this->id] = ['name' => $this->getField('name', true)];
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user