299 Commits
v1.0 ... v1.1

Author SHA1 Message Date
Sarjuuk
160c15a5f2 Misc/Mergefix
* added clean db dump
 * cleanup fails from profiler merge
 * maybe fixed setup being unable to find images for processing
 * version push
2018-03-26 19:57:54 +02:00
Sarjuuk
0ff0e5b59e Profiler/Frontend
* add pages and templates to display arena teams, guilds and chars
2018-03-22 18:17:16 +01:00
Sarjuuk
3fd25ca889 Profiler/Backend
* added core functions nessecary for profiler
2018-03-22 17:36:28 +01:00
Sarjuuk
b0a5f477c1 Profiler/CLI
* move command line related functions to its own class
2018-03-22 16:32:29 +01:00
Sarjuuk
f25498d4d0 Profiler/Gearscore
* implement gearscore calculation

im sad and i hate myself
2018-03-22 15:24:36 +01:00
Sarjuuk
fbcf00e0fc Profiler/Filter
* implemented flag options when searching for strings
2018-03-22 14:45:26 +01:00
Sarjuuk
3303722bc7 Profiler/Prep:
* misc changes from profiler branch. No real changes on its own
2018-03-22 13:59:30 +01:00
Sarjuuk
5abb3f78cf Auth/Fixup
* fix external login after 49a10e22
2018-02-02 21:15:25 +01:00
Sarjuuk
457e5f1eb5 Items/Armor
* fixed calculation of extra armor
2018-02-02 19:26:29 +01:00
Sarjuuk
ada57d4384 Misc/Versions
* AOWOW_REVISION is no longer expected to be numeric
   this allows for user-defined versions (e.g.4.1c) when changes
   within css/js files need to be forced into the web browser or
   existing cache files need to be invalidated
2017-09-04 16:50:16 +02:00
Sarjuuk
f2fce0f602 Misc/Typos
* fixed typo/php-warning introduced in d83f370fb4
2017-09-04 16:41:00 +02:00
Magnifikator
55dfe0ccc9 Misc/Git
added *.orig files to .gitignore
2017-09-04 16:39:14 +02:00
Magnifikator
69abd8c434 Misc/Git
added extracted sounds and setup files to .gitignore
2017-09-03 17:31:28 +02:00
Magnifikator
011de295fd Page/Home
Fixes 2 html errors
2017-09-03 17:27:01 +02:00
Sarjuuk
dbb4ad0b2a Setup/Sounds
* change extension of the temporary audio files for easier use with the supplied converter scripts.
2017-09-03 17:19:21 +02:00
Sarjuuk
bd56a5dad9 Factions/Articles
* added french localization

thx @Endalaust
2017-09-03 16:32:52 +02:00
Sarjuuk
79d9225c02 Setup/Firstrun:
* try to resolve bad inputs for *_host and force_ssl, when they result in redirects (301, 302)
2017-09-03 15:50:54 +02:00
Sarjuuk
d83f370fb4 Page/Maintenance
* send appropriate status code: 503 (temporarily unavailable)

thx @Magnifikator for the heads up
2017-09-03 15:25:30 +02:00
Sarjuuk
889f14a64f User/Auth
* import user access from external auth source if available
2017-08-30 17:25:40 +02:00
Sarjuuk
858e5a0492 [PH] spell_group error suppressor 2017-05-18 00:56:42 +02:00
Sarjuuk
c61fb787ae Setup/Images
* fixed error, causing everything to be displayed as not dound if anything was missing
 * added readable output to source finding stage
 * should now be obvious if something is missing
2017-05-17 10:50:30 +02:00
Sarjuuk
57d4dc2459 Quests/DetailPage
* correctly interpret exclusiveGroup values (maybe.. (who knows..))
2017-05-17 01:25:17 +02:00
Sarjuuk
a17c45a698 README
* added another gotcha with auto-minifiers
2017-05-15 22:22:47 +02:00
Sarjuuk
94321ea178 Spell/ShapeshiftForms
* move all localized strings to class Lang
 * added missing negation in flag check, causing stances not to be displayed
2017-05-15 15:16:58 +02:00
Sarjuuk
bf6bf20ae4 Quests/DetailPage
* link to quest-ending spell if possible
2017-05-10 03:03:04 +02:00
Sarjuuk
7b7ec2d0aa Admin/siteconfig
* also rebuild js-locales when updating available locales
 * somewhat unhappy with the access to generated files. need to find a better solution.
2017-05-09 19:11:47 +02:00
Sarjuuk
1bb7fdf89a NPCs/DetailPage
* fixed extraCols for 'teaches' tab
2017-05-08 15:09:55 +02:00
Sarjuuk
05ce1f4242 README
* removed SoX for WIN in favor of FFmpeg
 * added example scripts for batch audio conversion.
 * added a curious error case to the Q/A-section
2017-05-07 23:09:40 +02:00
Sarjuuk
9c6a789d2b Admin/config
* added onUpdate handler to rebuild affected files
2017-05-07 18:40:45 +02:00
Sarjuuk
33a1a45a63 Misc/Notices
* fixed display of GemConditions on enchantment detail page
 * correctly initialize class and race masks for items
2017-05-07 01:19:43 +02:00
Sarjuuk
39e917927f Adomin/Config
* urlencode values or loose data on submit
2017-05-06 12:43:00 +02:00
Sarjuuk
79861569a2 Readme
* bump for new TDB dependancy
2017-05-04 21:50:11 +02:00
Sarjuuk
e762340953 DB/Structure
* implemented changes from d17a45ee7e (creature text localization)
2017-05-04 21:45:53 +02:00
Sarjuuk
4a8fe6de7d DB/Structure
* implemented changed from 309a7f7e2a (achievement reward mail localization)
2017-05-04 20:58:36 +02:00
Sarjuuk
7c456b970c Misc/Fixup
* added missing leading whitespace to update queries
2017-05-04 03:58:29 +02:00
Sarjuuk
dd63a6a2ab Accounts
* added config option for external recovery & creation
 * fixed typos in contribute brick for locale 2
2017-05-04 02:29:59 +02:00
Sarjuuk
00342b0b61 Readme
* updated TDB requirement
2017-05-02 22:41:12 +02:00
Sarjuuk
d34f7c304e DB/Structure
* implemented changes from 3739d8444b (quest localization)
2017-05-02 22:41:12 +02:00
Sarjuuk
6168937901 DB/Structure
* implemented changes from 460e2c5dc5 (item localization)
2017-05-02 22:41:12 +02:00
Sarjuuk
189ac56fc0 DB/Structure
* implemented changes from 9a3b28894d (broadcastText localization)
2017-05-02 22:41:12 +02:00
Sarjuuk
f7f5cafec2 Spells/Tooltips
* added base stats to be parsed (where skipped before)
 * dummy effects do not singal a healing spell
2017-04-30 13:33:36 +02:00
Sarjuuk
8248dad50a Misc/Errors
* fixes some notices from log
 * fixed error when handling cookies
2017-04-29 19:49:46 +02:00
Sarjuuk
12b9e3fa89 Tooltips
* place tooltip for daily quests
 * place tooltip for real time duration
2017-04-28 20:36:50 +02:00
Sarjuuk
269487f32c Misc/Fixup
* stray backet braking enchantments list page
2017-04-24 22:35:44 +02:00
Sarjuuk
d19a920c09 Filters/Items
* fixed forgotten rename, breaking page with certain tabs
 * fixed typo, breaking stat weights
2017-04-24 17:06:08 +02:00
Sarjuuk
29c8c3d0af Misc/Debug
* remove leftover output
2017-04-24 00:53:57 +02:00
Sarjuuk
a59fd45076 Pages/Spell
* display category cooldown in table
2017-04-23 22:55:40 +02:00
Sarjuuk
4316d3ad40 Zones/DetailPage
* directly display AudioControls for all ZoneMusic & Ambience
2017-04-23 20:20:44 +02:00
Sarjuuk
6377a9e659 Misc
* added option to 'verbose' concatenate a list of items to class Lang
 * added option to strip tags from a string to class Markdown
 * fixed several instances of wrong categories on pages
 * actually display world state conditions for zone music
 * some typos all over the place
2017-04-23 20:20:44 +02:00
Sarjuuk
959d0ace0b ListPages/Filter
* vastly improved input validation
 * content and type validation occurs when filter is created
 * contextual filters like itemTypes are now only applied in context
2017-04-23 20:20:34 +02:00
Sarjuuk
178a67e180 Zones/Misc
* fix allowed categories on ListPage
 * fix instances for zone 3523
2017-04-22 00:35:51 +02:00
Sarjuuk
a695a4188c Util/Game
* cleanup Util 2/2
 * move game related functions and variables to new class: Game
2017-04-19 01:47:03 +02:00
Sarjuuk
f503967c45 Util/Lang
* cleanup Util 1/2
 * move spell effect/aura strings to Lang
2017-04-19 01:47:03 +02:00
Sarjuuk
c1af1fd884 DB/PageText
* update structure for TrinityCore/TrinityCore@fbbe247114
2017-04-19 00:05:06 +02:00
Sarjuuk
e5013e79fd Fixup/Typo
* bracket order fail
2017-04-14 01:22:37 +02:00
Sarjuuk
6fddbe40a4 Lang/Items
* try to account for non-latin formating
 * use correct words provided by GlobalStrings.lua
2017-04-13 19:19:26 +02:00
Sarjuuk
5a45c6a5e6 Lang/Items
* apply templates from GlobalStrings.lua
 * fix some translation errors in the process .. also, if s.o. knows, what constructs like |3-1(%s) do to the passed string, please tell me.
2017-04-13 02:01:12 +02:00
Sarjuuk
b5c7faff65 Setup/DBC
* index column Id => id
 * why was this exception even there.
 * also DBCs copied directly into aowow_tables should work again
2017-04-11 15:32:37 +02:00
Sarjuuk
6df3a27279 Creatures
* flag creatures using creaturedisplayinfoextra.dbc as humanoid
2017-04-11 15:11:18 +02:00
Sarjuuk
dde09a8a02 WIN/Misc
* windows compatibility changes
   - prevent crash on very large querys by reducing query size
   - set memory limit to something less than 2GB. Old value caused memory_limit to be set to something miniscule. (with the changes to DBC extraction this should now be feasable)
   - be a tad more lenient with time when generating enchant-datasets
2017-04-10 22:06:44 +02:00
Sarjuuk
fa9d518980 Setup/DB
* table prefix for aowow is now fixed to 'aowow_'
 * no more input required
2017-04-09 22:04:40 +02:00
Sarjuuk
d63ac946f6 Setup/DBCs
* vastly improved memory usage when handling DBCs
 * DBCs are no longer read entirely into memory, instead appropriate file handles are used.
 * read data is written to DB in reasonable intervals instead of all at once
 * DBC-class can no longer read from DB, but that wasn't used anyway

CLISetup
 * can now interpret the HOME-shorthand (~) in paths
2017-04-09 21:20:31 +02:00
Sarjuuk
33b1e4a978 Misc/Errors
* Item/XML
    - fixed setting typeId when lookup was item name
    - fixed lookup by name with utf8-chars
 * Items
    - do not display page for invalid categories
 * NPC
    - fixed reputation display
 * Quest
    - broke infinite loop occuring when quest is in chain with itself
 * Filters
    - handle different sizes of cr, crs and crv passed
2017-04-08 03:10:37 +02:00
Sarjuuk
bb00355ca3 Currencies
* fixed sold-by tab on detail pag
 * fixed display if rewarded for quest
2017-04-07 18:56:55 +02:00
Sarjuuk
24e1c46a8c Merge remote-tracking branch 'github/master' into ghMaster 2017-04-06 23:54:26 +02:00
Sarjuuk
0360f6f600 Misc/Fixups
* fixed file generators involving ?_icons table
 * known spells onspell detail page should have a localized tooltip
 * fixed numer formating in spell tooltips for locales with a ',' as decimal
 * fixed php-tag in contribute template, causing parse error
2017-04-06 23:53:22 +02:00
Sarjuuk
b74b03151b Fixups
* fixed two typos
2017-04-02 16:05:32 +02:00
Sarjuuk
cc328baf19 Fixups
* fixed two typos
2017-04-02 16:04:04 +02:00
Sarjuuk
ec05120118 README
* updated audio generation as suggested in #93
* fixed indentation
2017-04-02 14:49:58 +02:00
Sarjuuk
acad94770b Item/Tooltips
* restore icon to tooltips after iconDB-changes tripped over a long forgotten change
2017-04-01 21:28:54 +02:00
Sarjuuk
92394e415a Spells/Effects
* only use MiscValue as spellid for SpellEffect:155
 * interpret and display SpellEffect 293
 * SpellEffects 131 & 132 can both play audio
2017-03-31 01:05:38 +02:00
Sarjuuk
f3d694ac79 README
fixed line breaks in Q/A-secion
2017-03-29 22:38:30 +02:00
Sarjuuk
cd9f90cea9 IconDB/Fixups (obligatory)
* table had wrong engine
 * two hunter pets have unique icons only referenced in creaturefamily.dbc
 * fixed links to rel-tabs
2017-03-29 22:16:08 +02:00
Sarjuuk
ee568da6ec IconDB
* initial implementation
 * this includes a complete reindexing of everything touching icons
 * this also means, no linking to red-rocket-site though, they index them differently
2017-03-29 21:48:14 +02:00
Sarjuuk
4651165e4d DB/SQL-Modes
* force disable special modes that will prevent our funny queries
2017-03-29 19:45:21 +02:00
Sarjuuk
e5de7a8a45 PHP/Compatibility
* should now work with php5 again
2017-03-29 18:51:02 +02:00
Sarjuuk
9ee7b44ddb Layout
* make main-contents slightly wider to prevent overlaps
 * make classes listview name column slightly wider to prevent overlap
2017-03-29 14:37:27 +02:00
Sarjuuk
e400155a64 Articles/Classes
* added localization for frFR (thx @Endalaust and his translators)
2017-03-29 14:11:55 +02:00
Sarjuuk
0fc006d2d4 Contribute
* added option to hide contribute tabs depending on subject (screenshots for sounds .. yeah, right)
2017-03-29 03:24:28 +02:00
Sarjuuk
5082a6155d Setup/Icons
* fixed offsets for class / race icons (should no longer capture icon border from tile)

not important, but you may want to run: aowow --build=simpleImg --icons -f
2017-03-28 23:22:29 +02:00
Sarjuuk
fd96f8f758 Errors / Logging
* try to log fatal errors if able
2017-03-28 18:00:08 +02:00
Sarjuuk
d91090e806 Admin/Screenshots
* do not limit screenshot sources

Search
 * display floored maps
2017-03-26 17:18:00 +02:00
Sarjuuk
1a04ee3354 Merge remote-tracking branch 'github/master' into ghMaster 2017-03-25 01:35:21 +01:00
Sarjuuk
f0e1e02f03 Admin/Screenshots
* do not display deleted screenshots on initial listing
2017-03-25 01:33:38 +01:00
Sarjuuk
8c56610418 Screenshots/filesize
* UHD screens are pretty large
 * also nginx is a thing and not affected by htaccess (just to keep in mind)
2017-03-25 01:26:22 +01:00
Sarjuuk
b6b734ff36 Fixups
* undefined variables in Markup.js
 * logic error preventing screenshot uploads
 * bitshift by negative value in spell
 * and i'm pretty sure i already added an exception handler at one point
2017-03-25 01:26:22 +01:00
Sarjuuk
a937fb7ac6 Setup/Sounds
* updated sql update for changed generator
Setup/Updates
 * do not throw an invalid scripts supplied - error if no actual scripts where suppied
   (in case of update it is skipped, in every other case it generates everything anyway)
2017-03-22 15:19:10 +01:00
Sarjuuk
efe7b468c9 Setup/Firstrun
* forgot to add sounds generator to firstrun script
2017-03-22 12:58:40 +01:00
Sarjuuk
f9416c549d Updates/SQL
* noticed having comments inside querys in these files breaks them
 * also there appear to be files with \r line endings left
2017-03-21 23:09:55 +01:00
Sarjuuk
9516affde5 topUsres
* implemented top users overview
 * w/o achievements, uploads, forum posts, yadda yadda
2017-03-21 16:22:17 +01:00
Sarjuuk
c85ba1a208 Setup/Sounds
* resolved duplicate naming of setup function
2017-03-21 11:20:00 +01:00
Sarjuuk
00de81c60d Errors
* try to die a bit more gracefull if errors occur
2017-03-21 10:43:31 +01:00
Sarjuuk
62a95effb8 Privilege
* add forgotten template file
2017-03-21 02:36:15 +01:00
Sarjuuk
85e1accebc Privileges
* add individual pages and corresponding articles
 * yes the sql-update was forgotten
2017-03-20 23:44:41 +01:00
Sarjuuk
90a31ac99f Config/Sanity
* Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn
2017-03-20 23:38:00 +01:00
Sarjuuk
8b9b653028 Disables
* display db-entries as disabled or unavailable if appropriate
 * affects now achievements and quests in addition to spells
 * flag items without source as unavailable
 * flag quests without starter als unavailable

Privileges
 * i guess i forgott to add the template...
 ...among other things
2017-03-20 18:00:12 +01:00
Sarjuuk
4f1854b138 Misc
* Github link to home-footer
 * formating errors in README.md

 * oh, i hate this markdown syntax sooo much :x
2017-03-20 13:16:41 +01:00
Sarjuuk
33554e8942 User/Contribs
* added reputation threshold for replys to config
 * check canComment and canReply separately
 * disable contrib-tabs if the user is banned
2017-03-20 01:12:36 +01:00
Sarjuuk
f0231c8d9f SiteConfig
* always place newly added php-configurations in the misc-category
2017-03-19 23:54:54 +01:00
Sarjuuk
5360b09d0f Articles
* distinguish between articles for dbpage and other articles
 * can be served by url instead of arbitrary picked type/typeId-pair
 * save access to article in db (not used yet)
 * serve correct g_pageInfo if on article-page

SiteReputation/Privileges
 * implemented info pages
2017-03-19 23:41:49 +01:00
Sarjuuk
939a3f1e31 Markup
* add some more options to tags (mostly directly setting class and style)
2017-03-19 22:57:52 +01:00
Sarjuuk
bf0f218b28 Template
* merge text-page-generic into list-page-generic
2017-03-19 14:42:08 +01:00
Sarjuuk
9fabd2c728 Reputation
* apply reputation_reward_rate to rewards on Quest Detail Page
 * apply reputation_reward_rate to NPC Detail Page
2017-03-16 21:32:23 +01:00
Sarjuuk
a163279fab Spell/Detail Page
* parse effect 103 (Reputation)
 * calc actual value from custom_reward_rate
2017-03-16 19:12:43 +01:00
Sarjuuk
5598b80ef4 Quests/Map
* correctly apply styles to map selector
 * correcly prepare map selector string
2017-03-16 18:49:11 +01:00
Sarjuuk
d6b722a967 Articles/Races
* added french translation (thx @Endalaust and his translators)
2017-03-16 15:33:13 +01:00
Sarjuuk
2ec5fda3e7 Items/Menu
* fixed preselection of menu items on Items List Page
 * fixed display and preselection of 'Type' option list
2017-03-16 15:13:25 +01:00
Sarjuuk
13d0dfeb07 Quest/Map
* assign objective text to map pips if possible
2017-03-13 22:07:44 +01:00
Sarjuuk
ddce998cad Spells/Tooltips
* fixed parser for lastValue/gender-switch (previous pattern matched against that too)
 * also its a stupid idea to replace a var with itself
2017-03-13 16:58:26 +01:00
Sarjuuk
103251a537 Spell/Effects
* do not rely displayInfo for spell effects, that can occur multiple times per spell
Items/CurrencyFor
 * always send all currency-info to javascript (an item may require more then one to be bought)
2017-03-13 16:10:54 +01:00
Sarjuuk
e1fd2db64d Admin/Screenshots
* restrict ability to irrevocably delete files to U_GROUP_ADMIN
Pages/Quests
 * be a tad more lenient when filtering excessive sources
2017-03-13 15:37:43 +01:00
Sarjuuk
0c492531b8 Localization/frFR
* some touch-ups
 * thx @Endalaust
2017-03-13 15:37:31 +01:00
Sarjuuk
8b9854804b Pages/Quest
* added map to quest detail page
 * some minor issues rooted in weired loot templates remain
2017-03-12 05:12:53 +01:00
Sarjuuk
849872d181 Misc
* fixed note in quest tab on zone detail page
 * fixed a notice when creating item tooltips while passing an empty randomEnchantment value
 * fixed currencies having no icons on zone detail page
2017-03-09 12:48:53 +01:00
Sarjuuk
a48f30e8a4 Misc
* edited example in README.md to be more clear
 * fixed a typo in RedButton template.
 * fixed a typo in comment ajax. Can now use sticky again.
 * fixed a logic error in comment reply js. do not line break image sources.
2017-03-09 02:21:08 +01:00
Sarjuuk
fce8b01493 Setup/ItemEnchantment
* updated setup to for f609e7a08b
2017-03-08 14:00:36 +01:00
Sarjuuk
05d24dbde8 revision push 2017-03-06 18:26:51 +01:00
Sarjuuk
20a1829317 implementation of Sound DB
Client sounds are cross-referenced with as many other DB-Types as possible. Including, but not limited to:
 * Character VOs (Errors, Emotes)
 * Creature VOs (Boss Dialogue)
 * Zone Music and Ambience
 * Sounds triggerd by spells
 * Sounds from general item/spell usage, creature behavior

Restrictions:
 * only one locale is supported. Choose wisely!
2017-03-06 15:16:34 +01:00
Sarjuuk
8fcd8ea429 Spells/Tooltips
* fixed range display when friend- and foe-distances differ
 * fixed interpretation of $l-switch

Item/DetailPage
 * do note prepare unnessecary data for the currency-for tab (eases load)
2017-02-26 21:42:30 +01:00
Sarjuuk
d73557ee5e Setup/Creatures
* hide creatures for internal use from listviews
 * correct Nightelf Player faction
 * trying to enforce strict line endings
2017-02-25 23:55:31 +01:00
Sarjuuk
3b1bcfe1a5 Fixup
* added missing brackets from copypasta-disaster
2017-02-25 18:39:16 +01:00
Sarjuuk
da59440106 Fixup
* access class property in screenshot/comment-admin by function. Aowow is compatible with PHP 5.5 again
 * changed line-endings across the board from \r\n to \n
2017-02-24 18:45:36 +01:00
Sarjuuk
83cc16107e Whoops
* partially revert 99760d7c72
 * fixed another type on quests list page
 * added default cases for empty data to pets and races list pages
 * improved typelist iterator to be recursively callable
2017-02-23 19:46:15 +01:00
Sarjuuk
99760d7c72 Screenshots
* aproving a screenshot now flags the corresponding db entry as having a screenshot
 * deleting & moving screenshots works equivalent
 * instancing a new TypeClass without parameters no longer grabs everything from the corresponding tables ( pass a "[true]" to retain this behaviour)
 * retroactively flag db entries for having screenshots
 * fixed a typo
2017-02-23 16:30:58 +01:00
Sarjuuk
ab8d7caced Config/Locales
* if your current language becomes invalid select the first viable one instead of defaulting to LOCALE_EN
2017-02-23 00:47:25 +01:00
Sarjuuk
090410cbf8 Nav/RedButtons
* pass parameters as ready to use - objects
2017-02-20 02:52:07 +01:00
Sarjuuk
739e7bc81a Articles
* escape article content again
 * restore home logo on maintenance page
2017-02-18 20:59:25 +01:00
Sarjuuk
f1483f4595 Forms/Placeholder
* removed placeholder text images for input fields
 * readded text as placeholder attribute
2017-02-17 01:37:43 +01:00
Sarjuuk
62a4bcf2d2 Item/Shared CD
* items should not share a cooldown with themself
2017-02-16 19:50:25 +01:00
Sarjuuk
8de6296878 Spell/Trainer
* do not use trainer templates when getting trainer by spell
2017-02-16 19:45:13 +01:00
Sarjuuk
e5a5ad425a Spells/Description
* support parsing another form of formula
 * mod regen auras (mostly food) have an implicit periode of 5sec.
2017-02-16 19:40:12 +01:00
Sarjuuk
b3369d0b3f Items/Currency
* do not display unique-limited amount on item if currency token
 * display as cap on currency page instead
2017-02-13 02:22:22 +01:00
Sarjuuk
294ba109df Item/Tooltip
* fixed quote in faction link
2017-02-13 01:43:59 +01:00
Sarjuuk
3eeecf11cb Setup/Creature
* fixed sync for creature_template_locale
2017-02-12 22:54:53 +01:00
Sarjuuk
d00252ad72 Misc
* ignore one more generated file
 * also its 2017 already
2017-02-12 20:03:19 +01:00
Sarjuuk
dcff6f6525 Setup/Zones
* fixed flagging valid zones as hidden if no displayable map was present
2017-02-12 13:20:26 +01:00
Sarjuuk
ffddd3c6b8 User/Weightscales
* saving now always updates the name
 * name is now urldecoded
2017-02-12 03:56:38 +01:00
Sarjuuk
d0ec950dc1 Setup/Misc
* fixed complexImage always selecting images for LOCALE_EN if able
 * fixed serverside events being displayed in the calendar
 * set initial memory limit to 4GB
 * set initial db_version to current version (forgotten to do that for a long time :x )
 * updated README.md and .gitignore; revision incremented
2017-02-12 02:42:59 +01:00
Sarjuuk
7b9d7bec1b Setup/Titles
* fixed title sources, where one title has multiple sources
2017-02-12 02:37:23 +01:00
Sarjuuk
88e21d8597 Setup/Misc
* wrap LEFT JOINed fields in IFNULL() to prevent errors on setup
 * also adapt to changed TC table structure
 * add ORDER to prevent skipping entries, if not already ordered in source table
2017-02-12 02:37:23 +01:00
Sarjuuk
b81e1745f6 Spell/Procs
* updated to get data from new table
 * fixed proc data being displayed for unprocable effects
2017-02-12 02:37:23 +01:00
Sarjuuk
201f31b3ac Setuo/enchants
* reduced overhead by directly querying for spells instead of wrapping them in a SpellList (that functions i didn't neeed)
 * skip enchantments from items without source
2017-02-12 02:37:23 +01:00
Sarjuuk
a31e018959 Types/Items
* prevent a possible recursion occuring when preparing ItemSources
2017-02-11 19:54:32 +01:00
Sarjuuk
efa93e218a Setup/Items
* fixed an sql error by forgotten var
2017-02-11 16:19:05 +01:00
Sarjuuk
ca0e57b99c Libs/jQuery
* updated to 1.12.4 (plus compatibility changes in affected javascripts)
 * fixed double escapes on featuredbox text
 * fixed rogue whitespace
2017-02-11 15:38:55 +01:00
Sarjuuk
d0f64fe66c Home/Featuredbox
* allow defining a time span during which the box should be displayed
 * added fields optional fields to point to diffenret header and home logos for the duration
2017-02-11 15:38:16 +01:00
Sarjuuk
8fd52835a6 Page/Header
* send 404 if an incorrect page or typeId was requested
2017-01-08 23:00:11 +01:00
Sarjuuk
8a01124eed comments/formating
* added missing whitespace betweeen date and patch version
 * reordered dmy to fit selected locale.
2016-12-24 13:27:51 +01:00
Sarjuuk
a50ddb98dd Pages/Achievements
* display additional crosslinkable conditions from achievement_criteria_data
2016-11-22 13:48:39 +01:00
Sarjuuk
e896daa148 setup/quests
* fixed fatal typo (thx @Fra298)
2016-10-25 15:26:58 +02:00
Sarjuuk
1a46e58477 Items/XML
* added source and cooldown to xml
2016-10-16 16:12:06 +02:00
Sarjuuk
e2ab00b4ba Spells/PageTexts
* fixed numeric & gender switch for russian locale
 * treat $| like a $l-switch (only one case iirc)
 * remived output for unhandled text variables
 * fixed display of pageText for items
2016-10-04 21:55:56 +02:00
Sarjuuk
568677ff32 Locales/esES
* some more square bracket shuffling (should be fine now)
2016-10-04 19:52:09 +02:00
Sarjuuk
321c9ba7f4 Setup/Sqlgen
* fixed setup skipping custom data if it was >stepSize away from the last entry
2016-10-04 19:45:13 +02:00
Sarjuuk
674a2a7814 Markdown
* added [money ..] and [currency=] to tag parser
 * added missing styles to properly display [quote ..] and diffs
 * added missing js to display [n5 ..] and progress bars
2016-10-04 19:45:13 +02:00
Sarjuuk
2bc40e9421 Misc/Fixes: fixed erronous sql-update 2016-10-04 19:45:12 +02:00
Sarjuuk
770f11321f Screenshots/Upload
* use fileInfo to determine the filetype (getimagesize fails if the file is not an actual image)
2016-10-04 19:45:12 +02:00
Sarjuuk
bb87762467 Merge/Fixup:
restore array structure after merge
2016-10-04 19:10:22 +02:00
AFROM
317e12eddf Update spanish localization (#64)
Update spanish localization
2016-10-04 19:05:27 +02:00
AFROM
1cbe1aea76 Update spanish localization in static/js/locale_eses.js (#65)
Update spanish localization
2016-10-04 18:58:39 +02:00
Sarjuuk
8f77774569 Misc/Fixup
* really fix broken sql structure after c75777e5c3 (fix the fix!)
2016-03-30 00:26:06 +02:00
Sarjuuk
a521935934 User/Auth
* use char limit appropriate to current auth method, when checking usernames
2016-03-29 21:16:16 +02:00
Sarjuuk
b84854d4ba Misc/Fixes
* fixed db_structure.sql broken after
 * readded %-column to GO-Loot listviews
2016-03-29 20:51:21 +02:00
Sarjuuk
c75777e5c3 Misc/Fixups
* added forgotten mapper strings for esES, ruRU, frFR
 * readded default name for screenshots and videos tab
 * fixed referencing not set $id in opensearch (wich now displays icons once again)
 * fixed referencing a long removed variable in admin=screenshots, preventing delete operations
2016-03-23 22:14:15 +01:00
Sarjuuk
44c90b71fa Pages/Quest
* fixed display of spell rewards, if rewardSpellCast was negative
2016-03-20 18:43:59 +01:00
Sarjuuk
a8d686104e Fixup
override foreign key checks while applying update
2016-03-18 18:43:14 +01:00
Sarjuuk
e80015a7cd Weight Scales/Presets:
* moved wt_presets to DB
 * scales are now saved sensibly instead of just being dumped as is (also, tables have now constrains)
 * added admin=weight-presets to edit presets
 * added an internal user on id:0 who 'owns' the wt-presets and the +1 rates on new comments
 * consequently added constraints to comment-related tables

Misc:
 * Util::toJSON() priorizes its flags over CFG_DEBUG, wich fixes the modelviewer-redButton
 * moved terrible javascript-dump from admin-page to template
2016-03-12 14:05:39 +01:00
Sarjuuk
71e44e572a Misc/Misc
* fixed auth against TCs auth table
 * check for real paths when creating emote aliasses
 * races page no longer identifies as classes
 * fixed item comparison tool
 * fixed js error when trying to display spawn maps
2016-02-27 23:23:37 +01:00
Sarjuuk
741ebf38f9 Users/Markup
* implemented pending-status for newly created accounts
 * Markup should now properly recognize external links
 * Markup will remove links by pending accounts
2016-02-17 00:23:00 +01:00
Sarjuuk
08717c36d0 Templates/Listviews
* removed most templates by sensibly restructuring lvTab-data. Util::toJSON() does the rest.
 * taught Util::toJSON() to recognize values prefixed with $ as js-code
 * use Util::toJSON whenever possible
 * misc
   - mail attachments are displyed again
   - always show all currency rewards on Quest-LVs
   - suppress an error, when encountering unused ItemMods
2016-02-11 22:14:42 +01:00
Sarjuuk
fe7f05c89c Comments/rating
* do not include the +1 every comment starts with in the distribution and preview
2016-02-06 21:18:02 +01:00
Sarjuuk
447f7f02a7 Pages/Zone
* fixed displaying nonexisting multiples of spawns on the map
2016-02-06 14:17:42 +01:00
Sarjuuk
c1b92df9bf PHP7/compatibility
* changes to search.php somehow slipped through
2016-01-31 22:54:18 +01:00
Sarjuuk
7c1e61d843 Admin/Config
* fixed calling $this in static context (broke web-config)
2016-01-31 22:35:27 +01:00
Sarjuuk
63cd83c102 Setup/Update
* fixed errors introduced in c3fe4b0224
2016-01-31 21:57:46 +01:00
Sarjuuk
0220587637 Template/Home
* update footer to current year
 * display current revision
2016-01-31 19:05:12 +01:00
Sarjuuk
f076a30180 AoWoW is now compatible with php7
(php7 is recommended but not required)
2016-01-31 16:32:31 +01:00
Sarjuuk
5abdbe2080 Misc/Fixes
* fixed stats when comparing items with scaling random enchantments
 * fixed BOM on compare.php
 * fixed multiple typos and notices
2016-01-31 15:46:20 +01:00
Sarjuuk
2bc85dd109 Acc/Passwords
* do not enforce minimum password length for imported accounts
2016-01-26 23:12:41 +01:00
Sarjuuk
bd2a5acf21 Template/JS
* removed some readability formating, that caused an arbitrary text-node to appear in between DIVs, screwing with javascript
2016-01-23 21:17:50 +01:00
Sarjuuk
278176a48e Setup/DBCs
* directly extract integers as signed (may or may not help with a reported case of extracted nonsensical values)
2016-01-23 11:46:39 +01:00
Sarjuuk
eddb034a5c User/Passwords
* allow for passwords longer than 15 characters
2016-01-23 11:29:18 +01:00
Sarjuuk
c3fe4b0224 Setup/Update
* get files/tables, which need to be regenerated after! applying sql updates
 * small wording change, to make exiting dbConfig feel less like an error occured
2016-01-16 10:37:37 +01:00
Sarjuuk
432223264e Items/Block
* fixed formating of xml-export
 * fixed handling of block value (can have multiple sources per item (itemMods, spell, set individually) and is then displayed separately in tooltip, but summed for calculations)
2016-01-13 21:57:38 +01:00
Sarjuuk
99eb8f426b Misc/Fixes
* fixed typo preventing management of custom weight scales
 * added forgotten GET-Parameter to sanitization, preventing rating of comments
 * avoid hardcoding locales
 * do not show tooltip on BUTTON_WOWHEAD
2016-01-13 20:58:06 +01:00
Sarjuuk
ff3babacb7 Skills/Trainer
* fixed copy/paste-error asigning riding trainer-templates to first aid
2015-12-06 20:05:06 +01:00
Sarjuuk
4daf474ff4 Admin/AjaxHandler
* removed rogue \s, which prevented handling of id-lists
 * affected mass handling of screenshots and comments
2015-12-06 11:21:38 +01:00
Sarjuuk
f01c624f82 Comments
* do not double escape chat message body
2015-12-05 20:33:25 +01:00
Sarjuuk
5661f09454 Tools/Submodules
* removed submodules from project
 * both tools can now be found at https://github.com/Sarjuuk?tab=repositories
2015-12-05 00:46:01 +01:00
Sarjuuk
2f53abdb3e Setup/Spells:
* fixed some talents with erronous itemCreateIds to have an altIcon and quality color assigned
2015-11-28 17:23:06 +01:00
Sarjuuk
3eace0680d Setup/Fixups
- also change function call talents() to talentCalc() for firstrun script
 - filter waypoint_data query for creature_addon with path_id 0 (where did this get lost...)
2015-11-22 20:52:16 +01:00
Sarjuuk
3b32ce6b1f Conf
- moved misc categoty to index 0 as adding new categories became complicated.
 - display misc category last...
2015-11-22 16:25:25 +01:00
Sarjuuk
74f68c8a69 Setup/TalentCalc
* renamed file generator function to avoid naming conflict with sql generator function
2015-11-19 19:46:50 +01:00
Sarjuuk
e197072409 Locale/FR
* added some more translations

 thanks @joshwhedon
2015-11-18 23:49:21 +01:00
Sarjuuk
4c4cde1165 Items/GemPerfection
* use skill_perfect_item_template to display Gem Perfection procs
2015-11-18 23:47:53 +01:00
Sarjuuk
d55989510a Updated setup to TDB r60
* removed --resume command. Always offer to resume first time setup instead.
 * added skill_perfect_item_template to sources
2015-11-18 23:47:53 +01:00
Sarjuuk
99fdad29dd AjaxHandler
* separated into appropriate subclasses
* unified sanitizing of $_GET and $_POST data using build in filter_input()
* index now always tries to resolve page calls with ajaxHandler first and as a page last

minor bug-fixes to bugs that wre not reported yet, because they didn't occur yet
(e.g.: nobody tried to compose a comment with >7500 characters yet)
2015-11-08 23:14:17 +01:00
Sarjuuk
141bc10901 DB/Setup
* allow "build" and "sql" fields to be NULL in ?_dbversion
2015-11-07 18:35:49 +01:00
Sarjuuk
b5d73f0ac4 Setup/Readme
* added alternative versions to the mpqExtractor and blpConverter to readme
2015-11-01 11:30:30 +01:00
Sarjuuk
e69811c4de NPCs/Bosses
* link boss loot chests to boss
Home/Locales
 * properly set selected locale if featuredbox is not displayed

(also include fresh db dump)
2015-10-31 13:55:22 +01:00
Sarjuuk
b6e1bcaeac Setup/Tracking
* added tracking code for Google Analytics
   - enabling is optional
   - add account in config to enable tracking
 * removed usage tracking
   - the results were just seriously depressing..
2015-10-20 00:38:48 +02:00
Sarjuuk
7eda6dd778 Markup/Videos:
* updated to use current youtube-api
2015-10-16 16:41:15 +02:00
Sarjuuk
dabf307c13 Setup/firstrun
* also added check for port on this option
 * hide control chars from error input request for win
2015-10-06 22:04:12 +02:00
Sarjuuk
ef5d7cce3b Setup/DBConfig
* fixed parsing port on host ip (already worked, but setup threw an error)
2015-10-03 12:20:50 +02:00
Sarjuuk
d7c6092d5f Strings/Encoding
* replaced some more uses of string functions with multibyte equivalents where it might matter
   note: i didn't touch the spell-tooltip parser as it seems to work as intended regardless
2015-09-19 16:11:10 +02:00
Sarjuuk
6dc6124673 Merge pull request #37 from Toshik/patch-2
Quests/Encoding:
 * string manipulations should be multibyte
2015-09-19 15:32:53 +02:00
Sarjuuk
f33a15db4d Util/Encoding
* set multibyte-encoding globally as suggested by @Toshik
2015-09-19 15:27:20 +02:00
Toshik
6603cbf099 String cutting should support multibyte (UTF-8) 2015-09-18 13:12:25 +03:00
Sarjuuk
b1ef81c7e9 Merge pull request #36 from Toshik/patch-1
Setup/Quests
 - update to f7c7b92ef3
2015-09-12 22:26:55 +02:00
Sarjuuk
00bd33abfb CLISetup/OSCompatibility
- generally check for readline handler
 - only ignore \r for WIN
2015-09-12 22:15:12 +02:00
Toshik
973d2d90aa Quest DB structure fix
Fixes related to latest DB structure changes:
2015-09-10 11:51:15 +03:00
Sarjuuk
58babe24fe Util/Realms
* check for accessibility
 * use for achievements/realm firsts
2015-08-30 14:04:27 +02:00
Sarjuuk
645894fdbf Setup/Source:
* fixup
2015-08-29 13:48:21 +02:00
Sarjuuk
cb1916c484 Setup/Fixups
* some additions to latest pull requests
   - added missing dependancies
   - added quest_template changes to source.func.php
2015-08-19 17:29:21 +02:00
Sarjuuk
af7015097b Setup/Source
* get starter spells from playercreateinfo_skill instead of playercreateinfo_spell
   (changed in 2a3546ca36)

thanks @Toshik for pointing that out
2015-08-19 16:52:14 +02:00
Sarjuuk
c9aa0b3f6d Merge pull request #33 from Toshik/patch-6
Update game object DB structure to https://github.com/TrinityCore/TrinityCore/commit/9245bad
2015-08-19 13:44:50 +02:00
Toshik
ce915cbada Update game object DB structure
Corresponds to TC changeset TrinityCore/TrinityCore@9245bad
Closes #28
2015-08-19 13:08:09 +03:00
Sarjuuk
b0194a6e81 Merge pull request #30 from Toshik/patch-2
Update quests DB structure to  5178395a4f
2015-08-18 21:31:37 +02:00
Toshik
482a8dbd7d Update quests DB structure
According to 5178395a4f
2015-08-18 22:05:16 +03:00
Sarjuuk
59e254be54 Spawns
* fixed any spawns in Northrend refering to Dalarans dungeon maps and thus being inaccurate
2015-08-18 21:01:03 +02:00
Sarjuuk
17fdd13d3d fixed copy/paste error 2015-08-18 17:00:21 +02:00
Sarjuuk
e825ed84e8 Items
* fixed displaying zero value stats as socketbonus
2015-08-18 14:43:07 +02:00
Sarjuuk
a7eabb4b9d Itemsats
* fixed an translation issue on itemMod <=> statId causing wrong search results
 * fixed an sql-error breaking stat-based searches for items
2015-08-17 21:44:39 +02:00
Sarjuuk
960e48b7aa Localization
* replace null with "" for spelltriggers on items (otherwise valid values will throw an error)
2015-08-17 21:11:04 +02:00
Sarjuuk
8394a3863f Misc
* added column bitIdx to ?_titles
 * actually don't use COLLATE as it ignores indizes. Just search with lowerCase and properCase simultaneously
2015-08-15 00:09:48 +02:00
Sarjuuk
23260c2366 Tooltips
* fixed display for spells after e28c00ccda
   (if you are loading something, you DO need to register it afterwards)
2015-08-13 14:33:55 +02:00
Sarjuuk
a2d4b014fc updated submodule MPQExtractor as requested in #26 and #27
if you never inited the module you are fine. If you did, you have to
 * git submodule deinit -f includes/tools/MPQExtractor
 * rm -rf .git/modules/includes/tools/MPQExtractor
 * git submodule sync
 * git submodule update --init --recursive --remote
or something among those lines (good lord..)
2015-08-13 12:43:44 +02:00
Sarjuuk
11584a68d6 Profiler preparations
* Setup
   - accessing realm info by way of Util function
   - added table ?_talents
 * Lists
   - added support for querying multiple databases with the same structure at once (read: realms)
   - added support for exact string matches and forcing collate of fields to ci
 * JS
   - renamed occurences of 'subregion' to 'battlegroup' to be in line with other scripts (the usage is the same)
2015-08-11 15:03:42 +02:00
Sarjuuk
bd2e0ccada Emotes:
* display empty listview if no data
Setup
 * do not pass null instead of expected type array
 * added setup step for emotes to --firstrun *d'oh!*
 * fixed a wrong constant
2015-08-10 00:04:33 +02:00
Sarjuuk
3c69284e15 Misc
* fixed some recuring errors from db-log (mostly missing checks)
2015-08-08 00:47:52 +02:00
Sarjuuk
220fb81833 forgot to apply sql-update to dbversion-update 2015-08-07 20:01:36 +02:00
Sarjuuk
00f53fbb09 Spell/Tooltips
* added an exception for a bugged tooltip
 * better handle refs to unknown spells
2015-08-07 19:47:43 +02:00
Sarjuuk
63756b23d9 Spell/Tooltips
* only trim floats, when in topLevel while parsing tooltips
 * there is still an issue, where html-code gets returned from lower levels, screwing the eval()

should probably not bulk-handle all spellVars/formulas/variables on top level, but one after another
2015-08-06 21:13:11 +02:00
Sarjuuk
44e6e2ed38 Setup/Update
* using --update now reads scripts that need to be executed from ?_dbversion
 * if the script fails it will be kept in ?_dbversion until it was successfully run via --update
2015-08-06 20:48:28 +02:00
Sarjuuk
81e6098960 Home
* added variable titles and oneliners to frontpage
 * added cascading foreign keys to news_overlay -> news
 * renamed news -> featuredbox for consistency

Misc
 * aowow_dbversion is now part of the basic sql
 * also the db-dump is a fresh one and i'm shocked how often i forgot to apply updated there :<
 * from now on shorttags will be used for 'echo' in template (e.g. <?=$var; ?>)
2015-08-06 18:33:05 +02:00
Sarjuuk
38ac7b1dcb tabs2spaces in global.js 2015-08-06 12:49:38 +02:00
Sarjuuk
bfa1f261e0 Tooltips/Itemset
* handle enforced locale as espected
2015-08-04 23:23:37 +02:00
Sarjuuk
a0887f189f Faction
* fixed display of reputation columns on listviews on detail page
2015-08-04 21:25:36 +02:00
Sarjuuk
7ce72bf623 Articles
* convert all holidayIds to eventIds (by TC standard assignment)
2015-08-04 21:16:27 +02:00
Sarjuuk
320ad252d1 Tooltips/Items
* skipping an error, when handling heirloom-armor with scaled level on external tooltips
   the tooltip will now be displayed but without the armor-class changing at level 40
 * prevent assigning arbitrary randomEnchantments to items (e.g. Warglaive of Azzinoth of the Whale)
2015-08-04 19:09:37 +02:00
Sarjuuk
ef686c9c57 Achievements:
* display criterium-Ids for everyone on detail page
 * unified display with subitems on item detail page
2015-08-04 16:04:55 +02:00
Sarjuuk
e28c00ccda Misc:
* also load item-scaling data for spell-tooltips
 * fixed an event reference in the markup-guide (holidayIds became eventIds some time ago)
 * removed underline from anchor elements
 * also increment version
2015-08-04 14:58:22 +02:00
Sarjuuk
e2beb82f29 Items
* display randomEnchantment-Id as title on suffix-text on detail page
Compare
 * fixed stats display in subitem-picker
2015-08-04 14:02:51 +02:00
Sarjuuk
b1a6911ede Optimization
* minor improvements to the use of Util::checkNumeric
 * gargantuan improvements to ItemList handling random enchantments (sec -> ms)
2015-08-04 11:45:10 +02:00
Sarjuuk
f35be8fa36 Util/MostComments
* fixed wrong indexing on counting comments per page
2015-08-04 00:34:08 +02:00
Sarjuuk
1d5436bbd2 don't ask.. 2015-08-03 23:24:04 +02:00
Sarjuuk
a5f13325c0 Enchantments
* forgot notFound message
 * added Enchantments Tab to Spell Detail Page
2015-08-03 22:31:55 +02:00
Sarjuuk
7ac0601f88 Enchantments
* added tab: used by socketbonus to detail page
 * format ppm as float
2015-08-03 21:26:25 +02:00
Sarjuuk
7f08708555 added enchantments to DB
* added markdown [enchantment=123] and [emote=456]
 * updated item_stats to accomodate enchantments
 * updated tables for community content to handle emotes and enchantments

Spells:
 * getStatGain() now reads aura 123 as Spell Penetration
 * fixed error in tooltips when displaying effect ranges (e.g. 10 - 10 damage)
 * display the miscValue of aura 50, 102, 131 and 180 human readable

Emote:
 * no longer displays empty quickInfo on detail page
 * detail page no longer pretends to be a pet

Setup:
 * sync no longer regenerates all tables/files, when the supplied world-table affects nothing
 * fixed dependancy of ?_source from ?_achievment (damn plural-s)

Misc:
 * FireFox doesn't understand <node>.innerText, use <node>.textContent instead

please refresh the affected tables and files
   php aowow --sql=itemenchantment,item_stats
   php aowow --build=enchants,gems
2015-08-03 20:05:39 +02:00
Sarjuuk
f6e15c35fc Tooltips
* added word events to tooltips
  * added option to pass achieved criteria to achievement tooltips via rel-parameter
2015-07-25 00:14:59 +02:00
Sarjuuk
7b30c49785 Setup/Emotes
fixed field count after 8fa83eab64
2015-07-23 22:48:46 +02:00
Sarjuuk
d771103428 added forgotten file
should have been committed in 8fa83eab64
2015-07-23 19:57:11 +02:00
Sarjuuk
2afcceaefb Heirlooms
* fixed expected structure of ScalingStatValues (js-scaling heirlooms should be working again)
  * weapons are now properly assigned ranged- or melee-dps

  need to rerun: php aowow --build=itemScaling
2015-07-21 23:24:06 +02:00
Sarjuuk
3d08de51e1 Items/Heirlooms
* damage range of melee weapons is noe 20% and ranged weapons 30%
  * removed localized number format from item-tooltips. this broke damage recalculation
User/Weightscales
  * fixed managing weightscales by moving ! around
2015-07-21 21:16:36 +02:00
Sarjuuk
8fa83eab64 Misc/Fixups
* made emotes random-searchable
 * fixed error with pageTexts on Item Detail Page
2015-07-21 11:54:35 +02:00
Sarjuuk
4ab558858f Users
* do not numeric-check g_user in case of numeric user names (wich then break js string functions)
2015-07-21 01:07:57 +02:00
Sarjuuk
6f59afe8e6 DB/Emotes
* added emotes to DB .. why? just because!
  * also added to search
  * cross-linked achievements and emotes
  * data is generated via: php aowow --sql=emotes
  * setup requires GlobalStrings.lua (see README.md)
2015-07-21 01:07:21 +02:00
Sarjuuk
5239cbd293 NPCs / Quests
display reputattion change in list when using corresponding filters
2015-07-20 17:01:39 +02:00
Sarjuuk
7276fed9e7 Setup/Quests
* fixed wrong offset on questXP.dbc and factionReward.dbc
  * xp & reputation should be correct after running `aowow --sql=quests`
2015-07-20 15:36:29 +02:00
Sarjuuk
022ceba20d fixup to 998763b
so .. basicly everything was wrong with this one
2015-07-20 12:03:36 +02:00
Sarjuuk
5b8a862df9 DB/Structure (thx @Carbenium for the heads up & suggestions)
* adapted structure to TDB 335.59
  * dropped questItem[1-6] fields from ?_creature and ?_object (why were they even there)

 - removed a function-stub from Util (already forgot, what it was supposed to achieve)
2015-07-19 22:25:21 +02:00
Sarjuuk
11ab3e0f19 typo fix 2015-07-18 16:51:28 +02:00
Sarjuuk
998763be7b Errors/Logging
* log addNotes to DB as php user-errors
  * always skip the native php error handler
2015-07-18 16:48:00 +02:00
Sarjuuk
58a235e2e3 PageText
handle cases where pageTextId is given but pageText is not in DB
2015-07-18 15:16:35 +02:00
Sarjuuk
3e7a34a2ea Spells/Tooltips
* implemented 'know'-parameter (modifies tooltips based on known spells)
  * displayed on spell detail page
  * usale as urlParam (&know=<spellId>) or rel-attribute (rel="know=<spellId>)
2015-07-18 13:19:18 +02:00
Sarjuuk
111e53aaef Spells/Tooltips
* fixed text-parsing for spells with a combat rating in the tooltip, that is inside a formula (e.g. spell 24574)
  * fixed client tooltip updates when modifying level for affected tooltips
  * fixed spell-links on item tooltips with multiple combat ratings
2015-07-18 03:41:13 +02:00
Sarjuuk
c1f5d2ea9b Setup/Spawns:
* only priorize captials over their surrounding if there is no alphaMap present
  * Dalaran and Shattrath are capitals
  * truncate old data as it may not get overwitten
2015-07-18 01:52:08 +02:00
Sarjuuk
20732e38d8 DB/Structure
added proper keys to ?_comments
2015-07-14 21:51:20 +02:00
Sarjuuk
cea81d1810 fixup#2
* save sessions under absolute path
  * somehow forgot to add garbage collect - divisor to config
2015-07-13 00:29:27 +02:00
Sarjuuk
8b317d4b21 fixup
add forgtten categories to config values
2015-07-12 23:19:07 +02:00
Sarjuuk
dd0bb30481 Comments/Replies
* fixed reply editing
  * fixed displaying error messages
2015-07-12 01:40:09 +02:00
Sarjuuk
051334da22 Events
drop usage of holidayIds (as far as possible)
  the obvious change is, that all events are now refenreced by a positive eventId.
  (?event=375 will probably become ?event=5)
Comments
  fixed malformed db-table. It can now hold negative typeIds.
  applying this commit will drop any comments related to events without holiday
  added gain of SiteReputation for comment-replies

resyncing dependencies of 'game_event' is required
2015-07-12 00:06:21 +02:00
Sarjuuk
e9399c169a Config
move default_charset from .htaccess to DB
2015-07-11 19:48:52 +02:00
Sarjuuk
ce82a8f09f rather enforce interpretation as php for aowow/aowow than hiding it (wich broke filter forms) 2015-07-11 19:27:36 +02:00
Sarjuuk
54695a9490 Currency
* expanded tooltips
  * moved cap from hardcoded to DB
  * can now set description (manually)
Itemset
  * expanded tooltips
Lang
  * number formating is now locale-aware
2015-07-11 19:27:28 +02:00
Sarjuuk
08270ae09e Screenshots
* reworked upload process to be more user-friendly
  * handle bricks that consist mostly of localized text separately
  * added handle to display errors, when adding CommunityContent on the page they originated from
2015-07-11 14:19:59 +02:00
Sarjuuk
f6d80cf473 Spawns
* display phaseMask as hexadecimal
  * hide phasemasks, containing every single phase (0xFFFF)
2015-07-09 23:29:20 +02:00
Sarjuuk
0cb5d6b896 CLISetup
* added msg-level INFO
  * changed some WARN-level messages to INFO
Util
  * added function to handle directories aowow wants to write to / read from
SiteConfig
  * group options to be less cluttered
  * allow empty strings (numerical values must still at least be 0)
  * renamed account related config values to be make more sense
  * make cache path configurable
  * make session save path configurable - use this to avoid the garbage collect cron job on Debian or Ubuntu, that cleans sessions and only depends on your php.ini (NOTE: putting this inside a web-enabled directory is a risk!)
2015-07-09 21:04:00 +02:00
Sarjuuk
4a47900860 Utility/RSS
use SimpleXML to generate rss-feed
  send rss as utf-8
  fixed some misc errors when generating a feed
2015-07-06 01:07:12 +02:00
Sarjuuk
f2ec843f7e Lang
rename /gameObjects?/ => /objects?/ so it can be localized via typeString
2015-07-05 21:17:55 +02:00
Sarjuuk
ed67493bb8 Items/Filter
fixed error, when appending empty upg= to query
NPC/Text
  handled rare case of uppercase string-placeholders
2015-07-05 19:15:36 +02:00
Sarjuuk
09e0886cdd Utility
* implemented sub-page: latest-videos (can't be suggested yet, i know!)
  * also show subject on latest-screenshots
2015-07-05 00:37:54 +02:00
Sarjuuk
5ecb46633a UserPage
* display amount of pending screenshots / videos
  * corrected amount of comment ratings on display
2015-07-05 00:06:48 +02:00
Sarjuuk
5db946590e display user reputation in top-menu 2015-07-04 19:37:10 +02:00
Sarjuuk
444e372a66 Utility/Random
no longer tries to lookup a random profile
Utility/Comments
  implemented sub-page: most-comments
2015-07-04 18:42:24 +02:00
Sarjuuk
af39933cc8 Utility
implemented subpage: latest-screenshots
2015-07-04 16:40:48 +02:00
Sarjuuk
2619c88db4 Admins/Screenshots
* allow empty captions on edit
  * set date of reputation reward to time of upload not approve
2015-07-04 16:24:15 +02:00
Sarjuuk
b76fc3b2a7 added memory_limit to initial config 2015-07-01 21:54:03 +02:00
Sarjuuk
cc886b4db9 Quest Detail Page:
properly distinguish between provided, required and provided-required items
2015-07-01 21:13:22 +02:00
Sarjuuk
a3c9b52073 Screenshots:
fixed sql-error in manager
Comments:
  no longer appear as upvoted for anonymous user
  can be voted on, again
Class detail page:
  removed default limit on class ability query (300). now shows all spells
Filter/Items:
  added missing table-prefix; fixed search for iconString
Spells/Search:
  display triggered player abilities as misc. spells. (previously hidden)
Misc:
  removed some obscure piece of old config
2015-07-01 19:44:44 +02:00
Sarjuuk
7673e256c8 Setup: added some checks when useing WIN
lets just say, the CLI works somewhat differently..
2015-06-29 00:10:03 +02:00
Sarjuuk
2f767ba835 misc fixes 2015-06-28 19:23:26 +02:00
Sarjuuk
7ec07c0dca Merge remote-tracking branch 'github/master' into ghMaster 2015-06-28 17:17:41 +02:00
Sarjuuk
c779e9139a Setup: unpacked db_structure.sql
README: added required db-version
2015-06-28 17:16:53 +02:00
Sarjuuk
97b3e860a0 Merge pull request #23 from Carbenium/setup
Setup: Fix two issues which lead to an setup error
2015-06-28 17:04:09 +02:00
Sarjuuk
4a80934346 added premium-related placeholder images
(not for acutal use, just a layout reference)
2015-06-28 16:56:36 +02:00
Carbenium
9897a6cf23 Setup: folder 'static/download/searchplugins/' has to be created during setup 2015-06-28 15:30:15 +02:00
Carbenium
7c0a2192f7 Setup: testSelf should reference README.md instead of README, otherwise setup will fail 2015-06-28 15:28:11 +02:00
Sarjuuk
92b180ca05 Updated README with installation instructions
thats it folks .. lets call it v1.0
2015-06-27 21:52:20 +02:00
396 changed files with 43253 additions and 21446 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
*.php text eol=lf
*.js text eol=lf
*.css text eol=lf
*.sql text eol=lf

17
.gitignore vendored
View File

@@ -1,6 +1,13 @@
# Git
*.orig
# cache # cache
/cache/template/* /cache/template/*
/setup/generated/alphaMaps/*.png /setup/generated/alphaMaps/*.png
/cache/firstrun
# extract from MPQ
/setup/mpqdata/*
# generated files # generated files
/static/js/profile_all.js /static/js/profile_all.js
@@ -11,20 +18,26 @@
/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/icons* /static/images/wow/hunterpettalents/*
/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
View File

@@ -1,6 +0,0 @@
[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

View File

@@ -1,11 +1,19 @@
Order Deny,Allow Order Deny,Allow
<FilesMatch "\.(conf|php|in)|aowow$"> <FilesMatch "\.(conf|php|in)$">
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
@@ -13,13 +21,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>
# 5MB should be enough for the largest screenshots in the land # UHD screenshots can get pretty large (cannot be set in config)
php_value default_charset UTF-8 php_value upload_max_filesize 20M
php_value upload_max_filesize 5M php_value post_max_size 25M
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

BIN
README

Binary file not shown.

131
README.md Normal file
View File

@@ -0,0 +1,131 @@
![logo](static/images/logos/home.png)
## Build Status
![fuck it ship it](http://forthebadge.com/images/badges/fuck-it-ship-it.svg)
## 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 puposes of any kind!
## Requirements
+ Webserver running PHP ≥ 5.5.0 including extensions:
+ SimpleXML
+ GD
+ Mysqli
+ mbString
+ MySQL ≥ 5.5.30
+ [TDB 335.63](https://github.com/TrinityCore/TrinityCore/releases/tag/TDB335.63) - including world updates up to 04.05.2017
+ Tools require cmake: Please refer to the individual repositories for detailed information
+ [MPQExtractor](https://github.com/Sarjuuk/MPQExtractor) / [FFmpeg](https://ffmpeg.org/download.html) / [BLPConverter](https://github.com/Sarjuuk/BLPConverter) (optional)
+ 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/) / [BLPConverter](https://github.com/PatrickCyr/BLPConverter) (optional)
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 TrintyCore server will greatly increase the accuracy of spawn points
> Calculate.Creature.Zone.Area.Data = 1
> Calculate.Gameoject.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/Glues/Credits/
> \<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/
.. optionaly (not used in AoWoW):
> \<localeCode>/Interface/GLUES/LOADINGSCREENS/
#### 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 --firstrun`.
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 poperly. 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 confict 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 priorize 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 automaticly 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 agains the characters database and have to use the incomplete/outdated cached profiles of AoWoW.
## 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
## Special Thanks
Said website with the red smiling rocket, for providing this beautifull website!
Please do not reagard 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".
![uses badges](http://forthebadge.com/images/badges/uses-badges.svg)

0
aowow Normal file → Executable file
View File

View File

@@ -4,7 +4,7 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
function extAuth($user, $pass, &$userId = 0) function extAuth($user, $pass, &$userId = 0, &$userGroup = -1)
{ {
/* /*
insert some auth mechanism here insert some auth mechanism here

View File

@@ -1,74 +0,0 @@
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}
}
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,138 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxAccount extends AjaxHandler
{
protected $validParams = ['exclude', 'weightscales'];
protected $_post = array(
'groups' => [FILTER_SANITIZE_NUMBER_INT, null],
'save' => [FILTER_SANITIZE_NUMBER_INT, null],
'delete' => [FILTER_SANITIZE_NUMBER_INT, null],
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'name' => [FILTER_CALLBACK, ['options' => 'AjaxAccount::checkName']],
'scale' => [FILTER_CALLBACK, ['options' => 'AjaxAccount::checkScale']],
'reset' => [FILTER_SANITIZE_NUMBER_INT, null],
'mode' => [FILTER_SANITIZE_NUMBER_INT, null],
'type' => [FILTER_SANITIZE_NUMBER_INT, null],
);
protected $_get = array(
'locale' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkLocale']]
);
public function __construct(array $params)
{
parent::__construct($params);
if (is_numeric($this->_get['locale']))
User::useLocale($this->_get['locale']);
if (!$this->params || !User::$id)
return;
// select handler
if ($this->params[0] == 'exclude')
$this->handler = 'handleExclude';
else if ($this->params[0] == 'weightscales')
$this->handler = 'handleWeightscales';
}
protected function handleExclude()
{
if (!User::$id)
return;
if ($this->_post['mode'] == 1) // directly set exludes
{
$type = $this->_post['type'];
$ids = $this->_post['id'];
if (!isset(Util::$typeStrings[$type]) || empty($ids))
return;
// 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)', $type, $ids);
foreach ($ids as $typeId)
DB::Aowow()->query('INSERT INTO ?_account_excludes (`userId`, `type`, `typeId`, `mode`) VALUES (?a) ON DUPLICATE KEY UPDATE mode = (mode ^ 0x3)', array(
User::$id, $type, $typeId, in_array($includes, $typeId) ? 2 : 1
));
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);
return;
}
protected function handleWeightscales()
{
if ($this->_post['save'])
{
if (!$this->_post['scale'])
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))
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)
{
list($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 $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);
else
return 0;
}
protected function checkScale($val)
{
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return $val;
return null;
}
protected function checkName($val)
{
$var = trim(urldecode($val));
return filter_var($var, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW);
}
}

View File

@@ -0,0 +1,497 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxAdmin extends AjaxHandler
{
protected $validParams = ['screenshots', 'siteconfig', 'weight-presets'];
protected $_get = array(
'action' => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH
'id' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkId']],
'key' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkKey']],
'all' => [FILTER_UNSAFE_RAW, null],
'type' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']],
'typeid' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']],
'user' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkUser']],
'val' => [FILTER_UNSAFE_RAW, null]
);
protected $_post = array(
'alt' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW],
'id' => [FILTER_SANITIZE_NUMBER_INT, null],
'scale' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkScale']],
'__icon' => [FILTER_CALLBACK, ['options' => 'AjaxAdmin::checkKey']],
);
public function __construct(array $params)
{
parent::__construct($params);
// requires 'action' parameter in any case
if (!$this->_get['action'] || !$this->params)
return;
if ($this->params[0] == 'screenshots')
{
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')
{
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')
{
if (!User::isInGroup(U_GROUP_DEV | U_GROUP_ADMIN | U_GROUP_BUREAU))
return;
if ($this->_get['action'] == 'save')
$this->handler = 'wtSave';
}
}
// get all => null (optional)
// evaled response .. UNK
protected function ssList()
{
// 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()
{
$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 displayName = ?', $this->_get['user']))
$res = CommunityContent::getScreenshotsForManager(0, 0, $uId);
return 'ssm_screenshotData = '.Util::toJSON($res);
}
// get: id => SSid
// resp: ''
protected function ssEditAlt()
{
// 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]);
return '';
}
// get: id => comma-separated SSids
// resp: ''
protected function ssApprove()
{
if (!$this->_get['id'])
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 ($_ = 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)))
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($_['userIdOwner'], SITEREP_ACTION_UPLOAD, ['id' => $id, 'what' => 1, 'date' => $_['date']]);
// flag DB entry as having screenshots
if (Util::$typeClasses[$_['type']] && ($tbl = get_class_vars(Util::$typeClasses[$_['type']])['dataTable']))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_SCREENSHOT, $_['typeId']);
}
}
return '';
}
// get: id => comma-separated SSids
// resp: ''
protected function ssSticky()
{
if (!$this->_get['id'])
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);
}
return '';
}
// get: id => comma-separated SSids
// resp: ''
// 2 steps: 1) remove from sight, 2) remove from disk
protected function ssDelete()
{
if (!$this->_get['id'])
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 && Util::$typeClasses[$type] && ($tbl = get_class_vars(Util::$typeClasses[$type])['dataTable']))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags & ~?d WHERE id IN (?a)', CUSTOM_HAS_SCREENSHOT, array_keys($toUnflag));
}
return '';
}
// get: id => ssId, typeid => typeId (but not type..?)
// resp: ''
protected function ssRelocate()
{
if (!$this->_get['id'] || !$this->_get['typeid'])
return '';
$id = $this->_get['id'][0];
list($type, $oldTypeId) = array_values(DB::Aowow()->selectRow('SELECT type, typeId FROM ?_screenshots WHERE id = ?d', $id));
$typeId = (int)$this->_get['typeid'];
$tc = new Util::$typeClasses[$type]([['id', $typeId]]);
if (!$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);
}
return '';
}
protected function confAdd()
{
$key = trim($this->_get['key']);
$val = trim(urldecode($this->_get['val']));
if ($key === null)
return 'empty option name given';
if (!strlen($key))
return 'invalid chars in option name: [a-z 0-9 _ . -] are allowed';
if (ini_get($key) === false || ini_set($key, $val) === false)
return 'this configuration option cannot be set';
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_config WHERE `flags` & ?d AND `key` = ?', CON_FLAG_PHP, $key))
return 'this configuration option is already in use';
DB::Aowow()->query('INSERT IGNORE INTO ?_config (`key`, `value`, `cat`, `flags`) VALUES (?, ?, 0, ?d)', $key, $val, CON_FLAG_TYPE_STRING | CON_FLAG_PHP);
return '';
}
protected function confRemove()
{
if (!$this->_get['key'])
return 'invalid configuration option given';
if (DB::Aowow()->query('DELETE FROM ?_config WHERE `key` = ? AND (`flags` & ?d) = 0', $this->_get['key'], CON_FLAG_PERSISTENT))
return '';
else
return 'option name is either protected or was not found';
}
protected function confUpdate()
{
$key = trim($this->_get['key']);
$val = trim(urldecode($this->_get['val']));
$msg = '';
if (!strlen($key))
return 'empty option name given';
$cfg = DB::Aowow()->selectRow('SELECT `flags`, `value` FROM ?_config WHERE `key` = ?', $key);
if (!$cfg)
return 'configuration option not found';
if (!($cfg['flags'] & CON_FLAG_TYPE_STRING) && !strlen($val))
return 'empty value given';
else if ($cfg['flags'] & CON_FLAG_TYPE_INT && !preg_match('/^-?\d+$/i', $val))
return "value must be integer";
else if ($cfg['flags'] & CON_FLAG_TYPE_FLOAT && !preg_match('/^-?\d*(,|.)?\d+$/i', $val))
return "value must be float";
else if ($cfg['flags'] & CON_FLAG_TYPE_BOOL)
$val = (int)!!$val; // *snort* bwahahaa
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $val, $key);
if (!$this->confOnChange($key, $val, $msg))
DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $cfg['value'], $key);
return $msg;
}
protected function wtSave()
{
if (!$this->_post['id'] || !$this->_post['__icon'])
return 3;
$writeFile = function($file, $content)
{
$success = false;
if ($handle = @fOpen($file, "w"))
{
if (fWrite($handle, $content))
$success = true;
fClose($handle);
}
else
die('me no file');
if ($success)
@chmod($file, Util::FILE_ACCESS);
return $success;
};
// 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)
{
list($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
$wtPresets = [];
$scales = DB::Aowow()->select('SELECT id, name, icon, class FROM ?_account_weightscales WHERE userId = 0 ORDER BY class, id ASC');
foreach ($scales as $s)
{
$weights = DB::Aowow()->selectCol('SELECT field AS ARRAY_KEY, val FROM ?_account_weightscale_data WHERE id = ?d', $s['id']);
if (!$weights)
continue;
$wtPresets[$s['class']]['pve'][$s['name']] = array_merge(['__icon' => $s['icon']], $weights);
}
$toFile = "var wt_presets = ".Util::toJSON($wtPresets).";";
$file = 'datasets/weight-presets';
if (!$writeFile($file, $toFile))
return 2;
// all done
return 0;
}
protected function checkId($val)
{
// expecting id-list
if (preg_match('/\d+(,\d+)*/', $val))
return array_map('intVal', explode(',', $val));
return null;
}
protected function checkKey($val)
{
// expecting string
if (preg_match('/[^a-z0-9_\.\-]/i', $val))
return '';
return strtolower($val);
}
protected function checkUser($val)
{
$n = Util::lower(trim(urldecode($val)));
if (User::isValidName($n))
return $n;
return null;
}
protected function checkScale($val)
{
if (preg_match('/^((\w+:\d+)(,\w+:\d+)*)$/', $val))
return $val;
return null;
}
private function confOnChange($key, $val, &$msg)
{
$fn = $buildList = null;
switch ($key)
{
case 'battlegroup':
$buildList = 'realms,realmMenu';
break;
case 'name_short':
$buildList = 'searchboxBody,demo,searchplugin';
break;
case 'site_host':
$buildList = 'searchplugin,demo,power,searchboxBody';
break;
case 'static_host':
$buildList = 'searchplugin,power,searchboxBody,searchboxScript';
break;
case 'locales':
$buildList = 'locales';
$msg .= ' * remember to rebuild all static files for the language you just added.<br />';
$msg .= ' * you can speed this up by supplying the regionCode to the setup: <pre class="q1">--locales=<regionCodes,> -f</pre>';
break;
case 'profiler_queue':
$fn = function($x) use (&$msg) {
if (!$x)
return true;
return Profiler::queueStart($msg);
};
break;
default: // nothing to do, everything is fine
return true;
}
if ($buildList)
{
// we need to use exec as build() can only be run from CLI
exec('php aowow --build='.$buildList, $out);
foreach ($out as $o)
if (strstr($o, 'ERR'))
$msg .= explode('0m]', $o)[1]."<br />\n";
}
return $fn ? $fn($val) : true;
}
}

View File

@@ -0,0 +1,82 @@
<?php
if (!defined('AOWOW_REVISION'))
die('invalid access');
class AjaxArenaTeam extends AjaxHandler
{
protected $validParams = ['resync', 'status'];
protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'profile' => [FILTER_CALLBACK, ['options' => '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()
{
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()
{
$response = Profiler::resyncStatus(TYPE_ARENA_TEAM, $this->_get['id']);
return Util::toJSON($response);
}
}
?>

View File

@@ -0,0 +1,410 @@
<?php
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_CALLBACK, ['options' => 'AjaxComment::checkId']],
'body' => [FILTER_UNSAFE_RAW, null],// escaped by json_encode
'commentbody' => [FILTER_UNSAFE_RAW, null],// escaped by json_encode
'response' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW],
'reason' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW],
'remove' => [FILTER_SANITIZE_NUMBER_INT, null],
'commentId' => [FILTER_SANITIZE_NUMBER_INT, null],
'replyId' => [FILTER_SANITIZE_NUMBER_INT, null],
'sticky' => [FILTER_SANITIZE_NUMBER_INT, null],
// 'username' => [FILTER_SANITIZE_STRING, 0xC] // FILTER_FLAG_STRIP_LOW | *_HIGH
);
protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']],
'type' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']],
'typeid' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']],
'rating' => [FILTER_SANITIZE_NUMBER_INT, null]
);
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()
{
if (!$this->_get['typeid'] || !$this->_get['type'] || !isset(Util::$typeClasses[$this->_get['type']]))
return; // whatever, we cant even send him back
// this type cannot be commented on
if (!(get_class_vars(Util::$typeClasses[$this->_get['type']])['contribute'] & CONTRIBUTE_CO))
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() && !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 ?_comments_rates (commentId, userId, value) VALUES (?d, 0, 1)', $postIdx);
// flag target with hasComment
if ($tbl = get_class_vars(Util::$typeClasses[$this->_get['type']])['dataTable'])
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $this->_get['typeid']);
}
}
$this->doRedirect = true;
return '?'.Util::$typeStrings[$this->_get['type']].'='.$this->_get['typeid'].'#comments';
}
protected function handleCommentEdit()
{
if ((!User::canComment() && !User::isInGroup(U_GROUP_MODERATOR)) || !$this->_get['id'] || !$this->_post['body'])
return;
if (mb_strlen($this->_post['body']) < self::COMMENT_LENGTH_MIN)
return;
// 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()
{
if (!$this->_post['id'] || !User::$id)
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()->selectRow('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 = ?d',
CC_FLAG_DELETED,
$this->_post['id']
);
if (!$coInfo['hasMore'] && Util::$typeClasses[$coInfo['type']] && ($tbl = get_class_vars(Util::$typeClasses[$coInfo['type']])['dataTable']))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags & ~?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $coInfo['typeId']);
}
}
protected function handleCommentUndelete()
{
if (!$this->_post['id'] || !User::$id)
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()->selectRow('SELECT type, typeId FROM ?_comments WHERE id = ?d', $this->_post['id']);
if (Util::$typeClasses[$coInfo['type']] && ($tbl = get_class_vars(Util::$typeClasses[$coInfo['type']])['dataTable']))
DB::Aowow()->query('UPDATE '.$tbl.' SET cuFlags = cuFlags | ?d WHERE id = ?d', CUSTOM_HAS_COMMENT, $coInfo['typeId']);
}
}
protected function handleCommentRating()
{
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 ?_comments_rates WHERE commentId = ?d and userId <> 0 GROUP BY commentId', $this->_get['id']))
return Util::toJSON($votes);
else
return Util::toJSON(['success' => 1, 'up' => 0, 'down' => 0]);
}
protected function handleCommentVote()
{
if (!User::$id || !$this->_get['id'] || !$this->_get['rating'])
return Util::toJSON(['error' => 1, 'message' => Lang::main('genericError')]);
$target = DB::Aowow()->selectRow('SELECT c.userId AS owner, cr.value FROM ?_comments c LEFT JOIN ?_comments_rates cr ON cr.commentId = c.id AND cr.userId = ?d WHERE c.id = ?d', User::$id, $this->_get['id']);
$val = User::canSupervote() ? 2 : 1;
if ($this->_get['rating'] < 0)
$val *= -1;
if (User::getCurDailyVotes() <= 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 ?_comments_rates WHERE commentId = ?d AND userId = ?d', $this->_get['id'], User::$id);
else // replace, because we may be overwriting an old, opposing vote
if ($ok = DB::Aowow()->query('REPLACE INTO ?_comments_rates (commentId, userId, value) VALUES (?d, ?d, ?d)', (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()
{
if (!$this->_post['id'] || !User::isInGroup(U_GROUP_MODERATOR))
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()
{
$this->contentType = 'text/plain';
if (!$this->_post['id'])
return 'The comment does not exist.';
$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 | 0x4 WHERE id = ?d', $this->_post['id'][0]);
else
$ok = DB::Aowow()->query('UPDATE ?_comments SET flags = flags & ~0x4 WHERE id = ?d', $this->_post['id'][0]);
}
else if (User::$id && !$this->_post['reason'] || mb_strlen($this->_post['reason']) < self::REPLY_LENGTH_MIN)
return 'Your message is too short.';
else if (User::$id) // only report as outdated
{
$ok = DB::Aowow()->query(
'INSERT INTO ?_reports (userId, mode, reason, subject, ip, description, userAgent, appName) VALUES (?d, 1, 17, ?d, ?, "<automated comment report>", ?, ?)',
User::$id,
$this->_post['id'][0],
User::$ip,
$_SERVER['HTTP_USER_AGENT'],
get_browser(null, true)['browser']
);
}
if ($ok) // this one is very special; as in: completely retarded
return 'ok'; // the script expects the actual characters 'ok' not some string like "ok"
return Lang::main('genericError');
}
protected function handleCommentShowReplies()
{
return Util::toJSON(!$this->_get['id'] ? [] : CommunityContent::getCommentReplies($this->_get['id']));
}
protected function handleReplyAdd()
{
$this->contentType = 'text/plain';
if (!User::canComment())
return 'You are not allowed to reply.';
else if (!$this->_post['commentId'] || !DB::Aowow()->selectCell('SELECT 1 FROM ?_comments WHERE id = ?d', $this->_post['commentId']))
return Lang::main('genericError');
else if (!$this->_post['body'] || mb_strlen($this->_post['body']) < self::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > self::REPLY_LENGTH_MAX)
return 'Your reply has '.mb_strlen($this->_post['body']).' characters and must have at least '.self::REPLY_LENGTH_MIN.' and at most '.self::REPLY_LENGTH_MAX.'.';
else 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']));
else
return Lang::main('genericError');
}
protected function handleReplyEdit()
{
$this->contentType = 'text/plain';
if (!User::canComment())
return 'You are not allowed to reply.';
else if (!$this->_post['replyId'] || !$this->_post['commentId'])
return Lang::main('genericError');
else if (!$this->_post['body'] || mb_strlen($this->_post['body']) < self::REPLY_LENGTH_MIN || mb_strlen($this->_post['body']) > self::REPLY_LENGTH_MAX)
return 'Your reply has '.mb_strlen($this->_post['body']).' characters and must have at least '.self::REPLY_LENGTH_MIN.' and at most '.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']));
else
return Lang::main('genericError');
}
protected function handleReplyDetach()
{
if (!User::isInGroup(U_GROUP_MODERATOR) || !$this->_post['id'])
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()
{
if (!User::$id || !$this->_post['id'])
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 ?_comments_rates WHERE commentId = ?d', $this->_post['id'][0]);
}
protected function handleReplyFlag()
{
if (!User::$id || !$this->_post['id'])
return;
DB::Aowow()->query(
'INSERT INTO ?_reports (userId, mode, reason, subject, ip, description, userAgent, appName) VALUES (?d, 1, 19, ?d, ?, "<automated commentreply report>", ?, ?)',
User::$id,
$this->_post['id'][0],
User::$ip,
$_SERVER['HTTP_USER_AGENT'],
get_browser(null, true)['browser']
);
}
protected function handleReplyUpvote()
{
if (!$this->_post['id'] || !User::canUpvote())
return;
$owner = DB::Aowow()->selectCell('SELECT userId FROM ?_comments WHERE id = ?d', $this->_post['id'][0]);
if (!$owner)
return;
$ok = DB::Aowow()->query(
'INSERT INTO ?_comments_rates (commentId, userId, value) VALUES (?d, ?d, ?d)',
$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();
}
}
protected function handleReplyDownvote()
{
if (!$this->_post['id'] || !User::canDownvote())
return;
$owner = DB::Aowow()->selectCell('SELECT userId FROM ?_comments WHERE id = ?d', $this->_post['id'][0]);
if (!$owner)
return;
$ok = DB::Aowow()->query(
'INSERT INTO ?_comments_rates (commentId, userId, value) VALUES (?d, ?d, ?d)',
$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();
}
}
protected function checkId($val)
{
// expecting id-list
if (preg_match('/\d+(,\d+)*/', $val))
return array_map('intVal', explode(',', $val));
return null;
}
}
?>

View File

@@ -0,0 +1,100 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxContactus extends AjaxHandler
{
protected $_post = array(
'mode' => [FILTER_SANITIZE_NUMBER_INT, null],
'reason' => [FILTER_SANITIZE_NUMBER_INT, null],
'ua' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW],
'appname' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW],
'page' => [FILTER_SANITIZE_URL, null],
'desc' => [FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW],
'id' => [FILTER_SANITIZE_NUMBER_INT, null],
'relatedurl' => [FILTER_SANITIZE_URL, null],
'email' => [FILTER_SANITIZE_EMAIL, null]
);
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()
{
$mode = $this->_post['mode'];
$rsn = $this->_post['reason'];
$ua = $this->_post['ua'];
$app = $this->_post['appname'];
$url = $this->_post['page'];
$desc = $this->_post['desc'];
$contexts = array(
[1, 2, 3, 4, 5, 6, 7, 8],
[15, 16, 17, 18, 19, 20],
[30, 31, 32, 33, 34, 35, 36, 37],
[45, 46, 47, 48],
[60, 61],
[45, 46, 47, 48],
[45, 46, 48]
);
if ($mode === null || $rsn === null || $ua === null || $app === null || $url === null)
return 'required field missing';
if (!isset($contexts[$mode]) || !in_array($rsn, $contexts[$mode]))
return 'mode invalid';
if (!$desc)
return 3;
if (mb_strlen($desc) > 500)
return 2;
if (!User::$id && !User::$ip)
return 'your ip could not be determined';
// check already reported
$field = User::$id ? 'userId' : 'ip';
if (DB::Aowow()->selectCell('SELECT 1 FROM ?_reports WHERE `mode` = ?d AND `reason`= ?d AND `subject` = ?d AND ?# = ?', $mode, $rsn, $this->_post['id'], $field, User::$id ?: User::$ip))
return 7;
$update = array(
'userId' => User::$id,
'mode' => $mode,
'reason' => $rsn,
'ip' => User::$ip,
'description' => $desc,
'userAgent' => $ua,
'appName' => $app,
'url' => $url
);
if ($_ = $this->_post['id'])
$update['subject'] = $_;
if ($_ = $this->_post['relatedurl'])
$update['relatedurl'] = $_;
if ($_ = $this->_post['email'])
$update['email'] = $_;
if (DB::Aowow()->query('INSERT INTO ?_reports (?#) VALUES (?a)', array_keys($update), array_values($update)))
return 0;
return 'save to db unsuccessful';
}
}

View File

@@ -0,0 +1,37 @@
<?php
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::$id)
return;
$this->_get = array(
$this->params[0] => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH
);
// 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()
{
if (User::$id && $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;
return null;
}
}

View File

@@ -0,0 +1,139 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxData extends AjaxHandler
{
protected $_get = array(
'locale' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkLocale']],
't' => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH
'catg' => [FILTER_SANITIZE_NUMBER_INT, null],
'skill' => [FILTER_CALLBACK, ['options' => 'AjaxData::checkSkill']],
'class' => [FILTER_SANITIZE_NUMBER_INT, null],
'callback' => [FILTER_CALLBACK, ['options' => 'AjaxData::checkCallback']]
);
public function __construct(array $params)
{
parent::__construct($params);
if (is_numeric($this->_get['locale']))
User::useLocale($this->_get['locale']);
// always this one
$this->handler = 'handleData';
}
/* responses
<string>
*/
protected function handleData()
{
$result = '';
// different data can be strung together
foreach ($this->params as $set)
{
// requires valid token to hinder automated access
if ($set != 'item-scaling')
if (!$this->_get['t'] || empty($_SESSION['dataKey']) || $this->_get['t'] != $_SESSION['dataKey'])
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, '778');
break;
case 'mounts':
$result .= $this->loadProfilerData($set, '777');
break;
case 'quests':
// &partial: im not doing this right
// it expects a full quest dump on first lookup but will query subCats again if clicked..?
// for now omiting the detail clicks with empty results and just set catg update
$catg = isset($this->_get['catg']) ? $this->_get['catg'] : 'null';
if ($catg == 'null')
$result .= $this->loadProfilerData($set);
else if ($this->_get['callback'])
$result .= "\n\$WowheadProfiler.loadOnDemand('quests', ".$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 independant
case 'quick-excludes':
case 'zones':
case 'weight-presets':
case 'item-scaling':
case 'realms':
case 'statistics':
if (!Util::loadStaticFile($set, $result) && CFG_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':
if (!Util::loadStaticFile($set, $result, true) && CFG_DEBUG)
$result .= "alert('could not fetch static data: ".$set." for locale: ".User::$localeString."');";
$result .= "\n\n";
break;
default:
break;
}
}
return $result;
}
protected function checkSkill($val)
{
return array_intersect([171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356], explode(',', $val));
}
protected function checkCallback($val)
{
return substr($val, 0, 29) == '$WowheadProfiler.loadOnDemand';
}
private function loadProfilerData($file, $catg = 'null')
{
$result = '';
if ($this->_get['callback'])
if (Util::loadStaticFile('p-'.$file, $result, true))
$result .= "\n\$WowheadProfiler.loadOnDemand('".$file."', ".$catg.");\n";
return $result;
}
}
?>

View File

@@ -0,0 +1,107 @@
<?php
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;
$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];
switch ($p[0])
{
case 'achievements':
$this->filter = (new AchievementListFilter(true, $opts));
break;
case 'enchantments':
$this->filter = (new EnchantmentListFilter(true, $opts));
break;
case 'icons':
$this->filter = (new IconListFilter(true, $opts));
break;
case 'items':
$this->filter = (new ItemListFilter(true, $opts));
break;
case 'itemsets':
$this->filter = (new ItemsetListFilter(true, $opts));
break;
case 'npcs':
$this->filter = (new CreatureListFilter(true, $opts));
break;
case 'objects':
$this->filter = (new GameObjectListFilter(true, $opts));
break;
case 'quests':
$this->filter = (new QuestListFilter(true, $opts));
break;
case 'sounds':
$this->filter = (new SoundListFilter(true, $opts));
break;
case 'spells':
$this->filter = (new SpellListFilter(true, $opts));
break;
case 'profiles':
$this->filter = (new ProfileListFilter(true, $opts));
break;
case 'guilds':
$this->filter = (new GuildListFilter(true, $opts));
break;
case 'arena-teams':
$this->filter = (new ArenaTeamListFilter(true, $opts));
break;
default:
return;
}
parent::__construct($params);
// always this one
$this->handler = 'handleFilter';
}
protected function handleFilter()
{
$url = '?'.$this->page;
$this->filter->mergeCat($this->cat);
if ($this->cat)
$url .= '='.implode('.', $this->cat);
$fi = [];
if ($x = $this->filter->getFilterString())
$url .= '&filter='.$x;
if ($this->filter->error)
$_SESSION['fiError'] = get_class($this->filter);
if ($fi)
$url .= '&filter='.implode(';', $fi);
// do get request
return $url;
}
}

View File

@@ -0,0 +1,36 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxGotocomment extends AjaxHandler
{
protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkInt']]
);
public function __construct(array $params)
{
parent::__construct($params);
// always this one
$this->handler = 'handleGoToComment';
$this->doRedirect = true;
}
/* responses
header()
*/
protected function handleGoToComment()
{
if (!$this->_get['id'])
exit; // just be blank
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 '?'.Util::$typeStrings[$_['type']].'='.$_['typeId'].'#comments:id='.$_['id'].($_['id'] != $this->_get['id'] ? ':reply='.$this->_get['id'] : null);
else
exit;
}
}
?>

View File

@@ -0,0 +1,82 @@
<?php
if (!defined('AOWOW_REVISION'))
die('invalid access');
class AjaxGuild extends AjaxHandler
{
protected $validParams = ['resync', 'status'];
protected $_get = array(
'id' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'profile' => [FILTER_CALLBACK, ['options' => '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()
{
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()
{
$response = Profiler::resyncStatus(TYPE_GUILD, $this->_get['id']);
return Util::toJSON($response);
}
}
?>

View File

@@ -0,0 +1,33 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class AjaxLocale extends AjaxHandler
{
protected $_get = array(
'locale' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkLocale']]
);
public function __construct(array $params)
{
parent::__construct($params);
// always this one
$this->handler = 'handleLocale';
$this->doRedirect = true;
}
/* responses
header()
*/
protected function handleLocale()
{
User::setLocale($this->_get['locale']);
User::save();
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '.';
}
}
?>

View File

@@ -0,0 +1,716 @@
<?php
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_CALLBACK, ['options' => 'AjaxHandler::checkIdList']],
'items' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkItemList']],
'size' => [FILTER_SANITIZE_STRING, 0xC], // FILTER_FLAG_STRIP_LOW | *_HIGH
'guild' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkEmptySet']],
'arena-team' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkEmptySet']],
);
protected $_post = array(
'name' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext']],
'level' => [FILTER_SANITIZE_NUMBER_INT, null],
'class' => [FILTER_SANITIZE_NUMBER_INT, null],
'race' => [FILTER_SANITIZE_NUMBER_INT, null],
'gender' => [FILTER_SANITIZE_NUMBER_INT, null],
'nomodel' => [FILTER_SANITIZE_NUMBER_INT, null],
'talenttree1' => [FILTER_SANITIZE_NUMBER_INT, null],
'talenttree2' => [FILTER_SANITIZE_NUMBER_INT, null],
'talenttree3' => [FILTER_SANITIZE_NUMBER_INT, null],
'activespec' => [FILTER_SANITIZE_NUMBER_INT, null],
'talentbuild1' => [FILTER_SANITIZE_STRING, 0xC],// FILTER_FLAG_STRIP_LOW | *_HIGH
'glyphs1' => [FILTER_SANITIZE_STRING, 0xC],
'talentbuild2' => [FILTER_SANITIZE_STRING, 0xC],
'glyphs2' => [FILTER_SANITIZE_STRING, 0xC],
'icon' => [FILTER_SANITIZE_STRING, 0xC],
'description' => [FILTER_CALLBACK, ['options' => 'AjaxHandler::checkFulltext']],
'source' => [FILTER_SANITIZE_NUMBER_INT, null],
'copy' => [FILTER_SANITIZE_NUMBER_INT, null],
'public' => [FILTER_SANITIZE_NUMBER_INT, null],
'gearscore' => [FILTER_SANITIZE_NUMBER_INT, null],
'inv' => [FILTER_CALLBACK, ['options' => 'AjaxProfile::checkItemString', 'flags' => FILTER_REQUIRE_ARRAY]],
);
public function __construct(array $params)
{
parent::__construct($params);
if (!$this->params)
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() // links char with account
{
if (!User::$id || empty($this->_get['id']))
return;
$uid = User::$id;
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
$uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user']);
else if ($this->_get['user'])
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);
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional]
return: null
*/
protected function handlePin() // (un)favorite
{
if (!User::$id || empty($this->_get['id'][0]))
return;
$uid = User::$id;
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
$uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user']);
else if ($this->_get['user'])
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 nesecary
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() // public visibility
{
if (!User::$id || empty($this->_get['id'][0]))
return;
$uid = User::$id;
if ($this->_get['user'] && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
$uid = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $this->_get['user']);
else if ($this->_get['user'])
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() // 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)))
return;
$this->contentType = 'image/'.$matches[2];
$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);
}
if ($matches[2] == 'gif')
imageGif($dest);
else
imageJpeg($dest);
return;
}
/* params
id: <prId1,prId2,..,prIdN>
user: <string> [optional, not used]
return: 1
*/
protected function handleResync()
{
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']);
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()
{
// 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'];
$response = Profiler::resyncStatus(TYPE_PROFILE, $ids);
return Util::toJSON($response);
}
/* params (get))
id: <prId1,0> [0: new profile]
params (post)
<various char data> [see below]
return:
proileId [onSuccess]
-1 [onError]
*/
protected function handleSave() // unKill a profile
{
// todo (med): detail check this post-data
$cuProfile = array(
'user' => User::$id,
// 'userName' => User::$displayName,
'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 ($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 realmGUID IS NULL', User::$id);
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'];
$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 $charId;
}
/* params
id: <prId1,prId2,..,prIdN>
return
null
*/
protected function handleDelete() // kill a profile
{
if (!$this->_get['id'])
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()
{
// 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'])
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;
$profile = array(
'id' => $pBase['id'],
'source' => $pBase['id'],
'level' => $pBase['level'],
'classs' => $pBase['class'],
'race' => $pBase['race'],
'faction' => Game::sideByRaceMask(1 << ($pBase['race'] - 1)) - 1,
'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 displayName 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_BATTLEGROUP), CFG_BATTLEGROUP];
$profile['realm'] = [Profiler::urlize($rData['name']), $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) => DisplayName]; DisplayName 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, 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', $pBase['id']))
{
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)
*/
$completion = DB::Aowow()->select('SELECT type AS ARRAY_KEY, typeId AS ARRAY_KEY2, cur, max FROM ?_profiler_completion WHERE id = ?d', $pBase['id']);
foreach ($completion as $type => $data)
{
switch ($type)
{
case TYPE_FACTION: // factionId => amount
$profile['reputation'] = array_combine(array_keys($data), array_column($data, 'cur'));
break;
case TYPE_TITLE:
foreach ($data as &$d)
$d = 1;
$profile['titles'] = $data;
break;
case TYPE_QUEST:
foreach ($data as &$d)
$d = 1;
$profile['quests'] = $data;
break;
case TYPE_SPELL:
foreach ($data as &$d)
$d = 1;
$profile['spells'] = $data;
break;
case TYPE_ACHIEVEMENT:
$achievements = array_filter($data, function ($x) { return $x['max'] === null; });
$statistics = array_filter($data, function ($x) { return $x['max'] !== null; });
// achievements
$profile['achievements'] = array_combine(array_keys($achievements), array_column($achievements, 'cur'));
$profile['achievementpoints'] = DB::Aowow()->selectCell('SELECT SUM(points) FROM ?_achievement WHERE id IN (?a)', array_keys($achievements));
// raid progression
$activity = array_filter($statistics, function ($x) { return $x['cur'] > (time() - MONTH); });
foreach ($activity as &$r)
$r = 1;
// ony .. subtract 10-man from 25-man
$profile['statistics'] = array_combine(array_keys($statistics), array_column($statistics, 'max'));
$profile['activity'] = $activity;
break;
case TYPE_SKILL:
foreach ($data as &$d)
$d = [$d['cur'], $d['max']];
$profile['skills'] = $data;
break;
}
}
$buff = '';
$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
$buff .= 'g_items.add('.$iId.', {name_'.User::$localeString.":'".Util::jsEscape($phItems->getField('name', true))."', quality:".$phItems->getField('quality').", icon:'".$phItems->getField('iconString')."', jsonequip:".Util::toJSON($data[$iId])."});\n";
$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_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
$buff .= 'g_items.add('.$i['item'].', {name_'.User::$localeString.":'".Util::jsEscape($itemz->getField('name', true))."', quality:".$itemz->getField('quality').", icon:'".$itemz->getField('iconString')."', jsonequip:".Util::toJSON($data[$i['item']])."});\n";
$profile['inventory'][$i['slot']] = [$i['item'], $i['subItem'], $i['permEnchant'], $i['tempEnchant'], $i['gem1'], $i['gem2'], $i['gem3'], $i['gem4']];
}
}
}
}
if ($buff)
$buff .= "\n";
// if ($au = $char->getField('auras'))
// {
// $auraz = new SpellList(array(['id', $char->getField('auras')], CFG_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']."', modifier:".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() { } // removes completion data (as uploaded by the wowhead client) Just fail silently if someone triggers this manually
protected function checkItemList($val)
{
// expecting item-list
if (preg_match('/\d+(:\d+)*/', $val))
return array_map('intval', explode(':', $val));
return null;
}
protected function checkItemString($val)
{
// expecting item-list
if (preg_match('/\d+(,\d+)*/', $val))
return array_map('intval', explode(',', $val));
return null;
}
}
?>

View File

@@ -29,8 +29,8 @@ class CommunityContent
a3.displayName AS deleteUser, a3.displayName AS deleteUser,
a4.displayName AS responseUser, a4.displayName AS responseUser,
IFNULL(SUM(cr.value), 0) AS rating, IFNULL(SUM(cr.value), 0) AS rating,
SUM(IF (cr.userId = ?d, value, 0)) AS userRating, SUM(IF(cr.userId > 0 AND cr.userId = ?d, cr.value, 0)) AS userRating,
SUM(IF (r.userId = ?d, 1, 0)) AS userReported SUM(IF( r.userId > 0 AND r.userId = ?d, 1, 0)) AS userReported
FROM FROM
?_comments c ?_comments c
JOIN JOIN
@@ -71,7 +71,7 @@ class CommunityContent
JOIN JOIN
?_account a ON c.userId = a.id ?_account a ON c.userId = a.id
LEFT JOIN LEFT JOIN
?_comments_rates cr ON cr.commentId = c.id ?_comments_rates cr ON cr.commentId = c.id AND cr.userId <> 0
LEFT JOIN LEFT JOIN
?_comments c2 ON c.replyTo = c2.id ?_comments c2 ON c.replyTo = c2.id
WHERE WHERE
@@ -121,6 +121,10 @@ class CommunityContent
case TYPE_RACE: $obj = new CharRaceList($cnd); break; case TYPE_RACE: $obj = new CharRaceList($cnd); break;
case TYPE_SKILL: $obj = new SkillList($cnd); break; case TYPE_SKILL: $obj = new SkillList($cnd); break;
case TYPE_CURRENCY: $obj = new CurrencyList($cnd); break; case TYPE_CURRENCY: $obj = new CurrencyList($cnd); break;
case TYPE_EMOTE: $obj = new EmoteList($cnd); break;
case TYPE_ENCHANTMENT: $obj = new EnchantmentList($cnd); break;
case TYPE_SOUND: $obj = new SoundList($cnd); break;
case TYPE_ICON: $obj = new IconList($cnd); break;
default: continue; default: continue;
} }
@@ -173,7 +177,7 @@ class CommunityContent
// limit whitespaces to one at a time // limit whitespaces to one at a time
$c['preview'] = preg_replace('/\s+/', ' ', $c['preview']); $c['preview'] = preg_replace('/\s+/', ' ', $c['preview']);
// limit previews to 100 chars + whatever it takes to make the last word full // limit previews to 100 chars + whatever it takes to make the last word full
if (strlen($c['preview']) > 100) if (mb_strlen($c['preview']) > 100)
{ {
$n = 0; $n = 0;
$b = []; $b = [];
@@ -181,7 +185,7 @@ class CommunityContent
while ($n < 100 && $parts) while ($n < 100 && $parts)
{ {
$_ = array_shift($parts); $_ = array_shift($parts);
$n += strlen($_); $n += mb_strlen($_);
$b[] = $_; $b[] = $_;
} }
@@ -190,7 +194,7 @@ class CommunityContent
} }
else else
{ {
Util::addNote(U_GROUP_STAFF, 'CommunityClass::getCommentPreviews - comment '.$c['id'].' belongs to nonexistant subject'); trigger_error('Comment '.$c['id'].' belongs to nonexistant subject.', E_USER_NOTICE);
unset($comments[$idx]); unset($comments[$idx]);
} }
} }
@@ -238,7 +242,7 @@ class CommunityContent
{ {
$screenshots = DB::Aowow()->select(' $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" 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, FROM ?_screenshots s
LEFT JOIN ?_account a ON s.userIdOwner = a.id LEFT JOIN ?_account a ON s.userIdOwner = a.id
WHERE WHERE
{ s.type = ?d} { s.type = ?d}
@@ -306,14 +310,14 @@ class CommunityContent
public static function getScreenshotPagesForManager($all, &$nFound) public static function getScreenshotPagesForManager($all, &$nFound)
{ {
// i GUESS .. ss_getALL ? everything : unapproved // i GUESS .. ss_getALL ? everything : pending
$nFound = 0; $nFound = 0;
$pages = DB::Aowow()->select(' $pages = DB::Aowow()->select('
SELECT s.`type`, s.`typeId`, count(1) AS "count", MIN(s.`date`) AS "date" SELECT s.`type`, s.`typeId`, count(1) AS "count", MIN(s.`date`) AS "date"
FROM ?_screenshots s FROM ?_screenshots s
{WHERE (s.status & ?d) = 0} {WHERE (s.status & ?d) = 0}
GROUP BY s.`type`, s.`typeId`', GROUP BY s.`type`, s.`typeId`',
$all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED $all ? DBSIMPLE_SKIP : CC_FLAG_APPROVED | CC_FLAG_DELETED
); );
if ($pages) if ($pages)
@@ -330,11 +334,7 @@ class CommunityContent
if (!$ids) if (!$ids)
continue; continue;
$cnd = [['id', $ids]]; $tClass = new Util::$typeClasses[$t](array(['id', $ids], CFG_SQL_LIMIT_NONE));
if ($t == TYPE_WORLDEVENT) // FKIN HOLIDAYS
array_push($cnd, ['holidayId', $ids], 'OR');
$tClass = new Util::$typeClasses[$t]($cnd);
foreach ($pages as &$p) foreach ($pages as &$p)
if ($p['type'] == $t) if ($p['type'] == $t)
if ($tClass->getEntry($p['typeId'])) if ($tClass->getEntry($p['typeId']))
@@ -345,7 +345,7 @@ class CommunityContent
{ {
if (empty($p['name'])) 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']); trigger_error('Screenshot linked to nonexistant type/typeId combination: '.$p['type'].'/'.$p['typeId'], E_USER_NOTICE);
unset($p); unset($p);
} }
else else
@@ -417,22 +417,26 @@ class CommunityContent
return $comments; return $comments;
} }
public static function getVideos($typeOrUser, $typeId = 0, &$nFound = 0) public static function getVideos($typeOrUser = 0, $typeId = 0, &$nFound = 0)
{ {
$videos = DB::Aowow()->selectPage($nFound, " $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 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 FROM ?_videos v
LEFT JOIN ?_account a ON v.userIdOwner = a.id 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", WHERE {v.userIdOwner = ?d AND }{v.type = ? AND }{v.typeId = ? AND }v.status & ?d AND (v.status & ?d) = 0
{ORDER BY ?# DESC}
{LIMIT ?d}",
CC_FLAG_STICKY, CC_FLAG_STICKY,
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP, $typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
CC_FLAG_APPROVED, CC_FLAG_APPROVED,
CC_FLAG_DELETED CC_FLAG_DELETED,
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
!$typeOrUser ? CFG_SQL_LIMIT_SEARCH : DBSIMPLE_SKIP
); );
if ($typeOrUser < 0) // only for user page if ($typeOrUser <= 0) // not for search by type/typeId
{ {
foreach ($videos as $v) foreach ($videos as $v)
self::addSubject($v['type'], $v['typeId']); self::addSubject($v['type'], $v['typeId']);
@@ -443,7 +447,7 @@ class CommunityContent
// format data to meet requirements of the js // format data to meet requirements of the js
foreach ($videos as &$v) foreach ($videos as &$v)
{ {
if ($typeOrUser < 0) // only for user page 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']])) if (!empty(self::$subjCache[$v['type']][$v['typeId']]) && !is_numeric(self::$subjCache[$v['type']][$v['typeId']]))
$v['subject'] = self::$subjCache[$v['type']][$v['typeId']]; $v['subject'] = self::$subjCache[$v['type']][$v['typeId']];
@@ -452,7 +456,7 @@ class CommunityContent
} }
$v['date'] = date(Util::$dateFormatInternal, $v['date']); $v['date'] = date(Util::$dateFormatInternal, $v['date']);
$v['videoType'] = 1; // always youtube $v['videoType'] = 1; // always youtube
if (!$v['sticky']) if (!$v['sticky'])
unset($v['sticky']); unset($v['sticky']);
@@ -464,22 +468,26 @@ class CommunityContent
return $videos; return $videos;
} }
public static function getScreenshots($typeOrUser, $typeId = 0, &$nFound = 0) public static function getScreenshots($typeOrUser = 0, $typeId = 0, &$nFound = 0)
{ {
$screenshots = DB::Aowow()->selectPage($nFound, " $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 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 FROM ?_screenshots s
LEFT JOIN ?_account a ON s.userIdOwner = a.id 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", WHERE {s.userIdOwner = ?d AND }{s.type = ? AND }{s.typeId = ? AND }s.status & ?d AND (s.status & ?d) = 0
{ORDER BY ?# DESC}
{LIMIT ?d}",
CC_FLAG_STICKY, CC_FLAG_STICKY,
$typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP, $typeOrUser < 0 ? -$typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeOrUser : DBSIMPLE_SKIP,
$typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP, $typeOrUser > 0 ? $typeId : DBSIMPLE_SKIP,
CC_FLAG_APPROVED, CC_FLAG_APPROVED,
CC_FLAG_DELETED CC_FLAG_DELETED,
!$typeOrUser ? 'date' : DBSIMPLE_SKIP,
!$typeOrUser ? CFG_SQL_LIMIT_SEARCH : DBSIMPLE_SKIP
); );
if ($typeOrUser < 0) // only for user page if ($typeOrUser <= 0) // not for search by type/typeId
{ {
foreach ($screenshots as $s) foreach ($screenshots as $s)
self::addSubject($s['type'], $s['typeId']); self::addSubject($s['type'], $s['typeId']);
@@ -490,7 +498,7 @@ class CommunityContent
// format data to meet requirements of the js // format data to meet requirements of the js
foreach ($screenshots as &$s) foreach ($screenshots as &$s)
{ {
if ($typeOrUser < 0) // only for user page 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']])) if (!empty(self::$subjCache[$s['type']][$s['typeId']]) && !is_numeric(self::$subjCache[$s['type']][$s['typeId']]))
$s['subject'] = self::$subjCache[$s['type']][$s['typeId']]; $s['subject'] = self::$subjCache[$s['type']][$s['typeId']];

View File

@@ -36,6 +36,13 @@ class DB
if ($options['prefix']) if ($options['prefix'])
$interface->setIdentPrefix($options['prefix']); $interface->setIdentPrefix($options['prefix']);
// disable STRICT_TRANS_TABLES and STRICT_ALL_TABLES off. It prevents usage of implicit default values.
if ($idx == DB_AOWOW)
$interface->query("SET SESSION sql_mode = 'NO_ENGINE_SUBSTITUTION'");
// disable ONLY_FULL_GROUP_BY (Allows for non-aggregated selects in a group-by query)
else
$interface->query("SET SESSION sql_mode = ''");
self::$interfaceCache[$idx] = &$interface; self::$interfaceCache[$idx] = &$interface;
self::$connectionCache[$idx] = true; self::$connectionCache[$idx] = true;
} }

View File

@@ -1,12 +1,14 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('invalid access'); die('illegal access');
/* /*
* Page * Page
*/ */
define('E_AOWOW', E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT));
// TypeIds // TypeIds
define('TYPE_NPC', 1); define('TYPE_NPC', 1);
define('TYPE_OBJECT', 2); define('TYPE_OBJECT', 2);
@@ -24,7 +26,16 @@ define('TYPE_CLASS', 13);
define('TYPE_RACE', 14); define('TYPE_RACE', 14);
define('TYPE_SKILL', 15); define('TYPE_SKILL', 15);
define('TYPE_CURRENCY', 17); define('TYPE_CURRENCY', 17);
define('TYPE_USER', 100); // internal use only define('TYPE_SOUND', 19);
define('TYPE_ICON', 29);
define('TYPE_PROFILE', 100);
// internal types (not published to js)
define('TYPE_GUILD', 101);
define('TYPE_ARENA_TEAM', 102);
define('TYPE_USER', 500);
define('TYPE_EMOTE', 501);
define('TYPE_ENCHANTMENT', 502);
define('TYPE_AREATRIGGER', 503); // not for display, but indexing in ?_spawns-table
define('CACHE_TYPE_NONE', 0); // page will not be cached define('CACHE_TYPE_NONE', 0); // page will not be cached
define('CACHE_TYPE_PAGE', 1); define('CACHE_TYPE_PAGE', 1);
@@ -39,7 +50,7 @@ define('SEARCH_TYPE_REGULAR', 0x10000000);
define('SEARCH_TYPE_OPEN', 0x20000000); define('SEARCH_TYPE_OPEN', 0x20000000);
define('SEARCH_TYPE_JSON', 0x40000000); define('SEARCH_TYPE_JSON', 0x40000000);
define('SEARCH_MASK_OPEN', 0x007DC1FF); // open search define('SEARCH_MASK_OPEN', 0x007DC1FF); // open search
define('SEARCH_MASK_ALL', 0x07FFFFFF); // normal search define('SEARCH_MASK_ALL', 0x0FFFFFFF); // normal search
// Databases // Databases
define('DB_AOWOW', 0); define('DB_AOWOW', 0);
@@ -129,12 +140,13 @@ define('U_GROUP_LOCALIZER', 0x0200);
define('U_GROUP_SALESAGENT', 0x0400); define('U_GROUP_SALESAGENT', 0x0400);
define('U_GROUP_SCREENSHOT', 0x0800); define('U_GROUP_SCREENSHOT', 0x0800);
define('U_GROUP_VIDEO', 0x1000); define('U_GROUP_VIDEO', 0x1000);
// define('U_GROUP_APIONLY, 0x2000); // the heck..? define('U_GROUP_APIONLY', 0x2000); // not used
// define('U_GROUP_PENDING, 0x4000); // would restrict some markup like urls define('U_GROUP_PENDING', 0x4000); // restricts usage of urls in comments
define('U_GROUP_STAFF', (U_GROUP_ADMIN|U_GROUP_EDITOR|U_GROUP_MOD|U_GROUP_BUREAU|U_GROUP_DEV|U_GROUP_BLOGGER|U_GROUP_LOCALIZER|U_GROUP_SALESAGENT)); define('U_GROUP_STAFF', (U_GROUP_ADMIN|U_GROUP_EDITOR|U_GROUP_MOD|U_GROUP_BUREAU|U_GROUP_DEV|U_GROUP_BLOGGER|U_GROUP_LOCALIZER|U_GROUP_SALESAGENT));
define('U_GROUP_EMPLOYEE', (U_GROUP_ADMIN|U_GROUP_BUREAU|U_GROUP_DEV)); define('U_GROUP_EMPLOYEE', (U_GROUP_ADMIN|U_GROUP_BUREAU|U_GROUP_DEV));
define('U_GROUP_GREEN_TEXT', (U_GROUP_MOD|U_GROUP_BUREAU|U_GROUP_DEV)); define('U_GROUP_GREEN_TEXT', (U_GROUP_MOD|U_GROUP_BUREAU|U_GROUP_DEV));
define('U_GROUP_PREMIUMISH', (U_GROUP_PREMIUM|U_GROUP_EDITOR));
define('U_GROUP_MODERATOR', (U_GROUP_ADMIN|U_GROUP_MOD|U_GROUP_BUREAU)); define('U_GROUP_MODERATOR', (U_GROUP_ADMIN|U_GROUP_MOD|U_GROUP_BUREAU));
define('U_GROUP_COMMENTS_MODERATOR', (U_GROUP_MODERATOR|U_GROUP_LOCALIZER)); define('U_GROUP_COMMENTS_MODERATOR', (U_GROUP_MODERATOR|U_GROUP_LOCALIZER));
define('U_GROUP_PREMIUM_PERMISSIONS', (U_GROUP_PREMIUM|U_GROUP_STAFF|U_GROUP_VIP)); define('U_GROUP_PREMIUM_PERMISSIONS', (U_GROUP_PREMIUM|U_GROUP_STAFF|U_GROUP_VIP));
@@ -155,6 +167,8 @@ define('BUTTON_LINKS', 4);
define('BUTTON_FORUM', 5); define('BUTTON_FORUM', 5);
define('BUTTON_TALENT', 6); define('BUTTON_TALENT', 6);
define('BUTTON_EQUIP', 7); define('BUTTON_EQUIP', 7);
define('BUTTON_PLAYLIST', 8);
define('BUTTON_RESYNC', 9);
// generic filter handler // generic filter handler
define('FILTER_CR_BOOLEAN', 1); define('FILTER_CR_BOOLEAN', 1);
@@ -163,6 +177,13 @@ define('FILTER_CR_NUMERIC', 3);
define('FILTER_CR_STRING', 4); define('FILTER_CR_STRING', 4);
define('FILTER_CR_ENUM', 5); define('FILTER_CR_ENUM', 5);
define('FILTER_CR_STAFFFLAG', 6); define('FILTER_CR_STAFFFLAG', 6);
define('FILTER_CR_CALLBACK', 7);
define('FILTER_CR_NYI_PH', 999);
define('FILTER_V_EQUAL', 8);
define('FILTER_V_RANGE', 9);
define('FILTER_V_LIST', 10);
define('FILTER_V_CALLBACK', 11);
define('FILTER_V_REGEX', 12);
define('FILTER_ENUM_ANY', -2323); define('FILTER_ENUM_ANY', -2323);
define('FILTER_ENUM_NONE', -2324); define('FILTER_ENUM_NONE', -2324);
@@ -183,12 +204,20 @@ define('ITEMINFO_MODEL', 0x20);
define('NPCINFO_TAMEABLE', 0x1); define('NPCINFO_TAMEABLE', 0x1);
define('NPCINFO_MODEL', 0x2); define('NPCINFO_MODEL', 0x2);
define('NPCINFO_REP', 0x4);
define('ACHIEVEMENTINFO_PROFILE', 0x1); define('ACHIEVEMENTINFO_PROFILE', 0x1);
define('PROFILEINFO_PROFILE', 0x1);
define('PROFILEINFO_CHARACTER', 0x2);
define('PROFILEINFO_GUILD', 0x10); // like &roster
define('PROFILEINFO_ARENA', 0x20);
define('SPAWNINFO_ZONES', 1); // not a mask, mutually exclusive define('SPAWNINFO_ZONES', 1); // not a mask, mutually exclusive
define('SPAWNINFO_SHORT', 2); define('SPAWNINFO_SHORT', 2);
define('SPAWNINFO_FULL', 3); define('SPAWNINFO_FULL', 3);
define('SPAWNINFO_QUEST', 4);
// Community Content // Community Content
define('CC_FLAG_STICKY', 0x1); define('CC_FLAG_STICKY', 0x1);
@@ -196,6 +225,25 @@ define('CC_FLAG_DELETED', 0x2);
define('CC_FLAG_OUTDATED', 0x4); define('CC_FLAG_OUTDATED', 0x4);
define('CC_FLAG_APPROVED', 0x8); define('CC_FLAG_APPROVED', 0x8);
define('SOUND_TYPE_OGG', 1);
define('SOUND_TYPE_MP3', 2);
define('CONTRIBUTE_NONE', 0x0);
define('CONTRIBUTE_CO', 0x1);
define('CONTRIBUTE_SS', 0x2);
define('CONTRIBUTE_VI', 0x4);
define('CONTRIBUTE_ANY', CONTRIBUTE_CO | CONTRIBUTE_SS | CONTRIBUTE_VI);
define('NUM_ANY', 0);
define('NUM_CAST_INT', 1);
define('NUM_CAST_FLOAT', 2);
define('NUM_REQ_INT', 3);
define('NUM_REQ_FLOAT', 4);
define('STR_LOCALIZED', 0x1);
define('STR_MATCH_EXACT', 0x2);
define('STR_ALLOW_SHORT', 0x4);
/* /*
* Game * Game
*/ */
@@ -204,9 +252,9 @@ define('CC_FLAG_APPROVED', 0x8);
define('CUSTOM_HAS_COMMENT', 0x01000000); define('CUSTOM_HAS_COMMENT', 0x01000000);
define('CUSTOM_HAS_SCREENSHOT', 0x02000000); define('CUSTOM_HAS_SCREENSHOT', 0x02000000);
define('CUSTOM_HAS_VIDEO', 0x04000000); define('CUSTOM_HAS_VIDEO', 0x04000000);
define('CUSTOM_DISABLED', 0x08000000); define('CUSTOM_DISABLED', 0x08000000); // contained in world.disables
define('CUSTOM_SERVERSIDE', 0x10000000); define('CUSTOM_SERVERSIDE', 0x10000000);
define('CUSTOM_UNAVAILABLE', 0x20000000); define('CUSTOM_UNAVAILABLE', 0x20000000); // no source for X or questFlag
define('CUSTOM_EXCLUDE_FOR_LISTVIEW', 0x40000000); // will not show up in search or on listPage (override for staff) define('CUSTOM_EXCLUDE_FOR_LISTVIEW', 0x40000000); // will not show up in search or on listPage (override for staff)
// Custom Flags (per type) // Custom Flags (per type)
@@ -249,6 +297,12 @@ define('QUEST_CU_SKIP_LOG', 0x10);
define('QUEST_CU_AUTO_ACCEPT', 0x20); define('QUEST_CU_AUTO_ACCEPT', 0x20);
define('QUEST_CU_PVP_ENABLED', 0x40); define('QUEST_CU_PVP_ENABLED', 0x40);
define('PROFILER_CU_PUBLISHED', 0x01);
define('PROFILER_CU_PINNED', 0x02);
define('PROFILER_CU_DELETED', 0x04);
define('PROFILER_CU_PROFILE', 0x08);
define('PROFILER_CU_NEEDS_RESYNC', 0x10);
define('MAX_LEVEL', 80); define('MAX_LEVEL', 80);
define('WOW_BUILD', 12340); define('WOW_BUILD', 12340);
@@ -356,6 +410,7 @@ define('SPELL_SCHOOL_NATURE', 3);
define('SPELL_SCHOOL_FROST', 4); define('SPELL_SCHOOL_FROST', 4);
define('SPELL_SCHOOL_SHADOW', 5); define('SPELL_SCHOOL_SHADOW', 5);
define('SPELL_SCHOOL_ARCANE', 6); define('SPELL_SCHOOL_ARCANE', 6);
define('SPELL_MAGIC_SCHOOLS', 0x7E);
define('SPELL_ALL_SCHOOLS', 0x7F); define('SPELL_ALL_SCHOOLS', 0x7F);
// CharacterSlot // CharacterSlot
@@ -710,7 +765,7 @@ define('ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION', 46);
// define('ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT', 51); // define('ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT', 51);
define('ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS', 52); define('ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS', 52);
define('ACHIEVEMENT_CRITERIA_TYPE_HK_RACE', 53); define('ACHIEVEMENT_CRITERIA_TYPE_HK_RACE', 53);
// define('ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE', 54); define('ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE', 54);
// define('ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE', 55); // define('ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE', 55);
// define('ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS', 56); // define('ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS', 56);
define('ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM', 57); define('ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM', 57);
@@ -830,4 +885,31 @@ define('CND_DISTANCE_TO', 35); // distance to targe
define('CND_ALIVE', 36); // target is alive: NULL, NULL, NULL define('CND_ALIVE', 36); // target is alive: NULL, NULL, NULL
define('CND_HP_VAL', 37); // targets absolute health: amount, operator, NULL define('CND_HP_VAL', 37); // targets absolute health: amount, operator, NULL
define('CND_HP_PCT', 38); // targets relative health: amount, operator, NULL define('CND_HP_PCT', 38); // targets relative health: amount, operator, NULL
// profiler queue interactions
define('PR_QUEUE_STATUS_ENDED', 0);
define('PR_QUEUE_STATUS_WAITING', 1);
define('PR_QUEUE_STATUS_WORKING', 2);
define('PR_QUEUE_STATUS_READY', 3);
define('PR_QUEUE_STATUS_ERROR', 4);
define('PR_QUEUE_ERROR_UNK', 0);
define('PR_QUEUE_ERROR_CHAR', 1);
define('PR_QUEUE_ERROR_ARMORY', 2);
// profiler completion manager
define('PR_EXCLUDE_GROUP_UNAVAILABLE', 0x001);
define('PR_EXCLUDE_GROUP_TCG', 0x002);
define('PR_EXCLUDE_GROUP_COLLECTORS_EDITION', 0x004);
define('PR_EXCLUDE_GROUP_PROMOTION', 0x008);
define('PR_EXCLUDE_GROUP_WRONG_REGION', 0x010);
define('PR_EXCLUDE_GROUP_REQ_ALLIANCE', 0x020);
define('PR_EXCLUDE_GROUP_REQ_HORDE', 0x040);
define('PR_EXCLUDE_GROUP_OTHER_FACTION', PR_EXCLUDE_GROUP_REQ_ALLIANCE | PR_EXCLUDE_GROUP_REQ_HORDE);
define('PR_EXCLUDE_GROUP_REQ_FISHING', 0x080);
define('PR_EXCLUDE_GROUP_REQ_ENGINEERING', 0x100);
define('PR_EXCLUDE_GROUP_REQ_TAILORING', 0x200);
define('PR_EXCLUDE_GROUP_WRONG_PROFESSION', PR_EXCLUDE_GROUP_REQ_FISHING | PR_EXCLUDE_GROUP_REQ_ENGINEERING | PR_EXCLUDE_GROUP_REQ_TAILORING);
define('PR_EXCLUDE_GROUP_REQ_CANT_BE_EXALTED', 0x400);
define('PR_EXCLUDE_GROUP_ANY', 0x7FF);
?> ?>

242
includes/game.php Normal file
View File

@@ -0,0 +1,242 @@
<?php
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'
);
private static $combatRatingToItemMod = array( // zero-indexed idx:CR; val:Mod
null, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 30, null, null, null, 37, 44
);
public static $lvlIndepRating = array( // rating doesn't scale with level
ITEM_MOD_MANA, ITEM_MOD_HEALTH, ITEM_MOD_ATTACK_POWER, ITEM_MOD_MANA_REGENERATION, ITEM_MOD_SPELL_POWER,
ITEM_MOD_HEALTH_REGEN, ITEM_MOD_SPELL_PENETRATION, ITEM_MOD_BLOCK_VALUE
);
public static $questClasses = array( // taken from old aowow: 2 & 3 partially point to pointless mini-areas in front of dungeons
-2 => [ 0],
0 => [ 1, 3, 4, 8, 10, 11, 12, 25, 28, 33, 36, 38, 40, 41, 44, 45, 46, 47, 51, 85, 130, 139, 267, 279, 1497, 1519, 1537, 2257, 3430, 3433, 3487, 4080, 4298],
1 => [ 14, 15, 16, 17, 141, 148, 215, 331, 357, 361, 400, 405, 406, 440, 490, 493, 618, 1216, 1377, 1637, 1638, 1657, 3524, 3525, 3557],
/*todo*/ 2 => [ 133, 206, 209, 491, 717, 718, 719, 722, 796, 978, 1196, 1337, 1417, 1581, 1583, 1584, 1941, 2017, 2057, 2100, 2366, 2367, 2437, 2557, 3477, 3562, 3713, 3714, 3715, 3716, 3717, 3789, 3790, 3791, 3792, 3845, 3846, 3847, 3849, 3905, 4095, 4100, 4120, 4196, 4228, 4264, 4272, 4375, 4415, 4494, 4723],
/*todo*/ 3 => [ 1977, 2159, 2562, 2677, 2717, 3428, 3429, 3456, 3606, 3805, 3836, 3840, 3842, 4273, 4500, 4722, 4812],
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], // Skettis is no parent
9 => [-1006, -1005, -1003, -1002, -1001, -376, -375, -374, -370, -369, -366, -364, -284, -41, -22], // 22: seasonal, 284: special => not in the actual menu
10 => [ 65, 66, 67, 210, 394, 495, 3537, 3711, 4024, 4197, 4395, 4742] // Coldara is no parent
);
/* 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 $trainerTemplates = array( // TYPE => Id => templateList
TYPE_CLASS => array(
1 => [-200001, -200002], // Warrior
2 => [-200003, -200004, -200020, -200021], // Paladin
3 => [-200013, -200014], // Hunter
4 => [-200015, -200016], // Rogue
5 => [-200011, -200012], // Priest
6 => [-200019], // DK
7 => [-200017, -200018], // Shaman (HighlevelAlly Id missing..?)
8 => [-200007, -200008], // Mage
9 => [-200009, -200010], // Warlock
11 => [-200005, -200006] // Druid
),
TYPE_SKILL => array(
171 => [-201001, -201002, -201003], // Alchemy
164 => [-201004, -201005, -201006, -201007, -201008],// Blacksmithing
333 => [-201009, -201010, -201011], // Enchanting
202 => [-201012, -201013, -201014, -201015, -201016, -201017], // Engineering
182 => [-201018, -201019, -201020], // Herbalism
773 => [-201021, -201022, -201023], // Inscription
755 => [-201024, -201025, -201026], // Jewelcrafting
165 => [-201027, -201028, -201029, -201030, -201031, -201032], // Leatherworking
186 => [-201033, -201034, -201035], // Mining
393 => [-201036, -201037, -201038], // Skinning
197 => [-201039, -201040, -201041, -201042], // Tailoring
356 => [-202001, -202002, -202003], // Fishing
185 => [-202004, -202005, -202006], // Cooking
129 => [-202007, -202008, -202009], // First Aid
762 => [-202010, -202011, -202012] // Riding
)
);
public static $sockets = array( // jsStyle Strings
'meta', 'red', 'yellow', 'blue'
);
// 'replicates' $WH.g_statToJson
public static $itemMods = array( // zero-indexed; "mastrtng": unused mastery; _[a-z] => taken mods..
'dmg', 'mana', 'health', 'agi', 'str', 'int', 'spi',
'sta', 'energy', 'rage', 'focus', 'runicpwr', 'defrtng', 'dodgertng',
'parryrtng', 'blockrtng', 'mlehitrtng', 'rgdhitrtng', 'splhitrtng', 'mlecritstrkrtng', 'rgdcritstrkrtng',
'splcritstrkrtng', '_mlehitrtng', '_rgdhitrtng', '_splhitrtng', '_mlecritstrkrtng', '_rgdcritstrkrtng', '_splcritstrkrtng',
'mlehastertng', 'rgdhastertng', 'splhastertng', 'hitrtng', 'critstrkrtng', '_hitrtng', '_critstrkrtng',
'resirtng', 'hastertng', 'exprtng', 'atkpwr', 'rgdatkpwr', 'feratkpwr', 'splheal',
'spldmg', 'manargn', 'armorpenrtng', 'splpwr', 'healthrgn', 'splpen', 'block', // ITEM_MOD_BLOCK_VALUE
'mastrtng', 'armor', 'firres', 'frores', 'holres', 'shares', 'natres',
'arcres', 'firsplpwr', 'frosplpwr', 'holsplpwr', 'shasplpwr', 'natsplpwr', 'arcsplpwr'
);
public static $class2SpellFamily = array(
// null Warrior Paladin Hunter Rogue Priest DK Shaman Mage Warlock null Druid
null, 4, 10, 9, 8, 6, 15, 11, 3, 5, null, 7
);
public static function itemModByRatingMask($mask)
{
if (($mask & 0x1C000) == 0x1C000) // special case resilience
return ITEM_MOD_RESILIENCE_RATING;
if (($mask & 0x00E0) == 0x00E0) // hit rating - all subcats (mle, rgd, spl)
return ITEM_MOD_HIT_RATING;
if (($mask & 0x0700) == 0x0700) // crit rating - all subcats (mle, rgd, spl)
return ITEM_MOD_CRIT_RATING;
for ($j = 0; $j < count(self::$combatRatingToItemMod); $j++)
{
if (!self::$combatRatingToItemMod[$j])
continue;
if (!($mask & (1 << $j)))
continue;
return self::$combatRatingToItemMod[$j];
}
return 0;
}
public static function sideByRaceMask($race)
{
// Any
if (!$race || ($race & RACE_MASK_ALL) == RACE_MASK_ALL)
return SIDE_BOTH;
// Horde
if ($race & RACE_MASK_HORDE && !($race & RACE_MASK_ALLIANCE))
return SIDE_HORDE;
// Alliance
if ($race & RACE_MASK_ALLIANCE && !($race & RACE_MASK_HORDE))
return SIDE_ALLIANCE;
return SIDE_BOTH;
}
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) != '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
array_walk($data, function (&$v, $k) {
$v = intVal($v);
});
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', User::$localeId, User::$localeString, $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;
}
}
?>

View File

@@ -9,10 +9,18 @@ if (file_exists('config/config.php'))
else else
$AoWoWconf = []; $AoWoWconf = [];
mb_internal_encoding('UTF-8');
define('OS_WIN', substr(PHP_OS, 0, 3) == 'WIN');
require_once 'includes/defines.php'; 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/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/utilities.php'; // helper functions
require_once 'includes/ajaxHandler.class.php'; // handles ajax and jsonp requests require_once 'includes/game.php'; // game related data & functions
require_once 'includes/profiler.class.php';
require_once 'includes/user.class.php'; require_once 'includes/user.class.php';
require_once 'includes/markup.class.php'; // manipulate markup text require_once 'includes/markup.class.php'; // manipulate markup text
require_once 'includes/database.class.php'; // wrap DBSimple require_once 'includes/database.class.php'; // wrap DBSimple
@@ -24,7 +32,7 @@ require_once 'pages/genericPage.class.php';
// autoload List-classes, associated filters and pages // autoload List-classes, associated filters and pages
spl_autoload_register(function ($class) { spl_autoload_register(function ($class) {
$class = strtolower(str_replace('Filter', '', $class)); $class = strtolower(str_replace('ListFilter', 'List', $class));
if (class_exists($class)) // already registered if (class_exists($class)) // already registered
return; return;
@@ -32,18 +40,37 @@ spl_autoload_register(function ($class) {
if (preg_match('/[^\w]/i', $class)) // name should contain only letters if (preg_match('/[^\w]/i', $class)) // name should contain only letters
return; return;
if (strpos($class, 'list')) if (stripos($class, 'list'))
{ {
if (!class_exists('BaseType')) require_once 'includes/basetype.class.php';
require_once 'includes/types/basetype.class.php';
if (file_exists('includes/types/'.strtr($class, ['list' => '']).'.class.php')) $cl = strtr($class, ['list' => '']);
require_once 'includes/types/'.strtr($class, ['list' => '']).'.class.php'; if ($cl == 'remoteprofile' || $cl == 'localprofile')
$cl = 'profile';
if ($cl == 'remotearenateam' || $cl == 'localarenateam')
$cl = 'arenateam';
if ($cl == 'remoteguild' || $cl == 'localguild')
$cl = 'guild';
if (file_exists('includes/types/'.$cl.'.class.php'))
require_once 'includes/types/'.$cl.'.class.php';
else
throw new Exception('could not register type class: '.$cl);
return; return;
} }
else if (stripos($class, 'ajax') === 0)
{
require_once 'includes/ajaxHandler.class.php'; // handles ajax and jsonp requests
if (file_exists('pages/'.strtr($class, ['page' => '']).'.php')) if (file_exists('includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php'))
require_once 'includes/ajaxHandler/'.strtr($class, ['ajax' => '']).'.class.php';
else
throw new Exception('could not register ajaxHandler class: '.$class);
return;
}
else if (file_exists('pages/'.strtr($class, ['page' => '']).'.php'))
require_once 'pages/'.strtr($class, ['page' => '']).'.php'; require_once 'pages/'.strtr($class, ['page' => '']).'.php';
}); });
@@ -68,12 +95,15 @@ if (!empty($AoWoWconf['characters']))
$sets = DB::isConnectable(DB_AOWOW) ? DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value`, `flags` FROM ?_config') : []; $sets = DB::isConnectable(DB_AOWOW) ? DB::Aowow()->select('SELECT `key` AS ARRAY_KEY, `value`, `flags` FROM ?_config') : [];
foreach ($sets as $k => $v) foreach ($sets as $k => $v)
{ {
// this should not have been possible
if (!strlen($v['value']))
continue;
$php = $v['flags'] & CON_FLAG_PHP; $php = $v['flags'] & CON_FLAG_PHP;
// this should not have been possible
if (!strlen($v['value']) && !($v['flags'] & CON_FLAG_TYPE_STRING) && !$php)
{
trigger_error('Aowow config value CFG_'.strtoupper($k).' is empty - config will not be used!', E_USER_ERROR);
continue;
}
if ($v['flags'] & CON_FLAG_TYPE_INT) if ($v['flags'] & CON_FLAG_TYPE_INT)
$val = intVal($v['value']); $val = intVal($v['value']);
else if ($v['flags'] & CON_FLAG_TYPE_FLOAT) else if ($v['flags'] & CON_FLAG_TYPE_FLOAT)
@@ -81,10 +111,15 @@ foreach ($sets as $k => $v)
else if ($v['flags'] & CON_FLAG_TYPE_BOOL) else if ($v['flags'] & CON_FLAG_TYPE_BOOL)
$val = (bool)$v['value']; $val = (bool)$v['value'];
else if ($v['flags'] & CON_FLAG_TYPE_STRING) else if ($v['flags'] & CON_FLAG_TYPE_STRING)
$val = preg_replace('/[^\p{L}0-9~\s_\-\'\/\.:,]/ui', '', $v['value']); $val = preg_replace("/[\p{C}]/ui", '', $v['value']);
else else if ($php)
{ {
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!'); trigger_error('PHP config value '.strtolower($k).' has no type set - config will not be used!', E_USER_ERROR);
continue;
}
else // if (!$php)
{
trigger_error('Aowow config value CFG_'.strtoupper($k).' has no type set - value forced to 0!', E_USER_ERROR);
$val = 0; $val = 0;
} }
@@ -95,11 +130,13 @@ foreach ($sets as $k => $v)
} }
// handle occuring errors // handle non-fatal errors and notices
error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? (E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)) : 0); error_reporting(!empty($AoWoWconf['aowow']) && CFG_DEBUG ? E_AOWOW : 0);
$errHandled = false; set_error_handler(function($errNo, $errStr, $errFile, $errLine)
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 $errName = 'unknown error'; // errors not in this list can not be handled by set_error_handler (as per documentation) or are ignored
$uGroup = U_GROUP_EMPLOYEE;
if ($errNo == E_WARNING) // 0x0002 if ($errNo == E_WARNING) // 0x0002
$errName = 'E_WARNING'; $errName = 'E_WARNING';
else if ($errNo == E_PARSE) // 0x0004 else if ($errNo == E_PARSE) // 0x0004
@@ -111,29 +148,58 @@ set_error_handler(function($errNo, $errStr, $errFile, $errLine) use (&$errHandle
else if ($errNo == E_USER_WARNING) // 0x0200 else if ($errNo == E_USER_WARNING) // 0x0200
$errName = 'E_USER_WARNING'; $errName = 'E_USER_WARNING';
else if ($errNo == E_USER_NOTICE) // 0x0400 else if ($errNo == E_USER_NOTICE) // 0x0400
{
$errName = 'E_USER_NOTICE'; $errName = 'E_USER_NOTICE';
$uGroup = U_GROUP_STAFF;
}
else if ($errNo == E_RECOVERABLE_ERROR) // 0x1000 else if ($errNo == E_RECOVERABLE_ERROR) // 0x1000
$errName = 'E_RECOVERABLE_ERROR'; $errName = 'E_RECOVERABLE_ERROR';
if (User::isInGroup(U_GROUP_STAFF)) Util::addNote($uGroup, $errName.' - '.$errStr.' @ '.$errFile. ':'.$errLine);
{
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)) 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()', 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 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); return true;
}, E_ALL & ~(E_DEPRECATED | E_USER_DEPRECATED | E_STRICT)); }, E_AOWOW);
// handle exceptions
set_exception_handler(function ($ex)
{
Util::addNote(U_GROUP_EMPLOYEE, 'Exception - '.$ex->getMessage().' @ '.$ex->getFile(). ':'.$ex->getLine()."\n".$ex->getTraceAsString());
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, $ex->getCode(), $ex->getFile(), $ex->getLine(), CLI ? 'CLI' : $_SERVER['QUERY_STRING'], User::$groups, $ex->getMessage()
);
if (!CLI)
(new GenericPage(null))->error();
else
echo 'Exception - '.$ex->getMessage()."\n ".$ex->getFile(). '('.$ex->getLine().")\n".$ex->getTraceAsString()."\n";
});
// handle fatal errors
register_shutdown_function(function()
{
if (($e = error_get_last()) && $e['type'] & (E_ERROR | E_COMPILE_ERROR | E_CORE_ERROR))
{
Util::addNote(U_GROUP_EMPLOYEE, 'Fatal Error - '.$e['message'].' @ '.$e['file']. ':'.$e['line']);
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, $e['type'], $e['file'], $e['line'], CLI ? 'CLI' : $_SERVER['QUERY_STRING'], User::$groups, $e['message']
);
if (CLI)
echo 'Fatal Error - '.$e['message'].' @ '.$e['file']. ':'.$e['line']."\n";
// cant generate a page for web view :(
die();
}
});
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($AoWoWconf['aowow']) && CFG_FORCE_SSL); $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') || (!empty($AoWoWconf['aowow']) && CFG_FORCE_SSL);
if (defined('CFG_STATIC_HOST')) // points js to images & scripts if (defined('CFG_STATIC_HOST')) // points js to images & scripts
@@ -145,30 +211,19 @@ if (defined('CFG_SITE_HOST')) // points js to exec
if (!CLI) if (!CLI)
{ {
if (!defined('CFG_SITE_HOST') || !defined('CFG_STATIC_HOST'))
die('error: SITE_HOST or STATIC_HOST not configured');
// Setup Session // Setup Session
if (CFG_SESSION_CACHE_DIR && Util::writeDir(CFG_SESSION_CACHE_DIR))
session_save_path(getcwd().'/'.CFG_SESSION_CACHE_DIR);
session_set_cookie_params(15 * YEAR, '/', '', $secure, true); session_set_cookie_params(15 * YEAR, '/', '', $secure, true);
session_cache_limiter('private'); session_cache_limiter('private');
session_start(); session_start();
if (!empty($AoWoWconf['aowow']) && User::init()) if (!empty($AoWoWconf['aowow']) && User::init())
User::save(); // save user-variables in session User::save(); // save user-variables in session
// todo: (low) - move to setup web-interface (when it begins its existance)
if (!defined('CFG_SITE_HOST') || !defined('CFG_STATIC_HOST'))
{
$host = substr($_SERVER['SERVER_NAME'].strtr($_SERVER['SCRIPT_NAME'], ['index.php' => '']), 0, -1);
define('HOST_URL', ($secure ? 'https://' : 'http://').$host);
define('STATIC_URL', ($secure ? 'https://' : 'http://').$host.'/static');
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..?) // hard-override locale for this call (should this be here..?)
// all strings attached.. // all strings attached..
if (!empty($AoWoWconf['aowow'])) if (!empty($AoWoWconf['aowow']))

View File

@@ -30,7 +30,7 @@ class DbSimple_Mysqli extends DbSimple_Database
* constructor(string $dsn) * constructor(string $dsn)
* Connect to MySQL server. * Connect to MySQL server.
*/ */
function DbSimple_Mysqli($dsn) function __construct($dsn)
{ {
if (!is_callable("mysqli_connect")) if (!is_callable("mysqli_connect"))
@@ -178,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 (mysql_error()) return $this->_setDbError($this->_lastQuery); if (mysqli_error($this->link)) return $this->_setDbError($this->_lastQuery);
if ($row === false) return null; if ($row === false) return null;
return $row; return $row;
} }

View File

@@ -1,7 +1,7 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('invalid access'); die('illegal access');
/* from TC wiki /* from TC wiki
@@ -63,7 +63,7 @@ class Loot
$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); return json_encode($stack, JSON_NUMERIC_CHECK); // do not replace with Util::toJSON !
} }
private function storeJSGlobals($data) private function storeJSGlobals($data)
@@ -175,7 +175,7 @@ class Loot
} }
else // shouldn't have happened else // shouldn't have happened
{ {
Util::addNote(U_GROUP_EMPLOYEE, 'Loot::getByContainerRecursive: unhandled case in calculating chance for item '.$entry['Item'].'!'); trigger_error('Unhandled case in calculating chance for item '.$entry['Item'].'!', E_USER_WARNING);
continue; continue;
} }
@@ -189,7 +189,7 @@ class Loot
$sum = 0; $sum = 0;
else if ($sum >= 100.01) else if ($sum >= 100.01)
{ {
Util::addNote(U_GROUP_EMPLOYEE, 'Loot::getByContainerRecursive: entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!'); trigger_error('Loot entry '.$lootId.' / group '.$k.' has a total chance of '.number_format($sum, 2).'%. Some items cannot drop!', E_USER_WARNING);
$sum = 100; $sum = 100;
} }
@@ -312,10 +312,10 @@ 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;
@@ -378,13 +378,13 @@ class Loot
{ {
// check for possible database inconsistencies // check for possible database inconsistencies
if (!$ref['chance'] && !$ref['isGrouped']) if (!$ref['chance'] && !$ref['isGrouped'])
Util::addNote(U_GROUP_EMPLOYEE, 'Loot by Item: ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!'); trigger_error('Loot by Item: Ungrouped Item/Ref '.$ref['item'].' has 0% chance assigned!', E_USER_WARNING);
if ($ref['isGrouped'] && $ref['sumChance'] > 100) 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!'); 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']) 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%!'); 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; $chance = abs($ref['chance'] ?: (100 - $ref['sumChance']) / $ref['nZeroItems']) / 100;
@@ -505,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';
} }
@@ -530,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 entry FROM achievement_reward WHERE item = ?d{ OR mailTemplate IN (?a)}', $this->entry, $ids ?: DBSIMPLE_SKIP)) if ($ar = DB::World()->selectCol('SELECT ID FROM achievement_reward WHERE ItemID = ?d{ OR MailTemplateID 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);
@@ -563,7 +563,7 @@ 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'])) if ($srcObj->hasSetFields(['reagent1']))
$tabsFinal[16][6][] = 'reagents'; $tabsFinal[16][6][] = 'reagents';
@@ -601,14 +601,33 @@ class Loot
$tabId = abs($tabId); // general case (skinning) $tabId = abs($tabId); // general case (skinning)
$tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]); $tabsFinal[$tabId][1][] = array_merge($srcData[$srcObj->id], $result[$srcObj->getField($field)]);
$tabsFinal[$tabId][4][] = 'Listview.extraCols.percent'; $tabsFinal[$tabId][4][] = '$Listview.extraCols.percent';
} }
} }
break; break;
} }
} }
$this->results = $tabsFinal; foreach ($tabsFinal as $tabId => $data)
{
$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] = [$data[0], $tabData];
}
return true; return true;
} }

View File

@@ -1,7 +1,7 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('invalid access'); die('illegal access');
/* /*
this is just a skeleton for now this is just a skeleton for now
@@ -14,6 +14,8 @@ class Markup
private $text = ''; private $text = '';
private $jsGlobals = []; private $jsGlobals = [];
private static $dbTagPattern = '/(?<!\\\\)\[(npc|object|item|itemset|quest|spell|zone|faction|pet|achievement|statistic|title|event|class|race|skill|currency|emote|enchantment|money|sound|icondb)=(-?\d+)[^\]]*\]/i';
public function __construct($text) public function __construct($text)
{ {
$this->text = $text; $this->text = $text;
@@ -21,14 +23,37 @@ class Markup
public function parseGlobalsFromText(&$jsg = []) 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)) if (preg_match_all(self::$dbTagPattern, $this->text, $matches, PREG_SET_ORDER))
{ {
foreach ($matches as $match) foreach ($matches as $match)
{ {
if ($match[1] == 'statistic') if ($match[1] == 'statistic')
$match[1] = 'achievement'; $match[1] = 'achievement';
else if ($match[1] == 'icondb')
$match[1] = 'icon';
else 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)
$this->jsGlobals[TYPE_ITEM][$sm[$i]] = $sm[$i];
}
}
if ($type = array_search($match[1], Util::$typeStrings)) 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)
$this->jsGlobals[TYPE_CURRENCY][$sm[$i]] = $sm[$i];
}
}
}
else if ($type = array_search($match[1], Util::$typeStrings))
$this->jsGlobals[$type][$match[2]] = $match[2]; $this->jsGlobals[$type][$match[2]] = $match[2];
} }
} }
@@ -38,6 +63,79 @@ class Markup
return $this->jsGlobals; return $this->jsGlobals;
} }
public function stripTags($globals = [])
{
// since this is an article the db-tags should already be parsed
$text = preg_replace_callback(self::$dbTagPattern, function ($match) use ($globals) {
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($globals[TYPE_ITEM][1][$sm[$i]]))
$moneys[] = $globals[TYPE_ITEM][1][$sm[$i]]['name'];
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($globals[TYPE_CURRENCY][1][$sm[$i]]))
$moneys[] = $globals[TYPE_CURRENCY][1][$sm[$i]]['name'];
else
$moneys[] = Util::ucFirst(Lang::game('curency')).' #'.$sm[$i];
}
}
}
return Lang::concat($moneys);
}
if ($type = array_search($match[1], Util::$typeStrings))
{
if (!empty($globals[$type][1][$match[2]]))
return $globals[$type][1][$match[2]]['name'];
else
return Util::ucFirst(Lang::game($match[1])).' #'.$match[2];
}
trigger_error('Markup::stripTags() - encountered unhandled db-tag: '.var_export($match));
return '';
}, $this->text);
$text = str_replace('[br]', "\n", $text);
$stripped = '';
$inTag = false;
for ($i = 0; $i < strlen($text); $i++)
{
if ($text[$i] == '[')
$inTag = true;
if (!$inTag)
$stripped .= $text[$i];
if ($text[$i] == ']')
$inTag = false;
}
return $stripped;
}
public function fromHtml() public function fromHtml()
{ {
} }

828
includes/profiler.class.php Normal file
View File

@@ -0,0 +1,828 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class Profiler
{
const PID_FILE = 'config/pr-queue-pid';
const CHAR_GMFLAGS = 0x1 | 0x8 | 0x10 | 0x20; // PLAYER_EXTRA_ :: GM_ON | TAXICHEAT | GM_INVISIBLE | GM_CHAT
private static $realms = [];
public static $slot2InvType = array(
1 => [INVTYPE_HEAD], // head
2 => [INVTYPE_NECK], // neck
3 => [INVTYPE_SHOULDERS], // shoulder
4 => [INVTYPE_BODY], // shirt
5 => [INVTYPE_CHEST, INVTYPE_ROBE], // chest
6 => [INVTYPE_WAIST], // waist
7 => [INVTYPE_LEGS], // legs
8 => [INVTYPE_FEET], // feet
9 => [INVTYPE_WRISTS], // wrists
10 => [INVTYPE_HANDS], // hands
11 => [INVTYPE_FINGER], // finger1
12 => [INVTYPE_FINGER], // finger2
13 => [INVTYPE_TRINKET], // trinket1
14 => [INVTYPE_TRINKET], // trinket2
15 => [INVTYPE_CLOAK], // chest
16 => [INVTYPE_WEAPONMAINHAND, INVTYPE_WEAPON, INVTYPE_2HWEAPON], // mainhand
17 => [INVTYPE_WEAPONOFFHAND, INVTYPE_WEAPON, INVTYPE_HOLDABLE, INVTYPE_SHIELD], // offhand
18 => [INVTYPE_RANGED, INVTYPE_THROWN, INVTYPE_RELIC], // ranged + relic
19 => [INVTYPE_TABARD], // tabard
);
public static $raidProgression = array( // statisticAchievement => relevantCriterium
1098 => 3271, // Onyxia's Lair 10
1756 => 13345, // Onyxia's Lair 25
4031 => 12230, 4034 => 12234, 4038 => 12238, 4042 => 12242, 4046 => 12246, // Trial of the Crusader 25 nh
4029 => 12231, 4035 => 12235, 4039 => 12239, 4043 => 12243, 4047 => 12247, // Trial of the Crusader 25 hc
4030 => 12229, 4033 => 12233, 4037 => 12237, 4041 => 12241, 4045 => 12245, // Trial of the Crusader 10 hc
4028 => 12228, 4032 => 12232, 4036 => 12236, 4040 => 12240, 4044 => 12244, // Trial of the Crusader 10 nh
4642 => 13091, 4656 => 13106, 4661 => 13111, 4664 => 13114, 4667 => 13117, 4670 => 13120, 4673 => 13123, 4676 => 13126, 4679 => 13129, 4682 => 13132, 4685 => 13135, 4688 => 13138, // Icecrown Citadel 25 hc
4641 => 13092, 4655 => 13105, 4660 => 13109, 4663 => 13112, 4666 => 13115, 4669 => 13118, 4672 => 13121, 4675 => 13124, 4678 => 13127, 4681 => 13130, 4683 => 13133, 4687 => 13136, // Icecrown Citadel 25 nh
4640 => 13090, 4654 => 13104, 4659 => 13110, 4662 => 13113, 4665 => 13116, 4668 => 13119, 4671 => 13122, 4674 => 13125, 4677 => 13128, 4680 => 13131, 4684 => 13134, 4686 => 13137, // Icecrown Citadel 10 hc
4639 => 13089, 4643 => 13093, 4644 => 13094, 4645 => 13095, 4646 => 13096, 4647 => 13097, 4648 => 13098, 4649 => 13099, 4650 => 13100, 4651 => 13101, 4652 => 13102, 4653 => 13103, // Icecrown Citadel 10 nh
// 4823 => 13467, // Ruby Sanctum 25 hc
// 4820 => 13465, // Ruby Sanctum 25 nh
// 4822 => 13468, // Ruby Sanctum 10 hc
// 4821 => 13466, // Ruby Sanctum 10 nh
);
public static function getBuyoutForItem($itemId)
{
if (!$itemId)
return 0;
// try, when having filled char-DB at hand
// return DB::Characters()->selectCell('SELECT SUM(a.buyoutprice) / SUM(ii.count) FROM auctionhouse a JOIN item_instance ii ON ii.guid = a.itemguid WHERE ii.itemEntry = ?d', $itemId);
return 0;
}
public static function queueStart(&$msg = '')
{
$queuePID = self::queueStatus();
if ($queuePID)
{
$msg = 'queue already running';
return true;
}
if (OS_WIN) // here be gremlins! .. suggested was "start /B php prQueue" as background process. but that closes itself
pclose(popen('start php prQueue --log=cache/profiling.log', 'r'));
else
exec('php prQueue --log=cache/profiling.log > /dev/null 2>/dev/null &');
usleep(500000);
if (self::queueStatus())
return true;
else
{
$msg = 'failed to start queue';
return false;
}
}
public static function queueStatus()
{
if (!file_exists(self::PID_FILE))
return 0;
$pid = file_get_contents(self::PID_FILE);
$cmd = OS_WIN ? 'tasklist /NH /FO CSV /FI "PID eq %d"' : 'ps --no-headers p %d';
exec(sprintf($cmd, $pid), $out);
if ($out && stripos($out[0], $pid) !== false)
return $pid;
// have pidFile but no process with this pid
self::queueFree();
return 0;
}
public static function queueLock($pid)
{
$queuePID = self::queueStatus();
if ($queuePID && $queuePID != $pid)
{
trigger_error('pSync - another queue with PID #'.$queuePID.' is already running', E_USER_ERROR);
CLI::write('Profiler::queueLock() - another queue with PID #'.$queuePID.' is already runnung', CLI::LOG_ERROR);
return false;
}
// no queue running; create or overwrite pidFile
$ok = false;
if ($fh = fopen(self::PID_FILE, 'w'))
{
if (fwrite($fh, $pid))
$ok = true;
fclose($fh);
}
return $ok;
}
public static function queueFree()
{
unlink(self::PID_FILE);
}
public static function urlize($str, $allowLocales = false, $profile = false)
{
$search = ['<', '>', ' / ', "'"];
$replace = ['&lt;', '&gt;', '-', '' ];
$str = str_replace($search, $replace, $str);
if ($profile)
{
$str = str_replace(['(', ')'], ['', ''], $str);
$accents = array(
"ß" => "ss",
"á" => "a", "ä" => "a", "à" => "a", "â" => "a",
"è" => "e", "ê" => "e", "é" => "e", "ë" => "e",
"í" => "i", "î" => "i", "ì" => "i", "ï" => "i",
"ñ" => "n",
"ò" => "o", "ó" => "o", "ö" => "o", "ô" => "o",
"ú" => "u", "ü" => "u", "û" => "u", "ù" => "u",
"œ" => "oe",
"Á" => "A", "Ä" => "A", "À" => "A", "Â" => "A",
"È" => "E", "Ê" => "E", "É" => "E", "Ë" => "E",
"Í" => "I", "Î" => "I", "Ì" => "I", "Ï" => "I",
"Ñ" => "N",
"Ò" => "O", "Ó" => "O", "Ö" => "O", "Ô" => "O",
"Ú" => "U", "Ü" => "U", "Û" => "U", "Ù" => "U",
"Œ" => "Oe"
);
$str = strtr($str, $accents);
}
$str = trim($str);
if ($allowLocales)
$str = str_replace(' ', '-', $str);
else
$str = preg_replace('/[^a-z0-9]/i', '-', $str);
$str = str_replace('--', '-', $str);
$str = str_replace('--', '-', $str);
$str = rtrim($str, '-');
$str = strtolower($str);
return $str;
}
public static function getRealms()
{
if (DB::isConnectable(DB_AUTH) && !self::$realms)
{
self::$realms = DB::Auth()->select('SELECT id AS ARRAY_KEY, name, IF(timezone IN (8, 9, 10, 11, 12), "eu", "us") AS region FROM realmlist WHERE allowedSecurityLevel = 0 AND gamebuild = ?d', WOW_BUILD);
foreach (self::$realms as $rId => $rData)
{
if (DB::isConnectable(DB_CHARACTERS . $rId))
continue;
// realm in db but no connection info set
unset(self::$realms[$rId]);
}
}
return self::$realms;
}
private static function queueInsert($realmId, $guid, $type, $localId)
{
if ($rData = DB::Aowow()->selectRow('SELECT requestTime AS time, status FROM ?_profiler_sync WHERE realm = ?d AND realmGUID = ?d AND `type` = ?d AND typeId = ?d AND status <> ?d', $realmId, $guid, $type, $localId, PR_QUEUE_STATUS_WORKING))
{
// not on already scheduled - recalc time and set status to PR_QUEUE_STATUS_WAITING
if ($rData['status'] != PR_QUEUE_STATUS_WAITING)
{
$newTime = CFG_DEBUG ? time() : max($rData['time'] + CFG_PROFILER_RESYNC_DELAY, time());
DB::Aowow()->query('UPDATE ?_profiler_sync SET requestTime = ?d, status = ?d, errorCode = 0 WHERE realm = ?d AND realmGUID = ?d AND `type` = ?d AND typeId = ?d', $newTime, PR_QUEUE_STATUS_WAITING, $realmId, $guid, $type, $localId);
}
}
else
DB::Aowow()->query('REPLACE INTO ?_profiler_sync (realm, realmGUID, `type`, typeId, requestTime, status, errorCode) VALUES (?d, ?d, ?d, ?d, UNIX_TIMESTAMP(), ?d, 0)', $realmId, $guid, $type, $localId, PR_QUEUE_STATUS_WAITING);
}
public static function scheduleResync($type, $realmId, $guid)
{
$newId = 0;
switch ($type)
{
case TYPE_PROFILE:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, TYPE_PROFILE, $newId);
break;
case TYPE_GUILD:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, TYPE_GUILD, $newId);
break;
case TYPE_ARENA_TEAM:
if ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $guid))
self::queueInsert($realmId, $guid, TYPE_ARENA_TEAM, $newId);
break;
default:
trigger_error('scheduling resync for unknown type #'.$type.' omiting..', E_USER_WARNING);
return 0;
}
if (!$newId)
trigger_error('Profiler::scheduleResync() - tried to resync type #'.$type.' guid #'.$guid.' from realm #'.$realmId.' without preloaded data', E_USER_ERROR);
else if (!self::queueStart($msg))
trigger_error('Profiler::scheduleResync() - '.$msg, E_USER_ERROR);
return $newId;
}
public static function resyncStatus($type, array $subjectGUIDs)
{
$response = [CFG_PROFILER_QUEUE ? 2 : 0]; // in theory you could have multiple queues; used as divisor for: (15 / x) + 2
if (!$subjectGUIDs)
$response[] = [PR_QUEUE_STATUS_ENDED, 0, 0, PR_QUEUE_ERROR_CHAR];
else
{
// error out all profiles with status WORKING, that are older than 60sec
DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE status = ?d AND requestTime < ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_UNK, PR_QUEUE_STATUS_WORKING, time() - MINUTE);
$subjectStatus = DB::Aowow()->select('SELECT typeId AS ARRAY_KEY, status, realm FROM ?_profiler_sync WHERE `type` = ?d AND typeId IN (?a)', $type, $subjectGUIDs);
$queue = DB::Aowow()->selectCol('SELECT CONCAT(type, ":", typeId) FROM ?_profiler_sync WHERE status = ?d AND requestTime < UNIX_TIMESTAMP() ORDER BY requestTime ASC', PR_QUEUE_STATUS_WAITING);
foreach ($subjectGUIDs as $guid)
{
if (empty($subjectStatus[$guid])) // whelp, thats some error..
$response[] = [PR_QUEUE_STATUS_ERROR, 0, 0, PR_QUEUE_ERROR_UNK];
else if ($subjectStatus[$guid]['status'] == PR_QUEUE_STATUS_ERROR)
$response[] = [PR_QUEUE_STATUS_ERROR, 0, 0, $subjectStatus[$guid]['errCode']];
else
$response[] = array(
$subjectStatus[$guid]['status'],
$subjectStatus[$guid]['status'] != PR_QUEUE_STATUS_READY ? CFG_PROFILER_RESYNC_PING : 0,
array_search($type.':'.$guid, $queue) + 1,
0,
1 // nResycTries - unsure about this one
);
}
}
return $response;
}
public static function getCharFromRealm($realmId, $charGuid)
{
$char = DB::Characters($realmId)->selectRow('SELECT c.* FROM characters c WHERE c.guid = ?d', $charGuid);
if (!$char)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a char listview is created or profile detail page is called
$profileId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $realmId, $char['guid']);
CLI::write('fetching char #'.$charGuid.' from realm #'.$realmId);
CLI::write('writing...');
/*************/
/* equipment */
/*************/
/* enchantment-Indizes
* 0: permEnchant
* 3: tempEnchant
* 6: gem1
* 9: gem2
* 12: gem3
* 15: socketBonus [not used]
* 18: extraSocket [only check existance]
* 21 - 30: randomProp enchantments
*/
DB::Aowow()->query('DELETE FROM ?_profiler_items WHERE id = ?d', $profileId);
$items = DB::Characters($realmId)->select('SELECT ci.slot AS ARRAY_KEY, ii.itemEntry, ii.enchantments, ii.randomPropertyId FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ?d AND bag = 0 AND slot BETWEEN 0 AND 18', $char['guid']);
$gemItems = [];
$permEnch = [];
$mhItem = 0;
$ohItem = 0;
foreach ($items as $slot => $item)
{
$ench = explode(' ', $item['enchantments']);
$gEnch = [];
foreach ([6, 9, 12] as $idx)
if ($ench[$idx])
$gEnch[$idx] = $ench[$idx];
if ($gEnch)
{
$gi = DB::Aowow()->selectCol('SELECT gemEnchantmentId AS ARRAY_KEY, id FROM ?_items WHERE class = 3 AND gemEnchantmentId IN (?a)', $gEnch);
foreach ($gEnch as $eId)
{
if (isset($gemItems[$eId]))
$gemItems[$eId][1]++;
else
$gemItems[$eId] = [$gi[$eId], 1];
}
}
if ($slot + 1 == 16)
$mhItem = $item['itemEntry'];
if ($slot + 1 == 17)
$ohItem = $item['itemEntry'];
if ($ench[0])
$permEnch[$slot] = $ench[0];
$data = array(
'id' => $profileId,
'slot' => $slot + 1,
'item' => $item['itemEntry'],
'subItem' => $item['randomPropertyId'],
'permEnchant' => $ench[0],
'tempEnchant' => $ench[3],
'extraSocket' => (int)!!$ench[18],
'gem1' => isset($gemItems[$ench[6]]) ? $gemItems[$ench[6]][0] : 0,
'gem2' => isset($gemItems[$ench[9]]) ? $gemItems[$ench[9]][0] : 0,
'gem3' => isset($gemItems[$ench[12]]) ? $gemItems[$ench[12]][0] : 0,
'gem4' => 0 // serverside items cant have more than 3 sockets. (custom profile thing)
);
DB::Aowow()->query('INSERT INTO ?_profiler_items (?#) VALUES (?a)', array_keys($data), array_values($data));
}
CLI::write(' ..inventory');
/**************/
/* basic info */
/**************/
$data = array(
'realm' => $realmId,
'realmGUID' => $charGuid,
'name' => $char['name'],
'race' => $char['race'],
'class' => $char['class'],
'level' => $char['level'],
'gender' => $char['gender'],
'skincolor' => $char['playerBytes'] & 0xFF,
'facetype' => ($char['playerBytes'] >> 8) & 0xFF, // maybe features
'hairstyle' => ($char['playerBytes'] >> 16) & 0xFF,
'haircolor' => ($char['playerBytes'] >> 24) & 0xFF,
'features' => $char['playerBytes2'] & 0xFF, // maybe facetype
'title' => $char['chosenTitle'] ? DB::Aowow()->selectCell('SELECT id FROM ?_titles WHERE bitIdx = ?d', $char['chosenTitle']) : 0,
'playedtime' => $char['totaltime'],
'nomodelMask' => ($char['playerFlags'] & 0x400 ? (1 << SLOT_HEAD) : 0) | ($char['playerFlags'] & 0x800 ? (1 << SLOT_BACK) : 0),
'talenttree1' => 0,
'talenttree2' => 0,
'talenttree3' => 0,
'talentbuild1' => '',
'talentbuild2' => '',
'glyphs1' => '',
'glyphs2' => '',
'activespec' => $char['activespec'],
'guild' => null,
'guildRank' => null,
'gearscore' => 0,
'achievementpoints' => 0
);
/********************/
/* talents + glyphs */
/********************/
$t = DB::Characters($realmId)->selectCol('SELECT spec AS ARRAY_KEY, spell AS ARRAY_KEY2, spell FROM character_talent WHERE guid = ?d', $char['guid']);
$g = DB::Characters($realmId)->select('SELECT spec AS ARRAY_KEY, glyph1 AS g1, glyph2 AS g4, glyph3 AS g5, glyph4 AS g2, glyph5 AS g3, glyph6 AS g6 FROM character_glyphs WHERE guid = ?d', $char['guid']);
for ($i = 0; $i < 2; $i++)
{
// talents
for ($j = 0; $j < 3; $j++)
{
$_ = DB::Aowow()->selectCol('SELECT spell AS ARRAY_KEY, MAX(IF(spell in (?a), rank, 0)) FROM ?_talents WHERE class = ?d AND tab = ?d GROUP BY id ORDER BY row, col ASC', !empty($t[$i]) ? $t[$i] : [0], $char['class'], $j);
$data['talentbuild'.($i + 1)] .= implode('', $_);
if ($char['activespec'] == $i)
$data['talenttree'.($j + 1)] = array_sum($_);
}
// glyphs
if (isset($g[$i]))
{
$gProps = [];
for ($j = 1; $j <= 6; $j++)
if ($g[$i]['g'.$j])
$gProps[$j] = $g[$i]['g'.$j];
if ($gProps)
if ($gItems = DB::Aowow()->selectCol('SELECT i.id FROM ?_glyphproperties gp JOIN ?_spell s ON s.effect1MiscValue = gp.id AND s.effect1Id = 74 JOIN ?_items i ON i.class = 16 AND i.spellId1 = s.id WHERE gp.id IN (?a)', $gProps))
$data['glyphs'.($i + 1)] = implode(':', $gItems);
}
}
$t = array(
'spent' => [$data['talenttree1'], $data['talenttree2'], $data['talenttree3']],
'spec' => 0
);
if ($t['spent'][0] > $t['spent'][1] && $t['spent'][0] > $t['spent'][2])
$t['spec'] = 1;
else if ($t['spent'][1] > $t['spent'][0] && $t['spent'][1] > $t['spent'][2])
$t['spec'] = 2;
else if ($t['spent'][2] > $t['spent'][1] && $t['spent'][2] > $t['spent'][0])
$t['spec'] = 3;
// calc gearscore
if ($items)
$data['gearscore'] += (new ItemList(array(['id', array_column($items, 'itemEntry')])))->getScoreTotal($data['class'], $t, $mhItem, $ohItem);
if ($gemItems)
{
$gemScores = new ItemList(array(['id', array_column($gemItems, 0)]));
foreach ($gemItems as list($itemId, $mult))
if (isset($gemScores->json[$itemId]['gearscore']))
$data['gearscore'] += $gemScores->json[$itemId]['gearscore'] * $mult;
}
if ($permEnch) // fuck this shit .. we are guestimating this!
{
// enchantId => multiple spells => multiple items with varying itemlevels, quality, whatevs
// cant reasonably get to the castItem from enchantId and slot
$profSpec = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, skillLevel AS "1", skillLine AS "0" FROM ?_itemenchantment WHERE id IN (?a)', $permEnch);
foreach ($permEnch as $eId)
{
if ($x = Util::getEnchantmentScore(0, 0, !!$profSpec[$eId][1], $eId))
$data['gearscore'] += $x;
else if ($profSpec[$eId][0] != 776) // not runeforging
$data['gearscore'] += 17; // assume high quality enchantment for unknown cases
}
}
$data['lastupdated'] = time();
CLI::write(' ..basic info');
/***************/
/* hunter pets */
/***************/
if ((1 << ($char['class'] - 1)) == CLASS_HUNTER)
{
DB::Aowow()->query('DELETE FROM ?_profiler_pets WHERE owner = ?d', $profileId);
$pets = DB::Characters($realmId)->select('SELECT id AS ARRAY_KEY, id, entry, modelId, name FROM character_pet WHERE owner = ?d', $charGuid);
foreach ($pets as $petGuid => $petData)
{
$morePet = DB::Aowow()->selectRow('SELECT p.`type`, c.family FROM ?_pet p JOIN ?_creature c ON c.family = p.id WHERE c.id = ?d', $petData['entry']);
$petSpells = DB::Characters($realmId)->selectCol('SELECT spell FROM pet_spell WHERE guid = ?d', $petGuid);
$_ = DB::Aowow()->selectCol('SELECT spell AS ARRAY_KEY, MAX(IF(spell in (?a), rank, 0)) FROM ?_talents WHERE class = 0 AND petTypeMask = ?d GROUP BY id ORDER BY row, col ASC', $petSpells ?: [0], 1 << $morePet['type']);
$pet = array(
'id' => $petGuid,
'owner' => $profileId,
'name' => $petData['name'],
'family' => $morePet['family'],
'npc' => $petData['entry'],
'displayId' => $petData['modelId'],
'talents' => implode('', $_)
);
DB::Aowow()->query('INSERT INTO ?_profiler_pets (?#) VALUES (?a)', array_keys($pet), array_values($pet));
}
CLI::write(' ..hunter pets');
}
/*******************/
/* completion data */
/*******************/
DB::Aowow()->query('DELETE FROM ?_profiler_completion WHERE id = ?d', $profileId);
// done quests
if ($quests = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, quest AS typeId FROM character_queststatus_rewarded WHERE guid = ?d', $profileId, TYPE_QUEST, $char['guid']))
foreach (Util::createSqlBatchInsert($quests) as $q)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$q, array_keys($quests[0]));
CLI::write(' ..quests');
// known skills (professions only)
$skAllowed = DB::Aowow()->selectCol('SELECT id FROM ?_skillline WHERE typeCat IN (9, 11) AND (cuFlags & ?d) = 0', CUSTOM_EXCLUDE_FOR_LISTVIEW);
$skills = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, skill AS typeId, `value` AS cur, max FROM character_skills WHERE guid = ?d AND skill IN (?a)', $profileId, TYPE_SKILL, $char['guid'], $skAllowed);
// manually apply racial profession bonuses
foreach ($skills as &$sk)
{
// Blood Elves - Arcane Affinity
if ($sk['typeId'] == 333 && $char['race'] == 10)
{
$sk['cur'] += 10;
$sk['max'] += 10;
}
// Draenei - Gemcutting
if ($sk['typeId'] == 755 && $char['race'] == 11)
{
$sk['cur'] += 5;
$sk['max'] += 5;
}
// Tauren - Cultivation
// Gnomes - Engineering Specialization
if (($sk['typeId'] == 182 && $char['race'] == 6) ||
($sk['typeId'] == 202 && $char['race'] == 7))
{
$sk['cur'] += 15;
$sk['max'] += 15;
}
}
unset($sk);
if ($skills)
foreach (Util::createSqlBatchInsert($skills) as $sk)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$sk, array_keys($skills[0]));
CLI::write(' ..professions');
// reputation
if ($reputation = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, faction AS typeId, standing AS cur FROM character_reputation WHERE guid = ?d AND (flags & 0xC) = 0', $profileId, TYPE_FACTION, $char['guid']))
foreach (Util::createSqlBatchInsert($reputation) as $rep)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$rep, array_keys($reputation[0]));
CLI::write(' ..reputation');
// known titles
$tBlocks = explode(' ', $char['knownTitles']);
$indizes = [];
for ($i = 0; $i < 6; $i++)
for ($j = 0; $j < 32; $j++)
if ($tBlocks[$i] & (1 << $j))
$indizes[] = $j + ($i * 32);
if ($indizes)
DB::Aowow()->query('INSERT INTO ?_profiler_completion SELECT ?d, ?d, id, NULL, NULL FROM ?_titles WHERE bitIdx IN (?a)', $profileId, TYPE_TITLE, $indizes);
CLI::write(' ..titles');
// achievements
if ($achievements = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, achievement AS typeId, date AS cur FROM character_achievement WHERE guid = ?d', $profileId, TYPE_ACHIEVEMENT, $char['guid']))
{
foreach (Util::createSqlBatchInsert($achievements) as $a)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$a, array_keys($achievements[0]));
$data['achievementpoints'] = DB::Aowow()->selectCell('SELECT SUM(points) FROM ?_achievement WHERE id IN (?a)', array_column($achievements, 'typeId'));
}
CLI::write(' ..achievements');
// raid progression
if ($progress = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, criteria AS typeId, date AS cur, counter AS `max` FROM character_achievement_progress WHERE guid = ?d AND criteria IN (?a)', $profileId, TYPE_ACHIEVEMENT, $char['guid'], self::$raidProgression))
{
array_walk($progress, function (&$val) { $val['typeId'] = array_search($val['typeId'], self::$raidProgression); });
foreach (Util::createSqlBatchInsert($progress) as $p)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$p, array_keys($progress[0]));
}
CLI::write(' ..raid progression');
// known spells
if ($spells = DB::Characters($realmId)->select('SELECT ?d AS id, ?d AS `type`, spell AS typeId FROM character_spell WHERE guid = ?d AND disabled = 0', $profileId, TYPE_SPELL, $char['guid']))
foreach (Util::createSqlBatchInsert($spells) as $s)
DB::Aowow()->query('INSERT INTO ?_profiler_completion (?#) VALUES '.$s, array_keys($spells[0]));
CLI::write(' ..known spells (vanity pets & mounts)');
/****************/
/* related data */
/****************/
// guilds
if ($guild = DB::Characters($realmId)->selectRow('SELECT g.name AS name, g.guildid AS id, gm.rank FROM guild_member gm JOIN guild g ON g.guildid = gm.guildid WHERE gm.guid = ?d', $char['guid']))
{
$guildId = 0;
if (!($guildId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guild['id'])))
{
$gData = array( // only most basic data
'realm' => $realmId,
'realmGUID' => $guild['id'],
'name' => $guild['name'],
'nameUrl' => self::urlize($guild['name']),
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
$guildId = DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES (?a)', array_keys($gData), array_values($gData));
}
$data['guild'] = $guildId;
$data['guildRank'] = $guild['rank'];
}
// arena teams
$teams = DB::Characters($realmId)->select('SELECT at.arenaTeamId AS ARRAY_KEY, at.name, at.type, IF(at.captainGuid = atm.guid, 1, 0) AS captain, atm.* FROM arena_team at JOIN arena_team_member atm ON atm.arenaTeamId = at.arenaTeamId WHERE atm.guid = ?d', $char['guid']);
foreach ($teams as $rGuid => $t)
{
$teamId = 0;
if (!($teamId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $rGuid)))
{
$team = array( // only most basic data
'realm' => $realmId,
'realmGUID' => $rGuid,
'name' => $t['name'],
'nameUrl' => self::urlize($t['name']),
'type' => $t['type'],
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
$teamId = DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($team), array_values($team));
}
$member = array(
'arenaTeamId' => $teamId,
'profileId' => $profileId,
'captain' => $t['captain'],
'weekGames' => $t['weekGames'],
'weekWins' => $t['weekWins'],
'seasonGames' => $t['seasonGames'],
'seasonWins' => $t['seasonWins'],
'personalRating' => $t['personalRating']
);
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES (?a) ON DUPLICATE KEY UPDATE ?a', array_keys($member), array_values($member), array_slice($member, 2));
}
CLI::write(' ..associated arena teams');
/*********************/
/* mark char as done */
/*********************/
if (DB::Aowow()->query('UPDATE ?_profiler_profiles SET ?a WHERE realm = ?d AND realmGUID = ?d', $data, $realmId, $charGuid) !== null)
DB::Aowow()->query('UPDATE ?_profiler_profiles SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $profileId);
return true;
}
public static function getGuildFromRealm($realmId, $guildGuid)
{
$guild = DB::Characters($realmId)->selectRow('SELECT guildId, name, createDate, info, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor FROM guild WHERE guildId = ?d', $guildGuid);
if (!$guild)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a team listview is created or team detail page is called
$guildId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $realmId, $guild['guildId']);
CLI::write('fetching guild #'.$guildGuid.' from realm #'.$realmId);
CLI::write('writing...');
/**************/
/* Guild Data */
/**************/
unset($guild['guildId']);
$guild['nameUrl'] = self::urlize($guild['name']);
DB::Aowow()->query('UPDATE ?_profiler_guild SET ?a WHERE realm = ?d AND realmGUID = ?d', $guild, $realmId, $guildGuid);
// ranks
DB::Aowow()->query('DELETE FROM ?_profiler_guild_rank WHERE guildId = ?d', $guildId);
if ($ranks = DB::Characters($realmId)->select('SELECT ?d AS guildId, rid AS rank, rname AS name FROM guild_rank WHERE guildid = ?d', $guildId, $guildGuid))
foreach (Util::createSqlBatchInsert($ranks) as $r)
DB::Aowow()->query('INSERT INTO ?_profiler_guild_rank (?#) VALUES '.$r, array_keys(reset($ranks)));
CLI::write(' ..guild data');
/***************/
/* Member Data */
/***************/
$conditions = array(
['g.guildid', $guildGuid],
['deleteInfos_Account', null],
['level', MAX_LEVEL, '<='], // prevents JS errors
[['extra_flags', self::CHAR_GMFLAGS, '&'], 0] // not a staff char
);
// this here should all happen within ProfileList
$members = new RemoteProfileList($conditions, ['sv' => $realmId]);
if (!$members->error)
$members->initializeLocalEntries();
else
return false;
CLI::write(' ..guild members');
/*********************/
/* mark guild as done */
/*********************/
DB::Aowow()->query('UPDATE ?_profiler_guild SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $guildId);
return true;
}
public static function getArenaTeamFromRealm($realmId, $teamGuid)
{
$team = DB::Characters($realmId)->selectRow('SELECT arenaTeamId, name, type, captainGuid, rating, seasonGames, seasonWins, weekGames, weekWins, rank, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor FROM arena_team WHERE arenaTeamId = ?d', $teamGuid);
if (!$team)
return false;
// reminder: this query should not fail: a placeholder entry is created as soon as a team listview is created or team detail page is called
$teamId = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_arena_team WHERE realm = ?d AND realmGUID = ?d', $realmId, $team['arenaTeamId']);
CLI::write('fetching arena team #'.$teamGuid.' from realm #'.$realmId);
CLI::write('writing...');
/*************/
/* Team Data */
/*************/
$captain = $team['captainGuid'];
unset($team['captainGuid']);
unset($team['arenaTeamId']);
$team['nameUrl'] = self::urlize($team['name']);
DB::Aowow()->query('UPDATE ?_profiler_arena_team SET ?a WHERE realm = ?d AND realmGUID = ?d', $team, $realmId, $teamGuid);
CLI::write(' ..team data');
/***************/
/* Member Data */
/***************/
$members = DB::Characters($realmId)->select('
SELECT
atm.guid AS ARRAY_KEY, atm.arenaTeamId, atm.weekGames, atm.weekWins, atm.seasonGames, atm.seasonWins, atm.personalrating
FROM
arena_team_member atm
JOIN
characters c ON c.guid = atm.guid AND
c.deleteInfos_Account IS NULL AND
c.level <= ?d AND
(c.extra_flags & ?d) = 0
WHERE
arenaTeamId = ?d',
MAX_LEVEL,
self::CHAR_GMFLAGS,
$teamGuid
);
$conditions = array(
['c.guid', array_keys($members)],
['deleteInfos_Account', null],
['level', MAX_LEVEL, '<='], // prevents JS errors
[['extra_flags', self::CHAR_GMFLAGS, '&'], 0] // not a staff char
);
$mProfiles = new RemoteProfileList($conditions, ['sv' => $realmId]);
if (!$mProfiles->error)
{
$mProfiles->initializeLocalEntries();
foreach ($mProfiles->iterate() as $__)
{
$mGuid = $mProfiles->getField('guid');
$members[$mGuid]['arenaTeamId'] = $teamId;
$members[$mGuid]['captain'] = (int)($mGuid == $captain);
$members[$mGuid]['profileId'] = $mProfiles->getField('id');
}
DB::Aowow()->query('DELETE FROM ?_profiler_arena_team_member WHERE arenaTeamId = ?d', $teamId);
foreach (Util::createSqlBatchInsert($members) as $m)
DB::Aowow()->query('INSERT INTO ?_profiler_arena_team_member (?#) VALUES '.$m, array_keys(reset($members)));
}
else
return false;
CLI::write(' ..team members');
/*********************/
/* mark team as done */
/*********************/
DB::Aowow()->query('UPDATE ?_profiler_arena_team SET cuFlags = cuFlags & ?d WHERE id = ?d', ~PROFILER_CU_NEEDS_RESYNC, $teamId);
return true;
}
}
?>

View File

@@ -1,10 +1,10 @@
<?php <?php
define('AOWOW_REVISION', 1); define('AOWOW_REVISION', 27);
define('CLI', PHP_SAPI === 'cli'); define('CLI', PHP_SAPI === 'cli');
$reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring']; $reqExt = ['SimpleXML', 'gd', 'mysqli', 'mbstring', 'fileinfo'];
$error = ''; $error = '';
foreach ($reqExt as $r) foreach ($reqExt as $r)
if (!extension_loaded($r)) if (!extension_loaded($r))

View File

@@ -8,17 +8,18 @@ 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' => [['si'], 'o' => 'orderInGroup ASC'], 'a' => [['ic'], 'o' => 'orderInGroup ASC'],
'si' => ['j' => ['?_icons si ON si.id = a.iconId', true], 's' => ', si.iconString'], 'ic' => ['j' => ['?_icons ic ON ic.id = a.iconId', true], 's' => ', ic.name AS 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
@@ -33,7 +34,22 @@ class AchievementList extends BaseType
// post processing // post processing
$rewards = DB::World()->select(' $rewards = DB::World()->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)', SELECT
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(arl6.Subject, "") AS subject_loc6, IFNULL(arl8.Subject, "") AS subject_loc8,
ar.Text AS text_loc0, IFNULL(arl2.Text, "") AS text_loc2, IFNULL(arl3.Text, "") AS text_loc3, IFNULL(arl6.Text, "") AS text_loc6, IFNULL(arl8.Text, "") 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 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()
); );
@@ -45,7 +61,7 @@ class AchievementList extends BaseType
{ {
$_curTpl = array_merge($rewards[$_id], $_curTpl); $_curTpl = array_merge($rewards[$_id], $_curTpl);
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();
@@ -54,22 +70,22 @@ class AchievementList extends BaseType
// $_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]['mailTemplate']); $mailRew = DB::World()->selectCol('SELECT Item FROM mail_loot_template WHERE Reference <= 0 AND entry = ?d', $rewards[$_id]['MailTemplateID']);
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['item'])) if (!empty($_curTpl['ItemID']))
$_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['item']]; $_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['ItemID']];
if (!empty($_curTpl['itemExtra'])) if (!empty($_curTpl['itemExtra']))
$_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['itemExtra']]; $_curTpl['rewards'][] = [TYPE_ITEM, $_curTpl['itemExtra']];
if (!empty($_curTpl['title_A'])) if (!empty($_curTpl['TitleA']))
$_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['title_A']]; $_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['TitleA']];
if (!empty($_curTpl['title_H'])) if (!empty($_curTpl['TitleH']))
if (empty($_curTpl['title_A']) || $_curTpl['title_A'] != $_curTpl['title_H']) if (empty($_curTpl['TitleA']) || $_curTpl['TitleA'] != $_curTpl['TitleH'])
$_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['title_H']]; $_curTpl['rewards'][] = [TYPE_TITLE, $_curTpl['TitleH']];
// icon // icon
$_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering'; $_curTpl['iconString'] = $_curTpl['iconString'] ?: 'trade_engineering';
@@ -206,10 +222,12 @@ class AchievementList extends BaseType
break; break;
} }
$criteria .= '<!--cr'.$crt['id'].':'.$crt['type'].':'.$crt['value1'].'-->- '.Util::jsEscape($crtName);
if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER) if ($crt['completionFlags'] & ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER)
$criteria .= '- '.Util::jsEscape($crtName).' <span class="moneygold">'.number_format($crt['value2' ] / 10000).'</span><br />'; $criteria .= '&nbsp;<span class="moneygold">'.Lang::nf($crt['value2' ] / 10000).'</span>';
else
$criteria .= '- '.Util::jsEscape($crtName).'<br />'; $criteria .= '<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>';
@@ -278,62 +296,42 @@ class AchievementListFilter extends Filter
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 $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward 2 => [FILTER_CR_BOOLEAN, 'reward_loc0', true ], // givesreward
3 => [FILTER_CR_STRING, 'reward', true ], // rewardtext 3 => [FILTER_CR_STRING, 'reward', STR_LOCALIZED ], // rewardtext
7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries 4 => [FILTER_CR_NYI_PH, null, 1, ], // location [enum]
9 => [FILTER_CR_NUMERIC, 'id', null, true], // id 5 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_FIRST_SERIES, null], // first in series [yn]
10 => [FILTER_CR_STRING, 'si.iconString', ], // icon 6 => [FILTER_CR_CALLBACK, 'cbSeries', ACHIEVEMENT_CU_LAST_SERIES, null], // last in series [yn]
18 => [FILTER_CR_STAFFFLAG, 'flags', ], // flags 7 => [FILTER_CR_BOOLEAN, 'chainId', ], // partseries
14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 9 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots 10 => [FILTER_CR_STRING, 'ic.name', ], // icon
16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos 11 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // related event [enum]
14 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
15 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
16 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
18 => [FILTER_CR_STAFFFLAG, 'flags', ] // flags
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 18], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C};:]/ui', true ], // criteria values - only printable chars, no delimiters
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / description - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // extended name search
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'si' => [FILTER_V_LIST, [1, 2, 3, -1, -2], false], // side
'minpt' => [FILTER_V_RANGE, [1, 99], false], // required level min
'maxpt' => [FILTER_V_RANGE, [1, 99], false] // required level max
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCr = $this->genericCriterion($cr)) if ($genCr = $this->genericCriterion($cr))
return $genCr; return $genCr;
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); unset($cr);
$this->error = true; $this->error = true;
return [1]; return [1];
@@ -359,45 +357,57 @@ class AchievementListFilter extends Filter
// points min // points min
if (isset($_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 (isset($_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 (isset($_v['si'])) if (isset($_v['si']))
{ {
switch ($_v['si']) switch ($_v['si'])
{ {
case 3: // both
$parts[] = ['faction', 0];
break;
case -1: // faction, exclusive both case -1: // faction, exclusive both
case -2: case -2:
$parts[] = ['faction', -$_v['si']]; $parts[] = ['faction', -$_v['si']];
break; break;
case 1: // faction, inclusive both case 1: // faction, inclusive both
case 2: case 2:
$parts[] = ['OR', ['faction', 0], ['faction', $_v['si']]]; case 3: // both
$parts[] = ['faction', $_v['si'], '&'];
break; break;
default:
unset($_v['si']);
} }
} }
return $parts; return $parts;
} }
protected function cbRelEvent($cr, $value)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
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 : '!'];
}
return false;
}
protected function cbSeries($cr, $value)
{
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['AND', ['chainId', 0, '!'], ['cuFlags', $value, '&']] : ['AND', ['chainId', 0, '!'], [['cuFlags', $value, '&'], 0]];
return false;
}
} }
?> ?>

View File

@@ -0,0 +1,346 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class ArenaTeamList extends BaseType
{
use profilerHelper, listviewHelper;
private $rankOrder = [];
public function getListviewData()
{
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'name' => $this->curTpl['name'],
'realm' => Profiler::urlize($this->curTpl['realmName']),
'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
{
public $extraOpts = [];
protected $genericFilter = [];
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'sz' => [FILTER_V_LIST, [2, 3, 5], false], // tema size
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
protected function createSQLForCriterium(&$cr) { }
protected function createSQLForValues()
{
$parts = [];
$_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to ArenaTeamList as miscData and handled there
// name [str]
if (!empty($_v['na']))
if ($_ = $this->modularizeString(['at.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on'))
$parts[] = $_;
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = ['c.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = ['c.race', [2, 5, 6, 8, 10]];
}
// size [int]
if (!empty($_v['sz']))
$parts[] = ['at.type', $_v['sz']];
return $parts;
}
protected function cbRegionCheck(&$v)
{
if ($v == 'eu' || $v == 'us')
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
}
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 = [];
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', 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_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 "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, 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
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
$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()
{
$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_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 IGNORE INTO ?_profiler_arena_team (?#) VALUES '.$ins, 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)
foreach ($team as $memberId => $member)
$memberData[] = array(
'arenaTeamId' => $localIds[$realmId.':'.$teamId],
'profileId' => $profiles[$realmId]->getEntry($realmId.':'.$memberId)['id'],
'captain' => $member[2]
);
foreach (Util::createSqlBatchInsert($memberData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team_member (?#) VALUES '.$ins, array_keys(reset($memberData)));
}
}
}
class LocalArenaTeamList extends ArenaTeamList
{
protected $queryBase = 'SELECT at.*, at.id AS ARRAY_KEY FROM ?_profiler_arena_team at';
public function __construct($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
// post processing
$members = DB::Aowow()->selectCol('SELECT *, arenaTeamId AS ARRAY_KEY, profileId AS ARRAY_KEY2 FROM ?_profiler_arena_team_member 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_BATTLEGROUP;
$curTpl['members'] = $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'))
));
}
}
?>

View File

@@ -6,10 +6,11 @@ if (!defined('AOWOW_REVISION'))
class CharClassList extends BaseType class CharClassList extends BaseType
{ {
public static $type = TYPE_CLASS; public static $type = TYPE_CLASS;
public static $brickFile = 'class'; public static $brickFile = 'class';
public static $dataTable = '?_classes';
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_classes c'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_classes c';
public function __construct($conditions = []) public function __construct($conditions = [])
{ {

View File

@@ -6,10 +6,11 @@ if (!defined('AOWOW_REVISION'))
class CharRaceList extends BaseType class CharRaceList extends BaseType
{ {
public static $type = TYPE_RACE; public static $type = TYPE_RACE;
public static $brickFile = 'race'; public static $brickFile = 'race';
public static $dataTable = '?_races';
protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_races r'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_races r';
public function getListviewData() public function getListviewData()
{ {

View File

@@ -8,11 +8,12 @@ class CreatureList extends BaseType
{ {
use spawnHelper; use spawnHelper;
public static $type = TYPE_NPC; public static $type = TYPE_NPC;
public static $brickFile = 'creature'; 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_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]],
@@ -76,7 +77,8 @@ class CreatureList extends BaseType
if ($type) if ($type)
$row3[] = Lang::game('ct', $type); $row3[] = Lang::game('ct', $type);
$row3[] = '('.Lang::npc('rank', $this->curTpl['rank']).')'; if ($_ = Lang::npc('rank', $this->curTpl['rank']))
$row3[] = '('.$_.')';
$x = '<table>'; $x = '<table>';
$x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>'; $x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>';
@@ -161,9 +163,22 @@ 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 $__)
{ {
@@ -207,6 +222,7 @@ 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;
@@ -215,6 +231,14 @@ 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];
}
} }
} }
@@ -253,6 +277,7 @@ class CreatureList extends BaseType
public function addRewardsToJScript(&$refs) { } public function addRewardsToJScript(&$refs) { }
} }
@@ -268,228 +293,66 @@ class CreatureListFilter extends Filter
// cr => [type, field, misc, extraCol] // cr => [type, field, misc, extraCol]
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair 1 => [FILTER_CR_CALLBACK, 'cbHealthMana', 'healthMax', 'healthMin'], // health [num]
6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin 2 => [FILTER_CR_CALLBACK, 'cbHealthMana', 'manaMin', 'manaMax' ], // mana [num]
9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable 3 => [FILTER_CR_CALLBACK, 'cbFaction', null, null ], // faction [enum]
11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable 5 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_REPAIRER ], // canrepair
18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer 6 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin
19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker 7 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [enum]
20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster 8 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [enum]
21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster 9 => [FILTER_CR_BOOLEAN, 'lootId', ], // lootable
22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster 10 => [FILTER_CR_BOOLEAN, 'cbRegularSkinLoot', NPC_TYPEFLAG_SPECIALLOOT ], // skinnable [yn]
23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper 11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable
24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner 12 => [FILTER_CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int]
25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor 15 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_HERBLOOT, null ], // gatherable [yn]
27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster 16 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_ENGINEERLOOT, null ], // minable [yn]
28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer 18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer
29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor 19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker 20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster
37 => [FILTER_CR_NUMERIC, 'id', null, true], // id 21 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_FLIGHT_MASTER ], // flightmaster
35 => [FILTER_CR_STRING, 'textureString' ], // useskin 22 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // guildmaster
32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss 23 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_INNKEEPER ], // innkeeper
33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 24 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_CLASS_TRAINER ], // talentunlearner
31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots 25 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_GUILD_MASTER ], // tabardvendor
40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos 27 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_STABLE_MASTER ], // stablemaster
28 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_TRAINER ], // trainer
29 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_VENDOR ], // vendor
31 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
32 => [FILTER_CR_FLAG, 'cuFlags', NPC_CU_INSTANCE_BOSS ], // instanceboss
33 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
34 => [FILTER_CR_NYI_PH, 1, null ], // usemodel [str] - displayId -> id:creatureDisplayInfo.dbc/model -> id:cratureModelData.dbc/modelPath
35 => [FILTER_CR_STRING, 'textureString' ], // useskin
37 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true ], // id
38 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null ], // relatedevent [enum]
40 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
41 => [FILTER_CR_NYI_PH, 1, null ], // haslocation [yn] [staff]
42 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum]
43 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum]
44 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_MININGLOOT, null ] // salvageable [yn]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [[1, 3],[5, 12], 15, 16, [18, 25], [27, 29], [31, 35], 37, 38, [40, 44]], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 9999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C}:;]/ui', true ], // criteria values - only printable chars, no delimiter
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / subname - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // also match subname
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'fa' => [FILTER_V_CALLBACK, 'cbPetFamily', true ], // pet family [list] - cat[0] == 1
'minle' => [FILTER_V_RANGE, [1, 99], false], // min level [int]
'maxle' => [FILTER_V_RANGE, [1, 99], false], // max level [int]
'cl' => [FILTER_V_RANGE, [0, 4], true ], // classification [list]
'ra' => [FILTER_V_LIST, [-1, 0, 1], false], // react alliance [int]
'rh' => [FILTER_V_LIST, [-1, 0, 1], false] // react horde [int]
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCr = $this->genericCriterion($cr)) if ($genCr = $this->genericCriterion($cr))
return $genCr; return $genCr;
unset($cr);
$this->error = true;
return [1];
}
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); unset($cr);
$this->error = true; $this->error = true;
return [1]; return [1];
@@ -515,65 +378,176 @@ class CreatureListFilter extends Filter
// pet family [list] // pet family [list]
if (isset($_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 (isset($_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 (isset($_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 (isset($_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 (isset($_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 (isset($_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(&$val)
{
if (!$this->parentCats || $this->parentCats[0] != 1)
return false;
if (!Util::checkNumeric($val, NUM_REQ_INT))
return false;
$type = FILTER_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($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;
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];
}
return false;
}
protected function cbMoneyDrop($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
return ['AND', ['((minGold + maxGold) / 2)', $cr[2], $cr[1]]];
}
protected function cbQuestRelation($cr, $field, $val)
{
switch ($cr[1])
{
case 1: // any
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!']];
case 2: // alliance
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde
return ['AND', ['qse.method', $val, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', $val, '&'], ['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'][] = $field.' = 0';
return [1];
}
return false;
}
protected function cbHealthMana($cr, $minField, $maxField)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
// remap OP for this special case
switch ($cr[1])
{
case '=': // min > max is totally possible
$this->extraOpts['ct']['h'][] = $minField.' = '.$maxField.' AND '.$minField.' = '.$cr[2];
break;
case '>':
case '>=':
case '<':
case '<=':
$this->extraOpts['ct']['h'][] = 'IF('.$minField.' > '.$maxField.', '.$maxField.', '.$minField.') '.$cr[1].' '.$cr[2];
break;
}
return [1]; // always true, use post-filter
}
protected function cbSpecialSkinLoot($cr, $typeFlag)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], ['typeFlags', $typeFlag, '&']];
else
return ['OR', ['skinLootId', 0], [['typeFlags', $typeFlag, '&'], 0]];
}
protected function cbRegularSkinLoot($cr, $typeFlag)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return ['AND', ['skinLootId', 0, '>'], [['typeFlags', $typeFlag, '&'], 0]];
else
return ['OR', ['skinLootId', 0], ['typeFlags', $typeFlag, '&']];
}
protected function cbReputation($cr, $op)
{
if (!in_array($cr[1], $this->enums[3])) // reuse
return false;
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1]))
$this->formData['reputationCols'][] = [$cr[1], 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)', $cr[1], $cr[1]))
return ['id', $cIds];
else
return [0];
}
protected function cbFaction($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;
if (!in_array($cr[1], $this->enums[$cr[0]]))
return false;
$facTpls = [];
$facs = new FactionList(array('OR', ['parentFactionId', $cr[1]], ['id', $cr[1]]));
foreach ($facs->iterate() as $__)
$facTpls = array_merge($facTpls, $facs->getField('templateIds'));
return $facTpls ? ['faction', $facTpls] : [0];
}
} }
?> ?>

View File

@@ -6,14 +6,25 @@ if (!defined('AOWOW_REVISION'))
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.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($conditions = [])
{
parent::__construct($conditions);
foreach ($this->iterate() as &$_curTpl)
if (!$_curTpl['iconString'])
$_curTpl['iconString'] = 'inv_misc_questionmark';
}
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,7 +63,25 @@ class CurrencyList extends BaseType
return $data; return $data;
} }
public function renderTooltip() { } public function renderTooltip()
{
if (!$this->curTpl)
return array();
$x = '<table><tr><td>';
$x .= '<b>'.Util::jsEscape($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">'.Util::jsEscape($_).'</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;
}
} }
?> ?>

View File

@@ -0,0 +1,58 @@
<?php
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.id AS ARRAY_KEY FROM ?_emotes e';
public function __construct($conditions = [])
{
parent::__construct($conditions);
// 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' => $this->getField('self', true) ?: ($this->getField('noTarget', true) ?: $this->getField('target', 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() { }
}
?>

View File

@@ -0,0 +1,353 @@
<?php
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 $jsonStats = [];
private $relSpells = [];
private $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($conditions = [])
{
parent::__construct($conditions);
// 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])
{
case 1:
$proc = -$this->getField('ppmRate') ?: ($this->getField('procChance') ?: $this->getField('amount'.$i));
$curTpl['spells'][$i] = [$curTpl['object'.$i], 2, $curTpl['charges'], $proc];
$this->relSpells[] = $curTpl['object'.$i];
break;
case 3:
$curTpl['spells'][$i] = [$curTpl['object'.$i], 1, $curTpl['charges'], 0];
$this->relSpells[] = $curTpl['object'.$i];
break;
case 7:
$curTpl['spells'][$i] = [$curTpl['object'.$i], 0, $curTpl['charges'], 0];
$this->relSpells[] = $curTpl['object'.$i];
break;
}
}
// floats are fetched as string from db :<
$curTpl['dmg'] = floatVal($curTpl['dmg']);
$curTpl['dps'] = floatVal($curTpl['dps']);
// remove zero-stats
foreach (Game::$itemMods as $str)
if ($curTpl[$str] == 0) // empty(0.0f) => true .. yeah, sure
unset($curTpl[$str]);
if ($curTpl['dps'] == 0)
unset($curTpl['dps']);
}
if ($this->relSpells)
$this->relSpells = new SpellList(array(['id', $this->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_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 $s)
{
// enchant is procing or onUse
if ($s[1] == 2 || $s[1] == 0)
$data[$this->id]['spells'][$s[0]] = $s[2];
// spell is procing
else if ($this->relSpells && $this->relSpells->getEntry($s[0]) && ($_ = $this->relSpells->canTriggerSpell()))
{
foreach ($_ as $idx)
{
$this->triggerIds[] = $this->relSpells->getField('effect'.$idx.'TriggerSpell');
$data[$this->id]['spells'][$this->relSpells->getField('effect'.$idx.'TriggerSpell')] = $s[2];
}
}
}
if (!$data[$this->id]['spells'])
unset($data[$this->id]['spells']);
Util::arraySumByKey($data[$this->id], $this->getStatGain());
}
return $data;
}
public function getStatGain($addScalingKeys = false)
{
$data = [];
foreach (Game::$itemMods as $str)
if (isset($this->curTpl[$str]))
$data[$str] = $this->curTpl[$str];
if (isset($this->curTpl['dps']))
$data['dps'] = $this->curTpl['dps'];
// scaling enchantments are saved as 0 to item_stats, thus return empty
if ($addScalingKeys)
{
$spellStats = [];
if ($this->relSpells)
$spellStats = $this->relSpells->getStatGain();
for ($h = 1; $h <= 3; $h++)
{
$obj = (int)$this->curTpl['object'.$h];
switch ($this->curTpl['type'.$h])
{
case 3: // TYPE_EQUIP_SPELL Spells from ObjectX (use of amountX?)
if (!empty($spellStats[$obj]))
foreach ($spellStats[$obj] as $mod => $_)
if ($str = Game::$itemMods[$mod])
Util::arraySumByKey($data, [$str => 0]);
$obj = null;
break;
case 4: // TYPE_RESISTANCE +AmountX resistance for ObjectX School
switch ($obj)
{
case 0: // Physical
$obj = ITEM_MOD_ARMOR;
break;
case 1: // Holy
$obj = ITEM_MOD_HOLY_RESISTANCE;
break;
case 2: // Fire
$obj = ITEM_MOD_FIRE_RESISTANCE;
break;
case 3: // Nature
$obj = ITEM_MOD_NATURE_RESISTANCE;
break;
case 4: // Frost
$obj = ITEM_MOD_FROST_RESISTANCE;
break;
case 5: // Shadow
$obj = ITEM_MOD_SHADOW_RESISTANCE;
break;
case 6: // Arcane
$obj = ITEM_MOD_ARCANE_RESISTANCE;
break;
default:
$obj = null;
}
break;
case 5: // TYPE_STAT +AmountX for Statistic by type of ObjectX
if ($obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
$obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
break; // stats are directly assigned below
default: // TYPE_NONE dnd stuff; skip assignment below
$obj = null;
}
if ($obj !== null)
if ($str = Game::$itemMods[$obj]) // check if we use these mods
Util::arraySumByKey($data, [$str => 0]);
}
}
return $data;
}
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 $enums = array(
3 => array( // requiresprof
null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, true, false, 356, 182, 773
)
);
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
3 => [FILTER_CR_ENUM, 'skillLine' ], // requiresprof
4 => [FILTER_CR_NUMERIC, 'skillLevel', NUM_CAST_INT ], // reqskillrank
5 => [FILTER_CR_BOOLEAN, 'conditionId' ], // hascondition
10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
12 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
20 => [FILTER_CR_NUMERIC, 'is.str', NUM_CAST_INT, true], // str
21 => [FILTER_CR_NUMERIC, 'is.agi', NUM_CAST_INT, true], // agi
22 => [FILTER_CR_NUMERIC, 'is.sta', NUM_CAST_INT, true], // sta
23 => [FILTER_CR_NUMERIC, 'is.int', NUM_CAST_INT, true], // int
24 => [FILTER_CR_NUMERIC, 'is.spi', NUM_CAST_INT, true], // spi
25 => [FILTER_CR_NUMERIC, 'is.arcres', NUM_CAST_INT, true], // arcres
26 => [FILTER_CR_NUMERIC, 'is.firres', NUM_CAST_INT, true], // firres
27 => [FILTER_CR_NUMERIC, 'is.natres', NUM_CAST_INT, true], // natres
28 => [FILTER_CR_NUMERIC, 'is.frores', NUM_CAST_INT, true], // frores
29 => [FILTER_CR_NUMERIC, 'is.shares', NUM_CAST_INT, true], // shares
30 => [FILTER_CR_NUMERIC, 'is.holres', NUM_CAST_INT, true], // holres
32 => [FILTER_CR_NUMERIC, 'is.dps', NUM_CAST_FLOAT, true], // dps
34 => [FILTER_CR_NUMERIC, 'is.dmg', NUM_CAST_FLOAT, true], // dmg
37 => [FILTER_CR_NUMERIC, 'is.mleatkpwr', NUM_CAST_INT, true], // mleatkpwr
38 => [FILTER_CR_NUMERIC, 'is.rgdatkpwr', NUM_CAST_INT, true], // rgdatkpwr
39 => [FILTER_CR_NUMERIC, 'is.rgdhitrtng', NUM_CAST_INT, true], // rgdhitrtng
40 => [FILTER_CR_NUMERIC, 'is.rgdcritstrkrtng', NUM_CAST_INT, true], // rgdcritstrkrtng
41 => [FILTER_CR_NUMERIC, 'is.armor' , NUM_CAST_INT, true], // armor
42 => [FILTER_CR_NUMERIC, 'is.defrtng', NUM_CAST_INT, true], // defrtng
43 => [FILTER_CR_NUMERIC, 'is.block', NUM_CAST_INT, true], // block
44 => [FILTER_CR_NUMERIC, 'is.blockrtng', NUM_CAST_INT, true], // blockrtng
45 => [FILTER_CR_NUMERIC, 'is.dodgertng', NUM_CAST_INT, true], // dodgertng
46 => [FILTER_CR_NUMERIC, 'is.parryrtng', NUM_CAST_INT, true], // parryrtng
48 => [FILTER_CR_NUMERIC, 'is.splhitrtng', NUM_CAST_INT, true], // splhitrtng
49 => [FILTER_CR_NUMERIC, 'is.splcritstrkrtng', NUM_CAST_INT, true], // splcritstrkrtng
50 => [FILTER_CR_NUMERIC, 'is.splheal', NUM_CAST_INT, true], // splheal
51 => [FILTER_CR_NUMERIC, 'is.spldmg', NUM_CAST_INT, true], // spldmg
52 => [FILTER_CR_NUMERIC, 'is.arcsplpwr', NUM_CAST_INT, true], // arcsplpwr
53 => [FILTER_CR_NUMERIC, 'is.firsplpwr', NUM_CAST_INT, true], // firsplpwr
54 => [FILTER_CR_NUMERIC, 'is.frosplpwr', NUM_CAST_INT, true], // frosplpwr
55 => [FILTER_CR_NUMERIC, 'is.holsplpwr', NUM_CAST_INT, true], // holsplpwr
56 => [FILTER_CR_NUMERIC, 'is.natsplpwr', NUM_CAST_INT, true], // natsplpwr
57 => [FILTER_CR_NUMERIC, 'is.shasplpwr', NUM_CAST_INT, true], // shasplpwr
60 => [FILTER_CR_NUMERIC, 'is.healthrgn', NUM_CAST_INT, true], // healthrgn
61 => [FILTER_CR_NUMERIC, 'is.manargn', NUM_CAST_INT, true], // manargn
77 => [FILTER_CR_NUMERIC, 'is.atkpwr', NUM_CAST_INT, true], // atkpwr
78 => [FILTER_CR_NUMERIC, 'is.mlehastertng', NUM_CAST_INT, true], // mlehastertng
79 => [FILTER_CR_NUMERIC, 'is.resirtng', NUM_CAST_INT, true], // resirtng
84 => [FILTER_CR_NUMERIC, 'is.mlecritstrkrtng', NUM_CAST_INT, true], // mlecritstrkrtng
94 => [FILTER_CR_NUMERIC, 'is.splpen', NUM_CAST_INT, true], // splpen
95 => [FILTER_CR_NUMERIC, 'is.mlehitrtng', NUM_CAST_INT, true], // mlehitrtng
96 => [FILTER_CR_NUMERIC, 'is.critstrkrtng', NUM_CAST_INT, true], // critstrkrtng
97 => [FILTER_CR_NUMERIC, 'is.feratkpwr', NUM_CAST_INT, true], // feratkpwr
101 => [FILTER_CR_NUMERIC, 'is.rgdhastertng', NUM_CAST_INT, true], // rgdhastertng
102 => [FILTER_CR_NUMERIC, 'is.splhastertng', NUM_CAST_INT, true], // splhastertng
103 => [FILTER_CR_NUMERIC, 'is.hastertng', NUM_CAST_INT, true], // hastertng
114 => [FILTER_CR_NUMERIC, 'is.armorpenrtng', NUM_CAST_INT, true], // armorpenrtng
115 => [FILTER_CR_NUMERIC, 'is.health', NUM_CAST_INT, true], // health
116 => [FILTER_CR_NUMERIC, 'is.mana', NUM_CAST_INT, true], // mana
117 => [FILTER_CR_NUMERIC, 'is.exprtng', NUM_CAST_INT, true], // exprtng
119 => [FILTER_CR_NUMERIC, 'is.hitrtng', NUM_CAST_INT, true], // hitrtng
123 => [FILTER_CR_NUMERIC, 'is.splpwr', NUM_CAST_INT, true] // splpwr
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 123], true ], // criteria ids
'crs' => [FILTER_V_RANGE, [1, 15], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - only numerals
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ty' => [FILTER_V_RANGE, [1, 8], true ] // types
);
protected function createSQLForCriterium(&$cr)
{
if (in_array($cr[0], array_keys($this->genericFilter)))
if ($genCr = $this->genericCriterion($cr))
return $genCr;
unset($cr);
$this->error = true;
return [1];
}
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
//string
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name_loc'.User::$localeId]))
$parts[] = $_;
// type
if (isset($_v['ty']))
{
$_ = (array)$_v['ty'];
if (!array_diff($_, [1, 2, 3, 4, 5, 6, 7, 8]))
$parts[] = ['OR', ['type1', $_], ['type2', $_], ['type3', $_]];
else
unset($_v['ty']);
}
return $parts;
}
}
?>

View File

@@ -6,15 +6,16 @@ if (!defined('AOWOW_REVISION'))
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($conditions = []) public function __construct($conditions = [])
{ {

View File

@@ -8,11 +8,12 @@ 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'],
@@ -92,8 +93,9 @@ class GameObjectList extends BaseType
$x = '<table>'; $x = '<table>';
$x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>'; $x .= '<tr><td><b class="q">'.$this->getField('name', true).'</b></td></tr>';
if ($_ = Lang::gameObject('type', $this->curTpl['typeCat'])) if ($this->curTpl['typeCat'])
$x .= '<tr><td>'.$_.'</td></tr>'; if ($_ = Lang::gameObject('type', $this->curTpl['typeCat']))
$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']))
@@ -140,98 +142,36 @@ class GameObjectListFilter extends Filter
public $extraOpts = []; public $extraOpts = [];
protected $genericFilter = array( protected $genericFilter = array(
1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin 1 => [FILTER_CR_ENUM, 's.areaId', null ], // foundin
7 => [FILTER_CR_NUMERIC, 'reqSkill', null ], // requiredskilllevel 2 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'startsQuests', 0x1 ], // startsquest [side]
11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT], // hasscreenshots 3 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 'endsQuests', 0x2 ], // endsquest [side]
13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 4 => [FILTER_CR_CALLBACK, 'cbOpenable', null, null], // openable [yn]
15 => [FILTER_CR_NUMERIC, 'id', null ], // id 5 => [FILTER_CR_NYI_PH, null, null ], // averagemoneycontained [op] [int] - GOs don't contain money, match against 0
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos 7 => [FILTER_CR_NUMERIC, 'reqSkill', NUM_CAST_INT ], // requiredskilllevel
11 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
13 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
15 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT ], // id
16 => [FILTER_CR_CALLBACK, 'cbRelEvent', null, null], // relatedevent (ignore removed by event)
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ] // hasvideos
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [[1, 5], 7, 11, 13, 15, 16, 18], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 5000]], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - only numeric input values expected
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false] // match any / all filter
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
case 4:
if (!$this->int2Bool($cr[1]))
break;
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); unset($cr);
$this->error = 1; $this->error = true;
return [1]; return [1];
} }
@@ -247,6 +187,61 @@ class GameObjectListFilter extends Filter
return $parts; return $parts;
} }
protected function cbOpenable($cr)
{
if ($this->int2Bool($cr[1]))
return $cr[1] ? ['OR', ['flags', 0x2, '&'], ['type', 3]] : ['AND', [['flags', 0x2, '&'], 0], ['type', 3, '!']];
return false;
}
protected function cbQuestRelation($cr, $field, $value)
{
switch ($cr[1])
{
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', RACE_MASK_HORDE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&']];
case 3: // horde only
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], [['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], 0], ['qt.reqRaceMask', RACE_MASK_HORDE, '&']];
case 4: // both
return ['AND', ['qse.method', $value, '&'], ['qse.questId', null, '!'], ['OR', ['AND', ['qt.reqRaceMask', RACE_MASK_ALLIANCE, '&'], ['qt.reqRaceMask', RACE_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 false;
}
protected function cbRelEvent($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;;
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];
}
return false;
}
} }
?> ?>

View File

@@ -0,0 +1,307 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class GuildList extends BaseType
{
use profilerHelper, listviewHelper;
public function getListviewData()
{
$this->getGuildScores();
$data = [];
foreach ($this->iterate() as $__)
{
$data[$this->id] = array(
'name' => "$'".$this->curTpl['name']."'", // MUST be a string
'members' => $this->curTpl['members'],
'faction' => $this->curTpl['faction'],
'achievementpoints' => $this->getField('achievementpoints'),
'gearscore' => $this->getField('gearscore'),
'realm' => Profiler::urlize($this->curTpl['realmName']),
'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
{
public $extraOpts = [];
protected $genericFilter = [];
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
protected function createSQLForCriterium(&$cr) { }
protected function createSQLForValues()
{
$parts = [];
$_v = $this->fiData['v'];
// region (rg), battlegroup (bg) and server (sv) are passed to GuildList as miscData and handled there
// name [str]
if (!empty($_v['na']))
if ($_ = $this->modularizeString(['g.name'], $_v['na'], !empty($_v['ex']) && $_v['ex'] == 'on'))
$parts[] = $_;
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = ['c.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = ['c.race', [2, 5, 6, 8, 10]];
}
return $parts;
}
protected function cbRegionCheck(&$v)
{
if ($v == 'eu' || $v == 'us')
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
}
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($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', 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_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('character "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// equalize distribution
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
$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 IGNORE INTO ?_profiler_guild (?#) VALUES '.$ins, 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($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
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_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'))
));
}
}
?>

View File

@@ -0,0 +1,237 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
class IconList extends BaseType
{
use listviewHelper;
public static $type = TYPE_ICON;
public static $brickFile = 'icon';
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($conditions)
{
parent::__construct($conditions);
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
{
public $extraOpts = null;
// cr => [type, field, misc, extraCol]
private $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]
);
private $totalUses = [];
protected $genericFilter = array(
1 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // items [num]
2 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // spells [num]
3 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // achievements [num]
6 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // currencies [num]
9 => [FILTER_CR_CALLBACK, 'cbUseAny' ], // hunterpets [num]
11 => [FILTER_CR_NYI_PH, null, null], // classes [num]
13 => [FILTER_CR_CALLBACK, 'cbUseAll' ] // used [num]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_LIST, [1, 2, 3, 6, 9, 11, 13], true ], // criteria ids
'crs' => [FILTER_V_RANGE, [1, 6], true ], // criteria operators
'crv' => [FILTER_V_RANGE, [0, 99999], true ], // criteria values - all criteria are numeric here
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false] // match any / all filter
);
private function _getCnd($op, $val, $tbl)
{
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;
}
$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 createSQLForCriterium(&$cr)
{
if (in_array($cr[0], array_keys($this->genericFilter)))
if ($genCr = $this->genericCriterion($cr))
return $genCr;
unset($cr);
$this->error = true;
return [1];
}
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
//string
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name']))
$parts[] = $_;
return $parts;
}
protected function cbUseAny($cr, $value)
{
if (Util::checkNumeric($cr[2], NUM_CAST_INT) && $this->int2Op($cr[1]))
return $this->_getCnd($cr[1], $cr[2], $this->criterion2field[$cr[0]]);
return false;
}
protected function cbUseAll($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
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 ($cr[1] == '=')
$cr[1] = '==';
$op = $cr[1];
if ($cr[1] == '<=' && $cr[2])
$op = '>';
else if ($cr[1] == '<' && $cr[2])
$op = '>=';
else if ($cr[1] == '!=' && $cr[2])
$op = '==';
$ids = array_filter($this->totalUses, function ($x) use ($op, $cr) { return eval('return '.$x.' '.$op.' '.$cr[2].';'); });
if ($cr[1] != $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

View File

@@ -8,14 +8,18 @@ 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 *, id AS ARRAY_KEY FROM ?_itemset `set`'; protected $queryBase = 'SELECT `set`.*, `set`.id AS ARRAY_KEY FROM ?_itemset `set`';
protected $queryOpts = ['set' => ['o' => 'maxlevel DESC']]; protected $queryOpts = array(
'set' => ['o' => 'maxlevel DESC'],
'e' => ['j' => ['?_events e ON e.id = `set`.eventId', true], 's' => ', e.holidayId']
);
public function __construct($conditions = []) public function __construct($conditions = [])
{ {
@@ -88,7 +92,77 @@ class ItemsetList extends BaseType
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').'">'.Util::jsEscape($this->getField('name', true)).'</span><br />';
$nClasses = 0;
if ($_ = $this->getField('classMask'))
{
$cl = Lang::getClassString($_, $__, $nClasses);
$x .= Util::ucFirst($nClasses > 1 ? Lang::game('classes') : Lang::game('class')).Lang::main('colon').$cl.'<br />';
}
if ($_ = $this->getField('contentGroup'))
$x .= Util::jsEscape(Lang::itemset('notes', $_)).($this->getField('heroic') ? ' <i class="q2">('.Lang::item('heroic').')</i>' : '').'<br />';
if (!$nClasses || !$this->getField('contentGroup'))
$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>'.Util::jsEscape($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;
}
} }
@@ -97,34 +171,40 @@ class ItemsetListFilter extends Filter
{ {
// cr => [type, field, misc, extraCol] // cr => [type, field, misc, extraCol]
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'id', null, true], // id 2 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
3 => [FILTER_CR_NUMERIC, 'npieces', ], // pieces 3 => [FILTER_CR_NUMERIC, 'npieces', NUM_CAST_INT ], // pieces
4 => [FILTER_CR_STRING, 'bonusText', true ], // bonustext 4 => [FILTER_CR_STRING, 'bonusText', STR_LOCALIZED ], // bonustext
5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic 5 => [FILTER_CR_BOOLEAN, 'heroic', ], // heroic
6 => [FILTER_CR_ENUM, 'holidayId', ], // relatedevent 6 => [FILTER_CR_ENUM, 'e.holidayId', ], // relatedevent
8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments 8 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots 9 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos 10 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
12 => [FILTER_CR_NYI_PH, null, 1 ] // available to players [yn] - ugh .. scan loot, quest and vendor templates and write to ?_itemset
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [2, 12], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 424]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C};:]/ui', true ], // criteria values - only printable chars, no delimiters
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / description - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'qu' => [FILTER_V_RANGE, [0, 7], true ], // quality
'ty' => [FILTER_V_RANGE, [1, 12], true ], // set type
'minle' => [FILTER_V_RANGE, [1, 999], false], // min item level
'maxle' => [FILTER_V_RANGE, [1, 999], false], // max itemlevel
'minrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // min required level
'maxrl' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // max required level
'cl' => [FILTER_V_LIST, [[1, 9], 11], false], // class
'ta' => [FILTER_V_RANGE, [1, 30], false] // tag / content group
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
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); unset($cr);
$this->error = true; $this->error = true;
return [1]; return [1];
@@ -142,65 +222,35 @@ class ItemsetListFilter extends Filter
// quality [enum] // quality [enum]
if (isset($_v['qu'])) if (isset($_v['qu']))
$parts[] = ['quality', (array)$_v['qu']]; $parts[] = ['quality', $_v['qu']];
// type [enum] // type [enum]
if (isset($_v['ty'])) if (isset($_v['ty']))
$parts[] = ['type', (array)$_v['ty']]; $parts[] = ['type', $_v['ty']];
// itemLevel min [int] // itemLevel min [int]
if (isset($_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 (isset($_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 (isset($_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 (isset($_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 (isset($_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 (isset($_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;
} }

View File

@@ -8,10 +8,15 @@ 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 *, id AS ARRAY_KEY FROM ?_pet p'; protected $queryBase = 'SELECT *, p.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()
{ {

View File

@@ -4,144 +4,81 @@ 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
{ {
public static $type = 0; // profiles dont actually have one use profilerHelper, listviewHelper;
public static $brickFile = 'profile';
protected $queryBase = ''; // SELECT p.*, p.id AS ARRAY_KEY FROM ?_profiles p'; public function getListviewData($addInfo = 0, array $reqCols = [])
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 __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 $__)
{ {
$tDistrib = $this->getTalentDistribution(); if ($this->getField('user') && User::$id != $this->getField('user') && !($this->getField('cuFlags') & PROFILER_CU_PUBLISHED))
continue;
if (($addInfo & PROFILEINFO_PROFILE) && !$this->isCustom())
continue;
if (($addInfo & PROFILEINFO_CHARACTER) && $this->isCustom())
continue;
$data[$this->id] = array( $data[$this->id] = array(
'id' => 0, 'id' => $this->getField('id'),
'name' => $this->curTpl['name'], 'name' => $this->getField('name'),
'achievementpoints' => $this->curTpl['achievementpoints'], 'race' => $this->getField('race'),
'guild' => $this->curTpl['guild'], // 0 if none 'classs' => $this->getField('class'),
'guildRank' => -1, 'gender' => $this->getField('gender'),
'realm' => $this->curTpl['realm'][0], 'level' => $this->getField('level'),
'realmname' => $this->curTpl['realm'][1], 'faction' => (1 << ($this->getField('race') - 1)) & RACE_MASK_ALLIANCE ? 0 : 1,
'battlegroup' => $this->curTpl['battlegroup'][0], 'talenttree1' => $this->getField('talenttree1'),
'battlegroupname' => $this->curTpl['battlegroup'][0], 'talenttree2' => $this->getField('talenttree2'),
'region' => $this->curTpl['region'][0], 'talenttree3' => $this->getField('talenttree3'),
'level' => $this->curTpl['level'], 'talentspec' => $this->getField('activespec') + 1, // 0 => 1; 1 => 2
'race' => $this->curTpl['race'], 'achievementpoints' => $this->getField('achievementpoints'),
'gender' => $this->curTpl['gender'], 'guild' => '$"'.$this->getField('guildname').'"', // force this to be a string
'classs' => $this->curTpl['classs'], 'guildrank' => $this->getField('guildrank'),
'faction' => $this->curTpl['faction'], 'realm' => Profiler::urlize($this->getField('realmName')),
'talenttree1' => $tDistrib[0], 'realmname' => $this->getField('realmName'),
'talenttree2' => $tDistrib[1], // 'battlegroup' => Profiler::urlize($this->getField('battlegroup')), // was renamed to subregion somewhere around cata release
'talenttree3' => $tDistrib[2], // 'battlegroupname' => $this->getField('battlegroup'),
'talentspec' => $this->curTpl['talents']['active'] 'gearscore' => $this->getField('gearscore')
); );
if (!empty($this->curTpl['description']))
$data[$this->id]['description'] = $this->curTpl['description'];
if (!empty($this->curTpl['icon'])) // for the lv this determins if the link is profile=<id> or profile=<region>.<realm>.<name>
$data[$this->id]['icon'] = $this->curTpl['icon']; if ($this->isCustom())
$data[$this->id]['published'] = (int)!!($this->getField('cuFlags') & PROFILER_CU_PUBLISHED);
else
$data[$this->id]['region'] = Profiler::urlize($this->getField('region'));
if ($this->curTpl['cuFlags'] & PROFILE_CU_PUBLISHED) if ($addInfo & PROFILEINFO_ARENA)
$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');
}
if ($this->curTpl['cuFlags'] & PROFILE_CU_PINNED) // Filter asked for skills - add them
foreach ($reqCols as $col)
$data[$this->id][$col] = $this->getField($col);
if ($addInfo & PROFILEINFO_PROFILE)
if ($_ = $this->getField('description'))
$data[$this->id]['description'] = $_;
if ($addInfo & PROFILEINFO_PROFILE)
if ($_ = $this->getField('icon'))
$data[$this->id]['icon'] = $_;
if ($this->getField('cuFlags') & PROFILER_CU_PINNED)
$data[$this->id]['pinned'] = 1; $data[$this->id]['pinned'] = 1;
if ($this->curTpl['cuFlags'] & PROFILE_CU_DELETED) if ($this->getField('cuFlags') & PROFILER_CU_DELETED)
$data[$this->id]['deleted'] = 1; $data[$this->id]['deleted'] = 1;
} }
return $data; return array_values($data);
} }
public function renderTooltip($interactive = false) public function renderTooltip($interactive = false)
@@ -149,65 +86,178 @@ class ProfileList extends BaseType
if (!$this->curTpl) if (!$this->curTpl)
return []; return [];
$title = '';
$name = $this->getField('name');
if ($_ = $this->getField('chosenTitle'))
$title = (new TitleList(array(['bitIdx', $_])))->getField($this->getField('gender') ? 'female' : 'male', true);
if ($this->isCustom())
$name .= ' (Custom Profile)';
else if ($title)
$name = sprintf($title, $name);
$x = '<table>'; $x = '<table>';
$x .= '<tr><td><b class="q">'.$this->getField('name').'</b></td></tr>'; $x .= '<tr><td><b class="q">'.$name.'</b></td></tr>';
if ($g = $this->getField('name')) if ($g = $this->getField('guildname'))
$x .= '<tr><td>&lt;'.$g.'&gt; ('.$this->getField('guildrank').')</td></tr>'; $x .= '<tr><td>&lt;'.$g.'&gt;</td></tr>';
else if ($d = $this->getField('description')) else if ($d = $this->getField('description'))
$x .= '<tr><td>'.$d.'</td></tr>'; $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 .= '<tr><td>'.Lang::game('level').' '.$this->getField('level').' '.Lang::game('ra', $this->getField('race')).' '.Lang::game('cl', $this->getField('class')).'</td></tr>';
$x .= '</table>'; $x .= '</table>';
return $x; return $x;
} }
public function getJSGlobals($addMask = 0) {} public function getJSGlobals($addMask = 0)
private function getTalentDistribution()
{ {
if (!empty($this->tDistribution)) $data = [];
$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)); $realms = Profiler::getRealms();
$result = []; foreach ($this->iterate() as $id => $__)
$start = 0;
foreach ($this->tDistribution[$this->curTpl['classId']] as $len)
{ {
$result[] = array_sum(str_split(substr($this->curTpl['talentString'], $start, $len))); if (($addMask & PROFILEINFO_PROFILE) && ($this->getField('cuFlags') & PROFILER_CU_PROFILE))
$start += $len; {
$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->getField('cuFlags') & PROFILER_CU_PROFILE))
{
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 $result; return $data;
}
public function isCustom()
{
return $this->getField('cuFlags') & PROFILER_CU_PROFILE;
} }
} }
class ProfileListFilter extends Filter class ProfileListFilter extends Filter
{ {
public $extraOpts = []; public $useLocalList = false;
public $extraOpts = [];
protected $genericFilter = array( private $realms = [];
protected $enums = array(
-1 => array( // arena team sizes
// by name by rating by contrib
12 => 2, 13 => 2, 14 => 2,
15 => 3, 16 => 3, 17 => 3,
18 => 5, 19 => 5, 20 => 5
)
); );
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
2 => [FILTER_CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
3 => [FILTER_CR_NUMERIC, 'achievementpoints', NUM_CAST_INT ], // achievementpoints [num]
5 => [FILTER_CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
6 => [FILTER_CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
7 => [FILTER_CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
9 => [FILTER_CR_STRING, 'g.name', ], // guildname
10 => [FILTER_CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
12 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname2v2
15 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname3v3
18 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname5v5
13 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng2v2
16 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng3v3
19 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng5v5
14 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib2v2 [num]
17 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib3v3 [num]
20 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib5v5 [num]
21 => [FILTER_CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
23 => [FILTER_CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
25 => [FILTER_CR_CALLBACK, 'cbProfession', 171, null], // alchemy [num]
26 => [FILTER_CR_CALLBACK, 'cbProfession', 164, null], // blacksmithing [num]
27 => [FILTER_CR_CALLBACK, 'cbProfession', 333, null], // enchanting [num]
28 => [FILTER_CR_CALLBACK, 'cbProfession', 202, null], // engineering [num]
29 => [FILTER_CR_CALLBACK, 'cbProfession', 182, null], // herbalism [num]
30 => [FILTER_CR_CALLBACK, 'cbProfession', 773, null], // inscription [num]
31 => [FILTER_CR_CALLBACK, 'cbProfession', 755, null], // jewelcrafting [num]
32 => [FILTER_CR_CALLBACK, 'cbProfession', 165, null], // leatherworking [num]
33 => [FILTER_CR_CALLBACK, 'cbProfession', 186, null], // mining [num]
34 => [FILTER_CR_CALLBACK, 'cbProfession', 393, null], // skinning [num]
35 => [FILTER_CR_CALLBACK, 'cbProfession', 197, null], // tailoring [num]
36 => [FILTER_CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [1, 36], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 5000]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/[\p{C};]/ui', true ], // criteria values
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'ex' => [FILTER_V_EQUAL, 'on', false], // only match exact
'si' => [FILTER_V_LIST, [1, 2], false], // side
'ra' => [FILTER_V_LIST, [[1, 8], 10, 11], true ], // race
'cl' => [FILTER_V_LIST, [[1, 9], 11], true ], // class
'minle' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // min level
'maxle' => [FILTER_V_RANGE, [1, MAX_LEVEL], false], // max level
'rg' => [FILTER_V_CALLBACK, 'cbRegionCheck', false], // region
'sv' => [FILTER_V_CALLBACK, 'cbServerCheck', false], // server
);
/* 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($fromPOST = false, $opts = [])
{
if (!empty($opts['realms']))
$this->realms = $opts['realms'];
else
$this->realms = array_keys(Profiler::getRealms());
parent::__construct($fromPOST, $opts);
if (!empty($this->fiData['c']['cr']))
if (array_intersect($this->fiData['c']['cr'], [2, 3, 5, 6, 7, 21]))
$this->useLocalList = true;
}
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCR = $this->genericCriterion($cr)) if ($genCR = $this->genericCriterion($cr))
return $genCR; return $genCR;
unset($cr);
$this->error = true;
return [1];
}
switch ($cr[0])
{
default:
break;
}
unset($cr); unset($cr);
$this->error = 1; $this->error = true;
return [1]; return [1];
} }
@@ -216,13 +266,446 @@ class ProfileListFilter extends Filter
$parts = []; $parts = [];
$_v = $this->fiData['v']; $_v = $this->fiData['v'];
// name // region (rg), battlegroup (bg) and server (sv) are passed to ProflieList as miscData and handled there
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name_loc'.User::$localeId])) // table key differs between remote and local :<
$parts[] = $_; $k = $this->useLocalList ? 'p' : 'c';
// name [str] - the table is case sensitive. Since i down't want to destroy indizes, lets alter the search terms
if (!empty($_v['na']))
{
$lower = $this->modularizeString([$k.'.name'], Util::lower($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on');
$proper = $this->modularizeString([$k.'.name'], Util::ucWords($_v['na']), !empty($_v['ex']) && $_v['ex'] == 'on');
$parts[] = ['OR', $lower, $proper];
}
// side [list]
if (!empty($_v['si']))
{
if ($_v['si'] == 1)
$parts[] = [$k.'.race', [1, 3, 4, 7, 11]];
else if ($_v['si'] == 2)
$parts[] = [$k.'.race', [2, 5, 6, 8, 10]];
}
// race [list]
if (!empty($_v['ra']))
$parts[] = [$k.'.race', $_v['ra']];
// class [list]
if (!empty($_v['cl']))
$parts[] = [$k.'.class', $_v['cl']];
// min level [int]
if (isset($_v['minle']))
$parts[] = [$k.'.level', $_v['minle'], '>='];
// max level [int]
if (isset($_v['maxle']))
$parts[] = [$k.'.level', $_v['maxle'], '<='];
return $parts; return $parts;
} }
protected function cbRegionCheck(&$v)
{
if ($v == 'eu' || $v == 'us')
{
$this->parentCats[0] = $v; // directly redirect onto this region
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbServerCheck(&$v)
{
foreach (Profiler::getRealms() as $realm)
if ($realm['name'] == $v)
{
$this->parentCats[1] = Profiler::urlize($v);// directly redirect onto this server
$v = ''; // remove from filter
return true;
}
return false;
}
protected function cbProfession($cr, $skillId)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return;
$k = 'sk_'.Util::createHash(12);
$col = 'skill'.$skillId;
$this->formData['extraCols'][$skillId] = $col;
if ($this->useLocalList)
{
$this->extraOpts[$k] = array(
'j' => ['?_profiler_completion '.$k.' ON '.$k.'.id = p.id AND '.$k.'.`type` = '.TYPE_SKILL.' AND '.$k.'.typeId = '.$skillId.' AND '.$k.'.cur '.$cr[1].' '.$cr[2], true],
's' => [', '.$k.'.cur AS '.$col]
);
return [$k.'.typeId', null, '!'];
}
else
{
$this->extraOpts[$k] = array(
'j' => ['character_skills '.$k.' ON '.$k.'.guid = c.guid AND '.$k.'.skill = '.$skillId.' AND '.$k.'.value '.$cr[1].' '.$cr[2], true],
's' => [', '.$k.'.value AS '.$col]
);
return [$k.'.skill', null, '!'];
}
}
protected function cbCompletedAcv($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT))
return false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_achievement WHERE id = ?d', $cr[2]))
return false;
$k = 'acv_'.Util::createHash(12);
if ($this->useLocalList)
{
$this->extraOpts[$k] = ['j' => ['?_profiler_completion '.$k.' ON '.$k.'.id = p.id AND '.$k.'.`type` = '.TYPE_ACHIEVEMENT.' AND '.$k.'.typeId = '.$cr[2], true]];
return [$k.'.typeId', null, '!'];
}
else
{
$this->extraOpts[$k] = ['j' => ['character_achievement '.$k.' ON '.$k.'.guid = c.guid AND '.$k.'.achievement = '.$cr[2], true]];
return [$k.'.achievement', null, '!'];
}
}
protected function cbWearsItems($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT))
return false;
if (!DB::Aowow()->selectCell('SELECT 1 FROM ?_items WHERE id = ?d', $cr[2]))
return false;
$k = 'i_'.Util::createHash(12);
$this->extraOpts[$k] = ['j' => ['?_profiler_items '.$k.' ON '.$k.'.id = p.id AND '.$k.'.item = '.$cr[2], true]];
return [$k.'.item', null, '!'];
}
protected function cbHasGuild($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($this->useLocalList)
return ['p.guild', null, $cr[1] ? '!' : null];
else
return ['gm.guildId', null, $cr[1] ? '!' : null];
}
protected function cbHasGuildRank($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
if ($this->useLocalList)
return ['p.guildrank', $cr[2], $cr[1]];
else
return ['gm.rank', $cr[2], $cr[1]];
}
protected function cbTeamName($cr)
{
if ($_ = $this->modularizeString(['at.name'], $cr[2]))
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], $_];
return false;
}
protected function cbTeamRating($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
return ['AND', ['at.type', $this->enums[-1][$cr[0]]], ['at.rating', $cr[2], $cr[1]]];
}
} }
class RemoteProfileList extends ProfileList
{
protected $queryBase = 'SELECT `c`.*, `c`.`guid` AS ARRAY_KEY FROM characters c';
protected $queryOpts = array(
'c' => [['gm', 'g', 'ca', 'ct'], 'g' => 'ARRAY_KEY', 'o' => 'level DESC, name ASC'],
'ca' => ['j' => ['character_achievement ca ON ca.guid = c.guid', true], 's' => ', GROUP_CONCAT(DISTINCT ca.achievement SEPARATOR " ") AS _acvs'],
'ct' => ['j' => ['character_talent ct ON ct.guid = c.guid AND ct.spec = c.activespec', true], 's' => ', GROUP_CONCAT(DISTINCT ct.spell SEPARATOR " ") AS _talents'],
'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']
);
public function __construct($conditions = [], $miscData = null)
{
// select DB by realm
if (!$this->selectRealms($miscData))
{
trigger_error('no access to auth-db or table realmlist is empty', 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();
$acvCache = [];
$talentCache = [];
$atCache = [];
$distrib = null;
$talentData = [];
$limit = CFG_SQL_LIMIT_DEFAULT;
foreach ($conditions as $c)
if (is_int($c))
$limit = $c;
// post processing
foreach ($this->iterate() as $guid => &$curTpl)
{
// battlegroup
$curTpl['battlegroup'] = CFG_BATTLEGROUP;
// realm
$r = explode(':', $guid)[0];
if (!empty($realms[$r]))
{
$curTpl['realm'] = $r;
$curTpl['realmName'] = $realms[$r]['name'];
$curTpl['region'] = $realms[$r]['region'];
}
else
{
trigger_error('character "'.$curTpl['name'].'" belongs to nonexistant realm #'.$r, E_USER_WARNING);
unset($this->templates[$guid]);
continue;
}
// temp id
$curTpl['id'] = 0;
// achievement points pre
if ($acvs = explode(' ', $curTpl['_acvs']))
foreach ($acvs as $a)
if ($a && !isset($acvCache[$a]))
$acvCache[$a] = $a;
// talent points pre
if ($talents = explode(' ', $curTpl['_talents']))
foreach ($talents as $t)
if ($t && !isset($talentCache[$t]))
$talentCache[$t] = $t;
// equalize distribution
if ($limit != CFG_SQL_LIMIT_NONE)
{
if (empty($distrib[$curTpl['realm']]))
$distrib[$curTpl['realm']] = 1;
else
$distrib[$curTpl['realm']]++;
}
$curTpl['cuFlags'] = 0;
}
if ($talentCache)
$talentData = DB::Aowow()->select('SELECT spell AS ARRAY_KEY, tab, rank FROM ?_talents WHERE spell IN (?a)', $talentCache);
if ($distrib !== null)
{
$total = array_sum($distrib);
foreach ($distrib as &$d)
$d = ceil($limit * $d / $total);
}
if ($acvCache)
$acvCache = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, points FROM ?_achievement WHERE id IN (?a)', $acvCache);
foreach ($this->iterate() as $guid => &$curTpl)
{
if ($distrib !== null)
{
if ($limit <= 0 || $distrib[$curTpl['realm']] <= 0)
{
unset($this->templates[$guid]);
continue;
}
$distrib[$curTpl['realm']]--;
$limit--;
}
$a = explode(' ', $curTpl['_acvs']);
$t = explode(' ', $curTpl['_talents']);
unset($curTpl['_acvs']);
unset($curTpl['_talents']);
// achievement points post
$curTpl['achievementpoints'] = array_sum(array_intersect_key($acvCache, array_combine($a, $a)));
// talent points post
$curTpl['talenttree1'] = 0;
$curTpl['talenttree2'] = 0;
$curTpl['talenttree3'] = 0;
foreach ($talentData as $spell => $data)
if (in_array($spell, $t))
$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 => $__)
{
$baseData[$guid] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guid'),
'name' => $this->getField('name'),
'race' => $this->getField('race'),
'class' => $this->getField('class'),
'level' => $this->getField('level'),
'gender' => $this->getField('gender'),
'guild' => $this->getField('guild') ?: null,
'guildrank' => $this->getField('guild') ? $this->getField('guildrank') : null,
'cuFlags' => PROFILER_CU_NEEDS_RESYNC
);
if ($this->getField('guild'))
$guildData[] = array(
'realm' => $this->getField('realm'),
'realmGUID' => $this->getField('guild'),
'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 IGNORE INTO ?_profiler_guild (?#) VALUES '.$ins, 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 (Util::createSqlBatchInsert($baseData) as $ins)
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_profiles (?#) VALUES '.$ins, array_keys(reset($baseData)));
// 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($conditions = [], $miscData = null)
{
parent::__construct($conditions, $miscData);
if ($this->error)
return;
$realms = Profiler::getRealms();
// post processing
$acvPoints = DB::Aowow()->selectCol('SELECT pc.id AS ARRAY_KEY, SUM(a.points) FROM ?_profiler_completion pc LEFT JOIN ?_achievement a ON a.id = pc.typeId WHERE pc.`type` = ?d AND pc.id IN (?a) GROUP BY pc.id', TYPE_ACHIEVEMENT, $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_BATTLEGROUP;
$curTpl['achievementpoints'] = isset($acvPoints[$id]) ? $acvPoints[$id] : 0;
}
}
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'))
));
}
}
?> ?>

View File

@@ -8,6 +8,7 @@ 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 = [];
@@ -18,6 +19,7 @@ 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($conditions = [], $miscData = null) public function __construct($conditions = [], $miscData = null)
@@ -33,7 +35,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 (Util::$questClasses as $k => $arr) foreach (Game::$questClasses as $k => $arr)
{ {
if (in_array($_curTpl['cat1'], $arr)) if (in_array($_curTpl['cat1'], $arr))
{ {
@@ -120,12 +122,18 @@ 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($strict = false) public function isDaily()
{ {
if ($strict) if ($this->curTpl['flags'] & QUEST_FLAG_DAILY)
return $this->curTpl['flags'] & QUEST_FLAG_DAILY; return 1;
else
return $this->curTpl['flags'] & (QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY) || $this->curTpl['specialFlags'] & QUEST_FLAG_SPECIAL_MONTHLY; if ($this->curTpl['flags'] & QUEST_FLAG_WEEKLY)
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..?
@@ -164,7 +172,7 @@ class QuestList extends BaseType
foreach ($this->iterate() as $__) foreach ($this->iterate() as $__)
{ {
if (!(Util::sideByRaceMask($this->curTpl['reqRaceMask']) & $side)) if (!(Game::sideByRaceMask($this->curTpl['reqRaceMask']) & $side))
continue; continue;
list($series, $first) = DB::Aowow()->SelectRow( list($series, $first) = DB::Aowow()->SelectRow(
@@ -201,7 +209,7 @@ class QuestList extends BaseType
'level' => $this->curTpl['level'], 'level' => $this->curTpl['level'],
'reqlevel' => $this->curTpl['minLevel'], 'reqlevel' => $this->curTpl['minLevel'],
'name' => $this->getField('name', true), 'name' => $this->getField('name', true),
'side' => Util::sideByRaceMask($this->curTpl['reqRaceMask']), 'side' => Game::sideByRaceMask($this->curTpl['reqRaceMask']),
'wflags' => 0x0, 'wflags' => 0x0,
'xp' => $this->curTpl['rewardXP'] 'xp' => $this->curTpl['rewardXP']
); );
@@ -241,6 +249,8 @@ 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)
{ {
@@ -399,10 +409,8 @@ class QuestList extends BaseType
// 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 => $__)
$_ = $this->rewards[$this->id][TYPE_CURRENCY]; $data[TYPE_CURRENCY][$id] = $id;
$data[TYPE_CURRENCY] = array_combine(array_keys($_), array_keys($_));
}
} }
if ($addMask & GLOBALINFO_SELF) if ($addMask & GLOBALINFO_SELF)
@@ -422,189 +430,64 @@ class QuestListFilter extends Filter
38 => [null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false], 38 => [null, 1, 2, 3, 4, 5, 6, 7, 8, null, 10, 11, true, false],
); );
protected $genericFilter = array( protected $genericFilter = array(
1 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null], // increasesrepwith
2 => [FILTER_CR_NUMERIC, 'rewardXP', NUM_CAST_INT ], // experiencegained
3 => [FILTER_CR_NUMERIC, 'rewardOrReqMoney', NUM_CAST_INT ], // moneyrewarded
4 => [FILTER_CR_CALLBACK, 'cbSpellRewards', null, null], // spellrewarded [yn]
5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable
6 => [FILTER_CR_NUMERIC, 'timeLimit', NUM_CAST_INT ], // timer
7 => [FILTER_CR_NYI_PH, null, 1 ], // firstquestseries
9 => [FILTER_CR_CALLBACK, 'cbEarnReputation', null, null], // objectiveearnrepwith [enum]
10 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null], // decreasesrepwith
11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', NUM_CAST_INT ], // suggestedplayers
15 => [FILTER_CR_NYI_PH, null, 1 ], // lastquestseries
16 => [FILTER_CR_NYI_PH, null, 1 ], // partseries
18 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_SCREENSHOT ], // hasscreenshots
19 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 0x1, null], // startsfrom [enum]
21 => [FILTER_CR_CALLBACK, 'cbQuestRelation', 0x2, null], // endsat [enum]
22 => [FILTER_CR_CALLBACK, 'cbItemRewards', null, null], // itemrewards [op] [int]
23 => [FILTER_CR_CALLBACK, 'cbItemChoices', null, null], // itemchoices [op] [int]
24 => [FILTER_CR_CALLBACK, 'cbLacksStartEnd', null, null], // lacksstartend [yn]
25 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_COMMENT ], // hascomments
27 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily 27 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_DAILY ], // daily
28 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly 28 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_WEEKLY ], // weekly
29 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_REPEATABLE ], // repeatable 29 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_REPEATABLE ], // repeatable
30 => [FILTER_CR_NUMERIC, 'id', null, true], // id 30 => [FILTER_CR_NUMERIC, 'id', NUM_CAST_INT, true], // id
5 => [FILTER_CR_FLAG, 'flags', QUEST_FLAG_SHARABLE ], // sharable 33 => [FILTER_CR_ENUM, 'e.holidayId' ], // relatedevent
11 => [FILTER_CR_NUMERIC, 'suggestedPlayers', ], // suggestedplayers 34 => [FILTER_CR_CALLBACK, 'cbAvailable', null, null], // availabletoplayers [yn]
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 36 => [FILTER_CR_FLAG, 'cuFlags', CUSTOM_HAS_VIDEO ], // hasvideos
37 => [FILTER_CR_CALLBACK, 'cbClassSpec', null, null], // classspecific [enum]
37 => [FILTER_CR_CALLBACK, 'cbRaceSpec', null, null], // racespecific [enum]
42 => [FILTER_CR_STAFFFLAG, 'flags' ], // flags
43 => [FILTER_CR_CALLBACK, 'cbCurrencyReward', null, null], // currencyrewarded [enum]
44 => [FILTER_CR_CALLBACK, 'cbLoremaster', null, null], // countsforloremaster_stc [yn]
45 => [FILTER_CR_BOOLEAN, 'rewardTitleId' ] // titlerewarded
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'cr' => [FILTER_V_RANGE, [1, 45], true ], // criteria ids
'crs' => [FILTER_V_LIST, [FILTER_ENUM_NONE, FILTER_ENUM_ANY, [0, 99999]], true ], // criteria operators
'crv' => [FILTER_V_REGEX, '/\D/', true ], // criteria values - only numerals
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name / text - only printable chars, no delimiter
'ex' => [FILTER_V_EQUAL, 'on', false], // also match subname
'ma' => [FILTER_V_EQUAL, 1, false], // match any / all filter
'minle' => [FILTER_V_RANGE, [1, 99], false], // min quest level
'maxle' => [FILTER_V_RANGE, [1, 99], false], // max quest level
'minrl' => [FILTER_V_RANGE, [1, 99], false], // min required level
'maxrl' => [FILTER_V_RANGE, [1, 99], false], // max required level
'si' => [FILTER_V_LIST, [-2, -1, 1, 2, 3], false], // siede
'ty' => [FILTER_V_LIST, [0, 1, 21, 41, 62, [81, 85], 88, 89], true ] // type
); );
protected function createSQLForCriterium(&$cr) protected function createSQLForCriterium(&$cr)
{ {
if (in_array($cr[0], array_keys($this->genericFilter))) if (in_array($cr[0], array_keys($this->genericFilter)))
{
if ($genCr = $this->genericCriterion($cr)) if ($genCr = $this->genericCriterion($cr))
return $genCr; return $genCr;
unset($cr);
$this->error = true;
return [1];
}
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;
$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); unset($cr);
$this->error = 1; $this->error = true;
return [1]; return [1];
} }
@@ -628,39 +511,19 @@ class QuestListFilter extends Filter
// level min // level min
if (isset($_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 (isset($_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 (isset($_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 (isset($_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 (isset($_v['si'])) if (isset($_v['si']))
@@ -685,23 +548,172 @@ class QuestListFilter extends Filter
case -1: case -1:
$parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']]; $parts[] = ['AND', $ex, ['reqRaceMask', RACE_MASK_ALLIANCE, '&']];
break; break;
default:
unset($_v['si']);
} }
} }
// type [list] // type [list]
if (isset($_v['ty'])) 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($cr, $sign)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT) || $cr[1] <= 0)
return false;
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_factions WHERE id = ?d', $cr[1]))
$this->formData['reputationCols'][] = [$cr[1], Util::localizedString($_, 'name')];
return [
'OR',
['AND', ['rewardFactionId1', $cr[1]], ['rewardFactionValue1', 0, $sign]],
['AND', ['rewardFactionId2', $cr[1]], ['rewardFactionValue2', 0, $sign]],
['AND', ['rewardFactionId3', $cr[1]], ['rewardFactionValue3', 0, $sign]],
['AND', ['rewardFactionId4', $cr[1]], ['rewardFactionValue4', 0, $sign]],
['AND', ['rewardFactionId5', $cr[1]], ['rewardFactionValue5', 0, $sign]]
];
}
protected function cbQuestRelation($cr, $flags)
{
switch ($cr[1])
{
case 1: // npc
return ['AND', ['qse.type', TYPE_NPC], ['qse.method', $flags, '&']];
case 2: // object
return ['AND', ['qse.type', TYPE_OBJECT], ['qse.method', $flags, '&']];
case 3: // item
return ['AND', ['qse.type', TYPE_ITEM], ['qse.method', $flags, '&']];
}
return false;
}
protected function cbCurrencyReward($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT) || $cr[1] <= 0)
return false;
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]]
];
}
protected function cbAvailable($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
if ($cr[1])
return [['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0];
else
return ['cuFlags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'];
}
protected function cbItemChoices($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
$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];
}
protected function cbItemRewards($cr)
{
if (!Util::checkNumeric($cr[2], NUM_CAST_INT) || !$this->int2Op($cr[1]))
return false;
$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];
}
protected function cbLoremaster($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
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 , '&']];;
}
protected function cbSpellRewards($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
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]];
}
protected function cbEarnReputation($cr)
{
if (!Util::checkNumeric($cr[1], NUM_REQ_INT))
return false;
if ($cr[1] > 0)
return ['OR', ['reqFactionId1', $cr[1]], ['reqFactionId2', $cr[1]]];
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]];
return false;
}
protected function cbClassSpec($cr)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
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, '!']];
return false;
}
protected function cbRaceSpec($cr)
{
if (!isset($this->enums[$cr[0]][$cr[1]]))
return false;
$_ = $this->enums[$cr[0]][$cr[1]];
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, '!']];
return false;
}
protected function cbLacksStartEnd($cr)
{
if (!$this->int2Bool($cr[1]))
return false;
$missing = DB::Aowow()->selectCol('SELECT questId, max(method) a, min(method) b FROM ?_quests_startend GROUP BY questId HAVING (a | b) <> 3');
if ($cr[1])
return ['id', $missing];
else
return ['id', $missing, '!'];
}
} }

View File

@@ -8,11 +8,12 @@ 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.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' => [['si']], 'sl' => [['ic']],
'si' => ['j' => '?_icons si ON si.id = sl.iconId', 's' => ', si.iconString'], 'ic' => ['j' => ['?_icons ic ON ic.id = sl.iconId', true], 's' => ', ic.name AS iconString'],
); );
public function __construct($conditions = []) public function __construct($conditions = [])
@@ -31,6 +32,9 @@ class SkillList extends BaseType
while (count($_) < 5) while (count($_) < 5)
$_[] = 0; $_[] = 0;
} }
if (!$_curTpl['iconId'])
$_curTpl['iconString'] = 'inv_misc_questionmark';
} }
} }
@@ -53,7 +57,7 @@ class SkillList extends BaseType
'name' => Util::jsEscape($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']), 'specializations' => Util::toJSON($this->curTpl['specializations'], JSON_NUMERIC_CHECK),
'icon' => Util::jsEscape($this->curTpl['iconString']) 'icon' => Util::jsEscape($this->curTpl['iconString'])
); );
} }

View File

@@ -0,0 +1,136 @@
<?php
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.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($conditions = [])
{
parent::__construct($conditions);
// 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, `type` FROM ?_sounds_files sf WHERE id IN (?a)', array_keys($this->fileBuffer));
foreach ($files as $id => $data)
{
// skipp file extension
$data['title'] = substr($data['title'], 0, -4);
// enum to string
$data['type'] = self::$fileTypes[$data['type']];
// get real url
$data['url'] = STATIC_URL . '/wowsounds/' . $data['id'];
// v push v
$this->fileBuffer[$id] = $data;
}
}
}
public static function getName($id)
{
$this->getEntry($id);
return $this->getField('name');
}
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' => Util::jsEscape($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
{
// we have no criteria for this one...
protected function createSQLForCriterium(&$cr)
{
unset($cr);
$this->error = true;
return [1];
}
// fieldId => [checkType, checkValue[, fieldIsArray]]
protected $inputFields = array(
'na' => [FILTER_V_REGEX, '/[\p{C};]/ui', false], // name - only printable chars, no delimiter
'ty' => [FILTER_V_LIST, [[1, 4], 6, 9, 10, 12, 13, 14, 16, 17, [19, 23], [25, 31], 50, 52, 53], true ] // type
);
protected function createSQLForValues()
{
$parts = [];
$_v = &$this->fiData['v'];
// name [str]
if (isset($_v['na']))
if ($_ = $this->modularizeString(['name']))
$parts[] = $_;
// type [list]
if (isset($_v['ty']))
$parts[] = ['cat', $_v['ty']];
return $parts;
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -8,16 +8,17 @@ 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.*, 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($conditions = []) public function __construct($conditions = [])
{ {

View File

@@ -6,16 +6,17 @@ if (!defined('AOWOW_REVISION'))
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 $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() { }

View File

@@ -6,14 +6,15 @@ if (!defined('AOWOW_REVISION'))
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.id as id, -e.id AS ARRAY_KEY FROM ?_events e'; protected $queryBase = 'SELECT e.*, h.*, e.description AS nameINT, 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($conditions = []) public function __construct($conditions = [])
{ {
@@ -40,22 +41,18 @@ 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['description']; $this->curTpl['name_loc0'] = $this->curTpl['nameINT'];
$this->curTpl['iconString'] = 'trade_engineering'; $this->curTpl['iconString'] = 'trade_engineering';
$this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('description', true); $this->curTpl['name'] = '(SERVERSIDE) '.$this->getField('nameINT', true);
$replace[$this->id] = $this->curTpl; $replace[$this->id] = $this->curTpl;
} }
} }
@@ -69,10 +66,21 @@ class WorldEventList extends BaseType
public static function getName($id) public static function getName($id)
{ {
if ($id > 0) $row = DB::Aowow()->SelectRow('
$row = DB::Aowow()->SelectRow('SELECT * FROM ?_holidays WHERE Id = ?d', intVal($id)); SELECT
else IFNULL(h.name_loc0, e.description) AS name_loc0,
$row = DB::Aowow()->SelectRow('SELECT description as name FROM ?_events WHERE Id = ?d', intVal(-$id)); h.name_loc2,
h.name_loc3,
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');
} }
@@ -158,7 +166,31 @@ class WorldEventList extends BaseType
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>'.Util::jsEscape($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">'.Util::jsEscape($_).'</span></td></tr></table>';
return $x;
}
} }
?> ?>

View File

@@ -8,10 +8,11 @@ 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 *, id AS ARRAY_KEY FROM ?_zones z'; protected $queryBase = 'SELECT *, id AS ARRAY_KEY FROM ?_zones z';
public function __construct($conditions = [], $miscData = null) public function __construct($conditions = [], $miscData = null)
{ {

View File

@@ -17,10 +17,12 @@ class User
public static $dailyVotes = 0; public static $dailyVotes = 0;
public static $ip = null; public static $ip = null;
private static $reputation = 0; private static $reputation = 0;
private static $dataKey = ''; private static $dataKey = '';
private static $expires = false; private static $expires = false;
private static $passHash = ''; private static $passHash = '';
private static $excludeGroups = 1;
private static $profiles = null;
public static function init() public static function init()
{ {
@@ -39,7 +41,7 @@ class User
// check IP bans // check IP bans
if ($ipBan = DB::Aowow()->selectRow('SELECT count, unbanDate FROM ?_account_bannedips WHERE ip = ? AND type = 0', self::$ip)) if ($ipBan = DB::Aowow()->selectRow('SELECT count, unbanDate FROM ?_account_bannedips WHERE ip = ? AND type = 0', self::$ip))
{ {
if ($ipBan['count'] > CFG_FAILED_AUTH_COUNT && $ipBan['unbanDate'] > time()) if ($ipBan['count'] > CFG_ACC_FAILED_AUTH_COUNT && $ipBan['unbanDate'] > time())
return false; return false;
else if ($ipBan['unbanDate'] <= time()) else if ($ipBan['unbanDate'] <= time())
DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE ip = ?', self::$ip); DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE ip = ?', self::$ip);
@@ -54,7 +56,7 @@ class User
return false; return false;
$query = DB::Aowow()->SelectRow(' $query = DB::Aowow()->SelectRow('
SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes SELECT a.id, a.passHash, a.displayName, a.locale, a.userGroups, a.userPerms, a.allowExpire, BIT_OR(ab.typeMask) AS bans, IFNULL(SUM(r.amount), 0) as reputation, a.avatar, a.dailyVotes, a.excludeGroups
FROM ?_account a FROM ?_account a
LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP() LEFT JOIN ?_account_banned ab ON a.id = ab.userId AND ab.end > UNIX_TIMESTAMP()
LEFT JOIN ?_account_reputation r ON a.id = r.userId LEFT JOIN ?_account_reputation r ON a.id = r.userId
@@ -73,15 +75,26 @@ class User
return false; return false;
} }
self::$id = intval($query['id']); self::$id = intval($query['id']);
self::$displayName = $query['displayName']; self::$displayName = $query['displayName'];
self::$passHash = $query['passHash']; self::$passHash = $query['passHash'];
self::$expires = (bool)$query['allowExpire']; self::$expires = (bool)$query['allowExpire'];
self::$reputation = $query['reputation']; self::$reputation = $query['reputation'];
self::$banStatus = $query['bans']; self::$banStatus = $query['bans'];
self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']); self::$groups = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userGroups']);
self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']); self::$perms = $query['bans'] & (ACC_BAN_TEMP | ACC_BAN_PERM) ? 0 : intval($query['userPerms']);
self::$dailyVotes = $query['dailyVotes']; self::$dailyVotes = $query['dailyVotes'];
self::$excludeGroups = $query['excludeGroups'];
$conditions = array(
[['cuFlags', PROFILER_CU_DELETED, '&'], 0],
['OR', ['user', self::$id], ['ap.accountId', self::$id]]
);
if (self::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
array_shift($conditions);
self::$profiles = (new LocalProfileList($conditions));
if ($query['avatar']) if ($query['avatar'])
self::$avatar = $query['avatar']; self::$avatar = $query['avatar'];
@@ -112,7 +125,7 @@ class User
); );
// gain rep for daily visit // gain rep for daily visit
if (!(self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM))) if (!(self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM)) && !self::isInGroup(U_GROUP_PENDING))
Util::gainSiteReputation(self::$id, SITEREP_ACTION_DAILYVISIT); Util::gainSiteReputation(self::$id, SITEREP_ACTION_DAILYVISIT);
// increment consecutive visits (next day or first of new month and not more than 48h) // increment consecutive visits (next day or first of new month and not more than 48h)
@@ -140,11 +153,11 @@ class User
$rawIp = explode(',', $rawIp)[0]; // [ip, proxy1, proxy2] $rawIp = explode(',', $rawIp)[0]; // [ip, proxy1, proxy2]
// check IPv4 // check IPv4
if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
break; break;
// check IPv6 // check IPv6
if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) if ($ipAddr = filter_var($rawIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
break; break;
} }
} }
@@ -178,9 +191,18 @@ class User
} }
} }
// check // check; pick first viable if failed
if ($loc != LOCALE_EN && !(CFG_LOCALES & (1 << $loc))) if (CFG_LOCALES && !(CFG_LOCALES & (1 << $loc)))
$loc = LOCALE_EN; {
foreach (Util::$localeStrings as $idx => $__)
{
if (CFG_LOCALES & (1 << $idx))
{
$loc = $idx;
break;
}
}
}
// set // set
if (self::$id) if (self::$id)
@@ -213,7 +235,7 @@ class User
$user = 0; $user = 0;
$hash = ''; $hash = '';
switch (CFG_AUTH_MODE) switch (CFG_ACC_AUTH_MODE)
{ {
case AUTH_MODE_SELF: case AUTH_MODE_SELF:
{ {
@@ -223,11 +245,11 @@ class User
// handle login try limitation // handle login try limitation
$ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip); $ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip);
if (!$ip || $ip['unbanDate'] < time()) // no entry exists or time expired; set count to 1 if (!$ip || $ip['unbanDate'] < time()) // no entry exists or time expired; set count to 1
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, CFG_FAILED_AUTH_EXCLUSION); DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 0, 1, UNIX_TIMESTAMP() + ?d)', self::$ip, CFG_ACC_FAILED_AUTH_BLOCK);
else // entry already exists; increment count else // entry already exists; increment count
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ?', CFG_FAILED_AUTH_EXCLUSION, self::$ip); DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ?', CFG_ACC_FAILED_AUTH_BLOCK, self::$ip);
if ($ip && $ip['count'] >= CFG_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time()) if ($ip && $ip['count'] >= CFG_ACC_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time())
return AUTH_IPBANNED; return AUTH_IPBANNED;
$query = DB::Aowow()->SelectRow(' $query = DB::Aowow()->SelectRow('
@@ -245,9 +267,6 @@ class User
if (!self::verifyCrypt($pass)) if (!self::verifyCrypt($pass))
return AUTH_WRONGPASS; return AUTH_WRONGPASS;
if ($query['status'] & ACC_STATUS_NEW)
return AUTH_ACC_INACTIVE;
// successfull auth; clear bans for this IP // successfull auth; clear bans for this IP
DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip); DB::Aowow()->query('DELETE FROM ?_account_bannedips WHERE type = 0 AND ip = ?', self::$ip);
@@ -287,11 +306,13 @@ class User
return AUTH_INTERNAL_ERR; return AUTH_INTERNAL_ERR;
require 'config/extAuth.php'; require 'config/extAuth.php';
$result = extAuth($name, $pass, $extId);
$extGroup = -1;
$result = extAuth($name, $pass, $extId, $extGroup);
if ($result == AUTH_OK && $extId) if ($result == AUTH_OK && $extId)
{ {
if ($_ = self::checkOrCreateInDB($extId, $name)) if ($_ = self::checkOrCreateInDB($extId, $name, $extGroup))
$user = $_; $user = $_;
else else
return AUTH_INTERNAL_ERR; return AUTH_INTERNAL_ERR;
@@ -314,18 +335,28 @@ class User
} }
// create a linked account for our settings if nessecary // create a linked account for our settings if nessecary
private static function checkOrCreateInDB($extId, $name) private static function checkOrCreateInDB($extId, $name, $userGroup = -1)
{ {
if ($_ = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE extId = ?d', $extId)) if (!intVal($extId))
return $_; return 0;
$newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (extId, user, displayName, joinDate, prevIP, prevLogin, locale, status) VALUES (?d, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d)', $userGroup = intVal($userGroup);
if ($_ = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE extId = ?d', $extId))
{
if ($userGroup >= U_GROUP_NONE)
DB::Aowow()->query('UPDATE ?_account SET userGroups = ?d WHERE extId = ?d', $userGroup, $extId);
return $_;
}
$newId = DB::Aowow()->query('INSERT IGNORE INTO ?_account (extId, user, displayName, joinDate, prevIP, prevLogin, locale, status, userGroups) VALUES (?d, ?, ?, UNIX_TIMESTAMP(), ?, UNIX_TIMESTAMP(), ?d, ?d, ?d)',
$extId, $extId,
$name, $name,
Util::ucFirst($name), Util::ucFirst($name),
isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '', isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '',
User::$localeId, User::$localeId,
ACC_STATUS_OK ACC_STATUS_OK,
$userGroup >= U_GROUP_NONE ? $userGroup : U_GROUP_NONE
); );
if ($newId) if ($newId)
@@ -352,7 +383,7 @@ class User
public static function verifyCrypt($pass, $hash = '') public static function verifyCrypt($pass, $hash = '')
{ {
$_ = $hash ?: self::$passHash; $_ = $hash ?: self::$passHash;
return $_ == crypt($pass, $_); return $_ === crypt($pass, $_);
} }
// sha1 used by TC / MaNGOS // sha1 used by TC / MaNGOS
@@ -363,16 +394,30 @@ class User
private static function verifySHA1($name, $pass) private static function verifySHA1($name, $pass)
{ {
return self::$passHash == self::hashSHA1($name, $pass); return strtoupper(self::$passHash) === strtoupper(self::hashSHA1($name, $pass));
} }
public static function isValidName($name, &$errCode = 0) public static function isValidName($name, &$errCode = 0)
{ {
$errCode = 0; $errCode = 0;
if (strlen($name) < 4 || strlen($name) > 16) // different auth modes require different usernames
$min = 0; // external case
$max = 0;
if (CFG_ACC_AUTH_MODE == AUTH_MODE_SELF)
{
$min = 4;
$max = 16;
}
else if (CFG_ACC_AUTH_MODE == AUTH_MODE_REALM)
{
$min = 3;
$max = 32;
}
if (($min && mb_strlen($name) < $min) || ($max && mb_strlen($name) > $max))
$errCode = 1; $errCode = 1;
else if (preg_match('/[^\w\d]/i', $name)) else if (preg_match('/[^\w\d\-]/i', $name))
$errCode = 2; $errCode = 2;
return $errCode == 0; return $errCode == 0;
@@ -382,7 +427,8 @@ class User
{ {
$errCode = 0; $errCode = 0;
if (strlen($pass) < 6 || strlen($pass) > 16) // only enforce for own passwords
if (mb_strlen($pass) < 6 && CFG_ACC_AUTH_MODE == AUTH_MODE_SELF)
$errCode = 1; $errCode = 1;
// else if (preg_match('/[^\w\d!"#\$%]/', $pass)) // such things exist..? :o // else if (preg_match('/[^\w\d!"#\$%]/', $pass)) // such things exist..? :o
// $errCode = 2; // $errCode = 2;
@@ -430,6 +476,14 @@ class User
return self::$perms || self::$reputation >= CFG_REP_REQ_COMMENT; return self::$perms || self::$reputation >= CFG_REP_REQ_COMMENT;
} }
public static function canReply()
{
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return self::$perms || self::$reputation >= CFG_REP_REQ_REPLY;
}
public static function canUpvote() public static function canUpvote()
{ {
if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP)) if (!self::$id || self::$banStatus & (ACC_BAN_COMMENT | ACC_BAN_PERM | ACC_BAN_TEMP))
@@ -454,6 +508,22 @@ class User
return self::$reputation >= CFG_REP_REQ_SUPERVOTE; return self::$reputation >= CFG_REP_REQ_SUPERVOTE;
} }
public static function canUploadScreenshot()
{
if (!self::$id || self::$banStatus & (ACC_BAN_SCREENSHOT | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return true;
}
public static function canSuggestVideo()
{
if (!self::$id || self::$banStatus & (ACC_BAN_VIDEO | ACC_BAN_PERM | ACC_BAN_TEMP))
return false;
return true;
}
public static function isPremium() public static function isPremium()
{ {
return self::isInGroup(U_GROUP_PREMIUM) || self::$reputation >= CFG_REP_REQ_PREMIUM; return self::isInGroup(U_GROUP_PREMIUM) || self::$reputation >= CFG_REP_REQ_PREMIUM;
@@ -500,14 +570,19 @@ class User
if (!self::$id || self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM)) if (!self::$id || self::$banStatus & (ACC_BAN_TEMP | ACC_BAN_PERM))
return $gUser; return $gUser;
$gUser['commentban'] = (bool)(self::$banStatus & ACC_BAN_COMMENT); $gUser['commentban'] = !self::canComment();
$gUser['canUpvote'] = self::canUpvote(); $gUser['canUpvote'] = self::canUpvote();
$gUser['canDownvote'] = self::canDownvote(); $gUser['canDownvote'] = self::canDownvote();
$gUser['canPostReplies'] = self::canComment(); $gUser['canPostReplies'] = self::canReply();
$gUser['superCommentVotes'] = self::canSupervote(); $gUser['superCommentVotes'] = self::canSupervote();
$gUser['downvoteRep'] = CFG_REP_REQ_DOWNVOTE; $gUser['downvoteRep'] = CFG_REP_REQ_DOWNVOTE;
$gUser['upvoteRep'] = CFG_REP_REQ_UPVOTE; $gUser['upvoteRep'] = CFG_REP_REQ_UPVOTE;
$gUser['characters'] = self::getCharacters(); $gUser['characters'] = self::getCharacters();
$gUser['excludegroups'] = self::$excludeGroups;
$gUser['settings'] = (new StdClass); // profiler requires this to be set; has property premiumborder (NYI)
if ($_ = self::getProfilerExclusions())
$gUser = array_merge($gUser, $_);
if ($_ = self::getProfiles()) if ($_ = self::getProfiles())
$gUser['profiles'] = $_; $gUser['profiles'] = $_;
@@ -523,63 +598,45 @@ class User
public static function getWeightScales() public static function getWeightScales()
{ {
$data = []; $result = [];
$res = DB::Aowow()->select('SELECT * FROM ?_account_weightscales WHERE userId = ?d', self::$id); $res = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, name FROM ?_account_weightscales WHERE userId = ?d', self::$id);
foreach ($res as $i) if (!$res)
{ return $result;
$set = array (
'name' => $i['name'],
'id' => $i['id']
);
$weights = explode(',', $i['weights']); $weights = DB::Aowow()->selectCol('SELECT id AS ARRAY_KEY, `field` AS ARRAY_KEY2, val FROM ?_account_weightscale_data WHERE id IN (?a)', array_keys($res));
foreach ($weights as $weight) foreach ($weights as $id => $data)
{ $result[] = array_merge(['name' => $res[$id], 'id' => $id], $data);
$w = explode(':', $weight);
if ($w[1] === 'undefined') return $result;
$w[1] = 0; }
$set[$w[0]] = $w[1]; public static function getProfilerExclusions()
} {
$result = [];
$modes = [1 => 'excludes', 2 => 'includes'];
foreach ($modes as $mode => $field)
if ($ex = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, typeId AS ARRAY_KEY2, typeId FROM ?_account_excludes WHERE mode = ?d AND userId = ?d', $mode, self::$id))
foreach ($ex as $type => $ids)
$result[$field][$type] = array_values($ids);
$data[] = $set; return $result;
}
return $data;
} }
public static function getCharacters() public static function getCharacters()
{ {
// existing chars on realm(s) if (!self::$profiles)
$characters = array( return [];
// array(
// 'id' => 22,
// 'name' => 'Example Char',
// 'realmname' => 'Example Realm',
// 'region' => 'eu',
// 'realm' => 'example-realm',
// 'race' => 6,
// 'classs' => 11,
// 'level' => 80,
// 'gender' => 1,
// 'pinned' => 1
// )
);
return $characters; return self::$profiles->getJSGlobals(PROFILEINFO_CHARACTER);
} }
public static function getProfiles() public static function getProfiles()
{ {
// chars build in profiler if (!self::$profiles)
$profiles = array( return [];
// array('id' => 21, 'name' => 'Example Profile 1', 'race' => 4, 'classs' => 5, 'level' => 72, 'gender' => 1, 'icon' => 'inv_axe_04'),
// array('id' => 23, 'name' => 'Example Profile 2', 'race' => 11, 'classs' => 3, 'level' => 17, 'gender' => 0)
);
return $profiles; return self::$profiles->getJSGlobals(PROFILEINFO_PROFILE);
} }
public static function getCookies() public static function getCookies()

File diff suppressed because it is too large Load Diff

View File

@@ -22,19 +22,25 @@ switch ($pageCall)
case 'account': // account management [nyi] case 'account': // account management [nyi]
case 'achievement': case 'achievement':
case 'achievements': case 'achievements':
// case 'arena-team': case 'arena-team':
// case 'arena-teams': case 'arena-teams':
case 'class': case 'class':
case 'classes': case 'classes':
case 'currency': case 'currency':
case 'currencies': case 'currencies':
case 'compare': // tool: item comparison case 'compare': // tool: item comparison
case 'emote':
case 'emotes':
case 'enchantment':
case 'enchantments':
case 'event': case 'event':
case 'events': case 'events':
case 'faction': case 'faction':
case 'factions': case 'factions':
// case 'guild': case 'guild':
// case 'guilds': case 'guilds':
case 'icon':
case 'icons':
case 'item': case 'item':
case 'items': case 'items':
case 'itemset': case 'itemset':
@@ -60,8 +66,8 @@ switch ($pageCall)
case 'search': // tool: searches case 'search': // tool: searches
case 'skill': case 'skill':
case 'skills': case 'skills':
// case 'sound': // db: sounds for zone, creature, spell, ... case 'sound':
// case 'sounds': case 'sounds':
case 'spell': case 'spell':
case 'spells': case 'spells':
case 'talent': // tool: talent calculator case 'talent': // tool: talent calculator
@@ -71,17 +77,40 @@ switch ($pageCall)
case 'video': case 'video':
case 'zone': case 'zone':
case 'zones': case 'zones':
if (in_array($pageCall, ['admin', 'account', 'profile'])) /* called by script */
case 'data': // tool: dataset-loader
case 'cookie': // lossless cookies and user settings
case 'contactus':
case 'comment':
case 'filter': // pre-evaluate filter POST-data; sanitize and forward as GET-data
case 'go-to-comment': // find page the comment is on and forward
case 'locale': // subdomain-workaround, change the language
$cleanName = str_replace(['-', '_'], '', ucFirst($altClass ?: $pageCall));
try // can it be handled as ajax?
{ {
if (($_ = (new AjaxHandler($pageParam))->handle($pageCall)) !== null) $class = 'Ajax'.$cleanName;
$ajax = new $class(explode('.', $pageParam));
if ($ajax->handle($out))
{ {
header('Content-type: application/x-javascript; charset=utf-8'); Util::sendNoCacheHeader();
die((string)$_);
if ($ajax->doRedirect)
header('Location: '.$out, true, 302);
else
{
header('Content-type: '.$ajax->getContentType());
die($out);
}
} }
else
throw new Exception('not handled as ajax');
}
catch (Exception $e) // no, apparently not..
{
$class = $cleanName.'Page';
(new $class($pageCall, $pageParam))->display();
} }
$_ = ($altClass ?: $pageCall).'Page';
(new $_($pageCall, $pageParam))->display();
break; break;
/* other pages */ /* other pages */
case 'whats-new': case 'whats-new':
@@ -91,6 +120,10 @@ switch ($pageCall)
case 'help': case 'help':
case 'faq': case 'faq':
case 'aboutus': case 'aboutus':
case 'reputation':
case 'privilege':
case 'privileges':
case 'top-users':
(new MorePage($pageCall, $pageParam))->display(); (new MorePage($pageCall, $pageParam))->display();
break; break;
case 'latest-additions': case 'latest-additions':
@@ -104,19 +137,6 @@ switch ($pageCall)
case 'random': case 'random':
(new UtilityPage($pageCall, $pageParam))->display(); (new UtilityPage($pageCall, $pageParam))->display();
break; break;
/* called by script */
case 'data': // tool: dataset-loader
case 'cookie': // lossless cookies and user settings
case 'contactus':
case 'comment':
case 'go-to-comment': // find page the comment is on and forward
case 'locale': // subdomain-workaround, change the language
if (($_ = (new AjaxHandler($pageParam))->handle($pageCall)) !== null)
{
header('Content-type: application/x-javascript; charset=utf-8');
die((string)$_);
}
break;
default: // unk parameter given -> ErrorPage default: // unk parameter given -> ErrorPage
if (isset($_GET['power'])) if (isset($_GET['power']))
die('$WowheadPower.register(0, '.User::$localeId.', {})'); die('$WowheadPower.register(0, '.User::$localeId.', {})');

View File

@@ -8,25 +8,34 @@ class Lang
private static $user; private static $user;
private static $mail; private static $mail;
private static $game; private static $game;
private static $maps;
private static $profiler;
private static $screenshot;
private static $privileges;
// types
private static $achievement; private static $achievement;
private static $chrClass; private static $chrClass;
private static $currency; private static $currency;
private static $event; private static $event;
private static $faction; private static $faction;
private static $gameObject; private static $gameObject;
private static $icon;
private static $item; private static $item;
private static $itemset; private static $itemset;
private static $maps;
private static $npc; private static $npc;
private static $pet; private static $pet;
private static $quest; private static $quest;
private static $race; private static $race;
private static $skill; private static $skill;
private static $sound;
private static $spell; private static $spell;
private static $title; private static $title;
private static $zone; private static $zone;
private static $emote;
private static $enchantment;
public static function load($loc) public static function load($loc)
{ {
if (!file_exists('localization/locale_'.$loc.'.php')) if (!file_exists('localization/locale_'.$loc.'.php'))
@@ -37,33 +46,79 @@ class Lang
foreach ($lang as $k => $v) foreach ($lang as $k => $v)
self::$$k = $v; self::$$k = $v;
// *cough* .. reuse-hack // *cough* .. reuse-hacks (because copy-pastaing text for 5 locales sucks)
self::$item['cat'][2] = [self::$item['cat'][2], self::$spell['weaponSubClass']]; self::$item['cat'][2] = [self::$item['cat'][2], self::$spell['weaponSubClass']];
self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')'; self::$item['cat'][2][1][14] .= ' ('.self::$item['cat'][2][0].')';
// not localized .. for whatever reason
self::$profiler['regions'] = array(
'eu' => "Europe",
'us' => "US & Oceanic"
);
self::$main['moreTitles']['privilege'] = self::$privileges['_privileges'];
} }
// todo: make static props private and access through this
public static function __callStatic($prop, $args) public static function __callStatic($prop, $args)
{ {
if (!isset(self::$$prop)) if (!isset(self::$$prop))
{ {
Util::addNote(U_GROUP_STAFF, 'Lang::__callStatic() - tried to use undefined property Lang::$'.$prop); trigger_error('Lang - tried to use undefined property Lang::$'.$prop, E_USER_WARNING);
return null; return null;
} }
$vspfArgs = [];
$var = self::$$prop; $var = self::$$prop;
foreach ($args as $key) foreach ($args as $arg)
{ {
if (!isset($var[$key])) if (is_array($arg))
{ {
Util::addNote(U_GROUP_STAFF, 'Lang::__callStatic() - undefined key "'.$key.'" in property Lang::$'.$prop.'[\''.implode('\'][\'', $args).'\']'); $vspfArgs = $arg;
continue;
}
else if (!isset($var[$arg]))
{
trigger_error('Lang - undefined key "'.$arg.'" in property Lang::$'.$prop.'[\''.implode('\'][\'', $args).'\']', E_USER_WARNING);
return null; return null;
} }
$var = $var[$key]; $var = $var[$arg];
} }
return $var; // meh :x
if ($var === null && $prop == 'spell' && count($args) == 1)
{
if ($args[0] == 'effects')
$var = self::$$prop['unkEffect'];
else if ($args[0] == 'auras')
$var = self::$$prop['unkAura'];
}
return self::vspf($var, $vspfArgs);
}
public static function concat($args, $useAnd = true, $callback = null)
{
$b = '';
$i = 0;
$n = count($args);
foreach ($args as $k => $arg)
{
if (is_callable($callback))
$b .= $callback($arg, $k);
else
$b .= $arg;
if ($n > 1 && $i < ($n - 2))
$b .= ', ';
else if ($n > 1 && $i == $n - 2)
$b .= Lang::main($useAnd ? 'and' : 'or');
$i++;
}
return $b;
} }
public static function sort($prop, $group, $method = SORT_NATURAL) public static function sort($prop, $group, $method = SORT_NATURAL)
@@ -71,14 +126,14 @@ class Lang
if (!isset(self::$$prop)) if (!isset(self::$$prop))
{ {
Util::addNote(U_GROUP_STAFF, 'Lang::sort() - tried to use undefined property Lang::$'.$prop); trigger_error('Lang::sort - tried to use undefined property Lang::$'.$prop, E_USER_WARNING);
return null; return null;
} }
$var = &self::$$prop; $var = &self::$$prop;
if (!isset($var[$group])) if (!isset($var[$group]))
{ {
Util::addNote(U_GROUP_STAFF, 'Lang::sort() - tried to use undefined property Lang::$'.$prop.'[\''.$group.'\']'); trigger_error('Lang::sort - tried to use undefined property Lang::$'.$prop.'[\''.$group.'\']', E_USER_WARNING);
return null; return null;
} }
@@ -166,7 +221,7 @@ class Lang
public static function getReputationLevelForPoints($pts) public static function getReputationLevelForPoints($pts)
{ {
$_ = Util::getReputationLevelForPoints($pts); $_ = Game::getReputationLevelForPoints($pts);
return self::game('rep', $_); return self::game('rep', $_);
} }
@@ -205,12 +260,17 @@ class Lang
if ($mask & (1 << $k) && $str) if ($mask & (1 << $k) && $str)
$tmp[] = $str; $tmp[] = $str;
return implode(', ', $tmp); if (!$tmp && $class == ITEM_CLASS_ARMOR)
return self::spell('cat', -11, 8);
else if (!$tmp && $class == ITEM_CLASS_WEAPON)
return self::spell('cat', -11, 6);
else
return implode(', ', $tmp);
} }
public static function getStances($stanceMask) public static function getStances($stanceMask)
{ {
$stanceMask &= 0xFC27909F; // clamp to available stances/forms.. $stanceMask &= 0xFF37F6FF; // clamp to available stances/forms..
$tmp = []; $tmp = [];
$i = 1; $i = 1;
@@ -320,6 +380,116 @@ class Lang
return implode(', ', $tmp); return implode(', ', $tmp);
} }
public static function nf($number, $decimals = 0, $no1k = false)
{
// [decimal, thousand]
$seps = array(
LOCALE_EN => [',', '.'],
LOCALE_FR => [' ', ','],
LOCALE_DE => ['.', ','],
LOCALE_ES => ['.', ','],
LOCALE_RU => [' ', ',']
);
return number_format($number, $decimals, $seps[User::$localeId][1], $no1k ? '' : $seps[User::$localeId][0]);
}
private static function vspf($var, $args)
{
if (is_array($var))
{
foreach ($var as &$v)
$v == self::vspf($v, $args);
return $var;
}
if ($args)
$var = vsprintf($var, $args);
// line break
// |n
$var = str_replace('|n', '<br />', $var);
// color
// |c<aarrggbb><word>|r
$var = preg_replace('/\|cff([a-f0-9]{6})(.+?)\|r/i', '<span style="color: #$1;">$2</span>', $var);
// icon
// |T<imgPath>:0:0:0:-1|t - not used, skip if found
$var = preg_replace('/\|T[^\|]+\|t/', '', $var);
// hyperlink
// |H<hyperlinkStruct>|h<name>|h - not used, truncate structure if found
$var = preg_replace('/\|H[^\|]+\|h([^\|]+)\|h/', '$1', $var);
// french preposition : de
// |2 <word>
$var = preg_replace_callback('/\|2\s(\w)/i', function ($m) {
if (in_array(strtolower($m[1]), ['a', 'e', 'h', 'i', 'o', 'u']))
return "d'".$m[1];
else
return 'de '.$m[1];
}, $var);
// russian word cunjugation thingy
// |3-<number>(<word>)
$var = preg_replace_callback('/\|3-(\d)\(([^\)]+)\)/i', function ($m) {
switch ($m[0])
{
case 1: // seen cases
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
default: // passthrough .. unk case
return $m[1];
}
}, $var);
// numeric switch
// <number> |4<singular>:<plural>[:<plural2>];
$var = preg_replace_callback('/([\d\.\,]+)([^\d]*)\|4([^:]*):([^;]*);/i', function ($m) {
$plurals = explode(':', $m[4]);
$result = '';
if (count($plurals) == 2) // special case: ruRU
{
switch (substr($m[1], -1)) // check last digit of number
{
case 1:
// but not 11 (teen number)
if (!in_array($m[1], [11]))
{
$result = $m[3];
break;
}
case 2:
case 3:
case 4:
// but not 12, 13, 14 (teen number) [11 is passthrough]
if (!in_array($m[1], [11, 12, 13, 14]))
{
$result = $plurals[0];
break;
}
break;
default:
$result = $plurals[1];
}
}
else
$result = ($m[1] == 1 ? $m[3] : $plurals[0]);
return $m[1].$m[2].$result;
}, $var);
return $var;
}
} }
?> ?>

View File

@@ -14,8 +14,7 @@ $lang = array(
'timeUnits' => array( 'timeUnits' => array(
'sg' => ["Jahr", "Monat", "Woche", "Tag", "Stunde", "Minute", "Sekunde", "Millisekunde"], 'sg' => ["Jahr", "Monat", "Woche", "Tag", "Stunde", "Minute", "Sekunde", "Millisekunde"],
'pl' => ["Jahre", "Monate", "Wochen", "Tage", "Stunden", "Minuten", "Sekunden", "Millisekunden"], 'pl' => ["Jahre", "Monate", "Wochen", "Tage", "Stunden", "Minuten", "Sekunden", "Millisekunden"],
'ab' => ["J.", "M.", "W.", "Tag", "Std.", "Min.", "Sek.", "Ms."], 'ab' => ["J.", "M.", "W.", "Tag", "Std.", "Min.", "Sek.", "Ms."]
'ago' => 'vor %s'
), ),
'main' => array( 'main' => array(
'name' => "Name", 'name' => "Name",
@@ -47,16 +46,20 @@ $lang = array(
'forum' => "Forum", 'forum' => "Forum",
'n_a' => "n. v.", 'n_a' => "n. v.",
'siteRep' => "Ruf", 'siteRep' => "Ruf",
'yourRepHistory'=> "Dein Ruf-Verlauf",
'aboutUs' => "Über Aowow", 'aboutUs' => "Über Aowow",
'and' => " und ", 'and' => " und ",
'or' => " oder ", 'or' => " oder ",
'back' => "Zurück", 'back' => "Zurück",
'reputationTip' => "Rufpunkte",
'byUserTimeAgo' => 'Von <a href="'.HOST_URL.'/?user=%s">%1$s</a> vor %s',
'help' => "Hilfe",
// filter // filter
'extSearch' => "Erweiterte Suche", 'extSearch' => "Erweiterte Suche",
'addFilter' => "Weiteren Filter hinzufügen", 'addFilter' => "Weiteren Filter hinzufügen",
'match' => "Verwendete Filter", 'match' => "Verwendete Filter",
'allFilter' => "Alle Filters", 'allFilter' => "Alle Filter",
'oneFilter' => "Mindestens einer", 'oneFilter' => "Mindestens einer",
'applyFilter' => "Filter anwenden", 'applyFilter' => "Filter anwenden",
'resetForm' => "Formular zurücksetzen", 'resetForm' => "Formular zurücksetzen",
@@ -107,22 +110,6 @@ $lang = array(
'chooseClass' => "Wählt eine Klasse", 'chooseClass' => "Wählt eine Klasse",
'chooseFamily' => "Wählt eine Tierart", 'chooseFamily' => "Wählt eine Tierart",
// profiler
'realm' => "Realm",
'region' => "Region",
'viewCharacter' => "Charakter anzeigen",
'_cpHead' => "Charakter-Profiler",
'_cpHint' => "Der <b>Charakter-Profiler</b> gibt Euch die Möglichkeit, Euren Charakter zu editieren, bessere Ausrüstung zu finden, Eure Gearscore zu überprüfen, und mehr!",
'_cpHelp' => "Um loszulegen, folgt einfach den untenstehenden Schritten. Falls Ihr mehr Informationen benötigt, schaut auf unserer ausführlichen <a href=\"?help=profiler\">Hilfeseite</a> nach.",
'_cpFooter' => "Falls Ihr eine genauere Suche möchtet, probiert unsere <a href=\"?profiles\">erweiterten Suchoptionen</a>. Ihr könnt außerdem ein <a href=\"?profile&amp;new\">neues individuelles Profil</a> erstellen.",
// help
'help' => "Hilfe",
'helpTopics' => array(
"Wie man Kommentare schreibt", "Modellviewer", "Screenshots: Tipps & Tricks", "Gewichtung von Werten",
"Talentrechner", "Gegenstandsvergleich", "Profiler", "Markup Guide"
),
// search // search
'search' => "Suche", 'search' => "Suche",
'searchButton' => "Suche", 'searchButton' => "Suche",
@@ -143,29 +130,68 @@ $lang = array(
'bannedRating' => "Ihr wurdet davon gesperrt, Kommentare zu bewerten.", # LANG.tooltip_banned_rating 'bannedRating' => "Ihr wurdet davon gesperrt, Kommentare zu bewerten.", # LANG.tooltip_banned_rating
'tooManyVotes' => "Ihr habt die tägliche Grenze für erlaubte Bewertungen erreicht. Kommt morgen mal wieder!", # LANG.tooltip_too_many_votes 'tooManyVotes' => "Ihr habt die tägliche Grenze für erlaubte Bewertungen erreicht. Kommt morgen mal wieder!", # LANG.tooltip_too_many_votes
// screenshots 'moreTitles' => array(
'prepError' => "Bei der Aufbereitung eures Screenshots ist ein Fehler aufgetreten", 'reputation' => "Benutzerruf",
'cropHint' => "Schneidet das Bild zu, indem ihr die Auswahl verschiebt.<br>Bitte beachtet <a href=\"?help=screenshots-tips-tricks\">Screenshots: Tipps & Tricks</a> für eine optimale Darstellung.", 'whats-new' => "Was gibt's Neues?",
'searchbox' => "Suchbox",
'tooltips' => "Tooltips",
'faq' => "Häufig gestellte Fragen",
'aboutus' => "Was ist AoWoW?",
'searchplugins' => "Such-Plugins",
'privileges' => "Privilegien",
'top-users' => "Hilfreichste Benutzer",
'help' => array(
'commenting-and-you' => "Wie man Kommentare schreibt", 'modelviewer' => "Modellviewer", 'screenshots-tips-tricks' => "Screenshots: Tipps & Tricks",
'stat-weighting' => "Gewichtung von Werten", 'talent-calculator' => "Talentrechner", 'item-comparison' => "Gegenstandsvergleich",
'profiler' => "Profiler", 'markup-guide' => "Markup Guide"
)
)
),
'profiler' => array(
'realm' => "Realm",
'region' => "Region",
'viewCharacter' => "Charakter anzeigen",
'_cpHint' => "Der <b>Charakter-Profiler</b> gibt Euch die Möglichkeit, Euren Charakter zu editieren, bessere Ausrüstung zu finden, Eure Gearscore zu überprüfen, und mehr!",
'_cpHelp' => "Um loszulegen, folgt einfach den untenstehenden Schritten. Falls Ihr mehr Informationen benötigt, schaut auf unserer ausführlichen <a href=\"?help=profiler\">Hilfeseite</a> nach.",
'_cpFooter' => "Falls Ihr eine genauere Suche möchtet, probiert unsere <a href=\"?profiles\">erweiterten Suchoptionen</a>. Ihr könnt außerdem ein <a href=\"?profile&amp;new\">neues individuelles Profil</a> erstellen.",
'firstUseTitle' => "%s von %s",
'complexFilter' => "Komplexer Filter ausgewählt! Suchergebnisse sind auf gecachte Charaktere beschränkt.",
'resync' => "Resynchronisieren",
'guildRoster' => "Gildenliste für &lt;%s&gt;",
'arenaRoster' => "Arena-Teamliste für &lt;%s&gt",
'atCaptain' => "Teamkapitän",
'profiler' => "Charakter-Profiler",
'arenaTeams' => "Arena Teams",
'guilds' => "Gilden",
'notFound' => array(
'guild' => "Diese Gilde existiert nicht oder wurde noch nicht in die Datenbank übernommen.",
'arenateam' => "Dieses Arena Team existiert nicht oder wurde noch nicht in die Datenbank übernommen.",
'profile' => "Dieser Charakter existiert nicht oder wurde noch nicht in die Datenbank übernommen."
),
'dummyNPCs' => array(
100001 => "Luftschiffkampf", 200001 => "Bestien von Nordend", 200002 => "Fraktionschampions", 200003 => "Zwillingsval'kyr"
),
),
'screenshot' => array(
'submission' => "Screenshot-Einsendung",
'selectAll' => "Alles auswählen",
'cropHint' => "Ihr könnt Euren Screenshot zuschneiden und beschriften.",
'displayOn' => "Hochgeladen für:[br]%s - [%s=%d]",
'caption' => "Kurzbeschreibung", 'caption' => "Kurzbeschreibung",
'originalSize' => "Originalgröße", 'charLimit' => "Optional, bis zu 200 Zeichen",
'targetSize' => "Zielgröße", 'thanks' => array(
'minSize' => "Mindestgröße", 'contrib' => "Vielen Dank für Euren Beitrag!",
'displayOn' => "Hochgeladen für: %s[br][%s=%d]", 'goBack' => '<a href="?%s=%d">Klickt hier</a>, um zu der vorherigen Seite zurückzukehren.',
'ssEdit' => "Screenshot bearbeiten", 'note' => "Hinweis: Euer Screenshot muss zunächst zugelassen werden, bevor es auf der Seite erscheint. Dies kann bis zu 72 Stunden dauern."
'ssUpload' => "Screenshot hochladen", ),
'ssSubmit' => "Screenshot einsenden", 'error' => array(
'ssErrors' => array( 'unkFormat' => "Unbekanntes Bildformat.",
'noUpload' => "Die Datei wurde nicht hochgeladen!", 'tooSmall' => "Euer Screenshot ist viel zu klein. (&lt; ".CFG_SCREENSHOT_MIN_SIZE."x".CFG_SCREENSHOT_MIN_SIZE.").",
'maxSize' => "Die Datei überschreitet die max. Größe von %s!", 'selectSS' => "Wählt bitte den Screenshot aus, den Ihr hochladen möchtet.",
'interrupted' => "Der Vorgang wurde unterbrochen!",
'noFile' => "Es wurde keine Datei empfangen!",
'noDest' => "Die Seite auf welcher der Screenshot angezeigt werden sollte existiert nicht!",
'notAllowed' => "Es ist euch nicht erlaubt einen Screenshot hochzuladen!", 'notAllowed' => "Es ist euch nicht erlaubt einen Screenshot hochzuladen!",
'noImage' => "Die hochgeladene Datei ist kein Bild!",
'wrongFormat' => "Das Bild muss im PNG oder JPG-Format sein!",
'load' => "Das Bild konnte nicht geladen werden!",
'tooSmall' => "Die Abmessungen sind zu klein! (kleiner als %d x %d)",
'tooLarge' => "Die Abmessungen sind zu groß! (größer als %d x %d)"
) )
), ),
'game' => array( 'game' => array(
@@ -178,8 +204,12 @@ $lang = array(
'difficulty' => "Modus", 'difficulty' => "Modus",
'dispelType' => "Bannart", 'dispelType' => "Bannart",
'duration' => "Dauer", 'duration' => "Dauer",
'gameObject' => "Objekt", 'emote' => "Emote",
'gameObjects' => "Objekte", 'emotes' => "Emotes",
'enchantment' => "Verzauberung",
'enchantments' => "Verzauberungen",
'object' => "Objekt",
'objects' => "Objekte",
'glyphType' => "Glyphenart", 'glyphType' => "Glyphenart",
'race' => "Volk", 'race' => "Volk",
'races' => "Völker", 'races' => "Völker",
@@ -191,6 +221,8 @@ $lang = array(
'faction' => "Fraktion", 'faction' => "Fraktion",
'factions' => "Fraktionen", 'factions' => "Fraktionen",
'cooldown' => "%s Abklingzeit", 'cooldown' => "%s Abklingzeit",
'icon' => "Icon",
'icons' => "Icons",
'item' => "Gegenstand", 'item' => "Gegenstand",
'items' => "Gegenstände", 'items' => "Gegenstände",
'itemset' => "Ausrüstungsset", 'itemset' => "Ausrüstungsset",
@@ -209,12 +241,13 @@ $lang = array(
'requires' => "Benötigt %s", 'requires' => "Benötigt %s",
'requires2' => "Benötigt", 'requires2' => "Benötigt",
'reqLevel' => "Benötigt Stufe %s", 'reqLevel' => "Benötigt Stufe %s",
'reqLevelHlm' => "Benötigt Stufe %s",
'reqSkillLevel' => "Benötigte Fertigkeitsstufe", 'reqSkillLevel' => "Benötigte Fertigkeitsstufe",
'level' => "Stufe", 'level' => "Stufe",
'school' => "Magieart", 'school' => "Magieart",
'skill' => "Fertigkeit", 'skill' => "Fertigkeit",
'skills' => "Fertigkeiten", 'skills' => "Fertigkeiten",
'sound' => "Klang",
'sounds' => "Klänge",
'spell' => "Zauber", 'spell' => "Zauber",
'spells' => "Zauber", 'spells' => "Zauber",
'type' => "Art", 'type' => "Art",
@@ -260,10 +293,10 @@ $lang = array(
'rep' => ["Hasserfüllt", "Feindselig", "Unfreundlich", "Neutral", "Freundlich", "Wohlwollend", "Respektvoll", "Ehrfürchtig"], 'rep' => ["Hasserfüllt", "Feindselig", "Unfreundlich", "Neutral", "Freundlich", "Wohlwollend", "Respektvoll", "Ehrfürchtig"],
'st' => array( 'st' => array(
"Vorgabe", "Katzengestalt", "Baum des Lebens", "Reisegestalt", "Wassergestalt", "Bärengestalt", "Vorgabe", "Katzengestalt", "Baum des Lebens", "Reisegestalt", "Wassergestalt", "Bärengestalt",
null, null, "Terrorbärengestalt", null, null, null, "Umgebung", "Ghul", "Terrorbärengestalt", "Steves Ghul", "Skelett von Tharon'ja", "Dunkelmond-Test der Stärke",
null, "Schattentanz", null, null, "Geisterwolf", "Kampfhaltung", "BLB Spieler", "Schattentanz", "Kreatur - Bär", "Kreatur - Katze", "Geisterwolf", "Kampfhaltung",
"Verteidigungshaltung", "Berserkerhaltung", null, null, "Metamorphosis", null, "Verteidigungshaltung", "Berserkerhaltung", "Test", "Zombie", "Metamorphosis", null,
null, null, null, "Schnelle Fluggestalt", "Schattengestalt", "Fluggestalt", null, "Untotes Wesen", "Raserei", "Schnelle Fluggestalt", "Schattengestalt", "Fluggestalt",
"Verstohlenheit", "Mondkingestalt", "Geist der Erlösung" "Verstohlenheit", "Mondkingestalt", "Geist der Erlösung"
), ),
'me' => array( 'me' => array(
@@ -302,7 +335,7 @@ $lang = array(
'groups' => array( 'groups' => array(
-1 => "Keine", "Tester", "Administrator", "Editor", "Moderator", "Bürokrat", -1 => "Keine", "Tester", "Administrator", "Editor", "Moderator", "Bürokrat",
"Entwickler", "VIP", "Blogger", "Premium", "Übersetzer", "Handelsvertreter", "Entwickler", "VIP", "Blogger", "Premium", "Übersetzer", "Handelsvertreter",
"Screenshot-Verwalter", "Video-Verwalter" "Screenshot-Verwalter", "Video-Verwalter", "API-Partner", "Ausstehend"
), ),
// signIn // signIn
'doSignIn' => "Mit Eurem AoWoW-Konto anmelden", 'doSignIn' => "Mit Eurem AoWoW-Konto anmelden",
@@ -348,7 +381,7 @@ $lang = array(
'accActivated' => 'Euer Konto wurde soeben aktiviert.<br>Ihr könnt euch nun <a href="?account=signin&token=%s">anmelden</a>', 'accActivated' => 'Euer Konto wurde soeben aktiviert.<br>Ihr könnt euch nun <a href="?account=signin&token=%s">anmelden</a>',
'userNotFound' => "Ein Konto mit diesem Namen existiert nicht.", 'userNotFound' => "Ein Konto mit diesem Namen existiert nicht.",
'wrongPass' => "Dieses Kennwort ist ungültig.", 'wrongPass' => "Dieses Kennwort ist ungültig.",
'accInactive' => "Dieses Konto wurde bisher nicht aktiviert.", // 'accInactive' => "Dieses Konto wurde bisher nicht aktiviert.",
'loginExceeded' => "Die maximale Anzahl an Anmelde-Versuchen von dieser IP wurde überschritten. Bitte versucht es in %s erneut.", 'loginExceeded' => "Die maximale Anzahl an Anmelde-Versuchen von dieser IP wurde überschritten. Bitte versucht es in %s erneut.",
'signupExceeded'=> "Die maximale Anzahl an Regustrierungen von dieser IP wurde überschritten. Bitte versucht es in %s erneut.", 'signupExceeded'=> "Die maximale Anzahl an Regustrierungen von dieser IP wurde überschritten. Bitte versucht es in %s erneut.",
'errNameLength' => "Euer Benutzername muss mindestens 4 Zeichen lang sein.", // message_usernamemin 'errNameLength' => "Euer Benutzername muss mindestens 4 Zeichen lang sein.", // message_usernamemin
@@ -383,11 +416,30 @@ $lang = array(
'recoverUser' => ["Benutzernamenanfrage", "Folgt diesem Link um euch anzumelden.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."], 'recoverUser' => ["Benutzernamenanfrage", "Folgt diesem Link um euch anzumelden.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."],
'resetPass' => ["Kennwortreset", "Folgt diesem Link um euer Kennwort zurückzusetzen.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."] 'resetPass' => ["Kennwortreset", "Folgt diesem Link um euer Kennwort zurückzusetzen.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nFalls Ihr diese Mail nicht angefordert habt kann sie einfach ignoriert werden."]
), ),
'emote' => array(
'notFound' => "Dieses Emote existiert nicht.",
'self' => "An Euch selbst",
'target' => "An Andere mit Ziel",
'noTarget' => "An Andere ohne Ziel",
'isAnimated' => "Besitzt eine Animation",
'aliases' => "Aliasse",
'noText' => "Dieses Emote besitzt keinen Text.",
),
'enchantment' => array(
'details' => "Details",
'activation' => "Aktivierung",
'notFound' => "Diese Verzauberung existiert nicht.",
'types' => array(
1 => "Zauber (Auslösung)", 3 => "Zauber (Anlegen)", 7 => "Zauber (Benutzen)", 8 => "Prismatischer Sockel",
5 => "Statistik", 2 => "Waffenschaden", 6 => "DPS", 4 => "Verteidigung"
)
),
'gameObject' => array( 'gameObject' => array(
'notFound' => "Dieses Objekt existiert nicht .", 'notFound' => "Dieses Objekt existiert nicht.",
'cat' => [0 => "Anderes", 9 => "Bücher", 3 => "Behälter", -5 => "Truhen", 25 => "Fischschwärme", -3 => "Kräuter", -4 => "Erzadern", -2 => "Quest", -6 => "Werkzeuge"], 'cat' => [0 => "Anderes", 9 => "Bücher", 3 => "Behälter", -5 => "Truhen", 25 => "Fischschwärme", -3 => "Kräuter", -4 => "Erzadern", -2 => "Quest", -6 => "Werkzeuge"],
'type' => [ 9 => "Buch", 3 => "Behälter", -5 => "Truhe", 25 => "", -3 => "Kraut", -4 => "Erzvorkommen", -2 => "Quest", -6 => ""], 'type' => [ 9 => "Buch", 3 => "Behälter", -5 => "Truhe", 25 => "", -3 => "Kraut", -4 => "Erzvorkommen", -2 => "Quest", -6 => ""],
'unkPosition' => "Der Standort dieses Objekts ist nicht bekannt.", 'unkPosition' => "Der Standort dieses Objekts ist nicht bekannt.",
'npcLootPH' => 'Der Behälter <b>%s</b> beinhaltet die Beute vom Kampf gegen <a href="?npc=%d">%s</a>. Er erscheint nach seinem Tod.',
'key' => "Schlüssel", 'key' => "Schlüssel",
'focus' => "Zauberfokus", 'focus' => "Zauberfokus",
'focusDesc' => "Zauber, die diesen Fokus benötigen, können an diesem Objekt gewirkt werden.", 'focusDesc' => "Zauber, die diesen Fokus benötigen, können an diesem Objekt gewirkt werden.",
@@ -453,7 +505,7 @@ $lang = array(
'titleReward' => 'Euch wird der Titel "<a href="?title=%d">%s</a>" verliehen', 'titleReward' => 'Euch wird der Titel "<a href="?title=%d">%s</a>" verliehen',
'slain' => "getötet", 'slain' => "getötet",
'reqNumCrt' => "Benötigt", 'reqNumCrt' => "Benötigt",
'rfAvailable' => "Verfügbar auf Realm:", 'rfAvailable' => "Verfügbar auf Realm: ",
'_transfer' => 'Dieser Erfolg wird mit <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> vertauscht, wenn Ihr zur <span class="icon-%s">%s</span> wechselt.', '_transfer' => 'Dieser Erfolg wird mit <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> vertauscht, wenn Ihr zur <span class="icon-%s">%s</span> wechselt.',
), ),
'chrClass' => array( 'chrClass' => array(
@@ -481,6 +533,20 @@ $lang = array(
'Azeroth' => "Azeroth", 'Azeroth' => "Azeroth",
'CosmicMap' => "Kosmische Karte", 'CosmicMap' => "Kosmische Karte",
), ),
'privileges' => array(
'main' => "Auf unserer Seite könnt Ihr <a href=\"?reputation\">Ruf</a> erringen. Hauptsächlich erringt man Ruf dadurch, dass Eure Kommentare positiv bewertet werden.<br><br>Das heißt, Euer Ruf hängt in gewissem Maße davon ab, wie sehr Ihr der Community beiträgt.<br><br>Mit dem Sammeln von Ruf verdient Ihr Euch auch das Vertrauen der Gemeinschaft ein, und Ihr erhält Privilegien. Unten könnt Ihr eine vollständige Liste einsehen.",
'privilege' => "Privileg",
'privileges' => "Privilegien",
'requiredRep' => "Benötigter Ruf",
'reqPoints' => "Dieses Privileg benötigt <b>%s</b> Rufpunkte.",
'_privileges' => array(
null, "Kommentare einsenden", "Webseitenexterne Links einsenden", null,
"Kein CAPTCHA", "Kommentarbewertungen zählen mehr", null, null,
null, "Mehr Wertungsfreiheit", "Kommentare positiv bewerten", "Kommentare negativ bewerten",
"Kommentarantwort einsenden", "Rahmen: Außergewöhnlich", "Rahmen: Selten", "Rahmen: Episch",
"Rahmen: Legendär", "AoWoW Premium"
)
),
'zone' => array( 'zone' => array(
'notFound' => "Dieses Gebiet existiert nicht.", 'notFound' => "Dieses Gebiet existiert nicht.",
'attunement' => ["Einstimmung", "Heroische Einstimmung"], 'attunement' => ["Einstimmung", "Heroische Einstimmung"],
@@ -558,8 +624,9 @@ $lang = array(
'receiveAlso' => "Ihr bekommt außerdem", 'receiveAlso' => "Ihr bekommt außerdem",
'spellCast' => "Der folgende Zauber wird auf Euch gewirkt", 'spellCast' => "Der folgende Zauber wird auf Euch gewirkt",
'spellLearn' => "Ihr erlernt", 'spellLearn' => "Ihr erlernt",
'bonusTalents' => "Talentpunkte", 'bonusTalents' => "%d |4Talentpunkt:Talentpunkte;",
'spellDisplayed'=> ' (<a href="?spell=%d">%s</a> wird angezeigt)', 'spellDisplayed'=> ' (<a href="?spell=%d">%s</a> wird angezeigt)',
'attachment' => "Anlage",
'questInfo' => array( 'questInfo' => array(
0 => "Normal", 1 => "Gruppe", 21 => "Leben", 41 => "PvP", 62 => "Schlachtzug", 81 => "Dungeon", 82 => "Weltereignis", 0 => "Normal", 1 => "Gruppe", 21 => "Leben", 41 => "PvP", 62 => "Schlachtzug", 81 => "Dungeon", 82 => "Weltereignis",
83 => "Legendär", 84 => "Eskorte", 85 => "Heroisch", 88 => "Schlachtzug (10)", 89 => "Schlachtzug (25)" 83 => "Legendär", 84 => "Eskorte", 85 => "Heroisch", 88 => "Schlachtzug (10)", 89 => "Schlachtzug (25)"
@@ -635,6 +702,9 @@ $lang = array(
-2 => "Nicht kategorisiert" -2 => "Nicht kategorisiert"
) )
), ),
'icon' => array(
'notFound' => "Dieses Icon existiert nicht."
),
'title' => array( 'title' => array(
'notFound' => "Dieser Titel existiert nicht.", 'notFound' => "Dieser Titel existiert nicht.",
'_transfer' => 'Dieser Titel wird mit <a href="?title=%d" class="q1">%s</a> vertauscht, wenn Ihr zur <span class="icon-%s">%s</span> wechselt.', '_transfer' => 'Dieser Titel wird mit <a href="?title=%d" class="q1">%s</a> vertauscht, wenn Ihr zur <span class="icon-%s">%s</span> wechselt.',
@@ -656,10 +726,25 @@ $lang = array(
1 => "Verschiedenes", 2 => "Spieler gegen Spieler", 4 => "Classic", 21 => "Wrath of the Lich King", 22 => "Dungeon und Schlachtzug", 23 => "Burning Crusade", 41 => "Test", 3 => "Unbenutzt" 1 => "Verschiedenes", 2 => "Spieler gegen Spieler", 4 => "Classic", 21 => "Wrath of the Lich King", 22 => "Dungeon und Schlachtzug", 23 => "Burning Crusade", 41 => "Test", 3 => "Unbenutzt"
) )
), ),
'sound' => array(
'notFound' => "Dieser Klang existiert nicht.",
'foundIn' => "Dieser Klang befindet sich in",
'goToPlaylist' => "Gehe zu meiner Playlist",
'music' => "Musik",
'intro' => "Intromusik",
'ambience' => "Umgebung",
'cat' => array(
null, "Spells", "User Interface", "Footsteps", "Weapons Impacts", null, "Weapons Misses", null, null, "Pick Up/Put Down",
"NPC Combat", null, "Errors", "Nature", "Objects", null, "Death", "NPC Greetings", null, "Armor",
"Footstep Splash", "Water (Character)", "Water", "Tradeskills", "Misc Ambience", "Doodads", "Spell Fizzle", "NPC Loops", "Zone Music", "Emotes",
"Narration Music", "Narration", 50 => "Zone Ambience", 52 => "Emitters", 53 => "Vehicles", 1000 => "Meine Playlist"
)
),
'pet' => array( 'pet' => array(
'notFound' => "Diese Tierart existiert nicht.", 'notFound' => "Diese Tierart existiert nicht.",
'exotic' => "Exotisch", 'exotic' => "Exotisch",
'cat' => ["Wildheit", "Hartnäckigkeit", "Gerissenheit"] 'cat' => ["Wildheit", "Hartnäckigkeit", "Gerissenheit"],
'food' => ["Fleisch", "Fisch", "Käse", "Brot", "Fungus", "Obst", "Rohes Fleisch", "Roher Fisch"]
), ),
'faction' => array( 'faction' => array(
'notFound' => "Diese Fraktion existiert nicht.", 'notFound' => "Diese Fraktion existiert nicht.",
@@ -743,6 +828,8 @@ $lang = array(
'pctCostOf' => "vom Grund%s", 'pctCostOf' => "vom Grund%s",
'costPerSec' => ", plus %s pro Sekunde", 'costPerSec' => ", plus %s pro Sekunde",
'costPerLevel' => ", plus %s pro Stufe", 'costPerLevel' => ", plus %s pro Stufe",
'stackGroup' => "Stack Gruppierung",
'linkedWith' => "Verknüpft mit",
'_scaling' => "Skalierung", '_scaling' => "Skalierung",
'scaling' => array( 'scaling' => array(
'directSP' => "+%.2f%% der Zaubermacht zum direkten Effekt", 'directAP' => "+%.2f%% der Angriffskraft zum direkten Effekt", 'directSP' => "+%.2f%% der Zaubermacht zum direkten Effekt", 'directAP' => "+%.2f%% der Angriffskraft zum direkten Effekt",
@@ -815,7 +902,8 @@ $lang = array(
), ),
'traitShort' => array( 'traitShort' => array(
'atkpwr' => "Angr", 'rgdatkpwr' => "DAngr", 'splpwr' => "ZMacht", 'arcsplpwr' => "ArkM", 'firsplpwr' => "FeuM", 'atkpwr' => "Angr", 'rgdatkpwr' => "DAngr", 'splpwr' => "ZMacht", 'arcsplpwr' => "ArkM", 'firsplpwr' => "FeuM",
'frosplpwr' => "FroM", 'holsplpwr' => "HeiM", 'natsplpwr' => "NatM", 'shasplpwr' => "SchM", 'splheal' => "Heil" 'frosplpwr' => "FroM", 'holsplpwr' => "HeiM", 'natsplpwr' => "NatM", 'shasplpwr' => "SchM", 'splheal' => "Heil",
'str' => "Stä", 'agi' => "Bew", 'sta' => "Aus", 'int' => "Int", 'spi' => "Wil"
), ),
'spellModOp' => array( 'spellModOp' => array(
"Schaden", "Dauer", "Bedrohung", "Effekt 1", "Aufladungen", "Schaden", "Dauer", "Bedrohung", "Effekt 1", "Aufladungen",
@@ -841,36 +929,127 @@ $lang = array(
"Inschriftenkunde", "Vom Fahrzeug öffnen" "Inschriftenkunde", "Vom Fahrzeug öffnen"
), ),
'stealthType' => ["Allgemein", "Falle"], 'stealthType' => ["Allgemein", "Falle"],
'invisibilityType' => ["Allgemein", 3 => "Falle", 6 => "Trunkenheit"] 'invisibilityType' => ["Allgemein", 3 => "Falle", 6 => "Trunkenheit"],
'unkEffect' => 'Unknown Effect',
'effects' => array(
/*0-5 */ 'None', 'Instakill', 'School Damage', 'Dummy', 'Portal Teleport', 'Teleport Units',
/*6+ */ 'Apply Aura', 'Environmental Damage', 'Power Drain', 'Health Leech', 'Heal', 'Bind',
/*12+ */ 'Portal', 'Ritual Base', 'Ritual Specialize', 'Ritual Activate Portal', 'Quest Complete', 'Weapon Damage NoSchool',
/*18+ */ 'Resurrect', 'Add Extra Attacks', 'Dodge', 'Evade', 'Parry', 'Block',
/*24+ */ 'Create Item', 'Can Use Weapon', 'Defense', 'Persistent Area Aura', 'Summon', 'Leap',
/*30+ */ 'Energize', 'Weapon Damage Percent', 'Trigger Missile', 'Open Lock', 'Summon Change Item', 'Apply Area Aura Party',
/*36+ */ 'Learn Spell', 'Spell Defense', 'Dispel', 'Language', 'Dual Wield', 'Jump',
/*42+ */ 'Jump Dest', 'Teleport Units Face Caster','Skill Step', 'Add Honor', 'Spawn', 'Trade Skill',
/*48+ */ 'Stealth', 'Detect', 'Trans Door', 'Force Critical Hit', 'Guarantee Hit', 'Enchant Item Permanent',
/*54+ */ 'Enchant Item Temporary', 'Tame Creature', 'Summon Pet', 'Learn Pet Spell', 'Weapon Damage Flat', 'Create Random Item',
/*60+ */ 'Proficiency', 'Send Event', 'Power Burn', 'Threat', 'Trigger Spell', 'Apply Area Aura Raid',
/*66+ */ 'Create Mana Gem', 'Heal Max Health', 'Interrupt Cast', 'Distract', 'Pull', 'Pickpocket',
/*72+ */ 'Add Farsight', 'Untrain Talents', 'Apply Glyph', 'Heal Mechanical', 'Summon Object Wild', 'Script Effect',
/*78+ */ 'Attack', 'Sanctuary', 'Add Combo Points', 'Create House', 'Bind Sight', 'Duel',
/*84+ */ 'Stuck', 'Summon Player', 'Activate Object', 'WMO Damage', 'WMO Repair', 'WMO Change',
/*90+ */ 'Kill Credit', 'Threat All', 'Enchant Held Item', 'Force Deselect', 'Self Resurrect', 'Skinning',
/*96+ */ 'Charge', 'Cast Button', 'Knock Back', 'Disenchant', 'Inebriate', 'Feed Pet',
/*102+ */ 'Dismiss Pet', 'Reputation', 'Summon Object Slot1', 'Summon Object Slot2', 'Summon Object Slot3', 'Summon Object Slot4',
/*108+ */ 'Dispel Mechanic', 'Summon Dead Pet', 'Destroy All Totems', 'Durability Damage', 'Summon Demon', 'Resurrect Flat',
/*114+ */ 'Attack Me', 'Durability Damage Percent','Skin Player Corpse', 'Spirit Heal', 'Skill', 'Apply Area Aura Pet',
/*120+ */ 'Teleport Graveyard', 'Weapon Damage Normalized', null, 'Send Taxi', 'Pull Towards', 'Modify Threat Percent',
/*126+ */ 'Steal Beneficial Buff', 'Prospecting', 'Apply Area Aura Friend', 'Apply Area Aura Enemy', 'Redirect Threat', 'Play Sound',
/*132+ */ 'Play Music', 'Unlearn Specialization', 'Kill Credit2', 'Call Pet', 'Heal Percent', 'Energize Percent',
/*138+ */ 'Leap Back', 'Clear Quest', 'Force Cast', 'Force Cast With Value', 'Trigger Spell With Value', 'Apply Area Aura Owner',
/*144+ */ 'Knock Back Dest', 'Pull Towards Dest', 'Activate Rune', 'Quest Fail', null, 'Charge Dest',
/*150+ */ 'Quest Start', 'Trigger Spell 2', null, 'Create Tamed Pet', 'Discover Taxi', 'Dual Wield 2H Weapons',
/*156+ */ 'Enchant Item Prismatic', 'Create Item 2', 'Milling', 'Allow Rename Pet', null, 'Talent Spec Count',
/*162-164*/ 'Talent Spec Select', null, 'Remove Aura'
),
'unkAura' => 'Unknown Aura',
'auras' => array(
/*0- */ 'None', 'Bind Sight', 'Mod Possess', 'Periodic Damage', 'Dummy',
/*5+ */ 'Mod Confuse', 'Mod Charm', 'Mod Fear', 'Periodic Heal', 'Mod Attack Speed',
'Mod Threat', 'Taunt', 'Stun', 'Mod Damage Done Flat', 'Mod Damage Taken Flat',
'Damage Shield', 'Mod Stealth', 'Mod Stealth Detection', 'Mod Invisibility', 'Mod Invisibility Detection',
'Mod Health Percent', 'Mod Power Percent', 'Mod Resistance Flat', 'Periodic Trigger Spell', 'Periodic Energize',
/*25+ */ 'Pacify', 'Root', 'Silence', 'Reflect Spells', 'Mod Stat Flat',
'Mod Skill', 'Mod Increase Speed', 'Mod Increase Mounted Speed', 'Mod Decrease Speed', 'Mod Increase Health',
'Mod Increase Power', 'Shapeshift', 'Spell Effect Immunity', 'Spell Aura Immunity', 'School Immunity',
'Damage Immunity', 'Dispel Immunity', 'Proc Trigger Spell', 'Proc Trigger Damage', 'Track Creatures',
'Track Resources', 'Mod Parry Skill', 'Mod Parry Percent', null, 'Mod Dodge Percent',
/*50+ */ 'Mod Critical Healing Amount', 'Mod Block Percent', 'Mod Physical Crit Percent', 'Periodic Health Leech', 'Mod Hit Chance',
'Mod Spell Hit Chance', 'Transform', 'Mod Spell Crit Chance', 'Mod Increase Swim Speed', 'Mod Damage Done Versus Creature',
'Pacify Silence', 'Mod Scale', 'Periodic Health Funnel', 'Periodic Mana Funnel', 'Periodic Mana Leech',
'Mod Casting Speed (not stacking)', 'Feign Death', 'Disarm', 'Stalked', 'School Absorb',
'Extra Attacks', 'Mod Spell Crit Chance School', 'Mod Power Cost School Percent', 'Mod Power Cost School Flat', 'Reflect Spells School',
/*75+ */ 'Language', 'Far Sight', 'Mechanic Immunity', 'Mounted', 'Mod Damage Done Percent',
'Mod Stat Percent', 'Split Damage Percent', 'Water Breathing', 'Mod Base Resistance Flat', 'Mod Health Regeneration',
'Mod Power Regeneration', 'Channel Death Item', 'Mod Damage Taken Percent', 'Mod Health Regeneration Percent', 'Periodic Damage Percent',
'Mod Resist Chance', 'Mod Detect Range', 'Prevent Fleeing', 'Unattackable', 'Interrupt Regeneration',
'Ghost', 'Spell Magnet', 'Mana Shield', 'Mod Skill Value', 'Mod Attack Power',
/*100+ */ 'Auras Visible', 'Mod Resistance Percent', 'Mod Melee Attack Power Versus', 'Mod Total Threat', 'Water Walk',
'Feather Fall', 'Hover', 'Add Flat Modifier', 'Add Percent Modifier', 'Add Target Trigger',
'Mod Power Regeneration Percent', 'Add Caster Hit Trigger', 'Override Class Scripts', 'Mod Ranged Damage Taken Flat', 'Mod Ranged Damage Taken Percent',
'Mod Healing', 'Mod Regeneration During Combat', 'Mod Mechanic Resistance', 'Mod Healing Taken Percent', 'Share Pet Tracking',
'Untrackable', 'Empathy', 'Mod Offhand Damage Percent', 'Mod Target Resistance', 'Mod Ranged Attack Power',
/*125+ */ 'Mod Melee Damage Taken Flat', 'Mod Melee Damage Taken Percent', 'Ranged Attack Power Attacker Bonus', 'Possess Pet', 'Mod Speed Always',
'Mod Mounted Speed Always', 'Mod Ranged Attack Power Versus', 'Mod Increase Energy Percent', 'Mod Increase Health Percent', 'Mod Mana Regeneration Interrupt',
'Mod Healing Done Flat', 'Mod Healing Done Percent', 'Mod Total Stat Percentage', 'Mod Melee Haste', 'Force Reaction',
'Mod Ranged Haste', 'Mod Ranged Ammo Haste', 'Mod Base Resistance Percent', 'Mod Resistance Exclusive', 'Safe Fall',
'Mod Pet Talent Points', 'Allow Tame Pet Type', 'Mechanic Immunity Mask', 'Retain Combo Points', 'Reduce Pushback',
/*150+ */ 'Mod Shield Blockvalue Percent', 'Track Stealthed', 'Mod Detected Range', 'Split Damage Flat', 'Mod Stealth Level',
'Mod Water Breathing', 'Mod Reputation Gain', 'Pet Damage Multi', 'Mod Shield Blockvalue', 'No PvP Credit',
'Mod AoE Avoidance', 'Mod Health Regeneration In Combat', 'Power Burn Mana', 'Mod Crit Damage Bonus', null,
'Melee Attack Power Attacker Bonus', 'Mod Attack Power Percent', 'Mod Ranged Attack Power Percent', 'Mod Damage Done Versus', 'Mod Crit Percent Versus',
'Change Model', 'Mod Speed (not stacking)', 'Mod Mounted Speed (not stacking)', null, 'Mod Spell Damage Of Stat Percent',
/*175+ */ 'Mod Spell Healing Of Stat Percent', 'Spirit Of Redemption', 'AoE Charm', 'Mod Debuff Resistance', 'Mod Attacker Spell Crit Chance',
'Mod Spell Damage Versus', null, 'Mod Resistance Of Stat Percent', 'Mod Critical Threat', 'Mod Attacker Melee Hit Chance',
'Mod Attacker Ranged Hit Chance', 'Mod Attacker Spell Hit Chance', 'Mod Attacker Melee Crit Chance', 'Mod Attacker Ranged Crit Chance', 'Mod Rating',
'Mod Faction Reputation Gain', 'Use Normal Movement Speed', 'Mod Melee Ranged Haste', 'Mod Haste', 'Mod Target Absorb School',
'Mod Target Ability Absorb School', 'Mod Cooldown', 'Mod Attacker Spell And Weapon Crit Chance', null, 'Mod Increases Spell Percent to Hit',
/*200+ */ 'Mod XP Percent', 'Fly', 'Ignore Combat Result', 'Mod Attacker Melee Crit Damage', 'Mod Attacker Ranged Crit Damage',
'Mod School Crit Damage Taken', 'Mod Increase Vehicle Flight Speed', 'Mod Increase Mounted Flight Speed', 'Mod Increase Flight Speed', 'Mod Mounted Flight Speed Always',
'Mod Vehicle Speed Always', 'Mod Flight Speed (not stacking)', 'Mod Ranged Attack Power Of Stat Percent', 'Mod Rage from Damage Dealt', 'Tamed Pet Passive',
'Arena Preparation', 'Haste Spells', 'Killing Spree', 'Haste Ranged', 'Mod Mana Regeneration from Stat',
'Mod Rating from Stat', 'Ignore Threat', null, 'Raid Proc from Charge', null,
/*225+ */ 'Raid Proc from Charge With Value', 'Periodic Dummy', 'Periodic Trigger Spell With Value', 'Detect Stealth', 'Mod AoE Damage Avoidance',
'Mod Increase Health', 'Proc Trigger Spell With Value', 'Mod Mechanic Duration', 'Mod Display Model', 'Mod Mechanic Duration (not stacking)',
'Mod Dispel Resist', 'Control Vehicle', 'Mod Spell Damage Of Attack Power', 'Mod Spell Healing Of Attack Power', 'Mod Scale 2',
'Mod Expertise', 'Force Move Forward', 'Mod Spell Damage from Healing', 'Mod Faction', 'Comprehend Language',
'Mod Aura Duration By Dispel', 'Mod Aura Duration By Dispel (not stacking)', 'Clone Caster', 'Mod Combat Result Chance', 'Convert Rune',
/*250+ */ 'Mod Increase Health 2', 'Mod Enemy Dodge', 'Mod Speed Slow All', 'Mod Block Crit Chance', 'Mod Disarm Offhand',
'Mod Mechanic Damage Taken Percent', 'No Reagent Use', 'Mod Target Resist By Spell Class', 'Mod Spell Visual', 'Mod HoT Percent',
'Screen Effect', 'Phase', 'Ability Ignore Aurastate', 'Allow Only Ability', null,
null, null, 'Mod Immune Aura Apply School', 'Mod Attack Power Of Stat Percent', 'Mod Ignore Target Resist',
'Mod Ability Ignore Target Resist', 'Mod Damage Taken Percent From Caster', 'Ignore Melee Reset', 'X Ray', 'Ability Consume No Ammo',
/*275+ */ 'Mod Ignore Shapeshift', 'Mod Mechanic Damage Done Percent', 'Mod Max Affected Targets', 'Mod Disarm Ranged', 'Initialize Images',
'Mod Armor Penetration Percent', 'Mod Honor Gain Percent', 'Mod Base Health Percent', 'Mod Healing Received', 'Linked',
'Mod Attack Power Of Armor', 'Ability Periodic Crit', 'Deflect Spells', 'Ignore Hit Direction', null,
'Mod Crit Percent', 'Mod XP Quest Percent', 'Open Stable', 'Override Spells', 'Prevent Power Regeneration',
null, 'Set Vehicle Id', 'Block Spell Family', 'Strangulate', null,
/*300+ */ 'Share Damage Percent', 'School Heal Absorb', null, 'Mod Damage Done Versus Aurastate', 'Mod Fake Inebriate',
'Mod Minimum Speed', null, 'Heal Absorb Test', 'Hunter Trap', null,
'Mod Creature AoE Damage Avoidance', null, null, null, 'Prevent Ressurection',
/* -316*/ 'Underwater Walking', 'Periodic Haste'
)
), ),
'item' => array( 'item' => array(
'notFound' => "Dieser Gegenstand existiert nicht .", 'notFound' => "Dieser Gegenstand existiert nicht .",
'armor' => "%s Rüstung", 'armor' => "%s Rüstung",
'block' => "%s Blocken", 'block' => "%s Blocken",
'charges' => "Aufladungen", 'charges' => "%d |4Aufladung:Aufladungen;",
'locked' => "Verschlossen", 'locked' => "Verschlossen",
'ratingString' => "%s&nbsp;@&nbsp;L%s", 'ratingString' => "%s&nbsp;@&nbsp;L%s",
'heroic' => "Heroisch", 'heroic' => "Heroisch",
'unique' => "Einzigartig",
'uniqueEquipped'=> "Einzigartig anlegbar",
'startQuest' => "Dieser Gegenstand startet eine Quest", 'startQuest' => "Dieser Gegenstand startet eine Quest",
'bagSlotString' => "%d Platz %s", 'bagSlotString' => '%1$d Platz %2$s',
'dps' => "Schaden pro Sekunde",
'dps2' => "Schaden pro Sekunde",
'addsDps' => "Adds",
'fap' => "Angriffskraft in Tiergestalt", 'fap' => "Angriffskraft in Tiergestalt",
'durability' => "Haltbarkeit", 'durability' => 'Haltbarkeit %1$d / %2$d',
'realTime' => "Realzeit", 'realTime' => "Realzeit",
'conjured' => "Herbeigezauberter Gegenstand", 'conjured' => "Herbeigezauberter Gegenstand",
'damagePhys' => "%s Schaden",
'damageMagic' => "%s %sschaden",
'speed' => "Tempo",
'sellPrice' => "Verkaufspreis", 'sellPrice' => "Verkaufspreis",
'itemLevel' => "Gegenstandsstufe", 'itemLevel' => "Gegenstandsstufe %d",
'randEnchant' => "&lt;Zufällige Verzauberung&gt", 'randEnchant' => "&lt;Zufällige Verzauberung&gt",
'readClick' => "&lt;Zum Lesen rechtsklicken&gt", 'readClick' => "&lt;Zum Lesen rechtsklicken&gt",
'openClick' => "&lt;Zum Öffnen rechtsklicken&gt", 'openClick' => "&lt;Zum Öffnen rechtsklicken&gt",
'set' => "Set", 'setBonus' => "(%d) Set: %s",
'setName' => '%1$s (%2$d/%3$d)',
'partyLoot' => "Gruppenloot", 'partyLoot' => "Gruppenloot",
'smartLoot' => "Intelligente Beuteverteilung", 'smartLoot' => "Intelligente Beuteverteilung",
'indestructible'=> "Kann nicht zerstört werden", 'indestructible'=> "Kann nicht zerstört werden",
@@ -903,6 +1082,18 @@ $lang = array(
'buyout' => "Sofortkaufpreis", 'buyout' => "Sofortkaufpreis",
'each' => "Stück", 'each' => "Stück",
'tabOther' => "Anderes", 'tabOther' => "Anderes",
'reqMinLevel' => "Benötigt Stufe %d",
'reqLevelRange' => "Benötigt Stufe %d bis %d (%s)",
'unique' => ["Einzigartig", "Limitiert (%d)", "Einzigartig: %s (%d)" ],
'uniqueEquipped'=> ["Einzigartig anlegbar", null, "Einzigartig angelegt: %s (%d)"],
'speed' => "Tempo",
'dps' => "(%.1f Schaden pro Sekunde)",
'damage' => array( // *DAMAGE_TEMPLATE*
// basic, basic /w school, add basic, add basic /w school
'single' => ['%d Schaden', '%1$d %2$sschaden', '+ %1$d Schaden', '+ %1$d %2$sschaden' ],
'range' => ['%1$d - %2$d Schaden', '%1$d - %2$d %3$sschaden', '+ %1$d - %2$d Schaden', '+ %1$d - %2$d %3$sschaden' ],
'ammo' => ["Verursacht %g zusätzlichen Schaden pro Sekunde.", "Verursacht %g zusätzlichen %sschaden pro Sekunde", "+ %g Schaden pro Sekunde", "+ %g %sschaden pro Sekunde"]
),
'gems' => "Edelsteine", 'gems' => "Edelsteine",
'socketBonus' => "Sockelbonus", 'socketBonus' => "Sockelbonus",
'socket' => array( 'socket' => array(
@@ -912,22 +1103,22 @@ $lang = array(
"Meta", "Rot", "Gelb", "Blau" "Meta", "Rot", "Gelb", "Blau"
), ),
'gemConditions' => array( // ENCHANT_CONDITION_* in GlobalStrings.lua; 2 not in use (use as PH) 'gemConditions' => array( // ENCHANT_CONDITION_* in GlobalStrings.lua; 2 not in use (use as PH)
2 => ["weniger als %d Edelstein der Kategorie %s", "weniger als %d Edelsteine der Kategorie %s"], 2 => "weniger als %d |4Edelstein:Edelsteine; der Kategorie %s",
3 => "mehr Edelsteine der Kategorie %s als Edelsteine der Kategorie %s", 3 => "mehr Edelsteine der Kategorie %s als Edelsteine der Kategorie %s",
5 => ["mindestens %d Edelstein der Kategorie %s", "mindestens %d Edelsteine der Kategorie %s"] 5 => "mindestens %d |4Edelstein:Edelsteine; der Kategorie %s"
), ),
'reqRating' => array( // ITEM_REQ_ARENA_RATING* 'reqRating' => array( // ITEM_REQ_ARENA_RATING*
"Benötigt eine persönliche Arenawertung und Teamwertung von %d.", "Benötigt eine persönliche Arenawertung und Teamwertung von %d.",
"Benötigt eine persönliche und eine Teamwertung von %d<br>in 3v3- oder 5v5-Turnieren", "Benötigt eine persönliche und eine Teamwertung von %d|nin 3v3- oder 5v5-Turnieren",
"Benötigt eine persönliche und eine Teamwertung von %d<br>in 5v5-Turnieren" "Benötigt eine persönliche und eine Teamwertung von %d|nin 5v5-Turnieren"
), ),
'quality' => array( 'quality' => array(
"Schlecht", "Verbreitet", "Selten", "Rar", "Schlecht", "Verbreitet", "Selten", "Rar",
"Episch", "Legendär", "Artefakt", "Erbstücke", "Episch", "Legendär", "Artefakt", "Erbstücke",
), ),
'trigger' => array( 'trigger' => array(
"Benutzen: ", "Anlegen: ", "Chance bei Treffer: ", null, null, "Benutzen: ", "Anlegen: ", "Chance bei Treffer: ", "", "",
null, null "", ""
), ),
'bonding' => array( 'bonding' => array(
"Accountgebunden", "Wird beim Aufheben gebunden", "Wird beim Anlegen gebunden", "Accountgebunden", "Wird beim Aufheben gebunden", "Wird beim Anlegen gebunden",
@@ -1007,8 +1198,8 @@ $lang = array(
13 => "Schlüssel", 13 => "Schlüssel",
), ),
'statType' => array( 'statType' => array(
"Erhöht Euer Mana um %d.", "Mana",
"Erhöht Eure Gesundheit um %d.", "Gesundheit",
null, null,
"Beweglichkeit", "Beweglichkeit",
"Stärke", "Stärke",

View File

@@ -4,13 +4,17 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
// comments in CAPS point to items in \Interface\FrameXML\GlobalStrings.lua - lowercase sources are contextual
$lang = array( $lang = array(
// page variables // page variables
'timeUnits' => array( 'timeUnits' => array(
'sg' => ["year", "month", "week", "day", "hour", "minute", "second", "millisecond"], 'sg' => ["year", "month", "week", "day", "hour", "minute", "second", "millisecond"],
'pl' => ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"], 'pl' => ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds"],
'ab' => ["yr", "mo", "wk", "day", "hr", "min", "sec", "ms"], 'ab' => ["yr", "mo", "wk", "day", "hr", "min", "sec", "ms"]
'ago' => '%s ago'
), ),
'main' => array( 'main' => array(
'name' => "name", 'name' => "name",
@@ -42,10 +46,14 @@ $lang = array(
'forum' => "Forum", 'forum' => "Forum",
'n_a' => "n/a", 'n_a' => "n/a",
'siteRep' => "Reputation", 'siteRep' => "Reputation",
'yourRepHistory'=> "Your Reputation History",
'aboutUs' => "About us & contact", 'aboutUs' => "About us & contact",
'and' => " and ", 'and' => " and ",
'or' => " or ", 'or' => " or ",
'back' => "Back", 'back' => "Back",
'reputationTip' => "Reputation points",
'byUserTimeAgo' => 'By <a href="'.HOST_URL.'/?user=%s">%1$s</a> %s ago',
'help' => "Help",
// filter // filter
'extSearch' => "Extended search", 'extSearch' => "Extended search",
@@ -102,22 +110,6 @@ $lang = array(
'chooseClass' => "Choose a class", 'chooseClass' => "Choose a class",
'chooseFamily' => "Choose a pet family", 'chooseFamily' => "Choose a pet family",
// profiler
'realm' => "Realm",
'region' => "Region",
'viewCharacter' => "View Character",
'_cpHead' => "Character Profiler",
'_cpHint' => "The <b>Character Profiler</b> lets you edit your character, find gear upgrades, check your gearscore and more!",
'_cpHelp' => "To get started, just follow the steps below. If you'd like more information, check out our extensive <a href=\"?help=profiler\">help page</a>.",
'_cpFooter' => "If you want a more refined search try out our <a href=\"?profiles\">advanced search</a> options. You can also create a <a href=\"?profile&amp;new\">new custom profile</a>.",
// help
'help' => "Help",
'helpTopics' => array(
"Commenting and You", "Model Viewer", "Screenshots: Tips & Tricks", "Stat Weighting",
"Talent Calculator", "Item Comparison", "Profiler", "Markup Guide"
),
// search // search
'search' => "Search", 'search' => "Search",
'searchButton' => "Search", 'searchButton' => "Search",
@@ -138,29 +130,68 @@ $lang = array(
'bannedRating' => "You have been banned from rating comments.", # LANG.tooltip_banned_rating 'bannedRating' => "You have been banned from rating comments.", # LANG.tooltip_banned_rating
'tooManyVotes' => "You have reached the daily voting cap. Come back tomorrow!", # LANG.tooltip_too_many_votes 'tooManyVotes' => "You have reached the daily voting cap. Come back tomorrow!", # LANG.tooltip_too_many_votes
// screenshots 'moreTitles' => array(
'prepError' => "An error occured preparing your screenshot", 'reputation' => "Website Reputation",
'cropHint' => "Crop the image by dragging the selection.<br>Please refer to <a href=\"?help=screenshots-tips-tricks\">Screenshots: Tips & Tricks</a> for an optimal layout.", 'whats-new' => "What's New",
'searchbox' => "Search Box",
'tooltips' => "Tooltips",
'faq' => "Frequently Asked Questions",
'aboutus' => "What is AoWoW?",
'searchplugins' => "Search Plugins",
'privileges' => "Privileges",
'top-users' => "Top Users",
'help' => array(
'commenting-and-you' => "Commenting and You", 'modelviewer' => "Model Viewer", 'screenshots-tips-tricks' => "Screenshots: Tips & Tricks",
'stat-weighting' => "Stat Weighting", 'talent-calculator' => "Talent Calculator", 'item-comparison' => "Item Comparison",
'profiler' => "Profiler", 'markup-guide' => "Markup Guide"
)
)
),
'profiler' => array(
'realm' => "Realm",
'region' => "Region",
'viewCharacter' => "View Character",
'_cpHint' => "The <b>Character Profiler</b> lets you edit your character, find gear upgrades, check your gearscore and more!",
'_cpHelp' => "To get started, just follow the steps below. If you'd like more information, check out our extensive <a href=\"?help=profiler\">help page</a>.",
'_cpFooter' => "If you want a more refined search try out our <a href=\"?profiles\">advanced search</a> options. You can also create a <a href=\"?profile&amp;new\">new custom profile</a>.",
'firstUseTitle' => "%s of %s",
'complexFilter' => "Complex filter selected! Search results are limited to cached Characters.",
'resync' => "Resync",
'guildRoster' => "Guild Roster for &lt;%s&gt;",
'arenaRoster' => "Arena Team Roster for &lt;%s&gt",
'atCaptain' => "Arena Team Captain",
'profiler' => "Character Profiler",
'arenaTeams' => "Arena Teams",
'guilds' => "Guilds",
'notFound' => array(
'guild' => "This Guild doesn't exist or is not yet in the database.",
'arenateam' => "This Arena Team doesn't exist or is not yet in the database.",
'profile' => "This character doesn't exist or is not yet in the database."
),
'dummyNPCs' => array(
100001 => "Gunship Battle", 200001 => "Northrend Beasts", 200002 => "Faction Champions", 200003 => "Val'kyr Twins"
),
),
'screenshot' => array(
'submission' => "Screenshot Submission",
'selectAll' => "Select all",
'cropHint' => "You may crop your screenshot and enter a caption.",
'displayOn' => "Displayed on:[br]%s - [%s=%d]",
'caption' => "Caption", 'caption' => "Caption",
'originalSize' => "Original size", 'charLimit' => "Optional, up to 200 characters",
'targetSize' => "Target size", 'thanks' => array(
'minSize' => "Minimum size", 'contrib' => "Thanks a lot for your contribution!",
'displayOn' => "Displayed on: %s[br][%s=%d]", 'goBack' => '<a href="?%s=%d">Click here</a> to go back to the page you came from.',
'ssEdit' => "Edit uploaded screenshot", 'note' => "Note: Your screenshot will need to be approved before appearing on the site. This can take up to 72 hours."
'ssUpload' => "Screenshot Upload", ),
'ssSubmit' => "Submit Screenshot", 'error' => array(
'ssErrors' => array( 'unkFormat' => "Unknown image format.",
'noUpload' => "The file was not uploaded!", 'tooSmall' => "Your screenshot is way too small. (&lt; ".CFG_SCREENSHOT_MIN_SIZE."x".CFG_SCREENSHOT_MIN_SIZE.").",
'maxSize' => "The file exceeds the maximum size of %s!", 'selectSS' => "Please select the screenshot to upload.",
'interrupted' => "The upload process was interrupted!",
'noFile' => "The file was not received!",
'noDest' => "The page this screenshot should be displayed on, does not exist!",
'notAllowed' => "You are not allowed to upload screenshots!", 'notAllowed' => "You are not allowed to upload screenshots!",
'noImage' => "The uploaded file is not an image file!",
'wrongFormat' => "The image file must be a png or jpg!",
'load' => "The image file could not be loaded!",
'tooSmall' => "The image size is too small! (lower than %d x %d)",
'tooLarge' => "The image size is too large! (greater than %d x %d)"
) )
), ),
'game' => array( 'game' => array(
@@ -173,8 +204,12 @@ $lang = array(
'difficulty' => "Difficulty", 'difficulty' => "Difficulty",
'dispelType' => "Dispel type", 'dispelType' => "Dispel type",
'duration' => "Duration", 'duration' => "Duration",
'gameObject' => "object", 'emote' => "emote",
'gameObjects' => "Objects", 'emotes' => "Emotes",
'enchantment' => "enchantment",
'enchantments' => "Enchantments",
'object' => "object",
'objects' => "Objects",
'glyphType' => "Glyph type", 'glyphType' => "Glyph type",
'race' => "race", 'race' => "race",
'races' => "Races", 'races' => "Races",
@@ -186,6 +221,8 @@ $lang = array(
'faction' => "faction", 'faction' => "faction",
'factions' => "Factions", 'factions' => "Factions",
'cooldown' => "%s cooldown", 'cooldown' => "%s cooldown",
'icon' => "icon",
'icons' => "icons",
'item' => "item", 'item' => "item",
'items' => "Items", 'items' => "Items",
'itemset' => "item Set", 'itemset' => "item Set",
@@ -204,12 +241,13 @@ $lang = array(
'requires' => "Requires %s", 'requires' => "Requires %s",
'requires2' => "Requires", 'requires2' => "Requires",
'reqLevel' => "Requires Level %s", 'reqLevel' => "Requires Level %s",
'reqLevelHlm' => "Requires Level %s",
'reqSkillLevel' => "Required skill level", 'reqSkillLevel' => "Required skill level",
'level' => "Level", 'level' => "Level",
'school' => "School", 'school' => "School",
'skill' => "skill", 'skill' => "skill",
'skills' => "Skills", 'skills' => "Skills",
'sound' => "sound",
'sounds' => "Sounds",
'spell' => "spell", 'spell' => "spell",
'spells' => "Spells", 'spells' => "Spells",
'type' => "Type", 'type' => "Type",
@@ -217,14 +255,14 @@ $lang = array(
'zone' => "zone", 'zone' => "zone",
'zones' => "Zones", 'zones' => "Zones",
'pvp' => "PvP", 'pvp' => "PvP", // PVP
'honorPoints' => "Honor Points", 'honorPoints' => "Honor Points", // HONOR_POINTS
'arenaPoints' => "Arena Points", 'arenaPoints' => "Arena Points", // ARENA_POINTS
'heroClass' => "Hero class", 'heroClass' => "Hero class",
'resource' => "Resource", 'resource' => "Resource",
'resources' => "Resources", 'resources' => "Resources",
'role' => "Role", 'role' => "Role", // ROLE
'roles' => "Roles", 'roles' => "Roles", // LFG_TOOLTIP_ROLES
'specs' => "Specs", 'specs' => "Specs",
'_roles' => ["Healer", "Melee DPS", "Ranged DPS", "Tank"], '_roles' => ["Healer", "Melee DPS", "Ranged DPS", "Tank"],
@@ -240,28 +278,28 @@ $lang = array(
"Milled", "Mined", "Prospected", "Pickpocketed", "Salvaged", "Skinned", "Milled", "Mined", "Prospected", "Pickpocketed", "Salvaged", "Skinned",
"In-Game Store" "In-Game Store"
), ),
'languages' => array( 'languages' => array( // Languages.dbc
1 => "Orcish", 2 => "Darnassian", 3 => "Taurahe", 6 => "Dwarvish", 7 => "Common", 8 => "Demonic", 1 => "Orcish", 2 => "Darnassian", 3 => "Taurahe", 6 => "Dwarvish", 7 => "Common", 8 => "Demonic",
9 => "Titan", 10 => "Thalassian", 11 => "Draconic", 12 => "Kalimag", 13 => "Gnomish", 14 => "Troll", 9 => "Titan", 10 => "Thalassian", 11 => "Draconic", 12 => "Kalimag", 13 => "Gnomish", 14 => "Troll",
33 => "Gutterspeak", 35 => "Draenei", 36 => "Zombie", 37 => "Gnomish Binary", 38 => "Goblin Binary" 33 => "Gutterspeak", 35 => "Draenei", 36 => "Zombie", 37 => "Gnomish Binary", 38 => "Goblin Binary"
), ),
'gl' => [null, "Major", "Minor"], 'gl' => [null, "Major", "Minor"], // MAJOR_GLYPH, MINOR_GLYPH
'si' => [1 => "Alliance", -1 => "Alliance only", 2 => "Horde", -2 => "Horde only", 3 => "Both"], 'si' => [1 => "Alliance", -1 => "Alliance only", 2 => "Horde", -2 => "Horde only", 3 => "Both"],
'resistances' => [null, 'Holy Resistance', 'Fire Resistance', 'Nature Resistance', 'Frost Resistance', 'Shadow Resistance', 'Arcane Resistance'], 'resistances' => [null, 'Holy Resistance', 'Fire Resistance', 'Nature Resistance', 'Frost Resistance', 'Shadow Resistance', 'Arcane Resistance'], // RESISTANCE?_NAME
'dt' => [null, "Magic", "Curse", "Disease", "Poison", "Stealth", "Invisibility", null, null, "Enrage"], 'dt' => [null, "Magic", "Curse", "Disease", "Poison", "Stealth", "Invisibility", null, null, "Enrage"], // SpellDispalType.dbc
'sc' => ["Physical", "Holy", "Fire", "Nature", "Frost", "Shadow", "Arcane"], 'sc' => ["Physical", "Holy", "Fire", "Nature", "Frost", "Shadow", "Arcane"], // STRING_SCHOOL_*
'cl' => [null, "Warrior", "Paladin", "Hunter", "Rogue", "Priest", "Death Knight", "Shaman", "Mage", "Warlock", null, "Druid"], 'cl' => [null, "Warrior", "Paladin", "Hunter", "Rogue", "Priest", "Death Knight", "Shaman", "Mage", "Warlock", null, "Druid"], // ChrClasses.dbc
'ra' => [-2 => "Horde", -1 => "Alliance", "Both", "Human", "Orc", "Dwarf", "Night Elf", "Undead", "Tauren", "Gnome", "Troll", null, "Blood Elf", "Draenei"], 'ra' => [-2 => "Horde", -1 => "Alliance", "Both", "Human", "Orc", "Dwarf", "Night Elf", "Undead", "Tauren", "Gnome", "Troll", null, "Blood Elf", "Draenei"], // ChrRaces.dbc
'rep' => ["Hated", "Hostile", "Unfriendly", "Neutral", "Friendly", "Honored", "Revered", "Exalted"], 'rep' => ["Hated", "Hostile", "Unfriendly", "Neutral", "Friendly", "Honored", "Revered", "Exalted"], // FACTION_STANDING_LABEL*
'st' => array( 'st' => array( // SpellShapeshiftForm.dbc // with minor deviations on 27, 28
"Default", "Cat Form", "Tree of Life", "Travel Form", "Aquatic Form", "Bear From", "Default", "Cat Form", "Tree of Life", "Travel Form", "Aquatic Form", "Bear From",
null, null, "Dire Bear Form", null, null, null, "Ambient", "Ghoul", "Dire Bear Form", "Steve's Ghoul", "Tharon'ja Skeleton", "Darkmoon - Test of Strength",
null, "Shadowdance", null, null, "Ghostwolf", "Battle Stance", "BLB Player", "Shadowdance", "Creature - Bear", "Creature - Cat", "Ghostwolf", "Battle Stance",
"Defensive Stance", "Berserker Stance", null, null, "Metamorphosis", null, "Defensive Stance", "Berserker Stance", "Test", "Zombie", "Metamorphosis", null,
null, null, null, "Swift Flight Form", "Shadow Form", "Flight Form", null, "Undead", "Frenzy", "Swift Flight Form", "Shadow Form", "Flight Form",
"Stealth", "Moonkin Form", "Spirit of Redemption" "Stealth", "Moonkin Form", "Spirit of Redemption"
), ),
'me' => array( 'me' => array( // SpellMechanic.dbc .. not quite
null, "Charmed", "Disoriented", "Disarmed", "Distracted", "Fleeing", null, "Charmed", "Disoriented", "Disarmed", "Distracted", "Fleeing",
"Gripped", "Rooted", "Pacified", "Silenced", "Asleep", "Ensnared", "Gripped", "Rooted", "Pacified", "Silenced", "Asleep", "Ensnared",
"Stunned", "Frozen", "Incapacitated", "Bleeding", "Healing", "Polymorphed", "Stunned", "Frozen", "Incapacitated", "Bleeding", "Healing", "Polymorphed",
@@ -269,12 +307,12 @@ $lang = array(
"Horrified", "Invulnerable", "Interrupted", "Dazed", "Discovery", "Invulnerable", "Horrified", "Invulnerable", "Interrupted", "Dazed", "Discovery", "Invulnerable",
"Sapped", "Enraged" "Sapped", "Enraged"
), ),
'ct' => array( 'ct' => array( // CreatureType.dbc
"Uncategorized", "Beast", "Dragonkin", "Demon", "Elemental", "Giant", "Uncategorized", "Beast", "Dragonkin", "Demon", "Elemental", "Giant",
"Undead", "Humanoid", "Critter", "Mechanical", "Not specified", "Totem", "Undead", "Humanoid", "Critter", "Mechanical", "Not specified", "Totem",
"Non-combat Pet", "Gas Cloud" "Non-combat Pet", "Gas Cloud"
), ),
'fa' => array( 'fa' => array( // CreatureFamily.dbc
1 => "Wolf", 2 => "Cat", 3 => "Spider", 4 => "Bear", 5 => "Boar", 6 => "Crocolisk", 1 => "Wolf", 2 => "Cat", 3 => "Spider", 4 => "Bear", 5 => "Boar", 6 => "Crocolisk",
7 => "Carrion Bird", 8 => "Crab", 9 => "Gorilla", 11 => "Raptor", 12 => "Tallstrider", 20 => "Scorpid", 7 => "Carrion Bird", 8 => "Crab", 9 => "Gorilla", 11 => "Raptor", 12 => "Tallstrider", 20 => "Scorpid",
21 => "Turtle", 24 => "Bat", 25 => "Hyena", 26 => "Bird of Prey", 27 => "Wind Serpent", 30 => "Dragonhawk", 21 => "Turtle", 24 => "Bat", 25 => "Hyena", 26 => "Bird of Prey", 27 => "Wind Serpent", 30 => "Dragonhawk",
@@ -282,7 +320,7 @@ $lang = array(
38 => "Chimaera", 39 => "Devilsaur", 41 => "Silithid", 42 => "Worm", 43 => "Rhino", 44 => "Wasp", 38 => "Chimaera", 39 => "Devilsaur", 41 => "Silithid", 42 => "Worm", 43 => "Rhino", 44 => "Wasp",
45 => "Core Hound", 46 => "Spirit Beast" 45 => "Core Hound", 46 => "Spirit Beast"
), ),
'pvpRank' => array( 'pvpRank' => array( // PVP_RANK_\d_\d(_FEMALE)?
null, "Private / Scout", "Corporal / Grunt", null, "Private / Scout", "Corporal / Grunt",
"Sergeant / Sergeant", "Master Sergeant / Senior Sergeant", "Sergeant Major / First Sergeant", "Sergeant / Sergeant", "Master Sergeant / Senior Sergeant", "Sergeant Major / First Sergeant",
"Knight / Stone Guard", "Knight-Lieutenant / Blood Guard", "Knight-Captain / Legionnare", "Knight / Stone Guard", "Knight-Lieutenant / Blood Guard", "Knight-Captain / Legionnare",
@@ -297,7 +335,7 @@ $lang = array(
'groups' => array( 'groups' => array(
-1 => "None", "Tester", "Administrator", "Editor", "Moderator", "Bureaucrat", -1 => "None", "Tester", "Administrator", "Editor", "Moderator", "Bureaucrat",
"Developer", "VIP", "Blogger", "Premium", "Localizer", "Sales agent", "Developer", "VIP", "Blogger", "Premium", "Localizer", "Sales agent",
"Screenshot manager", "Video manager" "Screenshot manager", "Video manager", "API partner", "Pending"
), ),
// signIn // signIn
'doSignIn' => "Log in to your AoWoW Account", 'doSignIn' => "Log in to your AoWoW Account",
@@ -343,7 +381,7 @@ $lang = array(
'accActivated' => 'Your account has been activated.<br>Proceed to <a href="?account=signin&token=%s">sign in</a>', 'accActivated' => 'Your account has been activated.<br>Proceed to <a href="?account=signin&token=%s">sign in</a>',
'userNotFound' => "The username you entered does not exists.", 'userNotFound' => "The username you entered does not exists.",
'wrongPass' => "That password is not vaild.", 'wrongPass' => "That password is not vaild.",
'accInactive' => "That account has not yet been confirmed active.", // 'accInactive' => "That account has not yet been confirmed active.",
'loginExceeded' => "The maximum number of logins from this IP has been exceeded. Please try again in %s.", 'loginExceeded' => "The maximum number of logins from this IP has been exceeded. Please try again in %s.",
'signupExceeded'=> "The maximum number of signups from this IP has been exceeded. Please try again in %s.", 'signupExceeded'=> "The maximum number of signups from this IP has been exceeded. Please try again in %s.",
'errNameLength' => "Your username must be at least 4 characters long.", // message_usernamemin 'errNameLength' => "Your username must be at least 4 characters long.", // message_usernamemin
@@ -378,11 +416,30 @@ $lang = array(
'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."], 'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."],
'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."] 'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."]
), ),
'emote' => array(
'notFound' => "This Emote doesn't exist.",
'self' => "To Yourself",
'target' => "To others with a target",
'noTarget' => "To others without a target",
'isAnimated' => "Uses an animation",
'aliases' => "Aliases",
'noText' => "This Emote has no text.",
),
'enchantment' => array(
'details' => "Details",
'activation' => "Activation",
'notFound' => "This enchantment doesn't exist.",
'types' => array(
1 => "Proc Spell", 3 => "Equip Spell", 7 => "Use Spell", 8 => "Prismatic Socket",
5 => "Statistics", 2 => "Weapon Damage", 6 => "DPS", 4 => "Defense"
)
),
'gameObject' => array( 'gameObject' => array(
'notFound' => "This object doesn't exist.", 'notFound' => "This object doesn't exist.",
'cat' => [0 => "Other", 9 => "Books", 3 => "Containers", -5 => "Chests", 25 => "Fishing Pools", -3 => "Herbs", -4 => "Mineral Veins", -2 => "Quest", -6 => "Tools"], 'cat' => [0 => "Other", 9 => "Books", 3 => "Containers", -5 => "Chests", 25 => "Fishing Pools", -3 => "Herbs", -4 => "Mineral Veins", -2 => "Quest", -6 => "Tools"],
'type' => [ 9 => "Book", 3 => "Container", -5 => "Chest", 25 => "", -3 => "Herb", -4 => "Mineral Vein", -2 => "Quest", -6 => ""], 'type' => [ 9 => "Book", 3 => "Container", -5 => "Chest", 25 => "", -3 => "Herb", -4 => "Mineral Vein", -2 => "Quest", -6 => ""],
'unkPosition' => "The location of this object is unknown.", 'unkPosition' => "The location of this object is unknown.",
'npcLootPH' => 'The <b>%s</b> contains the loot from the fight against <a href="?npc=%d">%s</a>. It spawns after his death.',
'key' => "Key", 'key' => "Key",
'focus' => "Spell Focus", 'focus' => "Spell Focus",
'focusDesc' => "Spells requiring this Focus can be cast near this Object", 'focusDesc' => "Spells requiring this Focus can be cast near this Object",
@@ -448,7 +505,7 @@ $lang = array(
'titleReward' => 'You shall be granted the title "<a href="?title=%d">%s</a>"', 'titleReward' => 'You shall be granted the title "<a href="?title=%d">%s</a>"',
'slain' => "slain", 'slain' => "slain",
'reqNumCrt' => "Requires", 'reqNumCrt' => "Requires",
'rfAvailable' => "Available on realm:", 'rfAvailable' => "Available on realm: ",
'_transfer' => 'This achievement will be converted to <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> if you transfer to <span class="icon-%s">%s</span>.', '_transfer' => 'This achievement will be converted to <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> if you transfer to <span class="icon-%s">%s</span>.',
), ),
'chrClass' => array( 'chrClass' => array(
@@ -476,6 +533,20 @@ $lang = array(
'Azeroth' => "Azeroth", 'Azeroth' => "Azeroth",
'CosmicMap' => "Cosmic Map", 'CosmicMap' => "Cosmic Map",
), ),
'privileges' => array(
'main' => "Here on our Site you can generate <a href=\"?reputation\">reputation</a>. The main way to generate it is to get your comments upvotes.<br><br>So, reputation is a rough measure of how much you contributed to the community.<br><br>As you amass reputation you earn the community's trust and you will be granted with additional privileges. You can find a full list below.",
'privilege' => "Privilege",
'privileges' => "Privileges",
'requiredRep' => "Reputation Required",
'reqPoints' => "This privilege requires <b>%s</b> reputation points.",
'_privileges' => array(
null, "Post comments", "Post external links", null,
"No CAPTCHAs", "Comment votes worth more", null, null,
null, "More votes per day", "Upvote comments", "Downvote comments",
"Post comment replies", "Border: Uncommon", "Border: Rare", "Border: Epic",
"Border: Legendary", "AoWoW Premium"
)
),
'zone' => array( 'zone' => array(
'notFound' => "This zone doesn't exist.", 'notFound' => "This zone doesn't exist.",
'attunement' => ["Attunement", "Heroic attunement"], 'attunement' => ["Attunement", "Heroic attunement"],
@@ -484,12 +555,12 @@ $lang = array(
'raidFaction' => "Raid faction", 'raidFaction' => "Raid faction",
'boss' => "Final boss", 'boss' => "Final boss",
'reqLevels' => "Required levels: [tooltip=instancereqlevel_tip]%d[/tooltip], [tooltip=lfgreqlevel_tip]%d[/tooltip]", 'reqLevels' => "Required levels: [tooltip=instancereqlevel_tip]%d[/tooltip], [tooltip=lfgreqlevel_tip]%d[/tooltip]",
'zonePartOf' => "This zone is part of [zone=%].", 'zonePartOf' => "This zone is part of [zone=%s].",
'autoRez' => "Automatic resurrection", 'autoRez' => "Automatic resurrection",
'city' => "City", 'city' => "City",
'territory' => "Territory", 'territory' => "Territory",
'instanceType' => "Instance type", 'instanceType' => "Instance type",
'hcAvailable' => "Heroic mode available &nbsp;(%d)", 'hcAvailable' => "Heroic mode available&nbsp;(%d)",
'numPlayers' => "Number of players", 'numPlayers' => "Number of players",
'noMap' => "There is no map available for this zone.", 'noMap' => "There is no map available for this zone.",
'instanceTypes' => ["Zone", "Transit", "Dungeon", "Raid", "Battleground", "Dungeon", "Arena", "Raid", "Raid"], 'instanceTypes' => ["Zone", "Transit", "Dungeon", "Raid", "Battleground", "Dungeon", "Arena", "Raid", "Raid"],
@@ -504,7 +575,7 @@ $lang = array(
'_transfer' => 'This quest will be converted to <a href="?quest=%d" class="q1">%s</a> if you transfer to <span class="icon-%s">%s</span>.', '_transfer' => 'This quest will be converted to <a href="?quest=%d" class="q1">%s</a> if you transfer to <span class="icon-%s">%s</span>.',
'questLevel' => "Level %s", 'questLevel' => "Level %s",
'requirements' => "Requirements", 'requirements' => "Requirements",
'reqMoney' => "Required money", 'reqMoney' => "Required money", // REQUIRED_MONEY
'money' => "Money", 'money' => "Money",
'additionalReq' => "Additional requirements to obtain this quest", 'additionalReq' => "Additional requirements to obtain this quest",
'reqRepWith' => 'Your reputation with <a href="?faction=%d">%s</a> must be %s %s', 'reqRepWith' => 'Your reputation with <a href="?faction=%d">%s</a> must be %s %s',
@@ -540,7 +611,7 @@ $lang = array(
'enabledByQ' => "Enabled by", 'enabledByQ' => "Enabled by",
'enabledByQDesc'=> "This quest is available only, when one of these quests are active", 'enabledByQDesc'=> "This quest is available only, when one of these quests are active",
'gainsDesc' => "Upon completion of this quest you will gain", 'gainsDesc' => "Upon completion of this quest you will gain",
'theTitle' => 'the title "%s"', 'theTitle' => 'the title "%s"', // partly REWARD_TITLE
'mailDelivery' => "You will receive this letter%s%s", 'mailDelivery' => "You will receive this letter%s%s",
'mailBy' => ' by <a href="?npc=%d">%s</a>', 'mailBy' => ' by <a href="?npc=%d">%s</a>',
'mailIn' => " after %s", 'mailIn' => " after %s",
@@ -548,13 +619,14 @@ $lang = array(
'experience' => "experience", 'experience' => "experience",
'expConvert' => "(or %s if completed at level %d)", 'expConvert' => "(or %s if completed at level %d)",
'expConvert2' => "%s if completed at level %d", 'expConvert2' => "%s if completed at level %d",
'chooseItems' => "You will be able to choose one of these rewards", 'chooseItems' => "You will be able to choose one of these rewards", // REWARD_CHOICES
'receiveItems' => "You will receive", 'receiveItems' => "You will receive", // REWARD_ITEMS_ONLY
'receiveAlso' => "You will also receive", 'receiveAlso' => "You will also receive", // REWARD_ITEMS
'spellCast' => "The following spell will be cast on you", 'spellCast' => "The following spell will be cast on you", // REWARD_AURA
'spellLearn' => "You will learn", 'spellLearn' => "You will learn", // REWARD_SPELL
'bonusTalents' => "talent points", 'bonusTalents' => "%d talent |4point:points;", // partly LEVEL_UP_CHAR_POINTS
'spellDisplayed'=> ' (<a href="?spell=%d">%s</a> is displayed)', 'spellDisplayed'=> ' (<a href="?spell=%d">%s</a> is displayed)',
'attachment' => "Attachment",
'questInfo' => array( 'questInfo' => array(
0 => "Normal", 1 => "Group", 21 => "Life", 41 => "PvP", 62 => "Raid", 81 => "Dungeon", 82 => "World Event", 0 => "Normal", 1 => "Group", 21 => "Life", 41 => "PvP", 62 => "Raid", 81 => "Dungeon", 82 => "World Event",
83 => "Legendary", 84 => "Escort", 85 => "Heroic", 88 => "Raid (10)", 89 => "Raid (25)" 83 => "Legendary", 84 => "Escort", 85 => "Heroic", 88 => "Raid (10)", 89 => "Raid (25)"
@@ -630,6 +702,9 @@ $lang = array(
-2 => "Uncategorized" -2 => "Uncategorized"
) )
), ),
'icon' => array(
'notFound' => "This icon doesn't exist."
),
'title' => array( 'title' => array(
'notFound' => "This title doesn't exist.", 'notFound' => "This title doesn't exist.",
'_transfer' => 'This title will be converted to <a href="?title=%d" class="q1">%s</a> if you transfer to <span class="icon-%s">%s</span>.', '_transfer' => 'This title will be converted to <a href="?title=%d" class="q1">%s</a> if you transfer to <span class="icon-%s">%s</span>.',
@@ -651,10 +726,25 @@ $lang = array(
1 => "Miscellaneous", 2 => "Player vs. Player", 4 => "Classic", 21 => "Wrath of the Lich King", 22 => "Dungeon and Raid", 23 => "Burning Crusade", 41 => "Test", 3 => "Unused" 1 => "Miscellaneous", 2 => "Player vs. Player", 4 => "Classic", 21 => "Wrath of the Lich King", 22 => "Dungeon and Raid", 23 => "Burning Crusade", 41 => "Test", 3 => "Unused"
) )
), ),
'sound' => array(
'notFound' => "This sound doesn't exist.",
'foundIn' => "This sound can be found in",
'goToPlaylist' => "Go to My Playlist",
'music' => "Music",
'intro' => "Intro Music",
'ambience' => "Ambience",
'cat' => array(
null, "Spells", "User Interface", "Footsteps", "Weapons Impacts", null, "Weapons Misses", null, null, "Pick Up/Put Down",
"NPC Combat", null, "Errors", "Nature", "Objects", null, "Death", "NPC Greetings", null, "Armor",
"Footstep Splash", "Water (Character)", "Water", "Tradeskills", "Misc Ambience", "Doodads", "Spell Fizzle", "NPC Loops", "Zone Music", "Emotes",
"Narration Music", "Narration", 50 => "Zone Ambience", 52 => "Emitters", 53 => "Vehicles", 1000 => "My Playlist"
)
),
'pet' => array( 'pet' => array(
'notFound' => "This pet family doesn't exist.", 'notFound' => "This pet family doesn't exist.",
'exotic' => "Exotic", 'exotic' => "Exotic",
'cat' => ["Ferocity", "Tenacity", "Cunning"] 'cat' => ["Ferocity", "Tenacity", "Cunning"],
'food' => ["Meat", "Fish", "Cheese", "Bread", "Fungus", "Fruit", "Raw Meat", "Raw Fish"] // ItemPetFood.dbc
), ),
'faction' => array( 'faction' => array(
'notFound' => "This faction doesn't exist.", 'notFound' => "This faction doesn't exist.",
@@ -723,32 +813,34 @@ $lang = array(
'procChance' => "Proc chance", 'procChance' => "Proc chance",
'starter' => "Starter spell", 'starter' => "Starter spell",
'trainingCost' => "Training cost", 'trainingCost' => "Training cost",
'remaining' => "%s remaining", 'remaining' => "%s remaining", // SPELL_TIME_REMAINING_*
'untilCanceled' => "until canceled", 'untilCanceled' => "until cancelled", // SPELL_DURATION_UNTIL_CANCELLED
'castIn' => "%s sec cast", 'castIn' => "%s sec cast", // SPELL_CAST_TIME_SEC
'instantPhys' => "Instant", 'instantPhys' => "Instant", // SPELL_CAST_TIME_INSTANT_NO_MANA
'instantMagic' => "Instant cast", 'instantMagic' => "Instant cast", // SPELL_CAST_TIME_INSTANT
'channeled' => "Channeled", 'channeled' => "Channeled", // SPELL_CAST_CHANNELED
'range' => "%s yd range", 'range' => "%s yd range", // SPELL_RANGE / SPELL_RANGE_DUAL
'meleeRange' => "Melee Range", 'meleeRange' => "Melee Range", // MELEE_RANGE
'unlimRange' => "Unlimited Range", 'unlimRange' => "Unlimited Range", // SPELL_RANGE_UNLIMITED
'reagents' => "Reagents", 'reagents' => "Reagents", // SPELL_REAGENTS
'tools' => "Tools", 'tools' => "Tools", // SPELL_TOTEMS
'home' => "&lt;Inn&gt;", 'home' => "&lt;Inn&gt;",
'pctCostOf' => "of base %s", 'pctCostOf' => "of base %s",
'costPerSec' => ", plus %s per sec", 'costPerSec' => ", plus %s per sec", // see 'powerTypes'
'costPerLevel' => ", plus %s per level", 'costPerLevel' => ", plus %s per level", // not used?
'stackGroup' => "Stack Group",
'linkedWith' => "Linked with",
'_scaling' => "Scaling", '_scaling' => "Scaling",
'scaling' => array( 'scaling' => array(
'directSP' => "+%.2f%% of spell power to direct component", 'directAP' => "+%.2f%% of attack power to direct component", 'directSP' => "+%.2f%% of spell power to direct component", 'directAP' => "+%.2f%% of attack power to direct component",
'dotSP' => "+%.2f%% of spell power per tick", 'dotAP' => "+%.2f%% of attack power per tick" 'dotSP' => "+%.2f%% of spell power per tick", 'dotAP' => "+%.2f%% of attack power per tick"
), ),
'powerRunes' => ["Frost", "Unholy", "Blood", "Death"], 'powerRunes' => ["Frost", "Unholy", "Blood", "Death"], // RUNE_COST_* / COMBAT_TEXT_RUNE_*
'powerTypes' => array( 'powerTypes' => array(
// conventional // conventional - HEALTH, MANA, RAGE, FOCUS, ENERGY, HAPPINESS, RUNES, RUNIC_POWER / *_COST / *COST_PER_TIME
-2 => "Health", 0 => "Mana", 1 => "Rage", 2 => "Focus", 3 => "Energy", 4 => "Happiness", -2 => "Health", 0 => "Mana", 1 => "Rage", 2 => "Focus", 3 => "Energy", 4 => "Happiness",
5 => "Rune", 6 => "Runic Power", 5 => "Runes", 6 => "Runic Power",
// powerDisplay // powerDisplay - PowerDisplay.dbc -> GlobalStrings.lua POWER_TYPE_*
-1 => "Ammo", -41 => "Pyrite", -61 => "Steam Pressure", -101 => "Heat", -121 => "Ooze", -141 => "Blood Power", -1 => "Ammo", -41 => "Pyrite", -61 => "Steam Pressure", -101 => "Heat", -121 => "Ooze", -141 => "Blood Power",
-142 => "Wrath" -142 => "Wrath"
), ),
@@ -758,12 +850,12 @@ $lang = array(
'recipes' => '<a href="?items=9.%s">recipe items</a>', 'recipes' => '<a href="?items=9.%s">recipe items</a>',
'crafted' => '<a href="?items&filter=cr=86;crs=%s;crv=0">crafted items</a>' 'crafted' => '<a href="?items&filter=cr=86;crs=%s;crv=0">crafted items</a>'
), ),
'cat' => array( 'cat' => array( // as per menu in locale_enus.js
7 => "Class Skills", // classList 7 => "Class Skills", // classList
-13 => "Glyphs", // classList -13 => "Glyphs", // classList
-11 => array("Proficiencies", 8 => "Armor", 6 => "Weapon", 10 => "Languages"), -11 => array("Proficiencies", 8 => "Armor", 6 => "Weapon", 10 => "Languages"),
-4 => "Racial Traits", -4 => "Racial Traits",
-2 => "Talents", // classList -2 => "Talents", // classList
-6 => "Companions", -6 => "Companions",
-5 => "Mounts", -5 => "Mounts",
-3 => array( -3 => array(
@@ -794,12 +886,12 @@ $lang = array(
-9 => "GM Abilities", -9 => "GM Abilities",
0 => "Uncategorized" 0 => "Uncategorized"
), ),
'armorSubClass' => array( 'armorSubClass' => array( // ItemSubClass.dbc/2
"Miscellaneous", "Cloth Armor", "Leather Armor", "Mail Armor", "Plate Armor", "Miscellaneous", "Cloth Armor", "Leather Armor", "Mail Armor", "Plate Armor",
null, "Shields", "Librams", "Idols", "Totems", null, "Shields", "Librams", "Idols", "Totems",
"Sigils" "Sigils"
), ),
'weaponSubClass' => array( // ordered by content firts, then alphabeticaly 'weaponSubClass' => array( // ItemSubClass.dbc/4; ordered by content firts, then alphabeticaly
15 => "Daggers", 13 => "Fist Weapons", 0 => "One-Handed Axes", 4 => "One-Handed Maces", 7 => "One-Handed Swords", 15 => "Daggers", 13 => "Fist Weapons", 0 => "One-Handed Axes", 4 => "One-Handed Maces", 7 => "One-Handed Swords",
6 => "Polearms", 10 => "Staves", 1 => "Two-Handed Axes", 5 => "Two-Handed Maces", 8 => "Two-Handed Swords", 6 => "Polearms", 10 => "Staves", 1 => "Two-Handed Axes", 5 => "Two-Handed Maces", 8 => "Two-Handed Swords",
2 => "Bows", 18 => "Crossbows", 3 => "Guns", 16 => "Thrown", 19 => "Wands", 2 => "Bows", 18 => "Crossbows", 3 => "Guns", 16 => "Thrown", 19 => "Wands",
@@ -810,7 +902,8 @@ $lang = array(
), ),
'traitShort' => array( 'traitShort' => array(
'atkpwr' => "AP", 'rgdatkpwr' => "RAP", 'splpwr' => "SP", 'arcsplpwr' => "ArcP", 'firsplpwr' => "FireP", 'atkpwr' => "AP", 'rgdatkpwr' => "RAP", 'splpwr' => "SP", 'arcsplpwr' => "ArcP", 'firsplpwr' => "FireP",
'frosplpwr' => "FroP", 'holsplpwr' => "HolP", 'natsplpwr' => "NatP", 'shasplpwr' => "ShaP", 'splheal' => "Heal" 'frosplpwr' => "FroP", 'holsplpwr' => "HolP", 'natsplpwr' => "NatP", 'shasplpwr' => "ShaP", 'splheal' => "Heal",
'str' => "Str", 'agi' => "Agi", 'sta' => "Sta", 'int' => "Int", 'spi' => "Spi"
), ),
'spellModOp' => array( 'spellModOp' => array(
"Damage", "Duration", "Thread", "Effect 1", "Charges", "Damage", "Duration", "Thread", "Effect 1", "Charges",
@@ -828,7 +921,7 @@ $lang = array(
"Taken Critical Ranged Hit Chance", "Taken Critical Spell Hit Chance", "Melee Haste", "Ranged Haste", "Spell Haste", "Taken Critical Ranged Hit Chance", "Taken Critical Spell Hit Chance", "Melee Haste", "Ranged Haste", "Spell Haste",
"Mainhand Weapon Skill", "Offhand Weapon Skill", "Ranged Weapon Skill", "Expertise", "Armor Penetration" "Mainhand Weapon Skill", "Offhand Weapon Skill", "Ranged Weapon Skill", "Expertise", "Armor Penetration"
), ),
'lockType' => array( 'lockType' => array( // lockType.dbc
null, "Lockpicking", "Herbalism", "Mining", "Disarm Trap", null, "Lockpicking", "Herbalism", "Mining", "Disarm Trap",
"Open", "Treasure (DND)", "Calcified Elven Gems (DND)", "Close", "Arm Trap", "Open", "Treasure (DND)", "Calcified Elven Gems (DND)", "Close", "Arm Trap",
"Quick Open", "Quick Close", "Open Tinkering", "Open Kneeling", "Open Attacking", "Quick Open", "Quick Close", "Open Tinkering", "Open Kneeling", "Open Attacking",
@@ -836,36 +929,127 @@ $lang = array(
"Inscription", "Open From Vehicle" "Inscription", "Open From Vehicle"
), ),
'stealthType' => ["General", "Trap"], 'stealthType' => ["General", "Trap"],
'invisibilityType' => ["General", 3 => "Trap", 6 => "Drunk"] 'invisibilityType' => ["General", 3 => "Trap", 6 => "Drunk"],
'unkEffect' => 'Unknown Effect',
'effects' => array(
/*0-5 */ 'None', 'Instakill', 'School Damage', 'Dummy', 'Portal Teleport', 'Teleport Units',
/*6+ */ 'Apply Aura', 'Environmental Damage', 'Power Drain', 'Health Leech', 'Heal', 'Bind',
/*12+ */ 'Portal', 'Ritual Base', 'Ritual Specialize', 'Ritual Activate Portal', 'Quest Complete', 'Weapon Damage NoSchool',
/*18+ */ 'Resurrect', 'Add Extra Attacks', 'Dodge', 'Evade', 'Parry', 'Block',
/*24+ */ 'Create Item', 'Can Use Weapon', 'Defense', 'Persistent Area Aura', 'Summon', 'Leap',
/*30+ */ 'Energize', 'Weapon Damage Percent', 'Trigger Missile', 'Open Lock', 'Summon Change Item', 'Apply Area Aura Party',
/*36+ */ 'Learn Spell', 'Spell Defense', 'Dispel', 'Language', 'Dual Wield', 'Jump',
/*42+ */ 'Jump Dest', 'Teleport Units Face Caster','Skill Step', 'Add Honor', 'Spawn', 'Trade Skill',
/*48+ */ 'Stealth', 'Detect', 'Trans Door', 'Force Critical Hit', 'Guarantee Hit', 'Enchant Item Permanent',
/*54+ */ 'Enchant Item Temporary', 'Tame Creature', 'Summon Pet', 'Learn Pet Spell', 'Weapon Damage Flat', 'Create Random Item',
/*60+ */ 'Proficiency', 'Send Event', 'Power Burn', 'Threat', 'Trigger Spell', 'Apply Area Aura Raid',
/*66+ */ 'Create Mana Gem', 'Heal Max Health', 'Interrupt Cast', 'Distract', 'Pull', 'Pickpocket',
/*72+ */ 'Add Farsight', 'Untrain Talents', 'Apply Glyph', 'Heal Mechanical', 'Summon Object Wild', 'Script Effect',
/*78+ */ 'Attack', 'Sanctuary', 'Add Combo Points', 'Create House', 'Bind Sight', 'Duel',
/*84+ */ 'Stuck', 'Summon Player', 'Activate Object', 'WMO Damage', 'WMO Repair', 'WMO Change',
/*90+ */ 'Kill Credit', 'Threat All', 'Enchant Held Item', 'Force Deselect', 'Self Resurrect', 'Skinning',
/*96+ */ 'Charge', 'Cast Button', 'Knock Back', 'Disenchant', 'Inebriate', 'Feed Pet',
/*102+ */ 'Dismiss Pet', 'Reputation', 'Summon Object Slot1', 'Summon Object Slot2', 'Summon Object Slot3', 'Summon Object Slot4',
/*108+ */ 'Dispel Mechanic', 'Summon Dead Pet', 'Destroy All Totems', 'Durability Damage', 'Summon Demon', 'Resurrect Flat',
/*114+ */ 'Attack Me', 'Durability Damage Percent','Skin Player Corpse', 'Spirit Heal', 'Skill', 'Apply Area Aura Pet',
/*120+ */ 'Teleport Graveyard', 'Weapon Damage Normalized', null, 'Send Taxi', 'Pull Towards', 'Modify Threat Percent',
/*126+ */ 'Steal Beneficial Buff', 'Prospecting', 'Apply Area Aura Friend', 'Apply Area Aura Enemy', 'Redirect Threat', 'Play Sound',
/*132+ */ 'Play Music', 'Unlearn Specialization', 'Kill Credit2', 'Call Pet', 'Heal Percent', 'Energize Percent',
/*138+ */ 'Leap Back', 'Clear Quest', 'Force Cast', 'Force Cast With Value', 'Trigger Spell With Value', 'Apply Area Aura Owner',
/*144+ */ 'Knock Back Dest', 'Pull Towards Dest', 'Activate Rune', 'Quest Fail', null, 'Charge Dest',
/*150+ */ 'Quest Start', 'Trigger Spell 2', null, 'Create Tamed Pet', 'Discover Taxi', 'Dual Wield 2H Weapons',
/*156+ */ 'Enchant Item Prismatic', 'Create Item 2', 'Milling', 'Allow Rename Pet', null, 'Talent Spec Count',
/*162-164*/ 'Talent Spec Select', null, 'Remove Aura'
),
'unkAura' => 'Unknown Aura',
'auras' => array(
/*0- */ 'None', 'Bind Sight', 'Mod Possess', 'Periodic Damage', 'Dummy',
/*5+ */ 'Mod Confuse', 'Mod Charm', 'Mod Fear', 'Periodic Heal', 'Mod Attack Speed',
'Mod Threat', 'Taunt', 'Stun', 'Mod Damage Done Flat', 'Mod Damage Taken Flat',
'Damage Shield', 'Mod Stealth', 'Mod Stealth Detection', 'Mod Invisibility', 'Mod Invisibility Detection',
'Mod Health Percent', 'Mod Power Percent', 'Mod Resistance Flat', 'Periodic Trigger Spell', 'Periodic Energize',
/*25+ */ 'Pacify', 'Root', 'Silence', 'Reflect Spells', 'Mod Stat Flat',
'Mod Skill', 'Mod Increase Speed', 'Mod Increase Mounted Speed', 'Mod Decrease Speed', 'Mod Increase Health',
'Mod Increase Power', 'Shapeshift', 'Spell Effect Immunity', 'Spell Aura Immunity', 'School Immunity',
'Damage Immunity', 'Dispel Immunity', 'Proc Trigger Spell', 'Proc Trigger Damage', 'Track Creatures',
'Track Resources', 'Mod Parry Skill', 'Mod Parry Percent', null, 'Mod Dodge Percent',
/*50+ */ 'Mod Critical Healing Amount', 'Mod Block Percent', 'Mod Physical Crit Percent', 'Periodic Health Leech', 'Mod Hit Chance',
'Mod Spell Hit Chance', 'Transform', 'Mod Spell Crit Chance', 'Mod Increase Swim Speed', 'Mod Damage Done Versus Creature',
'Pacify Silence', 'Mod Scale', 'Periodic Health Funnel', 'Periodic Mana Funnel', 'Periodic Mana Leech',
'Mod Casting Speed (not stacking)', 'Feign Death', 'Disarm', 'Stalked', 'School Absorb',
'Extra Attacks', 'Mod Spell Crit Chance School', 'Mod Power Cost School Percent', 'Mod Power Cost School Flat', 'Reflect Spells School',
/*75+ */ 'Language', 'Far Sight', 'Mechanic Immunity', 'Mounted', 'Mod Damage Done Percent',
'Mod Stat Percent', 'Split Damage Percent', 'Water Breathing', 'Mod Base Resistance Flat', 'Mod Health Regeneration',
'Mod Power Regeneration', 'Channel Death Item', 'Mod Damage Taken Percent', 'Mod Health Regeneration Percent', 'Periodic Damage Percent',
'Mod Resist Chance', 'Mod Detect Range', 'Prevent Fleeing', 'Unattackable', 'Interrupt Regeneration',
'Ghost', 'Spell Magnet', 'Mana Shield', 'Mod Skill Value', 'Mod Attack Power',
/*100+ */ 'Auras Visible', 'Mod Resistance Percent', 'Mod Melee Attack Power Versus', 'Mod Total Threat', 'Water Walk',
'Feather Fall', 'Hover', 'Add Flat Modifier', 'Add Percent Modifier', 'Add Target Trigger',
'Mod Power Regeneration Percent', 'Add Caster Hit Trigger', 'Override Class Scripts', 'Mod Ranged Damage Taken Flat', 'Mod Ranged Damage Taken Percent',
'Mod Healing', 'Mod Regeneration During Combat', 'Mod Mechanic Resistance', 'Mod Healing Taken Percent', 'Share Pet Tracking',
'Untrackable', 'Empathy', 'Mod Offhand Damage Percent', 'Mod Target Resistance', 'Mod Ranged Attack Power',
/*125+ */ 'Mod Melee Damage Taken Flat', 'Mod Melee Damage Taken Percent', 'Ranged Attack Power Attacker Bonus', 'Possess Pet', 'Mod Speed Always',
'Mod Mounted Speed Always', 'Mod Ranged Attack Power Versus', 'Mod Increase Energy Percent', 'Mod Increase Health Percent', 'Mod Mana Regeneration Interrupt',
'Mod Healing Done Flat', 'Mod Healing Done Percent', 'Mod Total Stat Percentage', 'Mod Melee Haste', 'Force Reaction',
'Mod Ranged Haste', 'Mod Ranged Ammo Haste', 'Mod Base Resistance Percent', 'Mod Resistance Exclusive', 'Safe Fall',
'Mod Pet Talent Points', 'Allow Tame Pet Type', 'Mechanic Immunity Mask', 'Retain Combo Points', 'Reduce Pushback',
/*150+ */ 'Mod Shield Blockvalue Percent', 'Track Stealthed', 'Mod Detected Range', 'Split Damage Flat', 'Mod Stealth Level',
'Mod Water Breathing', 'Mod Reputation Gain', 'Pet Damage Multi', 'Mod Shield Blockvalue', 'No PvP Credit',
'Mod AoE Avoidance', 'Mod Health Regeneration In Combat', 'Power Burn Mana', 'Mod Crit Damage Bonus', null,
'Melee Attack Power Attacker Bonus', 'Mod Attack Power Percent', 'Mod Ranged Attack Power Percent', 'Mod Damage Done Versus', 'Mod Crit Percent Versus',
'Change Model', 'Mod Speed (not stacking)', 'Mod Mounted Speed (not stacking)', null, 'Mod Spell Damage Of Stat Percent',
/*175+ */ 'Mod Spell Healing Of Stat Percent', 'Spirit Of Redemption', 'AoE Charm', 'Mod Debuff Resistance', 'Mod Attacker Spell Crit Chance',
'Mod Spell Damage Versus', null, 'Mod Resistance Of Stat Percent', 'Mod Critical Threat', 'Mod Attacker Melee Hit Chance',
'Mod Attacker Ranged Hit Chance', 'Mod Attacker Spell Hit Chance', 'Mod Attacker Melee Crit Chance', 'Mod Attacker Ranged Crit Chance', 'Mod Rating',
'Mod Faction Reputation Gain', 'Use Normal Movement Speed', 'Mod Melee Ranged Haste', 'Mod Haste', 'Mod Target Absorb School',
'Mod Target Ability Absorb School', 'Mod Cooldown', 'Mod Attacker Spell And Weapon Crit Chance', null, 'Mod Increases Spell Percent to Hit',
/*200+ */ 'Mod XP Percent', 'Fly', 'Ignore Combat Result', 'Mod Attacker Melee Crit Damage', 'Mod Attacker Ranged Crit Damage',
'Mod School Crit Damage Taken', 'Mod Increase Vehicle Flight Speed', 'Mod Increase Mounted Flight Speed', 'Mod Increase Flight Speed', 'Mod Mounted Flight Speed Always',
'Mod Vehicle Speed Always', 'Mod Flight Speed (not stacking)', 'Mod Ranged Attack Power Of Stat Percent', 'Mod Rage from Damage Dealt', 'Tamed Pet Passive',
'Arena Preparation', 'Haste Spells', 'Killing Spree', 'Haste Ranged', 'Mod Mana Regeneration from Stat',
'Mod Rating from Stat', 'Ignore Threat', null, 'Raid Proc from Charge', null,
/*225+ */ 'Raid Proc from Charge With Value', 'Periodic Dummy', 'Periodic Trigger Spell With Value', 'Detect Stealth', 'Mod AoE Damage Avoidance',
'Mod Increase Health', 'Proc Trigger Spell With Value', 'Mod Mechanic Duration', 'Mod Display Model', 'Mod Mechanic Duration (not stacking)',
'Mod Dispel Resist', 'Control Vehicle', 'Mod Spell Damage Of Attack Power', 'Mod Spell Healing Of Attack Power', 'Mod Scale 2',
'Mod Expertise', 'Force Move Forward', 'Mod Spell Damage from Healing', 'Mod Faction', 'Comprehend Language',
'Mod Aura Duration By Dispel', 'Mod Aura Duration By Dispel (not stacking)', 'Clone Caster', 'Mod Combat Result Chance', 'Convert Rune',
/*250+ */ 'Mod Increase Health 2', 'Mod Enemy Dodge', 'Mod Speed Slow All', 'Mod Block Crit Chance', 'Mod Disarm Offhand',
'Mod Mechanic Damage Taken Percent', 'No Reagent Use', 'Mod Target Resist By Spell Class', 'Mod Spell Visual', 'Mod HoT Percent',
'Screen Effect', 'Phase', 'Ability Ignore Aurastate', 'Allow Only Ability', null,
null, null, 'Mod Immune Aura Apply School', 'Mod Attack Power Of Stat Percent', 'Mod Ignore Target Resist',
'Mod Ability Ignore Target Resist', 'Mod Damage Taken Percent From Caster', 'Ignore Melee Reset', 'X Ray', 'Ability Consume No Ammo',
/*275+ */ 'Mod Ignore Shapeshift', 'Mod Mechanic Damage Done Percent', 'Mod Max Affected Targets', 'Mod Disarm Ranged', 'Initialize Images',
'Mod Armor Penetration Percent', 'Mod Honor Gain Percent', 'Mod Base Health Percent', 'Mod Healing Received', 'Linked',
'Mod Attack Power Of Armor', 'Ability Periodic Crit', 'Deflect Spells', 'Ignore Hit Direction', null,
'Mod Crit Percent', 'Mod XP Quest Percent', 'Open Stable', 'Override Spells', 'Prevent Power Regeneration',
null, 'Set Vehicle Id', 'Block Spell Family', 'Strangulate', null,
/*300+ */ 'Share Damage Percent', 'School Heal Absorb', null, 'Mod Damage Done Versus Aurastate', 'Mod Fake Inebriate',
'Mod Minimum Speed', null, 'Heal Absorb Test', 'Hunter Trap', null,
'Mod Creature AoE Damage Avoidance', null, null, null, 'Prevent Ressurection',
/* -316*/ 'Underwater Walking', 'Periodic Haste'
)
), ),
'item' => array( 'item' => array(
'notFound' => "This item doesn't exist.", 'notFound' => "This item doesn't exist.",
'armor' => "%s Armor", 'armor' => "%s Armor", // ARMOR_TEMPLATE
'block' => "%s Block", 'block' => "%s Block", // SHIELD_BLOCK_TEMPLATE
'charges' => "Charges", 'charges' => "%d |4Charge:Charges;", // ITEM_SPELL_CHARGES
'locked' => "Locked", 'locked' => "Locked", // LOCKED
'ratingString' => "%s&nbsp;@&nbsp;L%s", 'ratingString' => "%s&nbsp;@&nbsp;L%s",
'heroic' => "Heroic", 'heroic' => "Heroic", // ITEM_HEROIC
'unique' => "Unique", 'startQuest' => "This Item Begins a Quest", // ITEM_STARTS_QUEST
'uniqueEquipped'=> "Unique-Equipped", 'bagSlotString' => "%d Slot %s", // CONTAINER_SLOTS
'startQuest' => "This Item Begins a Quest",
'bagSlotString' => "%d Slot %s",
'dps' => "damage per second",
'dps2' => "damage per second",
'addsDps' => "Adds",
'fap' => "Feral Attack Power", 'fap' => "Feral Attack Power",
'durability' => "Durability", 'durability' => "Durability %d / %d", // DURABILITY_TEMPLATE
'realTime' => "real time", 'realTime' => "real time",
'conjured' => "Conjured Item", 'conjured' => "Conjured Item", // ITEM_CONJURED
'damagePhys' => "%s Damage", 'sellPrice' => "Sell Price", // SELL_PRICE
'damageMagic' => "%s %s Damage", 'itemLevel' => "Item Level %d", // ITEM_LEVEL
'speed' => "Speed", 'randEnchant' => "&lt;Random enchantment&gt", // ITEM_RANDOM_ENCHANT
'sellPrice' => "Sell Price", 'readClick' => "&lt;Right Click To Read&gt", // ITEM_READABLE
'itemLevel' => "Item Level", 'openClick' => "&lt;Right Click To Open&gt", // ITEM_OPENABLE
'randEnchant' => "&lt;Random enchantment&gt", 'setBonus' => "(%d) Set: %s", // ITEM_SET_BONUS_GRAY
'readClick' => "&lt;Right Click To Read&gt", 'setName' => "%s (%d/%d)", // ITEM_SET_NAME
'openClick' => "&lt;Right Click To Open&gt",
'set' => "Set",
'partyLoot' => "Party loot", 'partyLoot' => "Party loot",
'smartLoot' => "Smart loot", 'smartLoot' => "Smart loot",
'indestructible'=> "Cannot be destroyed", 'indestructible'=> "Cannot be destroyed",
@@ -879,61 +1063,73 @@ $lang = array(
'consumable' => "Consumable", 'consumable' => "Consumable",
'nonConsumable' => "Non-consumable", 'nonConsumable' => "Non-consumable",
'accountWide' => "Account-wide", 'accountWide' => "Account-wide",
'millable' => "Millable", 'millable' => "Millable", // ITEM_MILLABLE
'noEquipCD' => "No equip cooldown", 'noEquipCD' => "No equip cooldown",
'prospectable' => "Prospectable", 'prospectable' => "Prospectable", // ITEM_PROSPECTABLE
'disenchantable'=> "Disenchantable", 'disenchantable'=> "Disenchantable", // ITEM_DISENCHANT_ANY_SKILL
'cantDisenchant'=> "Cannot be disenchanted", 'cantDisenchant'=> "Cannot be disenchanted", // ITEM_DISENCHANT_NOT_DISENCHANTABLE
'repairCost' => "Repair cost", 'repairCost' => "Repair cost", // REPAIR_COST
'tool' => "Tool", 'tool' => "Tool",
'cost' => "Cost", 'cost' => "Cost", // COSTS_LABEL
'content' => "Content", 'content' => "Content",
'_transfer' => 'This item will be converted to <a href="?item=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> if you transfer to <span class="icon-%s">%s</span>.', '_transfer' => 'This item will be converted to <a href="?item=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> if you transfer to <span class="icon-%s">%s</span>.',
'_unavailable' => "This item is not available to players.", '_unavailable' => "This item is not available to players.",
'_rndEnchants' => "Random Enchantments", '_rndEnchants' => "Random Enchantments",
'_chance' => "(%s%% chance)", '_chance' => "(%s%% chance)",
'slot' => "Slot", 'slot' => "Slot",
'_quality' => "Quality", '_quality' => "Quality", // QUALITY
'usableBy' => "Usable by", 'usableBy' => "Usable by",
'buyout' => "Buyout price", 'buyout' => "Buyout price", // BUYOUT_PRICE
'each' => "each", 'each' => "each",
'tabOther' => "Other", 'tabOther' => "Other",
'reqMinLevel' => "Requires Level %d", // ITEM_MIN_LEVEL
'reqLevelRange' => "Requires level %d to %d (%s)", // ITEM_LEVEL_RANGE_CURRENT
'unique' => ["Unique", "Unique (%d)", "Unique: %s (%d)" ], // ITEM_UNIQUE, ITEM_UNIQUE_MULTIPLE, ITEM_LIMIT_CATEGORY
'uniqueEquipped'=> ["Unique-Equipped", null, "Unique-Equipped: %s (%d)"], // ITEM_UNIQUE_EQUIPPABLE, null, ITEM_LIMIT_CATEGORY_MULTIPLE
'speed' => "Speed", // SPEED
'dps' => "(%.1f damage per second)", // DPS_TEMPLATE
'damage' => array( // *DAMAGE_TEMPLATE*
// basic, basic /w school, add basic, add basic /w school
'single' => ["%d Damage", "%d %s Damage", "+ %d Damage", "+%d %s Damage" ],
'range' => ["%d - %d Damage", "%d - %d %s Damage", "+ %d - %d Damage", "+%d - %d %s Damage" ],
'ammo' => ["Adds %g damage per second", "Adds %g %s damage per second", "+ %g damage per second", "+ %g %s damage per second" ]
),
'gems' => "Gems", 'gems' => "Gems",
'socketBonus' => "Socket Bonus", 'socketBonus' => "Socket Bonus", // ITEM_SOCKET_BONUS
'socket' => array( 'socket' => array( // EMPTY_SOCKET_*
"Meta Socket", "Red Socket", "Yellow Socket", "Blue Socket", -1 => "Prismatic Socket" "Meta Socket", "Red Socket", "Yellow Socket", "Blue Socket", -1 => "Prismatic Socket"
), ),
'gemColors' => array( 'gemColors' => array( // *_GEM
"meta", "red", "yellow", "blue" "meta", "red", "yellow", "blue"
), ),
'gemConditions' => array( // ENCHANT_CONDITION_* in GlobalStrings.lua 'gemConditions' => array( // ENCHANT_CONDITION_* in GlobalStrings.lua
2 => ["less than %d %s gem", "less than %d %s gems"], 2 => "less than %d %s |4gem:gems;",
3 => "more %s gems than %s gems", 3 => "more %s gems than %s gems",
5 => ["at least %d %s gem", "at least %d %s gems"] 5 => "at least %d %s |4gem:gems;"
), ),
'reqRating' => array( // ITEM_REQ_ARENA_RATING* 'reqRating' => array( // ITEM_REQ_ARENA_RATING*
"Requires personal and team arena rating of %d", "Requires personal and team arena rating of %d",
"Requires personal and team arena rating of %d<br>in 3v3 or 5v5 brackets", "Requires personal and team arena rating of %d|nin 3v3 or 5v5 brackets",
"Requires personal and team arena rating of %d<br>in 5v5 brackets" "Requires personal and team arena rating of %d|nin 5v5 brackets"
), ),
'quality' => array( 'quality' => array( // ITEM_QUALITY?_DESC
"Poor", "Common", "Uncommon", "Rare", "Poor", "Common", "Uncommon", "Rare",
"Epic", "Legendary", "Artifact", "Heirloom" "Epic", "Legendary", "Artifact", "Heirloom"
), ),
'trigger' => array( 'trigger' => array( // ITEM_SPELL_TRIGGER_*
"Use: ", "Equip: ", "Chance on hit: ", null, null, "Use: ", "Equip: ", "Chance on hit: ", "", "",
null, null "", ""
), ),
'bonding' => array( 'bonding' => array( // ITEM_BIND_*
"Binds to account", "Binds when picked up", "Binds when equipped", "Binds to account", "Binds when picked up", "Binds when equipped",
"Binds when used", "Quest Item", "Quest Item" "Binds when used", "Quest Item", "Quest Item"
), ),
"bagFamily" => array( "bagFamily" => array( // ItemSubClass.dbc/1
"Bag", "Quiver", "Ammo Pouch", "Soul Bag", "Leatherworking Bag", "Bag", "Quiver", "Ammo Pouch", "Soul Bag", "Leatherworking Bag",
"Inscription Bag", "Herb Bag", "Enchanting Bag", "Engineering Bag", null, /*Key*/ "Inscription Bag", "Herb Bag", "Enchanting Bag", "Engineering Bag", null, /*Key*/
"Gem Bag", "Mining Bag" "Gem Bag", "Mining Bag"
), ),
'inventoryType' => array( 'inventoryType' => array( // INVTYPE_*
null, "Head", "Neck", "Shoulder", "Shirt", null, "Head", "Neck", "Shoulder", "Shirt",
"Chest", "Waist", "Legs", "Feet", "Wrist", "Chest", "Waist", "Legs", "Feet", "Wrist",
"Hands", "Finger", "Trinket", "One-Hand", "Off Hand", /*Shield*/ "Hands", "Finger", "Trinket", "One-Hand", "Off Hand", /*Shield*/
@@ -941,23 +1137,23 @@ $lang = array(
null, /*Robe*/ "Main Hand", "Off Hand", "Held In Off-Hand", "Projectile", null, /*Robe*/ "Main Hand", "Off Hand", "Held In Off-Hand", "Projectile",
"Thrown", null, /*Ranged2*/ "Quiver", "Relic" "Thrown", null, /*Ranged2*/ "Quiver", "Relic"
), ),
'armorSubClass' => array( 'armorSubClass' => array( // ItemSubClass.dbc/2
"Miscellaneous", "Cloth", "Leather", "Mail", "Plate", "Miscellaneous", "Cloth", "Leather", "Mail", "Plate",
null, "Shield", "Libram", "Idol", "Totem", null, "Shield", "Libram", "Idol", "Totem",
"Sigil" "Sigil"
), ),
'weaponSubClass'=> array( 'weaponSubClass'=> array( // ItemSubClass.dbc/4
"Axe", "Axe", "Bow", "Gun", "Mace", "Axe", "Axe", "Bow", "Gun", "Mace",
"Mace", "Polearm", "Sword", "Sword", null, "Mace", "Polearm", "Sword", "Sword", null,
"Staff", null, null, "Fist Weapon", "Miscellaneous", "Staff", null, null, "Fist Weapon", "Miscellaneous",
"Dagger", "Thrown", null, "Crossbow", "Wand", "Dagger", "Thrown", null, "Crossbow", "Wand",
"Fishing Pole" "Fishing Pole"
), ),
'projectileSubClass' => array( 'projectileSubClass' => array( // ItemSubClass.dbc/6
null, null, "Arrow", "Bullet", null null, null, "Arrow", "Bullet", null
), ),
'elixirType' => [null, "Battle", "Guardian"], 'elixirType' => [null, "Battle", "Guardian"],
'cat' => array( // ordered by content first, then alphabeticaly 'cat' => array( // ordered by content first, then alphabeticaly; item menu from locale_enus.js
2 => "Weapons", // self::$spell['weaponSubClass'] 2 => "Weapons", // self::$spell['weaponSubClass']
4 => array("Armor", array( 4 => array("Armor", array(
1 => "Cloth Armor", 2 => "Leather Armor", 3 => "Mail Armor", 4 => "Plate Armor", 6 => "Shields", 7 => "Librams", 1 => "Cloth Armor", 2 => "Leather Armor", 3 => "Mail Armor", 4 => "Plate Armor", 6 => "Shields", 7 => "Librams",
@@ -1001,9 +1197,9 @@ $lang = array(
12 => "Quest", 12 => "Quest",
13 => "Keys", 13 => "Keys",
), ),
'statType' => array( 'statType' => array( // ITEM_MOD_*
"Increases your Mana by %d.", "Mana",
"Increases your Health by %d.", "Health",
null, null,
"Agility", "Agility",
"Strength", "Strength",
@@ -1011,7 +1207,7 @@ $lang = array(
"Spirit", "Spirit",
"Stamina", "Stamina",
null, null, null, null, null, null, null, null,
"Improves defense rating by %d.", "Increases defense rating by %d.",
"Increases your dodge rating by %d.", "Increases your dodge rating by %d.",
"Increases your parry rating by %d.", "Increases your parry rating by %d.",
"Increases your shield block rating by %d.", "Increases your shield block rating by %d.",
@@ -1036,17 +1232,17 @@ $lang = array(
"Improves critical avoidance rating by %d.", "Improves critical avoidance rating by %d.",
"Increases your resilience rating by %d.", "Increases your resilience rating by %d.",
"Increases your haste rating by %d.", "Increases your haste rating by %d.",
"Improves expertise rating by %d.", "Increases expertise rating by %d.",
"Improves attack power by %d.", "Increases attack power by %d.",
"Improves ranged attack power by %d.", "Increases ranged attack power by %d.",
"Improves attack power by %d in Cat, Bear, Dire Bear, and Moonkin forms only.", "Increases attack power by %d in Cat, Bear, Dire Bear, and Moonkin forms only.",
"Improves damage done by magical spells and effects by up to %d.", "Increases damage done by magical spells and effects by up to %d.",
"Improves healing done by magical spells and effects by up to %d.", "Increases healing done by magical spells and effects by up to %d.",
"Restores %d mana per 5 sec.", "Restores %d mana per 5 sec.",
"Increases your armor penetration rating by %d.", "Increases your armor penetration rating by %d.",
"Improves spell power by %d.", "Increases spell power by %d.",
"Restores %d health per 5 sec.", "Restores %d health per 5 sec.",
"Improves spell penetration by %d.", "Increases spell penetration by %d.",
"Increases the block value of your shield by %d.", "Increases the block value of your shield by %d.",
"Unknown Bonus #%d (%d)", "Unknown Bonus #%d (%d)",
) )

View File

@@ -14,8 +14,7 @@ $lang = array(
'timeUnits' => array( 'timeUnits' => array(
'sg' => ["año", "mes", "semana", "día", "hora", "minuto", "segundo", "milisegundo"], 'sg' => ["año", "mes", "semana", "día", "hora", "minuto", "segundo", "milisegundo"],
'pl' => ["años", "meses", "semanas", "dias", "horas", "minutos", "segundos", "milisegundos"], 'pl' => ["años", "meses", "semanas", "dias", "horas", "minutos", "segundos", "milisegundos"],
'ab' => ["año", "mes", "sem", "", "h", "min", "seg", "ms"], 'ab' => ["año", "mes", "sem", "", "h", "min", "seg", "ms"]
'ago' => 'hace %s'
), ),
'main' => array( 'main' => array(
'name' => "nombre", 'name' => "nombre",
@@ -43,14 +42,18 @@ $lang = array(
'cancel' => "Cancelar", 'cancel' => "Cancelar",
'rewards' => "Recompensas", 'rewards' => "Recompensas",
'gains' => "Ganancias", 'gains' => "Ganancias",
'login' => "[Login]", 'login' => "Ingresar",
'forum' => "Foro", 'forum' => "Foro",
'n_a' => "n/d", 'n_a' => "n/d",
'siteRep' => "Reputación", 'siteRep' => "Reputación",
'yourRepHistory'=> "Tu Historial de Reputación",
'aboutUs' => "Sobre Aowow", 'aboutUs' => "Sobre Aowow",
'and' => " y ", 'and' => " y ",
'or' => " o ", 'or' => " o ",
'back' => "Arrière", 'back' => "Atrás",
'reputationTip' => "Puntos de reputación",
'byUserTimeAgo' => 'Por <a href="'.HOST_URL.'/?user=%s">%1$s</a> hace %s',
'help' => "Ayuda",
// filter // filter
'extSearch' => "Extender búsqueda", 'extSearch' => "Extender búsqueda",
@@ -67,10 +70,10 @@ $lang = array(
// infobox // infobox
'unavailable' => "No está disponible a los jugadores", 'unavailable' => "No está disponible a los jugadores",
'disabled' => "[Disabled]", 'disabled' => "Deshabilitado",
'disabledHint' => "[Cannot be attained or completed]", 'disabledHint' => "No puede ser conseguido o completado",
'serverside' => "[Serverside]", 'serverside' => "Parte del Servidor",
'serversideHint'=> "[These informations are not in the Client and have been provided by sniffing and/or guessing.]", 'serversideHint'=> "Esta información no se encuentra en el Cliente y ha sido previsto mediante un analizador de paquetes (Sniffing) y/o suponiendo.",
// red buttons // red buttons
'links' => "Enlaces", 'links' => "Enlaces",
@@ -107,22 +110,6 @@ $lang = array(
'chooseClass' => "Escoge una clase", 'chooseClass' => "Escoge una clase",
'chooseFamily' => "Escoge una familia de mascota", 'chooseFamily' => "Escoge una familia de mascota",
// profiler
'realm' => "Reino",
'region' => "Región",
'viewCharacter' => "View Character",
'_cpHead' => "Perfiles de Personaje",
'_cpHint' => "l <b>Gestor de perfiles</b> te permite editar tu personaje, encontrar mejoras de equipo, comprobar tu gearscore, ¡y más!",
'_cpHelp' => "Para comenzar, sigue los pasos abajo indicados. Si quieres más información, revisa nuestra amplia <a href=\"?help=profiler\">página de ayuda</a>.",
'_cpFooter' => "Si quieres una búsqueda más refinada, prueba con nuestras opciones de <a href=\"?profiles\">búsqueda avanzada</a>. También puedes crear un <a href=\"?profile&amp;new\">perfil nuevo personalizado</a>.",
// help
'help' => "Ayuda",
'helpTopics' => array(
"Los comentarios y tú", "Visualizador de modelos", "Capturas de pantalla: Sugerencias y trucos", "Medición de atributos",
"Calculadora de talentos", "Comparación de objetos", "Perfiles", "Markup Guide"
),
// search // search
'search' => "Búsqueda", 'search' => "Búsqueda",
'searchButton' => "búsqueda", 'searchButton' => "búsqueda",
@@ -137,35 +124,74 @@ $lang = array(
'dateFmtLong' => "d/m/Y \a \l\a\s H:i", 'dateFmtLong' => "d/m/Y \a \l\a\s H:i",
// error // error
'intError' => "[An internal error occured.]", 'intError' => "Un error interno ha ocurrido.",
'intError2' => "[An internal error occured. (%s)]", 'intError2' => "Un error interno ha ocurrido. (%s)",
'genericError' => "Ha ocurrido un error; refresca la página e inténtalo de nuevo. Si el error persiste manda un correo a <a href='#contact'>feedback</a>", # LANG.genericerror 'genericError' => "Ha ocurrido un error; refresca la página e inténtalo de nuevo. Si el error persiste manda un correo a <a href='#contact'>feedback</a>", # LANG.genericerror
'bannedRating' => "Has sido baneado y no podrás valorar comentarios.", # LANG.tooltip_banned_rating 'bannedRating' => "Has sido baneado y no podrás valorar comentarios.", # LANG.tooltip_banned_rating
'tooManyVotes' => "Has alcanzado el límite diario de votos. Vuelve mañana.", # LANG.tooltip_too_many_votes 'tooManyVotes' => "Has alcanzado el límite diario de votos. Vuelve mañana.", # LANG.tooltip_too_many_votes
// screenshots 'moreTitles' => array(
'prepError' => "[An error occured preparing your screenshot]", 'reputation' => "Reputación de la web",
'cropHint' => "[Crop the image by dragging the selection.<br>Please refer to <a href=\"?help=screenshots-tips-tricks\">Screenshots: Tips & Tricks</a> for an optimal layout.]", 'whats-new' => "What's New",
'caption' => "[Caption]", 'searchbox' => "Caja de búsqueda",
'originalSize' => "[Original size]", 'tooltips' => "Tooltips",
'targetSize' => "[Target size]", 'faq' => "Preguntas frecuentes",
'minSize' => "[Minimum size]", 'aboutus' => "[What is AoWoW?]",
'displayOn' => "[Displayed on: %s[br][%s=%d]]", 'searchplugins' => "Extensiones de búsqueda",
'ssEdit' => "[Edit uploaded screenshot]", 'privileges' => "Privilegios",
'ssUpload' => "[Screenshot Upload]", 'top-users' => "Usuarios más populares",
'ssSubmit' => "[Submit Screenshot]", 'help' => array(
'ssErrors' => array( 'commenting-and-you' => "Los comentarios y tú", 'modelviewer' => "Visualizador de modelos", 'screenshots-tips-tricks' => "Capturas de pantalla: Sugerencias y trucos",
'noUpload' => "[The file was not uploaded!]", 'stat-weighting' => "Medición de atributos", 'talent-calculator' => "Calculadora de talentos", 'item-comparison' => "Comparación de objetos",
'maxSize' => "[The file exceeds the maximum size of %s!]", 'profiler' => "Perfiles", 'markup-guide' => "Margen de Guia"
'interrupted' => "[The upload process was interrupted!]", )
'noFile' => "[The file was not received!]", )
'noDest' => "[The page this screenshot should be displayed on, does not exist!]", ),
'notAllowed' => "[You are not allowed to upload screenshots!]", 'profiler' => array(
'noImage' => "[The uploaded file is not an image file!]", 'realm' => "Reino",
'wrongFormat' => "[The image file must be a png or jpg!]", 'region' => "Región",
'load' => "[The image file could not be loaded!]", 'viewCharacter' => "View Character",
'tooSmall' => "[The image size is too small! (lower than %d x %d)]", '_cpHint' => "l <b>Gestor de perfiles</b> te permite editar tu personaje, encontrar mejoras de equipo, comprobar tu gearscore, ¡y más!",
'tooLarge' => "[The image size is too large! (greater than %d x %d)]" '_cpHelp' => "Para comenzar, sigue los pasos abajo indicados. Si quieres más información, revisa nuestra amplia <a href=\"?help=profiler\">página de ayuda</a>.",
'_cpFooter' => "Si quieres una búsqueda más refinada, prueba con nuestras opciones de <a href=\"?profiles\">búsqueda avanzada</a>. También puedes crear un <a href=\"?profile&amp;new\">perfil nuevo personalizado</a>.",
'firstUseTitle' => "%s de %s",
'complexFilter' => "[Complex filter selected! Search results are limited to cached Characters.]",
'resync' => "Resincronizar",
'guildRoster' => "Lista de miembros de hermandad para &lt;%s&gt",
'arenaRoster' => "Personajes del Equipo de Arena para &lt;%s&gt",
'atCaptain' => "Capitán de equipo de arena",
'profiler' => "Gestor de Perfiles", // Perfiles de Personaje? (character profiler)
'arenaTeams' => "Equipos de Arena",
'guilds' => "Hermandades",
'notFound' => array(
'guild' => "[This Guild doesn't exist or is not yet in the database.]",
'arenateam' => "[This Arena Team doesn't exist or is not yet in the database.]",
'profile' => "Este personaje no existe o no está aun en la base de datos.",
),
'dummyNPCs' => array(
100001 => "Batalla de naves de guerra", 200001 => "Bestias de Rasganorte", 200002 => "Campeones de facciones", 200003 => "Gemelas Val'kyr"
),
),
'screenshot' => array(
'submission' => "Enviar una captura de pantalla",
'selectAll' => "Seleccionar todos",
'cropHint' => "Puede reducir su imagen e introducir una etiqueta.",
'displayOn' => "Mostrado en:[br]%s - [%s=%d]",
'caption' => "Anotación",
'charLimit' => "Opcional, hasta 200 caracteres",
'thanks' => array(
'contrib' => "¡Muchísimas gracias por tu aportación!",
'goBack' => '<a href="?%s=%d">aquí vuelve</a> a la página de la que viniste.',
'note' => "Nota: Su captura de imagen tiene que ser aprobada antes de que pueda aparecer en el sitio. Esto puede tomar hasta 72 horas."
),
'error' => array(
'unkFormat' => "Formato de imagen desconocido.",
'tooSmall' => "Su captura de pantalla es muy pequeña. (&lt; ".CFG_SCREENSHOT_MIN_SIZE."x".CFG_SCREENSHOT_MIN_SIZE.").",
'selectSS' => "Por favor seleccione la captura de pantalla para subir.",
'notAllowed' => "¡No estás permitido para subir capturas de pantalla!",
) )
), ),
'game' => array( 'game' => array(
@@ -178,8 +204,12 @@ $lang = array(
'difficulty' => "Dificultad", 'difficulty' => "Dificultad",
'dispelType' => "Tipo de disipación", 'dispelType' => "Tipo de disipación",
'duration' => "Duración", 'duration' => "Duración",
'gameObject' => "entidad", 'emote' => "emoción",
'gameObjects' => "Entidades", 'emotes' => "Emociones",
'enchantment' => "encantamiento",
'enchantments' => "Encantamientos",
'object' => "entidad",
'objects' => "Entidades",
'glyphType' => "Tipo de glifo", 'glyphType' => "Tipo de glifo",
'race' => "raza", 'race' => "raza",
'races' => "Razas", 'races' => "Razas",
@@ -191,6 +221,8 @@ $lang = array(
'faction' => "facción", 'faction' => "facción",
'factions' => "Facciones", 'factions' => "Facciones",
'cooldown' => "%s de reutilización", 'cooldown' => "%s de reutilización",
'icon' => "icono",
'icons' => "Iconos",
'item' => "objeto", 'item' => "objeto",
'items' => "Objetos", 'items' => "Objetos",
'itemset' => "conjunto de objetos", 'itemset' => "conjunto de objetos",
@@ -209,12 +241,13 @@ $lang = array(
'requires' => "Requiere %s", 'requires' => "Requiere %s",
'requires2' => "Requiere", 'requires2' => "Requiere",
'reqLevel' => "Necesitas ser de nivel %s", 'reqLevel' => "Necesitas ser de nivel %s",
'reqLevelHlm' => "Necesitas ser de nivel %s",
'reqSkillLevel' => "Requiere nivel de habilidad", 'reqSkillLevel' => "Requiere nivel de habilidad",
'level' => "Nivel", 'level' => "Nivel",
'school' => "Escuela", 'school' => "Escuela",
'skill' => "habilidad", 'skill' => "habilidad",
'skills' => "Habilidades", 'skills' => "Habilidades",
'sound' => "sonido",
'sounds' => "Sonidos",
'spell' => "hechizo", 'spell' => "hechizo",
'spells' => "Hechizos", 'spells' => "Hechizos",
'type' => "Tipo", 'type' => "Tipo",
@@ -260,10 +293,10 @@ $lang = array(
'rep' => ["Odiado", "Hostil", "Adverso", "Neutral", "Amistoso", "Honorable", "Reverenciado", "Exaltado"], 'rep' => ["Odiado", "Hostil", "Adverso", "Neutral", "Amistoso", "Honorable", "Reverenciado", "Exaltado"],
'st' => array( 'st' => array(
"Defecto", "Forma felina", "Árbol de vida", "Forma de viaje", "Forma acuática", "Forma de oso", "Defecto", "Forma felina", "Árbol de vida", "Forma de viaje", "Forma acuática", "Forma de oso",
null, null, "Forma de oso temible", null, null, null, "Ambiente", "Necrófago", "Forma de oso temible", "Steve's Ghoul", "Esqueleto Tharon'ja", "Luna Negra - Prueba de fuerza",
null, "Danza de las Sombras", null, null, "Lobo fantasmal", "Actitud de batalla", "BLB Player", "Danza de las Sombras", "Criatura: oso", "Criatura: felino", "Lobo fantasmal", "Actitud de batalla",
"Actitud defensiva", "Actitud rabiosa", null, null, "Metamorfosis", null, "Actitud defensiva", "Actitud rabiosa", "Test", "Zombi", "Metamorfosis", null,
null, null, null, "Forma de vuelo presto", "Forma de las Sombras", "Forma de vuelo", null, "No-muerto", "Furia", "Forma de vuelo presto", "Forma de las Sombras", "Forma de vuelo",
"Sigilo", "Forma de lechúcico lunar", "Espíritu redentor" "Sigilo", "Forma de lechúcico lunar", "Espíritu redentor"
), ),
'me' => array( 'me' => array(
@@ -288,11 +321,11 @@ $lang = array(
45 => "Can del Núcleo", 46 => "Bestia espíritu" 45 => "Can del Núcleo", 46 => "Bestia espíritu"
), ),
'pvpRank' => array( 'pvpRank' => array(
null, "Private / Scout", "Corporal / Grunt", null, "Soldado / Explorador", "Cabo / Bruto",
"Sergeant / Sergeant", "Master Sergeant / Senior Sergeant", "Sergeant Major / First Sergeant", "Sargento / Sargento", "Sargento maestro / Sargento jefe", "Sargento mayor / Sargento primero",
"Knight / Stone Guard", "Knight-Lieutenant / Blood Guard", "Knight-Captain / Legionnare", "Caballero / Guardian de Piedra", "Teniente caballero / Guardia de sangre", "Capitán caballero / Legionario",
"Knight-Champion / Centurion", "Lieutenant Commander / Champion", "Commander / Lieutenant General", "Campeón caballero / Centurion", "Teniente coronel / Campn", "Comandante / Teniente general",
"Marshal / General", "Field Marshal / Warlord", "Grand Marshal / High Warlord" "Mariscal / General", "Mariscal de campo / Señor de la Guerra", "Gran mariscal / Gran Señor de la Guerra"
), ),
), ),
'account' => array( 'account' => array(
@@ -302,9 +335,8 @@ $lang = array(
'groups' => array( 'groups' => array(
-1 => "Ninguno", "Probador", "Administrador", "Editor", "Moderador", "Burócrata", -1 => "Ninguno", "Probador", "Administrador", "Editor", "Moderador", "Burócrata",
"Desarrollador", "VIP", "Bloggor", "Premium", "Traductor", "Agente de ventas", "Desarrollador", "VIP", "Bloggor", "Premium", "Traductor", "Agente de ventas",
"Gestor de Capturas de pantalla","Gestor de vídeos" "Gestor de Capturas de pantalla","Gestor de vídeos", "Partner de API", "Pendiente"
), ),
// signIn // signIn
'doSignIn' => "Iniciar sesión con tu cuenta de Aowow", 'doSignIn' => "Iniciar sesión con tu cuenta de Aowow",
'signIn' => "Iniciar sesión", 'signIn' => "Iniciar sesión",
@@ -319,48 +351,48 @@ $lang = array(
// recovery // recovery
'recoverUser' => "Pedir nombre de usuario", 'recoverUser' => "Pedir nombre de usuario",
'recoverPass' => "Reiniciar contraseña: Paso %s de 2", 'recoverPass' => "Reiniciar contraseña: Paso %s de 2",
'newPass' => "New Password", 'newPass' => "Nueva Contraseña",
// creation // creation
'register' => "Inscripción: Paso %s de 2", 'register' => "Inscripción: Paso %s de 2",
'passConfirm' => "Confirmar contraseña", 'passConfirm' => "Confirmar contraseña",
// dashboard // dashboard
'ipAddress' => "IP-Adress", 'ipAddress' => "Dirección IP",
'lastIP' => "last used IP", 'lastIP' => "Última IP usada",
'myAccount' => "My Account", 'myAccount' => "Mi cuenta",
'editAccount' => "Simply use the forms below to update your account information", 'editAccount' => "Use el formulario siguienta para actualizar la información de la cuenta.",
'viewPubDesc' => 'View your Public Description in your <a href="?user=%s">Profile Page</a>', 'viewPubDesc' => 'Mira tu descripción pública en tu <a href="?user=%s">Página de perfil</a>',
// bans // bans
'accBanned' => "This Account was closed", 'accBanned' => "Esta cuenta fue cerrada.",
'bannedBy' => "Banned by", 'bannedBy' => "Suspendida por",
'ends' => "Ends on", 'ends' => "Finaliza en",
'permanent' => "The ban is permanent", 'permanent' => "La restricción es permanente",
'reason' => "Reason", 'reason' => "Razón",
'noReason' => "No reason was given.", 'noReason' => "Ningúna razón fue escrita.",
// form-text // form-text
'emailInvalid' => "Esa dirección de correo electrónico no es válida.", // message_emailnotvalid 'emailInvalid' => "Esa dirección de correo electrónico no es válida.", // message_emailnotvalid
'emailNotFound' => "The email address you entered is not associated with any account.<br><br>If you forgot the email you registered your account with email ".CFG_CONTACT_EMAIL." for assistance.", 'emailNotFound' => "El correo electrónico que ingresaste no está asociado con ninguna cuenta.<br><br>Si olvistaste el correo electronico con el que registraste la cuenta, escribe a ".CFG_CONTACT_EMAIL." para asistencia.",
'createAccSent' => "An email was sent to <b>%s</b>. Simply follow the instructions to create your account.", 'createAccSent' => "Un correo fue enviado a <b>%s</b>. Siga las instrucciones para crear su cuenta.",
'recovUserSent' => "An email was sent to <b>%s</b>. Simply follow the instructions to recover your username.", 'recovUserSent' => "Un correo fue enviado a <b>%s</b>. Siga las instrucciones para recuperar su nombre de usuario.",
'recovPassSent' => "An email was sent to <b>%s</b>. Simply follow the instructions to reset your password.", 'recovPassSent' => "Un correo fue enviado a <b>%s</b>. Siga las instrucciones para reiniciar su contraseña.",
'accActivated' => 'Your account has been activated.<br>Proceed to <a href="?account=signin&token=%s">sign in</a>', 'accActivated' => 'Su cuenta ha sido activada.<br>Ingrese a <a href="?account=signin&token=%s">para ingresar</a>',
'userNotFound' => "The username you entered does not exists.", 'userNotFound' => "El usuario que ha ingresado no existe",
'wrongPass' => "That password is not vaild.", 'wrongPass' => "La contraseña no es valida.",
'accInactive' => "That account has not yet been confirmed active.", // 'accInactive' => "That account has not yet been confirmed active.",
'loginExceeded' => "The maximum number of logins from this IP has been exceeded. Please try again in %s.", 'loginExceeded' => "Ha excedido la cantidad de inicios de sesion con esta IP. Por favor intente en %s",
'signupExceeded'=> "The maximum number of signups from this IP has been exceeded. Please try again in %s.", 'signupExceeded'=> "Ha excedido la cantidad de creaciones de cuentas con esta IP. Por favor intente en %s.",
'errNameLength' => "Tu nombre de usuario tiene que tener por lo menos cuatro caracteres.", // message_usernamemin 'errNameLength' => "Tu nombre de usuario tiene que tener por lo menos cuatro caracteres.", // message_usernamemin
'errNameChars' => "Tu nombre de usuario solo puede contener números y letras.", // message_usernamenotvalid 'errNameChars' => "Tu nombre de usuario solo puede contener números y letras.", // message_usernamenotvalid
'errPassLength' => "Tu contraseña tiene que tener por lo menos seis caracteres.", // message_passwordmin 'errPassLength' => "Tu contraseña tiene que tener por lo menos seis caracteres.", // message_passwordmin
'passMismatch' => "The passwords you entered do not match.", 'passMismatch' => "La contraseña que ingresó no concuerdan.",
'nameInUse' => "That username is already taken.", 'nameInUse' => "El nombre de usuario ya se encuentra utilzado",
'mailInUse' => "That email is already registered to an account.", 'mailInUse' => "El correo electrónico ya se encuentra registrado a una cuenta",
'isRecovering' => "This account is already recovering. Follow the instructions in your email or wait %s for the token to expire.", 'isRecovering' => "Esta cuenta ya se encuentra en proceso de recuperación. Siga las intrucciones en su correo o espere %s para que el token expire ",
'passCheckFail' => "Las contraseñas no son iguales.", // message_passwordsdonotmatch 'passCheckFail' => "Las contraseñas no son iguales.", // message_passwordsdonotmatch
'newPassDiff' => "Su nueva contraseña tiene que ser diferente a Su contraseña anterior." // message_newpassdifferent 'newPassDiff' => "Su nueva contraseña tiene que ser diferente a su contraseña anterior." // message_newpassdifferent
), ),
'user' => array( 'user' => array(
'notFound' => "¡No se encontró el usuario \"%s\"!", 'notFound' => "¡No se encontró el usuario \"%s\"!",
@@ -379,23 +411,43 @@ $lang = array(
'posts' => "Mensajes en los foros" 'posts' => "Mensajes en los foros"
), ),
'mail' => array( 'mail' => array(
'tokenExpires' => "This token expires in %s.", 'tokenExpires' => "Este token expira en %s",
'accConfirm' => ["Account Confirmation", "Welcome to ".CFG_NAME_SHORT."!\r\n\r\nClick the Link below to activate your account.\r\n\r\n".HOST_URL."?account=signup&token=%s\r\n\r\nIf you did not request this mail simply ignore it."], 'accConfirm' => ["Confirmación de Cuenta", "Bienvenido a ".CFG_NAME_SHORT."!\r\n\r\nHaga click en el enlace siguiente para activar su cuenta.\r\n\r\n".HOST_URL."?account=signup&token=%s\r\n\r\nSi usted no solicitó este correo, por favor ignorelo."],
'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."], 'recoverUser' => ["Recuperacion de Usuario", "Siga a este enlace para ingresar.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nSi usted no solicitó este correo, por favor ignorelo."],
'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."] 'resetPass' => ["Reinicio de Contraseña", "Siga este enlace para reiniciar su contraseña.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nSi usted no solicitó este correo, por favor ignorelo."]
),
'emote' => array(
'notFound' => "Este emoticón no existe",
'self' => "Para Usted",
'target' => "Para otros con un objetivo",
'noTarget' => "Para otros sin un objetivo",
'isAnimated' => "Usa una animación",
'aliases' => "Aliases",
'noText' => "Este emoticón no tiene texto",
),
'enchantment' => array(
'details' => "Detalles",
'activation' => "Activación",
'notFound' => "Este encantamiento no existe.",
'types' => array(
1 => "Prob. Hechizo", 3 => "Equipar Hechizo", 7 => "Usar Hechizo", 8 => "Ranura prismática",
5 => "Atributos", 2 => "Daño de arma", 6 => "DPS", 4 => "Defensa"
)
), ),
'gameObject' => array( 'gameObject' => array(
'notFound' => "Este entidad no existe.", 'notFound' => "Este entidad no existe.",
'cat' => [0 => "Otros", 9 => "Libros", 3 => "Contenedores", -5 => "Cofres", 25 => "Bancos de peces", -3 => "Hierbas", -4 => "Venas de minerales", -2 => "Misiones", -6 => "Herramientas"], 'cat' => [0 => "Otros", 9 => "Libros", 3 => "Contenedores", -5 => "Cofres", 25 => "Bancos de peces", -3 => "Hierbas", -4 => "Venas de minerales", -2 => "Misiones", -6 => "Herramientas"],
'type' => [ 9 => "Libro", 3 => "Contenedore", -5 => "Cofre", 25 => "", -3 => "Hierba", -4 => "Filóne de mineral", -2 => "Misión", -6 => ""], 'type' => [ 9 => "Libro", 3 => "Contenedore", -5 => "Cofre", 25 => "", -3 => "Hierba", -4 => "Filóne de mineral", -2 => "Misión", -6 => ""],
'unkPosition' => "No se conoce la ubicación de esta entidad.", 'unkPosition' => "No se conoce la ubicación de esta entidad.",
'npcLootPH' => 'El <b>%s</b> contiene el botín de la pelea contra <a href="?npc=%d">%s</a>. Aparece al morir.',
'key' => "Llave", 'key' => "Llave",
'focus' => "[Spell Focus]", 'focus' => "Foco del hechizo",
'focusDesc' => "[Spells requiring this Focus can be cast near this Object]", 'focusDesc' => "El hechizo que requiere este foco no puede ser lanzado cerca del objeto",
'trap' => "Trampa", 'trap' => "Trampa",
'triggeredBy' => "Accionado por", 'triggeredBy' => "Accionado por",
'capturePoint' => "Punto de captura", 'capturePoint' => "Punto de captura",
'restock' => "[Restocks every %s.]" 'foundIn' => "Este entidad se puede encontrar en",
'restock' => "Se renueva cada %s."
), ),
'npc' => array( 'npc' => array(
'notFound' => "Este PNJ no existe.", 'notFound' => "Este PNJ no existe.",
@@ -404,10 +456,10 @@ $lang = array(
'react' => "Reacción", 'react' => "Reacción",
'worth' => "Valor", 'worth' => "Valor",
'unkPosition' => "No se conoce la ubicación de este PNJ.", 'unkPosition' => "No se conoce la ubicación de este PNJ.",
'difficultyPH' => "[Este PNJ es un marcador de posición para un modo diferente de]", 'difficultyPH' => "Este PNJ es un marcador de posición para un modo diferente de",
'seat' => "[Seat]", 'seat' => "Asiento",
'accessory' => "[Accessory]", 'accessory' => "Accesorio",
'accessoryFor' => "[This creature is an accessory for vehicle]", 'accessoryFor' => "Esta criatura es una accesorio para vehículo",
'quotes' => "Citas", 'quotes' => "Citas",
'gainsDesc' => "Tras acabar con este PNJ ganarás", 'gainsDesc' => "Tras acabar con este PNJ ganarás",
'repWith' => "reputación con", 'repWith' => "reputación con",
@@ -417,12 +469,13 @@ $lang = array(
'melee' => "Cuerpo a cuerpo", 'melee' => "Cuerpo a cuerpo",
'ranged' => "Ataque a distancia", 'ranged' => "Ataque a distancia",
'armor' => "Armadura", 'armor' => "Armadura",
'foundIn' => "Este PNJ se puede encontrar en",
'tameable' => "Domesticable (%s)", 'tameable' => "Domesticable (%s)",
'waypoint' => "punto de recorrido", 'waypoint' => "punto de recorrido",
'wait' => "Tiempo de espera", 'wait' => "Tiempo de espera",
'respawnIn' => "Reingreso en", 'respawnIn' => "Reingreso en",
'rank' => [0 => "Normal", 1 => "Élite", 4 => "Raro", 2 => "Élite raro", 3 => "Jefe"], 'rank' => [0 => "Normal", 1 => "Élite", 4 => "Raro", 2 => "Élite raro", 3 => "Jefe"],
'textRanges' => [null, "[sent to area]", "[sent to zone]", "[sent to map]", "[sent to world]"], 'textRanges' => [null, "Mandar al área", "Mandar a zona", "Mandar al mapa", "Mandar al mundo"],
'textTypes' => [null, "grita", "dice", "susurra"], 'textTypes' => [null, "grita", "dice", "susurra"],
'modes' => array( 'modes' => array(
1 => ["Normal", "Heroico"], 1 => ["Normal", "Heroico"],
@@ -452,7 +505,7 @@ $lang = array(
'titleReward' => 'Deberías obtener el título "<a href="?title=%d">%s</a>"', 'titleReward' => 'Deberías obtener el título "<a href="?title=%d">%s</a>"',
'slain' => "matado", 'slain' => "matado",
'reqNumCrt' => "Requiere", 'reqNumCrt' => "Requiere",
'rfAvailable' => "Disponible en reino:", 'rfAvailable' => "Disponible en reino: ",
'_transfer' => 'Este logro será convertido a <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> si lo transfieres a la <span class="icon-%s">%s</span>.', '_transfer' => 'Este logro será convertido a <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> si lo transfieres a la <span class="icon-%s">%s</span>.',
), ),
'chrClass' => array( 'chrClass' => array(
@@ -480,15 +533,29 @@ $lang = array(
'Azeroth' => "Azeroth", 'Azeroth' => "Azeroth",
'CosmicMap' => "Mapa cósmico", 'CosmicMap' => "Mapa cósmico",
), ),
'privileges' => array(
'main' => "Aquí, en AoWoW, puedes conseguir <a href=\"?reputation\">reputación</a>. La forma principal de conseguirla es conseguir que tus comentarios sean votados de forma positiva.<br /><br />Así pues, la reputación es algo que mide, más o menos, cúanto has contribuido a la comunidad.<br /><br />Conforme consigues reputación, te ganas la confianza de la comunidad y tendrás privilegios adicionales. Puedes encontrar una lista completa debajo.",
'privilege' => "Privilegio",
'privileges' => "Privilegios",
'requiredRep' => "Reputación requerida",
'reqPoints' => "Este privilegio necesita <b>%s</b> puntos de reputación.",
'_privileges' => array(
null, "Escribir comentarios", "Escribir enlaces externos", null,
"Sin CAPTCHAs", "Los votos de comentario valen más", null, null,
null, "Más votos por día", "Dar una valoración positiva a comentarios", "Dar una valoración negativa a comentarios",
"Escribir respuestas a comentarios", "Borde: Poco Común", "Borde: Raro", "Borde: Épica",
"Borde: Legendaria", "AoWoW Premium"
)
),
'zone' => array( 'zone' => array(
'notFound' => "Esta zona no existe.", 'notFound' => "Esta zona no existe.",
'attunement' => ["Requisito", "Requisito heroica"], 'attunement' => ["Requisito", "Requisito heroica"],
'key' => ["Llave", "Llave heroica"], 'key' => ["Llave", "Llave heroica"],
'location' => "Ubicación", 'location' => "Ubicación",
'raidFaction' => "[Raid faction]", 'raidFaction' => "Facción de la banda",
'boss' => "Jefe Final", 'boss' => "Jefe Final",
'reqLevels' => "Niveles requeridos: [tooltip=instancereqlevel_tip]%d[/tooltip], [tooltip=lfgreqlevel_tip]%d[/tooltip]", 'reqLevels' => "Niveles requeridos: [tooltip=instancereqlevel_tip]%d[/tooltip], [tooltip=lfgreqlevel_tip]%d[/tooltip]",
'zonePartOf' => "Cette zone fait partie de la zone [zone=%d].", 'zonePartOf' => "Este campo es parte de la zona [zone=%d].",
'autoRez' => "Resurrección automática", 'autoRez' => "Resurrección automática",
'city' => "Ciudad", 'city' => "Ciudad",
'territory' => "Territorio", 'territory' => "Territorio",
@@ -510,7 +577,7 @@ $lang = array(
'requirements' => 'Requisitos', 'requirements' => 'Requisitos',
'reqMoney' => 'Dinero necesario', 'reqMoney' => 'Dinero necesario',
'money' => 'Dinero', 'money' => 'Dinero',
'additionalReq' => "[Additional requirements to obtain this quest]", 'additionalReq' => "Requerimientos adicionales para obtener esta misión",
'reqRepWith' => 'Tu reputación con <a href="?faction=%d">%s</a> debe ser %s %s', 'reqRepWith' => 'Tu reputación con <a href="?faction=%d">%s</a> debe ser %s %s',
'reqRepMin' => "de al menos", 'reqRepMin' => "de al menos",
'reqRepMax' => "menor que", 'reqRepMax' => "menor que",
@@ -557,8 +624,9 @@ $lang = array(
'receiveAlso' => "También recibirás", 'receiveAlso' => "También recibirás",
'spellCast' => "Te van a lanzar el siguiente hechizo", 'spellCast' => "Te van a lanzar el siguiente hechizo",
'spellLearn' => "Aprenderás", 'spellLearn' => "Aprenderás",
'bonusTalents' => "puntos de talento", 'bonusTalents' => "%d |4punto:puntos; de talento",
'spellDisplayed'=> ' (mostrando <a href="?spell=%d">%s</a>)', 'spellDisplayed'=> ' (mostrando <a href="?spell=%d">%s</a>)',
'attachment' => "Adjunto",
'questInfo' => array( 'questInfo' => array(
0 => "Normal", 1 => "Élite", 21 => "Vida", 41 => "JcJ", 62 => "Banda", 81 => "Mazmorra", 82 => "Evento del mundo", 0 => "Normal", 1 => "Élite", 21 => "Vida", 41 => "JcJ", 62 => "Banda", 81 => "Mazmorra", 82 => "Evento del mundo",
83 => "Legendaria", 84 => "Escolta", 85 => "Heroica", 88 => "Banda (10)", 89 => "Banda (25)" 83 => "Legendaria", 84 => "Escolta", 85 => "Heroica", 88 => "Banda (10)", 89 => "Banda (25)"
@@ -634,6 +702,9 @@ $lang = array(
-2 => "Sin categoría" -2 => "Sin categoría"
) )
), ),
'icon' => array(
'notFound' => "Este icono no existe."
),
'title' => array( 'title' => array(
'notFound' => "Este título no existe.", 'notFound' => "Este título no existe.",
'_transfer' => 'Este título será convertido a <a href="?title=%d" class="q1">%s</a> si lo transfieres a la <span class="icon-%s">%s</span>.', '_transfer' => 'Este título será convertido a <a href="?title=%d" class="q1">%s</a> si lo transfieres a la <span class="icon-%s">%s</span>.',
@@ -655,19 +726,34 @@ $lang = array(
1 => "Miscelánea", 2 => "Jugador contra Jugador", 4 => "Clásico", 21 => "Wrath of the Lich King", 22 => "Mazmorra y banda", 23 => "Burning Crusade", 41 => "Prueba", 3 => "No las uso" 1 => "Miscelánea", 2 => "Jugador contra Jugador", 4 => "Clásico", 21 => "Wrath of the Lich King", 22 => "Mazmorra y banda", 23 => "Burning Crusade", 41 => "Prueba", 3 => "No las uso"
) )
), ),
'sound' => array(
'notFound' => "Este sonido no existe.",
'foundIn' => "Este sonido se puede encontrar en",
'goToPlaylist' => "Ir a mi lista de reproducción",
'music' => "Música",
'intro' => "Música de introducción",
'ambience' => "Ambiente",
'cat' => array(
null, "Spells", "User Interface", "Footsteps", "Weapons Impacts", null, "Weapons Misses", null, null, "Pick Up/Put Down",
"NPC Combat", null, "Errors", "Nature", "Objects", null, "Death", "NPC Greetings", null, "Armor",
"Footstep Splash", "Water (Character)", "Water", "Tradeskills", "Misc Ambience", "Doodads", "Spell Fizzle", "NPC Loops", "Zone Music", "Emotes",
"Narration Music", "Narration", 50 => "Zone Ambience", 52 => "Emitters", 53 => "Vehicles", 1000 => "Mi Lista de Reproducción"
)
),
'pet' => array( 'pet' => array(
'notFound' => "Esta familia de mascotas no existe.", 'notFound' => "Esta familia de mascotas no existe.",
'exotic' => "Exótica", 'exotic' => "Exótica",
'cat' => ["Ferocidad", "Tenacidad", "Astucia"] 'cat' => ["Ferocidad", "Tenacidad", "Astucia"],
'food' => ["Carne", "Pescado", "Queso", "Pan", "Hongo", "Fruta", "Carne cruda", "Pescado crudo"]
), ),
'faction' => array( 'faction' => array(
'notFound' => "Esta facción no existe.", 'notFound' => "Esta facción no existe.",
'spillover' => "[Reputation Spillover]", 'spillover' => "Excedente de reputación",
'spilloverDesc' => "[Gaining reputation with this faction also yields a proportional gain with the factions listed below.]", 'spilloverDesc' => "Ganar reputación con esta facción tambien una proporción ganada con las facciones listadas a continuación.",
'maxStanding' => "Posición máxima", 'maxStanding' => "Posición máxima",
'quartermaster' => "Intendente", 'quartermaster' => "Intendente",
'customRewRate' => "[Custom Reward Rate]", 'customRewRate' => "Radio de recompenza personalizado",
'_transfer' => '[The reputation with this faction will be converted to <a href="?faction=%d" class="q1">%s</a> if you transfer to <span class="icon-%s">%s</span>.]', '_transfer' => '[La reputación de esta facción sera convertida a <a href="?faction=%d" class="q1">%s</a> Si te transfieres a <span class="icon-%s">%s</span>.]',
'cat' => array( 'cat' => array(
1118 => ["Clásicas", 469 => "Alianza", 169 => "Cártel Bonvapor", 67 => "Horda", 891 => "Fuerzas de la Alianza", 892 => "Fuerzas de la Horda"], 1118 => ["Clásicas", 469 => "Alianza", 169 => "Cártel Bonvapor", 67 => "Horda", 891 => "Fuerzas de la Alianza", 892 => "Fuerzas de la Horda"],
980 => ["The Burning Crusade", 936 => "Ciudad de Shattrath"], 980 => ["The Burning Crusade", 936 => "Ciudad de Shattrath"],
@@ -742,10 +828,12 @@ $lang = array(
'pctCostOf' => "del %s base", 'pctCostOf' => "del %s base",
'costPerSec' => ", mas %s por segundo", 'costPerSec' => ", mas %s por segundo",
'costPerLevel' => ", mas %s por nivel", 'costPerLevel' => ", mas %s por nivel",
'_scaling' => "[Scaling]", 'stackGroup' => "Grupo de aplilamiento",
'linkedWith' => "Asociado con",
'_scaling' => "Escala",
'scaling' => array( 'scaling' => array(
'directSP' => "[+%.2f%% of spell power to direct component]", 'directAP' => "[+%.2f%% of attack power to direct component]", 'directSP' => "+%.2f%% del poder de hechizo al componente directo", 'directAP' => "+%.2f%% del poder de ataque al componente directo",
'dotSP' => "[+%.2f%% of spell power per tick]", 'dotAP' => "[+%.2f%% of attack power per tick]" 'dotSP' => "+%.2f%% del poder de hechizo por tick", 'dotAP' => "+%.2f%% del poder de ataque por tick"
), ),
'powerRunes' => ["Escarcha", "Profano", "Sangre", "Muerte"], 'powerRunes' => ["Escarcha", "Profano", "Sangre", "Muerte"],
'powerTypes' => array( 'powerTypes' => array(
@@ -814,7 +902,8 @@ $lang = array(
), ),
'traitShort' => array( 'traitShort' => array(
'atkpwr' => "PA", 'rgdatkpwr' => "PA", 'splpwr' => "PH", 'arcsplpwr' => "PArc", 'firsplpwr' => "PFue", 'atkpwr' => "PA", 'rgdatkpwr' => "PA", 'splpwr' => "PH", 'arcsplpwr' => "PArc", 'firsplpwr' => "PFue",
'frosplpwr' => "PEsc", 'holsplpwr' => "PSag", 'natsplpwr' => "PNat", 'shasplpwr' => "PSom", 'splheal' => "Sana" 'frosplpwr' => "PEsc", 'holsplpwr' => "PSag", 'natsplpwr' => "PNat", 'shasplpwr' => "PSom", 'splheal' => "Sana",
'str' => "Fue", 'agi' => "Agi", 'sta' => "Agu", 'int' => "Int", 'spi' => "Esp"
), ),
'spellModOp' => array( 'spellModOp' => array(
"DAMAGE", "DURATION", "THREAT", "EFFECT1", "CHARGES", "DAMAGE", "DURATION", "THREAT", "EFFECT1", "CHARGES",
@@ -840,36 +929,127 @@ $lang = array(
"Inscripción", "Abrir desde vehículo" "Inscripción", "Abrir desde vehículo"
), ),
'stealthType' => ["GENERAL", "TRAP"], 'stealthType' => ["GENERAL", "TRAP"],
'invisibilityType' => ["GENERAL", 3 => "TRAP", 6 => "DRUNK"] 'invisibilityType' => ["GENERAL", 3 => "TRAP", 6 => "DRUNK"],
'unkEffect' => 'Unknown Effect',
'effects' => array(
/*0-5 */ 'None', 'Instakill', 'School Damage', 'Dummy', 'Portal Teleport', 'Teleport Units',
/*6+ */ 'Apply Aura', 'Environmental Damage', 'Power Drain', 'Health Leech', 'Heal', 'Bind',
/*12+ */ 'Portal', 'Ritual Base', 'Ritual Specialize', 'Ritual Activate Portal', 'Quest Complete', 'Weapon Damage NoSchool',
/*18+ */ 'Resurrect', 'Add Extra Attacks', 'Dodge', 'Evade', 'Parry', 'Block',
/*24+ */ 'Create Item', 'Can Use Weapon', 'Defense', 'Persistent Area Aura', 'Summon', 'Leap',
/*30+ */ 'Energize', 'Weapon Damage Percent', 'Trigger Missile', 'Open Lock', 'Summon Change Item', 'Apply Area Aura Party',
/*36+ */ 'Learn Spell', 'Spell Defense', 'Dispel', 'Language', 'Dual Wield', 'Jump',
/*42+ */ 'Jump Dest', 'Teleport Units Face Caster','Skill Step', 'Add Honor', 'Spawn', 'Trade Skill',
/*48+ */ 'Stealth', 'Detect', 'Trans Door', 'Force Critical Hit', 'Guarantee Hit', 'Enchant Item Permanent',
/*54+ */ 'Enchant Item Temporary', 'Tame Creature', 'Summon Pet', 'Learn Pet Spell', 'Weapon Damage Flat', 'Create Random Item',
/*60+ */ 'Proficiency', 'Send Event', 'Power Burn', 'Threat', 'Trigger Spell', 'Apply Area Aura Raid',
/*66+ */ 'Create Mana Gem', 'Heal Max Health', 'Interrupt Cast', 'Distract', 'Pull', 'Pickpocket',
/*72+ */ 'Add Farsight', 'Untrain Talents', 'Apply Glyph', 'Heal Mechanical', 'Summon Object Wild', 'Script Effect',
/*78+ */ 'Attack', 'Sanctuary', 'Add Combo Points', 'Create House', 'Bind Sight', 'Duel',
/*84+ */ 'Stuck', 'Summon Player', 'Activate Object', 'WMO Damage', 'WMO Repair', 'WMO Change',
/*90+ */ 'Kill Credit', 'Threat All', 'Enchant Held Item', 'Force Deselect', 'Self Resurrect', 'Skinning',
/*96+ */ 'Charge', 'Cast Button', 'Knock Back', 'Disenchant', 'Inebriate', 'Feed Pet',
/*102+ */ 'Dismiss Pet', 'Reputation', 'Summon Object Slot1', 'Summon Object Slot2', 'Summon Object Slot3', 'Summon Object Slot4',
/*108+ */ 'Dispel Mechanic', 'Summon Dead Pet', 'Destroy All Totems', 'Durability Damage', 'Summon Demon', 'Resurrect Flat',
/*114+ */ 'Attack Me', 'Durability Damage Percent','Skin Player Corpse', 'Spirit Heal', 'Skill', 'Apply Area Aura Pet',
/*120+ */ 'Teleport Graveyard', 'Weapon Damage Normalized', null, 'Send Taxi', 'Pull Towards', 'Modify Threat Percent',
/*126+ */ 'Steal Beneficial Buff', 'Prospecting', 'Apply Area Aura Friend', 'Apply Area Aura Enemy', 'Redirect Threat', 'Play Sound',
/*132+ */ 'Play Music', 'Unlearn Specialization', 'Kill Credit2', 'Call Pet', 'Heal Percent', 'Energize Percent',
/*138+ */ 'Leap Back', 'Clear Quest', 'Force Cast', 'Force Cast With Value', 'Trigger Spell With Value', 'Apply Area Aura Owner',
/*144+ */ 'Knock Back Dest', 'Pull Towards Dest', 'Activate Rune', 'Quest Fail', null, 'Charge Dest',
/*150+ */ 'Quest Start', 'Trigger Spell 2', null, 'Create Tamed Pet', 'Discover Taxi', 'Dual Wield 2H Weapons',
/*156+ */ 'Enchant Item Prismatic', 'Create Item 2', 'Milling', 'Allow Rename Pet', null, 'Talent Spec Count',
/*162-164*/ 'Talent Spec Select', null, 'Remove Aura'
),
'unkAura' => 'Unknown Aura',
'auras' => array(
/*0- */ 'None', 'Bind Sight', 'Mod Possess', 'Periodic Damage', 'Dummy',
/*5+ */ 'Mod Confuse', 'Mod Charm', 'Mod Fear', 'Periodic Heal', 'Mod Attack Speed',
'Mod Threat', 'Taunt', 'Stun', 'Mod Damage Done Flat', 'Mod Damage Taken Flat',
'Damage Shield', 'Mod Stealth', 'Mod Stealth Detection', 'Mod Invisibility', 'Mod Invisibility Detection',
'Mod Health Percent', 'Mod Power Percent', 'Mod Resistance Flat', 'Periodic Trigger Spell', 'Periodic Energize',
/*25+ */ 'Pacify', 'Root', 'Silence', 'Reflect Spells', 'Mod Stat Flat',
'Mod Skill', 'Mod Increase Speed', 'Mod Increase Mounted Speed', 'Mod Decrease Speed', 'Mod Increase Health',
'Mod Increase Power', 'Shapeshift', 'Spell Effect Immunity', 'Spell Aura Immunity', 'School Immunity',
'Damage Immunity', 'Dispel Immunity', 'Proc Trigger Spell', 'Proc Trigger Damage', 'Track Creatures',
'Track Resources', 'Mod Parry Skill', 'Mod Parry Percent', null, 'Mod Dodge Percent',
/*50+ */ 'Mod Critical Healing Amount', 'Mod Block Percent', 'Mod Physical Crit Percent', 'Periodic Health Leech', 'Mod Hit Chance',
'Mod Spell Hit Chance', 'Transform', 'Mod Spell Crit Chance', 'Mod Increase Swim Speed', 'Mod Damage Done Versus Creature',
'Pacify Silence', 'Mod Scale', 'Periodic Health Funnel', 'Periodic Mana Funnel', 'Periodic Mana Leech',
'Mod Casting Speed (not stacking)', 'Feign Death', 'Disarm', 'Stalked', 'School Absorb',
'Extra Attacks', 'Mod Spell Crit Chance School', 'Mod Power Cost School Percent', 'Mod Power Cost School Flat', 'Reflect Spells School',
/*75+ */ 'Language', 'Far Sight', 'Mechanic Immunity', 'Mounted', 'Mod Damage Done Percent',
'Mod Stat Percent', 'Split Damage Percent', 'Water Breathing', 'Mod Base Resistance Flat', 'Mod Health Regeneration',
'Mod Power Regeneration', 'Channel Death Item', 'Mod Damage Taken Percent', 'Mod Health Regeneration Percent', 'Periodic Damage Percent',
'Mod Resist Chance', 'Mod Detect Range', 'Prevent Fleeing', 'Unattackable', 'Interrupt Regeneration',
'Ghost', 'Spell Magnet', 'Mana Shield', 'Mod Skill Value', 'Mod Attack Power',
/*100+ */ 'Auras Visible', 'Mod Resistance Percent', 'Mod Melee Attack Power Versus', 'Mod Total Threat', 'Water Walk',
'Feather Fall', 'Hover', 'Add Flat Modifier', 'Add Percent Modifier', 'Add Target Trigger',
'Mod Power Regeneration Percent', 'Add Caster Hit Trigger', 'Override Class Scripts', 'Mod Ranged Damage Taken Flat', 'Mod Ranged Damage Taken Percent',
'Mod Healing', 'Mod Regeneration During Combat', 'Mod Mechanic Resistance', 'Mod Healing Taken Percent', 'Share Pet Tracking',
'Untrackable', 'Empathy', 'Mod Offhand Damage Percent', 'Mod Target Resistance', 'Mod Ranged Attack Power',
/*125+ */ 'Mod Melee Damage Taken Flat', 'Mod Melee Damage Taken Percent', 'Ranged Attack Power Attacker Bonus', 'Possess Pet', 'Mod Speed Always',
'Mod Mounted Speed Always', 'Mod Ranged Attack Power Versus', 'Mod Increase Energy Percent', 'Mod Increase Health Percent', 'Mod Mana Regeneration Interrupt',
'Mod Healing Done Flat', 'Mod Healing Done Percent', 'Mod Total Stat Percentage', 'Mod Melee Haste', 'Force Reaction',
'Mod Ranged Haste', 'Mod Ranged Ammo Haste', 'Mod Base Resistance Percent', 'Mod Resistance Exclusive', 'Safe Fall',
'Mod Pet Talent Points', 'Allow Tame Pet Type', 'Mechanic Immunity Mask', 'Retain Combo Points', 'Reduce Pushback',
/*150+ */ 'Mod Shield Blockvalue Percent', 'Track Stealthed', 'Mod Detected Range', 'Split Damage Flat', 'Mod Stealth Level',
'Mod Water Breathing', 'Mod Reputation Gain', 'Pet Damage Multi', 'Mod Shield Blockvalue', 'No PvP Credit',
'Mod AoE Avoidance', 'Mod Health Regeneration In Combat', 'Power Burn Mana', 'Mod Crit Damage Bonus', null,
'Melee Attack Power Attacker Bonus', 'Mod Attack Power Percent', 'Mod Ranged Attack Power Percent', 'Mod Damage Done Versus', 'Mod Crit Percent Versus',
'Change Model', 'Mod Speed (not stacking)', 'Mod Mounted Speed (not stacking)', null, 'Mod Spell Damage Of Stat Percent',
/*175+ */ 'Mod Spell Healing Of Stat Percent', 'Spirit Of Redemption', 'AoE Charm', 'Mod Debuff Resistance', 'Mod Attacker Spell Crit Chance',
'Mod Spell Damage Versus', null, 'Mod Resistance Of Stat Percent', 'Mod Critical Threat', 'Mod Attacker Melee Hit Chance',
'Mod Attacker Ranged Hit Chance', 'Mod Attacker Spell Hit Chance', 'Mod Attacker Melee Crit Chance', 'Mod Attacker Ranged Crit Chance', 'Mod Rating',
'Mod Faction Reputation Gain', 'Use Normal Movement Speed', 'Mod Melee Ranged Haste', 'Mod Haste', 'Mod Target Absorb School',
'Mod Target Ability Absorb School', 'Mod Cooldown', 'Mod Attacker Spell And Weapon Crit Chance', null, 'Mod Increases Spell Percent to Hit',
/*200+ */ 'Mod XP Percent', 'Fly', 'Ignore Combat Result', 'Mod Attacker Melee Crit Damage', 'Mod Attacker Ranged Crit Damage',
'Mod School Crit Damage Taken', 'Mod Increase Vehicle Flight Speed', 'Mod Increase Mounted Flight Speed', 'Mod Increase Flight Speed', 'Mod Mounted Flight Speed Always',
'Mod Vehicle Speed Always', 'Mod Flight Speed (not stacking)', 'Mod Ranged Attack Power Of Stat Percent', 'Mod Rage from Damage Dealt', 'Tamed Pet Passive',
'Arena Preparation', 'Haste Spells', 'Killing Spree', 'Haste Ranged', 'Mod Mana Regeneration from Stat',
'Mod Rating from Stat', 'Ignore Threat', null, 'Raid Proc from Charge', null,
/*225+ */ 'Raid Proc from Charge With Value', 'Periodic Dummy', 'Periodic Trigger Spell With Value', 'Detect Stealth', 'Mod AoE Damage Avoidance',
'Mod Increase Health', 'Proc Trigger Spell With Value', 'Mod Mechanic Duration', 'Mod Display Model', 'Mod Mechanic Duration (not stacking)',
'Mod Dispel Resist', 'Control Vehicle', 'Mod Spell Damage Of Attack Power', 'Mod Spell Healing Of Attack Power', 'Mod Scale 2',
'Mod Expertise', 'Force Move Forward', 'Mod Spell Damage from Healing', 'Mod Faction', 'Comprehend Language',
'Mod Aura Duration By Dispel', 'Mod Aura Duration By Dispel (not stacking)', 'Clone Caster', 'Mod Combat Result Chance', 'Convert Rune',
/*250+ */ 'Mod Increase Health 2', 'Mod Enemy Dodge', 'Mod Speed Slow All', 'Mod Block Crit Chance', 'Mod Disarm Offhand',
'Mod Mechanic Damage Taken Percent', 'No Reagent Use', 'Mod Target Resist By Spell Class', 'Mod Spell Visual', 'Mod HoT Percent',
'Screen Effect', 'Phase', 'Ability Ignore Aurastate', 'Allow Only Ability', null,
null, null, 'Mod Immune Aura Apply School', 'Mod Attack Power Of Stat Percent', 'Mod Ignore Target Resist',
'Mod Ability Ignore Target Resist', 'Mod Damage Taken Percent From Caster', 'Ignore Melee Reset', 'X Ray', 'Ability Consume No Ammo',
/*275+ */ 'Mod Ignore Shapeshift', 'Mod Mechanic Damage Done Percent', 'Mod Max Affected Targets', 'Mod Disarm Ranged', 'Initialize Images',
'Mod Armor Penetration Percent', 'Mod Honor Gain Percent', 'Mod Base Health Percent', 'Mod Healing Received', 'Linked',
'Mod Attack Power Of Armor', 'Ability Periodic Crit', 'Deflect Spells', 'Ignore Hit Direction', null,
'Mod Crit Percent', 'Mod XP Quest Percent', 'Open Stable', 'Override Spells', 'Prevent Power Regeneration',
null, 'Set Vehicle Id', 'Block Spell Family', 'Strangulate', null,
/*300+ */ 'Share Damage Percent', 'School Heal Absorb', null, 'Mod Damage Done Versus Aurastate', 'Mod Fake Inebriate',
'Mod Minimum Speed', null, 'Heal Absorb Test', 'Hunter Trap', null,
'Mod Creature AoE Damage Avoidance', null, null, null, 'Prevent Ressurection',
/* -316*/ 'Underwater Walking', 'Periodic Haste'
)
), ),
'item' => array( 'item' => array(
'notFound' => "Este objeto no existe.", 'notFound' => "Este objeto no existe.",
'armor' => "%s armadura", 'armor' => "%s armadura",
'block' => "%s bloqueo", 'block' => "%s bloqueo",
'charges' => "cargas", 'charges' => "%d |4carga:cargas;",
'locked' => "Cerrado", 'locked' => "Cerrado",
'ratingString' => "%s&nbsp;@&nbsp;L%s", 'ratingString' => "%s&nbsp;@&nbsp;L%s",
'heroic' => "Heroico", 'heroic' => "Heroico",
'unique' => "Único",
'uniqueEquipped'=> "Único-Equipado",
'startQuest' => "Este objeto inicia una misión", 'startQuest' => "Este objeto inicia una misión",
'bagSlotString' => "%s de %d casillas", 'bagSlotString' => '%2$s de %1$d casillas',
'dps' => "daño por segundo",
'dps2' => "daño por segundo",
'addsDps' => "Añade",
'fap' => "poder de ataque feral", 'fap' => "poder de ataque feral",
'durability' => "Durabilidad", 'durability' => "Durabilidad %d / %d",
'realTime' => "tiempo real", 'realTime' => "tiempo real",
'conjured' => "Objeto mágico", 'conjured' => "Objeto mágico",
'damagePhys' => "%s Daño",
'damageMagic' => "%s %s Daño",
'speed' => "Velocidad",
'sellPrice' => "Precio de venta", 'sellPrice' => "Precio de venta",
'itemLevel' => "Nivel de objeto", 'itemLevel' => "Nivel de objeto %d",
'randEnchant' => "&lt;Encantamiento aleatorio&gt", 'randEnchant' => "&lt;Encantamiento aleatorio&gt",
'readClick' => "&lt;Click derecho para leer&gt", 'readClick' => "&lt;Click derecho para leer&gt",
'openClick' => "&lt;Click derecho para abrir&gt", 'openClick' => "&lt;Click derecho para abrir&gt",
'set' => "Conjunto", 'setBonus' => "(%d) Bonif.: %s",
'setName' => "%s (%d/%d)",
'partyLoot' => "Despojo de grupo", 'partyLoot' => "Despojo de grupo",
'smartLoot' => "Botín inteligente", 'smartLoot' => "Botín inteligente",
'indestructible'=> "No puede ser destruido", 'indestructible'=> "No puede ser destruido",
@@ -902,6 +1082,18 @@ $lang = array(
'buyout' => "Precio de venta en subasta", 'buyout' => "Precio de venta en subasta",
'each' => "cada uno", 'each' => "cada uno",
'tabOther' => "Otros", 'tabOther' => "Otros",
'reqMinLevel' => "Necesitas ser de nivel %d",
'reqLevelRange' => "Requiere un nivel entre %d y %d (%s)",
'unique' => ["Único", "Único (%d)", "Único: %s (%d)" ],
'uniqueEquipped'=> ["Único-Equipado", null, "Único-Equipado: %s (%d)"],
'speed' => "Veloc.",
'dps' => "(%.1f daño por segundo)",
'damage' => array( // *DAMAGE_TEMPLATE*
// basic, basic /w school, add basic, add basic /w school
"single" => ["%d Daño", "%d %s Daño", "+ %d daño", "+%d %s daños" ],
"range" => ["%d - %d Daño", "%d - %d daño de %s", "+ %d: %d daño", "+%d - %d daño de %s" ],
'ammo' => ["Añade %g daño por segundo", "Añade %g %s daño por segundo", "+ %g daño por segundo", "+ %g %s daño por segundo"]
),
'gems' => "Gemas", 'gems' => "Gemas",
'socketBonus' => "Bono de ranura", 'socketBonus' => "Bono de ranura",
'socket' => array( 'socket' => array(
@@ -911,22 +1103,22 @@ $lang = array(
"meta", "roja(s)", "amarilla(s)", "azul(es)" "meta", "roja(s)", "amarilla(s)", "azul(es)"
), ),
'gemConditions' => array( // ENCHANT_CONDITION_* 'gemConditions' => array( // ENCHANT_CONDITION_*
2 => ["menos de %d %s gema", "menos de %d %s gemas"], 2 => "menos de %d |4gema:gemas; %s",
3 => "más gemas %s que gemas %s", 3 => "más gemas %s que gemas %s",
5 => ["al menos %d %s gema", "al menos %d %s gemas"] 5 => "al menos %d |4gema:gemas; %s"
), ),
'reqRating' => array( // ITEM_REQ_ARENA_RATING* 'reqRating' => array( // ITEM_REQ_ARENA_RATING*
"Requiere un índice de arena personal y de equipo de %d", "Requiere un índice de arena personal y de equipo de %d",
"Requiere un índice de arena personal y de equipo de %d<br>en la rama de 3c3 o de 5c5", "Requiere un índice de arena personal y de equipo de %d|nen la rama de 3c3 o de 5c5",
"Requiere un índice de arena personal y de equipo de %d<br>en la rama de 5c5" "Requiere un índice de arena personal y de equipo de %d|nen la rama de 5c5"
), ),
'quality' => array( 'quality' => array(
"Pobre", "Común", "Poco Común", "Raro", "Pobre", "Común", "Poco Común", "Raro",
"Épica", "Legendaria", "Artefacto", "Reliquia" "Épica", "Legendaria", "Artefacto", "Reliquia"
), ),
'trigger' => array( 'trigger' => array(
"Uso: ", "Equipar: ", "Probabilidad al acertar: ", null, null, "Uso: ", "Equipar: ", "Probabilidad al acertar: ", "", "",
null, null "", ""
), ),
'bonding' => array( 'bonding' => array(
"Se liga a la cuenta", "Se liga al recogerlo", "Se liga al equiparlo", "Se liga a la cuenta", "Se liga al recogerlo", "Se liga al equiparlo",
@@ -1006,8 +1198,8 @@ $lang = array(
13 => "Llaves", 13 => "Llaves",
), ),
'statType' => array( 'statType' => array(
"Aumenta tu maná %d p.", "Maná",
"Aumenta tu salud %d p.", "Salud",
null, null,
"agilidad", "agilidad",
"fuerza", "fuerza",
@@ -1034,16 +1226,16 @@ $lang = array(
"Mejora tu índice de celeridad cuerpo a cuerpo %d p.", "Mejora tu índice de celeridad cuerpo a cuerpo %d p.",
"Mejora tu índice de celeridad a distancia %d p.", "Mejora tu índice de celeridad a distancia %d p.",
"Mejora tu índice de celeridad con hechizos %d p.", "Mejora tu índice de celeridad con hechizos %d p.",
"Aumenta tu índice de golpe %d p.", "Mejora tu índice de golpe %d p.",
"Aumenta tu índice de golpe crítico %d p.", "Mejora tu índice de golpe crítico %d p.",
"Mejora tu índice de evasión %d p.", "Mejora tu índice de evasión %d p.",
"Mejora tu índice de evasión de golpes críticos %d p.", "Mejora tu índice de evasión de golpes críticos %d p.",
"Aumenta tu índice de temple %d p.", "Mejora tu índice de temple %d p.",
"Aumenta tu índice de celeridad %d p.", "Mejora tu índice de celeridad %d p.",
"Aumenta tu índice de pericia %d p.", "Aumenta tu índice de pericia %d p.",
"Aumenta el poder de ataque %d p.", "Aumenta el poder de ataque %d p.",
"Aumenta el poder de ataque a distancia %d p.", "Aumenta el poder de ataque a distancia %d p.",
"Aumenta en %d p. el poder de ataque bajo formas felinas, de oso, de oso temible y de lechúcico lunar.", "Aumenta el poder de ataque %d p. solo con las formas de gato, oso, oso temible y lechúcico lunar.",
"Aumenta el daño infligido con hechizos y efectos mágicos hasta %d p.", "Aumenta el daño infligido con hechizos y efectos mágicos hasta %d p.",
"Aumenta la sanación hecha con hechizos y efectos mágicos hasta %d p.", "Aumenta la sanación hecha con hechizos y efectos mágicos hasta %d p.",
"Restaura %d p. de maná cada 5 s.", "Restaura %d p. de maná cada 5 s.",

View File

@@ -14,8 +14,7 @@ $lang = array(
'timeUnits' => array( 'timeUnits' => array(
'sg' => ["année", "mois", "semaine", "jour", "heure", "minute", "seconde", "milliseconde"], 'sg' => ["année", "mois", "semaine", "jour", "heure", "minute", "seconde", "milliseconde"],
'pl' => ["années", "mois", "semaines", "jours", "heures", "minutes", "secondes", "millisecondes"], 'pl' => ["années", "mois", "semaines", "jours", "heures", "minutes", "secondes", "millisecondes"],
'ab' => ["an", "mo", "sem", "jour", "h", "min", "s", "ms"], 'ab' => ["an", "mo", "sem", "jour", "h", "min", "s", "ms"]
'ago' => 'il y a %s'
), ),
'main' => array( 'main' => array(
'name' => "nom", 'name' => "nom",
@@ -47,10 +46,14 @@ $lang = array(
'forum' => "Forum", 'forum' => "Forum",
'n_a' => "n/d", 'n_a' => "n/d",
'siteRep' => "Réputation", 'siteRep' => "Réputation",
'yourRepHistory'=> "Votre historique de réputation",
'aboutUs' => "À propos de Aowow", 'aboutUs' => "À propos de Aowow",
'and' => " et ", 'and' => " et ",
'or' => " ou ", 'or' => " ou ",
'back' => "Redro", 'back' => "Redro",
'reputationTip' => "Points de réputation",
'byUserTimeAgo' => 'Par <a href="'.HOST_URL.'/?user=%s">%1$s</a> il y a %s',
'help' => "Aide",
// filter // filter
'extSearch' => "Recherche avancée", 'extSearch' => "Recherche avancée",
@@ -67,10 +70,10 @@ $lang = array(
// infobox // infobox
'unavailable' => "Non disponible aux joueurs", 'unavailable' => "Non disponible aux joueurs",
'disabled' => "[Disabled]", 'disabled' => "Désactivé",
'disabledHint' => "[Cannot be attained or completed]", 'disabledHint' => "Ne peux pas être atteint ou complété",
'serverside' => "[Serverside]", 'serverside' => "Côté serveur",
'serversideHint'=> "[These informations are not in the Client and have been provided by sniffing and/or guessing.]", 'serversideHint'=> "Ces informations ne sont pas contenues dans le client et ont été obtenues via sniff ou ont été devinées.",
// red buttons // red buttons
'links' => "Liens", 'links' => "Liens",
@@ -107,22 +110,6 @@ $lang = array(
'chooseClass' => "Choisissez une classe", 'chooseClass' => "Choisissez une classe",
'chooseFamily' => "Choisissez un familier", 'chooseFamily' => "Choisissez un familier",
// profiler
'realm' => "Royaume",
'region' => "Région",
'viewCharacter' => "Voir Personnage",
'_cpHead' => "Profiler de Personnage",
'_cpHint' => "Le <b>Profiler de Personnage</b> vous permets de modifier votre personnage, trouver des améliorations d'équipement, vérifier votre score d'équipement et plus!",
'_cpHelp' => "Pour débuter, suivez simplement les étapes ci-dessous. Si vous voulez plus d'information, lisez notre <a href=\"?help=profiler\">page d'aide</a> détaillée.",
'_cpFooter' => "Si vous voulez une recherche plus raffinée, essayez nos options de <a href=\"?profiles\">recherche avancée</a>. Vous pouvez aussi créer un <a href=\"?profile&amp;new\">nouveau profile personnalisé</a>.",
// help
'help' => "Aide",
'helpTopics' => array(
"Le guide du commentaire", "Visionneuse 3D", "Captures d'écran : Trucs et astuces", "Échelles de valeurs",
"Calculateur de talents", "Comparaison d'objets", "Profiler", "Markup Guide"
),
// search // search
'search' => "Recherche", 'search' => "Recherche",
'searchButton' => "Rechercher", 'searchButton' => "Rechercher",
@@ -143,29 +130,68 @@ $lang = array(
'bannedRating' => "Vous avez été banni du score des commentaires.", # LANG.tooltip_banned_rating 'bannedRating' => "Vous avez été banni du score des commentaires.", # LANG.tooltip_banned_rating
'tooManyVotes' => "Vous avez voté trop souvent aujourd'hui! Revenez demain.", # LANG.tooltip_too_many_votes 'tooManyVotes' => "Vous avez voté trop souvent aujourd'hui! Revenez demain.", # LANG.tooltip_too_many_votes
// screenshots 'moreTitles' => array(
'prepError' => "[An error occured preparing your screenshot]", 'reputation' => "Réputation du site",
'cropHint' => "[Crop the image by dragging the selection.<br>Please refer to <a href=\"?help=screenshots-tips-tricks\">Screenshots: Tips & Tricks</a> for an optimal layout.]", 'whats-new' => "Quoi de neuf?",
'searchbox' => "Boîte de recherche",
'tooltips' => "Info-bulles",
'faq' => "[Frequently Asked Questions]",
'aboutus' => "[What is AoWoW?]",
'searchplugins' => "Plug-ins de recherche",
'privileges' => "Privilèges",
'top-users' => "Meilleurs Utilisateurs",
'help' => array(
'commenting-and-you' => "Le guide du commentaire", 'modelviewer' => "Visionneuse 3D", 'screenshots-tips-tricks' => "Captures d'écran : Trucs et astuces",
'stat-weighting' => "Échelles de valeurs", 'talent-calculator' => "Calculateur de talents", 'item-comparison' => "Comparaison d'objets",
'profiler' => "Profiler", 'markup-guide' => "Markup Guide"
)
)
),
'profiler' => array(
'realm' => "Royaume",
'region' => "Région",
'viewCharacter' => "Voir Personnage",
'_cpHint' => "Le <b>Profiler de Personnage</b> vous permets de modifier votre personnage, trouver des améliorations d'équipement, vérifier votre score d'équipement et plus!",
'_cpHelp' => "Pour débuter, suivez simplement les étapes ci-dessous. Si vous voulez plus d'information, lisez notre <a href=\"?help=profiler\">page d'aide</a> détaillée.",
'_cpFooter' => "Si vous voulez une recherche plus raffinée, essayez nos options de <a href=\"?profiles\">recherche avancée</a>. Vous pouvez aussi créer un <a href=\"?profile&amp;new\">nouveau profile personnalisé</a>.",
'firstUseTitle' => "%s de %s",
'complexFilter' => "[Complex filter selected! Search results are limited to cached Characters.]",
'resync' => "Resynchronisation",
'guildRoster' => "Liste des membres pour la guilde de &lt;%s&gt",
'arenaRoster' => "[Arena Team Roster for &lt;%s&gt]", // string probably lost
'atCaptain' => "Capitaine d'équipe",
'profiler' => "Profiler de Personnage",
'arenaTeams' => "Équipes d'Aréna",
'guilds' => "Guildes",
'notFound' => array(
'guild' => "[This Guild doesn't exist or is not yet in the database.]",
'arenateam' => "[This Arena Team doesn't exist or is not yet in the database.]",
'profile' => "[This character doesn't exist or is not yet in the database.]"
),
'dummyNPCs' => array(
100001 => "Bataille des canonnières", 200001 => "Bêtes du Norfendre", 200002 => "Champions de faction", 200003 => "Les jumelles val'kyrs"
),
),
'screenshot' => array(
'submission' => "Envoi d'une capture d'écran",
'selectAll' => "Sélectionner tout",
'cropHint' => "Vous pouvez recadrer votre capture d'écran.",
'displayOn' => "[Displayed on:[br]%s - [%s=%d]]",
'caption' => "[Caption]", 'caption' => "[Caption]",
'originalSize' => "[Original size]", 'charLimit' => "Optionnel, jusqu'à 200 caractères",
'targetSize' => "[Target size]", 'thanks' => array(
'minSize' => "[Minimum size]", 'contrib' => "Merci beaucoup de votre contribution!",
'displayOn' => "[Displayed on: %s[br][%s=%d]]", 'goBack' => '<a href="?%s=%d">ici</a> pour retourner à la page d\'où vous venez.',
'ssEdit' => "[Edit uploaded screenshot]", 'note' => "Note : Votre capture d'écran devra être approuvée avant d'apparaître sur le site. Cela peut prendre jusqu'à 72 heures."
'ssUpload' => "[Screenshot Upload]", ),
'ssSubmit' => "[Submit Screenshot]", 'error' => array(
'ssErrors' => array( 'unkFormat' => "Format d'image inconnu.",
'noUpload' => "[The file was not uploaded!]", 'tooSmall' => "Votre capture est bien trop petite. (&lt; ".CFG_SCREENSHOT_MIN_SIZE."x".CFG_SCREENSHOT_MIN_SIZE.").",
'maxSize' => "[The file exceeds the maximum size of %s!]", 'selectSS' => "Veuillez sélectionner la capture d'écran à envoyer.",
'interrupted' => "[The upload process was interrupted!]", 'notAllowed' => "Vous n'êtes pas autorisés à exporter des captures d'écran.",
'noFile' => "[The file was not received!]",
'noDest' => "[The page this screenshot should be displayed on, does not exist!]",
'notAllowed' => "[You are not allowed to upload screenshots!]",
'noImage' => "[The uploaded file is not an image file!]",
'wrongFormat' => "[The image file must be a png or jpg!]",
'load' => "[The image file could not be loaded!]",
'tooSmall' => "[The image size is too small! (lower than %d x %d)]",
'tooLarge' => "[The image size is too large! (greater than %d x %d)]"
) )
), ),
'game' => array( 'game' => array(
@@ -178,8 +204,12 @@ $lang = array(
'difficulty' => "Difficulté", 'difficulty' => "Difficulté",
'dispelType' => "Type de dissipation", 'dispelType' => "Type de dissipation",
'duration' => "Durée", 'duration' => "Durée",
'gameObject' => "entité", 'emote' => "emote",
'gameObjects' => "Entités", 'emotes' => "Emotes",
'enchantment' => "enchantement",
'enchantments' => "Enchantements",
'object' => "entité",
'objects' => "Entités",
'glyphType' => "Type de glyphe", 'glyphType' => "Type de glyphe",
'race' => "race", 'race' => "race",
'races' => "Races", 'races' => "Races",
@@ -191,6 +221,8 @@ $lang = array(
'faction' => "faction", 'faction' => "faction",
'factions' => "Factions", 'factions' => "Factions",
'cooldown' => "%s de recharge", 'cooldown' => "%s de recharge",
'icon' => "icône",
'icons' => "Icônes",
'item' => "objet", 'item' => "objet",
'items' => "Objets", 'items' => "Objets",
'itemset' => "ensemble d'objets", 'itemset' => "ensemble d'objets",
@@ -209,12 +241,13 @@ $lang = array(
'requires' => "%s requis", 'requires' => "%s requis",
'requires2' => "Requiert", 'requires2' => "Requiert",
'reqLevel' => "Niveau %s requis", 'reqLevel' => "Niveau %s requis",
'reqLevelHlm' => "Requiert Niveau %s",
'reqSkillLevel' => "Niveau de compétence requis", 'reqSkillLevel' => "Niveau de compétence requis",
'level' => "Niveau", 'level' => "Niveau",
'school' => "École", 'school' => "École",
'skill' => "compétence", 'skill' => "compétence",
'skills' => "Compétences", 'skills' => "Compétences",
'sound' => "son",
'sounds' => "Sons",
'spell' => "sort", 'spell' => "sort",
'spells' => "Sorts", 'spells' => "Sorts",
'type' => "Type", 'type' => "Type",
@@ -260,10 +293,10 @@ $lang = array(
'rep' => ["Détesté", "Hostile", "Inamical", "Neutre", "Amical", "Honoré", "Révéré", "Exalté"], 'rep' => ["Détesté", "Hostile", "Inamical", "Neutre", "Amical", "Honoré", "Révéré", "Exalté"],
'st' => array( 'st' => array(
"Défaut", "Forme de félin", "Arbre de vie", "Forme de voyage", "Forme aquatique", "Forme d'ours", "Défaut", "Forme de félin", "Arbre de vie", "Forme de voyage", "Forme aquatique", "Forme d'ours",
null, null, "Forme d'ours redoutable", null, null, null, "Ambiant", "Goule", "Forme d'ours redoutable", "Goule de Steve", "Squelette de Tharon'ja", "Sombrelune - épreuve de force",
null, "Danse de l'ombre", null, null, "Loup fantôme", "Posture de combat", "Joueur BLB", "Danse de l'ombre", "Créature - Ours", "Créature - Félin", "Loup fantôme", "Posture de combat",
"Posture défensive", "Posture berserker", null, null, "Métamorphe", null, "Posture défensive", "Posture berserker", "Test", "Zombie", "Métamorphe", null,
null, null, null, "Forme de vol rapide", "Forme d'Ombre", "Forme de vol", null, "Mort-vivant", "Frénésie", "Forme de vol rapide", "Forme d'Ombre", "Forme de vol",
"Camouflage", "Forme de sélénien", "Esprit de rédemption" "Camouflage", "Forme de sélénien", "Esprit de rédemption"
), ),
'me' => array( 'me' => array(
@@ -302,7 +335,7 @@ $lang = array(
'groups' => array( 'groups' => array(
-1 => "None", "Testeur", "Administrateur", "Éditeur", "Modérateur", "Bureaucrate", -1 => "None", "Testeur", "Administrateur", "Éditeur", "Modérateur", "Bureaucrate",
"Développeur", "VIP", "Bloggeur", "Premium", "Traducteur", "Agent de ventes", "Développeur", "VIP", "Bloggeur", "Premium", "Traducteur", "Agent de ventes",
"Gestionnaire de capture d'écran","Gestionnaire de vidéos" "Gestionnaire de capture d'écran","Gestionnaire de vidéos", "Partenaire API", "En attente"
), ),
// signIn // signIn
'doSignIn' => "Connexion à votre compte Aowow", 'doSignIn' => "Connexion à votre compte Aowow",
@@ -318,51 +351,51 @@ $lang = array(
// recovery // recovery
'recoverUser' => "Demande de nom d'utilisateur", 'recoverUser' => "Demande de nom d'utilisateur",
'recoverPass' => "Changement de mot de passe : Étape %s de 2", 'recoverPass' => "Changement de mot de passe : Étape %s de 2",
'newPass' => "New Password", 'newPass' => "Nouveau mot de passe",
// creation // creation
'register' => "Enregistrement : Étape %s de 2", 'register' => "Enregistrement : Étape %s de 2",
'passConfirm' => "Confirmez", 'passConfirm' => "Confirmez",
// dashboard // dashboard
'ipAddress' => "IP-Adress", 'ipAddress' => "Addresse IP",
'lastIP' => "last used IP", 'lastIP' => "Dernière IP utilisée",
'myAccount' => "My Account", 'myAccount' => "Mon compte",
'editAccount' => "Simply use the forms below to update your account information", 'editAccount' => "Utilisez les formulaires ci-dessous pour mettre à jour vos informations.",
'viewPubDesc' => 'View your Public Description in your <a href="?user=%s">Profile Page</a>', 'viewPubDesc' => 'Voyez vos informations publiques dans votre <a href="?user=%s">Profile Page</a>',
// bans // bans
'accBanned' => "This Account was closed", 'accBanned' => "Ce compte a été fermé.",
'bannedBy' => "Banned by", 'bannedBy' => "Banni par",
'ends' => "Ends on", 'ends' => "Termine le",
'permanent' => "The ban is permanent", 'permanent' => "Ce bannissement est permanent",
'reason' => "Reason", 'reason' => "Raison",
'noReason' => "No reason was given.", 'noReason' => "Aucune raison donnée.",
// form-text // form-text
'emailInvalid' => "Cette adresse courriel est invalide.", // message_emailnotvalid 'emailInvalid' => "Cette adresse courriel est invalide.", // message_emailnotvalid
'emailNotFound' => "The email address you entered is not associated with any account.<br><br>If you forgot the email you registered your account with email ".CFG_CONTACT_EMAIL." for assistance.", 'emailNotFound' => "L'address email que vous avez entrée n'est pas associée à un compte.<br><br>Si vous avez oublié l'address email avec laquelle vous avez enregistré votre compte".CFG_CONTACT_EMAIL." pour obtenir de l'aide.",
'createAccSent' => "An email was sent to <b>%s</b>. Simply follow the instructions to create your account.", 'createAccSent' => "Un email a été envoyé à <b>%s</b>. Suivez les instructions pour créer votre compte.",
'recovUserSent' => "An email was sent to <b>%s</b>. Simply follow the instructions to recover your username.", 'recovUserSent' => "Un email a été envoyé à <b>%s</b>. Suivez les instructions pour récupérer votre nom d'utilisateur.",
'recovPassSent' => "An email was sent to <b>%s</b>. Simply follow the instructions to reset your password.", 'recovPassSent' => "Un email a été envoyé à <b>%s</b>. Suivez les instructions pour réinitialiser votre mot de passe.",
'accActivated' => 'Your account has been activated.<br>Proceed to <a href="?account=signin&token=%s">sign in</a>', 'accActivated' => 'Votre compte a été activé.<br>Vous pouvez maintenant <a href="?account=signin&token=%s">vous connecter</a>',
'userNotFound' => "The username you entered does not exists.", 'userNotFound' => "Le nom d'utilisateur que vous avez saisi n'éxiste pas.",
'wrongPass' => "That password is not vaild.", 'wrongPass' => "Ce mot de passe est invalide.",
'accInactive' => "That account has not yet been confirmed active.", // 'accInactive' => "Ce compte n'a pas encore été activé.",
'loginExceeded' => "The maximum number of logins from this IP has been exceeded. Please try again in %s.", 'loginExceeded' => "Le nombre maximum de connections depuis cette IP a été dépassé. Essayez de nouevau dans %s.",
'signupExceeded'=> "The maximum number of signups from this IP has been exceeded. Please try again in %s.", 'signupExceeded'=> "Le nombre maximum d'inscriptions depuis cette IP a été dépassé. Essayez de nouveau dans %s.",
'errNameLength' => "Votre nom d'utilisateur doit faire au moins 4 caractères de long.", // message_usernamemin 'errNameLength' => "Votre nom d'utilisateur doit faire au moins 4 caractères de long.", // message_usernamemin
'errNameChars' => "Votre nom d'utilisateur doit contenir seulement des lettres et des chiffres.", // message_usernamenotvalid 'errNameChars' => "Votre nom d'utilisateur doit contenir seulement des lettres et des chiffres.", // message_usernamenotvalid
'errPassLength' => "Votre mot de passe doit faire au moins 6 caractères de long.", // message_passwordmin 'errPassLength' => "Votre mot de passe doit faire au moins 6 caractères de long.", // message_passwordmin
'passMismatch' => "The passwords you entered do not match.", 'passMismatch' => "Les mots de passe que vous avez saisis ne correspondent pas.",
'nameInUse' => "That username is already taken.", 'nameInUse' => "Ce nom d'utilisateur est déjà utilisé.",
'mailInUse' => "That email is already registered to an account.", 'mailInUse' => "Cette addresse email est déjà liée à un compte.",
'isRecovering' => "This account is already recovering. Follow the instructions in your email or wait %s for the token to expire.", 'isRecovering' => "Ce compte est déjà en train d'être récupéré. Suivez les instruction dans l'email reçu ou attendez %s pour que le token expire.",
'passCheckFail' => "Les mots de passe ne correspondent pas.", // message_passwordsdonotmatch 'passCheckFail' => "Les mots de passe ne correspondent pas.", // message_passwordsdonotmatch
'newPassDiff' => "Votre nouveau mot de passe doit être différent de l'ancien." // message_newpassdifferent 'newPassDiff' => "Votre nouveau mot de passe doit être différent de l'ancien." // message_newpassdifferent
), ),
'user' => array( 'user' => array(
'notFound' => "Utilisateur \"%s\" non trouvé !", 'notFound' => "Utilisateur \"%s\" non trouvé!",
'removed' => "(Supprimé)", 'removed' => "(Supprimé)",
'joinDate' => "Inscription", 'joinDate' => "Inscription",
'lastLogin' => "Dernière visite", 'lastLogin' => "Dernière visite",
@@ -379,22 +412,42 @@ $lang = array(
), ),
'mail' => array( 'mail' => array(
'tokenExpires' => "This token expires in %s.", 'tokenExpires' => "This token expires in %s.",
'accConfirm' => ["Account Confirmation", "Welcome to ".CFG_NAME_SHORT."!\r\n\r\nClick the Link below to activate your account.\r\n\r\n".HOST_URL."?account=signup&token=%s\r\n\r\nIf you did not request this mail simply ignore it."], 'accConfirm' => ["Activation de compte", "Bienvenue sur ".CFG_NAME_SHORT."!\r\n\r\nCliquez sur le lien ci-dessous pour activer votre compte.\r\n\r\n".HOST_URL."?account=signup&token=%s\r\n\r\nSi vous n'avez pas demandé cet email, ignorez le."],
'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."], 'recoverUser' => ["Récupération d'utilisateur", "Suivez ce lien pour vous connecter.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nSi vous n'avez pas demandé cet email, ignorez le."],
'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."] 'resetPass' => ["Réinitialisation du mot de passe", "Suivez ce lien pour réinitialiser votre mot de passe.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nSi vous n'avez pas fait de demande de réinitialisation, ignorez cet email."]
),
'emote' => array(
'notFound' => "[This Emote doesn't exist.]",
'self' => "Vers vous-même",
'target' => "Vers les autres avec une cible",
'noTarget' => "Vers les autres sans cible",
'isAnimated' => "Utilise une animation",
'aliases' => "Alias",
'noText' => "Cette émote n'a pas de texte.",
),
'enchantment' => array(
'details' => "En détail",
'activation' => "Activation",
'notFound' => "Cet enchantement n'existe pas.",
'types' => array(
1 => "Sort proc", 3 => "Sort équipé", 7 => "Sort utilisé", 8 => "Châsse prismatique",
5 => "Statistiques", 2 => "Dégâts d'arme", 6 => "DPS", 4 => "Défense"
)
), ),
'gameObject' => array( 'gameObject' => array(
'notFound' => "Cette entité n'existe pas.", 'notFound' => "Cette entité n'existe pas.",
'cat' => [0 => "Autre", 9 => "Livres", 3 => "Conteneurs", -5 => "Coffres", 25 => "Bancs de poissons", -3 => "Herbes", -4 => "Filons de minerai", -2 => "Quêtes", -6 => "Outils"], 'cat' => [0 => "Autre", 9 => "Livres", 3 => "Conteneurs", -5 => "Coffres", 25 => "Bancs de poissons", -3 => "Herbes", -4 => "Filons de minerai", -2 => "Quêtes", -6 => "Outils"],
'type' => [ 9 => "Livre", 3 => "Conteneur", -5 => "Coffre", 25 => "", -3 => "Herbe", -4 => "Filon de minerai", -2 => "Quête", -6 => ""], 'type' => [ 9 => "Livre", 3 => "Conteneur", -5 => "Coffre", 25 => "", -3 => "Herbe", -4 => "Filon de minerai", -2 => "Quête", -6 => ""],
'unkPosition' => "L'emplacement de cette entité est inconnu.", 'unkPosition' => "L'emplacement de cette entité est inconnu.",
'npcLootPH' => 'Le <b>%s</b> contient les récompenses du combat contre <a href="?npc=%d">%s</a>. Il apparaît après sa mort.',
'key' => "Clé", 'key' => "Clé",
'focus' => "[Spell Focus]", 'focus' => "Focus de sort",
'focusDesc' => "[Spells requiring this Focus can be cast near this Object]", 'focusDesc' => "Les sorts nécessitant ce focus peuvent être lancés près de cet entité.",
'trap' => "Piège", 'trap' => "Piège",
'triggeredBy' => "Déclenché par", 'triggeredBy' => "Déclenché par",
'capturePoint' => "Point de capture", 'capturePoint' => "Point de capture",
'restock' => "[Restocks every %s.]" 'foundIn' => "Cette entité se trouve dans",
'restock' => "Se remplit toutes les %s.]"
), ),
'npc' => array( 'npc' => array(
'notFound' => "Ce PNJ n'existe pas.", 'notFound' => "Ce PNJ n'existe pas.",
@@ -403,10 +456,10 @@ $lang = array(
'react' => "Réaction", 'react' => "Réaction",
'worth' => "Vaut", 'worth' => "Vaut",
'unkPosition' => "L'emplacement de ce PNJ est inconnu.", 'unkPosition' => "L'emplacement de ce PNJ est inconnu.",
'difficultyPH' => "[Ce PNJ est un espace réservé pour un autre mode de]", 'difficultyPH' => "Ce PNJ est un espace réservé pour un autre mode de difficulté.",
'seat' => "[Seat]", 'seat' => "Siège",
'accessory' => "[Accessory]", 'accessory' => "Passager",
'accessoryFor' => "[This creature is an accessory for vehicle]", 'accessoryFor' => "Ce PNJ est un passager pour un véhicule.",
'quotes' => "Citations", 'quotes' => "Citations",
'gainsDesc' => "Après avoir tué ce PNJ vous allez obtenir", 'gainsDesc' => "Après avoir tué ce PNJ vous allez obtenir",
'repWith' => "points de réputation avec", 'repWith' => "points de réputation avec",
@@ -416,6 +469,7 @@ $lang = array(
'melee' => "de mêlée", 'melee' => "de mêlée",
'ranged' => "à distance", 'ranged' => "à distance",
'armor' => "Armure", 'armor' => "Armure",
'foundIn' => "Ce PNJ se trouve dans",
'tameable' => "Domptable (%s)", 'tameable' => "Domptable (%s)",
'waypoint' => "Point de route", 'waypoint' => "Point de route",
'wait' => "Période d'attente", 'wait' => "Période d'attente",
@@ -479,6 +533,20 @@ $lang = array(
'Azeroth' => "Azeroth", 'Azeroth' => "Azeroth",
'CosmicMap' => "Carte cosmique", 'CosmicMap' => "Carte cosmique",
), ),
'privileges' => array(
'main' => "Sur AoWoW, vous pouvez accumuler de la <a href=\"?reputation\">réputation</a>. Le principal moyen d'en accumuler est d'avoir un score élevé pour vos commentaires.<br><br>Ainsi, la réputation est une vision sommaire de vos contributions à la communauté.<br><br>En amassant de la réputation, vous gagnez le respect de la communauté et vous obtiendrez certains privilèges. Vous pouvez en trouver la liste complète ci-dessous.",
'privilege' => "Privilège",
'privileges' => "Privilèges",
'requiredRep' => "Réputation Requise",
'reqPoints' => "Ce privilège requiert <b>%s</b> points de réputation.",
'_privileges' => array(
null, "Poster des commentaires", "Poster des liens externes", null,
"Pas de CAPTCHA", "Les votes des commentaires ont plus de valeur",null, null,
null, "Plus de votes par jour", "Emettre des votes positifs pour les commentaires", "Donner des votes négatifs aux commentaires",
"Envoyer des réponses aux commentaires","Bordure : Bonne", "Bordure : Rare", "Bordure : Épique",
"Bordure : Légendaire", "AoWoW Premium"
)
),
'zone' => array( 'zone' => array(
'notFound' => "Cette zone n'existe pas.", 'notFound' => "Cette zone n'existe pas.",
'attunement' => ["Accès", "Accès Héroïque"], 'attunement' => ["Accès", "Accès Héroïque"],
@@ -489,7 +557,7 @@ $lang = array(
'reqLevels' => "Niveaux requis : [tooltip=instancereqlevel_tip]%d[/tooltip], [tooltip=lfgreqlevel_tip]%d[/tooltip]", 'reqLevels' => "Niveaux requis : [tooltip=instancereqlevel_tip]%d[/tooltip], [tooltip=lfgreqlevel_tip]%d[/tooltip]",
'zonePartOf' => "Cette zone fait partie de la zone [zone=%d].", 'zonePartOf' => "Cette zone fait partie de la zone [zone=%d].",
'autoRez' => "Résurrection automatique", 'autoRez' => "Résurrection automatique",
'city' => "Город", 'city' => "Ville",
'territory' => "Territoire", 'territory' => "Territoire",
'instanceType' => "Type d'instance", 'instanceType' => "Type d'instance",
'hcAvailable' => "Mode héroïque disponible&nbsp;(%d)", 'hcAvailable' => "Mode héroïque disponible&nbsp;(%d)",
@@ -556,8 +624,9 @@ $lang = array(
'receiveAlso' => "Vous recevrez également", 'receiveAlso' => "Vous recevrez également",
'spellCast' => "Vous allez être la cible du sort suivant", 'spellCast' => "Vous allez être la cible du sort suivant",
'spellLearn' => "Vous apprendrez", 'spellLearn' => "Vous apprendrez",
'bonusTalents' => "points de talent", 'bonusTalents' => "%d |4point:points; de talent",
'spellDisplayed'=> ' (<a href="?spell=%d">%s</a> affichés)', 'spellDisplayed'=> ' (<a href="?spell=%d">%s</a> affichés)',
'attachment' => "[Attachment]",
'questInfo' => array( 'questInfo' => array(
0 => "Standard", 1 => "Groupe", 21 => "Vie", 41 => "JcJ", 62 => "Raid", 81 => "Donjon", 82 => "Évènement mondial", 0 => "Standard", 1 => "Groupe", 21 => "Vie", 41 => "JcJ", 62 => "Raid", 81 => "Donjon", 82 => "Évènement mondial",
83 => "Légendaire", 84 => "Escorte", 85 => "Héroïque", 88 => "Raid (10)", 89 => "Raid (25)" 83 => "Légendaire", 84 => "Escorte", 85 => "Héroïque", 88 => "Raid (10)", 89 => "Raid (25)"
@@ -628,10 +697,14 @@ $lang = array(
), ),
7 => array( "Divers", 7 => array( "Divers",
-365 => "Guerre d'Ahn'Qiraj", -1010 => "Chercheur de donjons", -1 => "Épique", -344 => "Légendaire", -367 => "Réputation", -365 => "Guerre d'Ahn'Qiraj", -1010 => "Chercheur de donjons", -1 => "Épique", -344 => "Légendaire", -367 => "Réputation",
-368 => "Invasion du fléau", -241 => "Tournoi"), -368 => "Invasion du fléau", -241 => "Tournoi"
),
-2 => "Non classés" -2 => "Non classés"
) )
), ),
'icon' => array(
'notFound' => "Cette icône n'existe pas."
),
'title' => array( 'title' => array(
'notFound' => "Ce titre n'existe pas.", 'notFound' => "Ce titre n'existe pas.",
'_transfer' => 'Ce titre sera converti en <a href="?title=%d" class="q1">%s</a> si vous transférez en <span class="icon-%s">%s</span>.', '_transfer' => 'Ce titre sera converti en <a href="?title=%d" class="q1">%s</a> si vous transférez en <span class="icon-%s">%s</span>.',
@@ -653,19 +726,34 @@ $lang = array(
1 => "Divers", 2 => "JcJ", 4 => "Classique", 21 => "Wrath of the Lich King", 22 => "Raid", 23 => "Burning Crusade", 41 => "Test", 3 => "Inutilisées" 1 => "Divers", 2 => "JcJ", 4 => "Classique", 21 => "Wrath of the Lich King", 22 => "Raid", 23 => "Burning Crusade", 41 => "Test", 3 => "Inutilisées"
) )
), ),
'sound' => array(
'notFound' => "Ce son n'existe pas.",
'foundIn' => "Ce son se trouve dans",
'goToPlaylist' => "Aller à votre playlist",
'music' => "Musique",
'intro' => "Musique d'introduction",
'ambience' => "Ambiance",
'cat' => array(
null, "Spells", "User Interface", "Footsteps", "Weapons Impacts", null, "Weapons Misses", null, null, "Pick Up/Put Down",
"NPC Combat", null, "Errors", "Nature", "Objects", null, "Death", "NPC Greetings", null, "Armor",
"Footstep Splash", "Water (Character)", "Water", "Tradeskills", "Misc Ambience", "Doodads", "Spell Fizzle", "NPC Loops", "Zone Music", "Emotes",
"Narration Music", "Narration", 50 => "Zone Ambience", 52 => "Emitters", 53 => "Vehicles", 1000 => "Ma playlist"
)
),
'pet' => array( 'pet' => array(
'notFound' => "Cette famille de familiers n'existe pas.", 'notFound' => "Cette famille de familiers n'existe pas.",
'exotic' => "Exotique", 'exotic' => "Exotique",
'cat' => ["Férocité", "Tenacité", "Ruse"] 'cat' => ["Férocité", "Tenacité", "Ruse"],
'food' => ["Viande", "Poisson", "Fromage", "Pain", "Champignon", "Fruit", "Viande crue", "Poisson cru"]
), ),
'faction' => array( 'faction' => array(
'notFound' => "Cette faction n'existe pas.", 'notFound' => "Cette faction n'existe pas.",
'spillover' => "[Reputation Spillover]", 'spillover' => "Partage de réputations",
'spilloverDesc' => "[Gaining reputation with this faction also yields a proportional gain with the factions listed below.]", 'spilloverDesc' => "Gagner de la réputation avec cette faction fourni une réputation proportionnelle avec les factions ci-dessous.",
'maxStanding' => "Niveau maximum", 'maxStanding' => "Niveau maximum",
'quartermaster' => "Intendant", 'quartermaster' => "Intendant",
'customRewRate' => "[Custom Reward Rate]", 'customRewRate' => "Taux de récompense personnalisé",
'_transfer' => '[The reputation with this faction will be converted to <a href="?faction=%d" class="q1">%s</a> if you transfer to <span class="icon-%s">%s</span>.]', '_transfer' => 'La réputation de cette faction sera convertie en <a href="?faction=%d" class="q1">%s</a> si vous transférez vers <span class="icon-%s">%s</span>.]',
'cat' => array( 'cat' => array(
1118 => ["Classique", 469 => "Alliance", 169 => "Cartel Gentepression", 67 => "Horde", 891 => "Forces de l'Alliance", 892 => "Forces de la Horde"], 1118 => ["Classique", 469 => "Alliance", 169 => "Cartel Gentepression", 67 => "Horde", 891 => "Forces de l'Alliance", 892 => "Forces de la Horde"],
980 => ["The Burning Crusade", 936 => "Shattrath"], 980 => ["The Burning Crusade", 936 => "Shattrath"],
@@ -740,10 +828,12 @@ $lang = array(
'pctCostOf' => "de la %s de base", 'pctCostOf' => "de la %s de base",
'costPerSec' => ", plus %s par seconde", 'costPerSec' => ", plus %s par seconde",
'costPerLevel' => ", plus %s par niveau", 'costPerLevel' => ", plus %s par niveau",
'stackGroup' => "[Stack Group]",
'linkedWith' => "[Linked with]",
'_scaling' => "[Scaling]", '_scaling' => "[Scaling]",
'scaling' => array( 'scaling' => array(
'directSP' => "[+%.2f%% of spell power to direct component]", 'directAP' => "[+%.2f%% of attack power to direct component]", 'directSP' => "+%.2f%% de la puissance des sorts directe", 'directAP' => "+%.2f%% de la puissance d'attaque directe",
'dotSP' => "[+%.2f%% of spell power per tick]", 'dotAP' => "[+%.2f%% of attack power per tick]" 'dotSP' => "+%.2f%% de la puissance des sorts par tick", 'dotAP' => "+%.2f%% de la puissance d'attaque par tick"
), ),
'powerRunes' => ["Givre", "Impie", "Sang", "Mort"], 'powerRunes' => ["Givre", "Impie", "Sang", "Mort"],
'powerTypes' => array( 'powerTypes' => array(
@@ -812,7 +902,8 @@ $lang = array(
), ),
'traitShort' => array( 'traitShort' => array(
'atkpwr' => "PA", 'rgdatkpwr' => "PAD", 'splpwr' => "PS", 'arcsplpwr' => "PArc", 'firsplpwr' => "PFeu", 'atkpwr' => "PA", 'rgdatkpwr' => "PAD", 'splpwr' => "PS", 'arcsplpwr' => "PArc", 'firsplpwr' => "PFeu",
'frosplpwr' => "PGiv", 'holsplpwr' => "PSac", 'natsplpwr' => "PNat", 'shasplpwr' => "POmb", 'splheal' => "Soins" 'frosplpwr' => "PGiv", 'holsplpwr' => "PSac", 'natsplpwr' => "PNat", 'shasplpwr' => "POmb", 'splheal' => "Soins",
'str' => "For", 'agi' => "Agi", 'sta' => "End", 'int' => "Int", 'spi' => "Esp"
), ),
'spellModOp' => array( 'spellModOp' => array(
"DAMAGE", "DURATION", "THREAT", "EFFECT1", "CHARGES", "DAMAGE", "DURATION", "THREAT", "EFFECT1", "CHARGES",
@@ -838,36 +929,127 @@ $lang = array(
"Calligraphie", "Ouverture à partir d'un véhicule", "Calligraphie", "Ouverture à partir d'un véhicule",
), ),
'stealthType' => ["GENERAL", "TRAP"], 'stealthType' => ["GENERAL", "TRAP"],
'invisibilityType' => ["GENERAL", 3 => "TRAP", 6 => "DRUNK"] 'invisibilityType' => ["GENERAL", 3 => "TRAP", 6 => "DRUNK"],
'unkEffect' => 'Unknown Effect',
'effects' => array(
/*0-5 */ 'None', 'Instakill', 'School Damage', 'Dummy', 'Portal Teleport', 'Teleport Units',
/*6+ */ 'Apply Aura', 'Environmental Damage', 'Power Drain', 'Health Leech', 'Heal', 'Bind',
/*12+ */ 'Portal', 'Ritual Base', 'Ritual Specialize', 'Ritual Activate Portal', 'Quest Complete', 'Weapon Damage NoSchool',
/*18+ */ 'Resurrect', 'Add Extra Attacks', 'Dodge', 'Evade', 'Parry', 'Block',
/*24+ */ 'Create Item', 'Can Use Weapon', 'Defense', 'Persistent Area Aura', 'Summon', 'Leap',
/*30+ */ 'Energize', 'Weapon Damage Percent', 'Trigger Missile', 'Open Lock', 'Summon Change Item', 'Apply Area Aura Party',
/*36+ */ 'Learn Spell', 'Spell Defense', 'Dispel', 'Language', 'Dual Wield', 'Jump',
/*42+ */ 'Jump Dest', 'Teleport Units Face Caster','Skill Step', 'Add Honor', 'Spawn', 'Trade Skill',
/*48+ */ 'Stealth', 'Detect', 'Trans Door', 'Force Critical Hit', 'Guarantee Hit', 'Enchant Item Permanent',
/*54+ */ 'Enchant Item Temporary', 'Tame Creature', 'Summon Pet', 'Learn Pet Spell', 'Weapon Damage Flat', 'Create Random Item',
/*60+ */ 'Proficiency', 'Send Event', 'Power Burn', 'Threat', 'Trigger Spell', 'Apply Area Aura Raid',
/*66+ */ 'Create Mana Gem', 'Heal Max Health', 'Interrupt Cast', 'Distract', 'Pull', 'Pickpocket',
/*72+ */ 'Add Farsight', 'Untrain Talents', 'Apply Glyph', 'Heal Mechanical', 'Summon Object Wild', 'Script Effect',
/*78+ */ 'Attack', 'Sanctuary', 'Add Combo Points', 'Create House', 'Bind Sight', 'Duel',
/*84+ */ 'Stuck', 'Summon Player', 'Activate Object', 'WMO Damage', 'WMO Repair', 'WMO Change',
/*90+ */ 'Kill Credit', 'Threat All', 'Enchant Held Item', 'Force Deselect', 'Self Resurrect', 'Skinning',
/*96+ */ 'Charge', 'Cast Button', 'Knock Back', 'Disenchant', 'Inebriate', 'Feed Pet',
/*102+ */ 'Dismiss Pet', 'Reputation', 'Summon Object Slot1', 'Summon Object Slot2', 'Summon Object Slot3', 'Summon Object Slot4',
/*108+ */ 'Dispel Mechanic', 'Summon Dead Pet', 'Destroy All Totems', 'Durability Damage', 'Summon Demon', 'Resurrect Flat',
/*114+ */ 'Attack Me', 'Durability Damage Percent','Skin Player Corpse', 'Spirit Heal', 'Skill', 'Apply Area Aura Pet',
/*120+ */ 'Teleport Graveyard', 'Weapon Damage Normalized', null, 'Send Taxi', 'Pull Towards', 'Modify Threat Percent',
/*126+ */ 'Steal Beneficial Buff', 'Prospecting', 'Apply Area Aura Friend', 'Apply Area Aura Enemy', 'Redirect Threat', 'Play Sound',
/*132+ */ 'Play Music', 'Unlearn Specialization', 'Kill Credit2', 'Call Pet', 'Heal Percent', 'Energize Percent',
/*138+ */ 'Leap Back', 'Clear Quest', 'Force Cast', 'Force Cast With Value', 'Trigger Spell With Value', 'Apply Area Aura Owner',
/*144+ */ 'Knock Back Dest', 'Pull Towards Dest', 'Activate Rune', 'Quest Fail', null, 'Charge Dest',
/*150+ */ 'Quest Start', 'Trigger Spell 2', null, 'Create Tamed Pet', 'Discover Taxi', 'Dual Wield 2H Weapons',
/*156+ */ 'Enchant Item Prismatic', 'Create Item 2', 'Milling', 'Allow Rename Pet', null, 'Talent Spec Count',
/*162-164*/ 'Talent Spec Select', null, 'Remove Aura'
),
'unkAura' => 'Unknown Aura',
'auras' => array(
/*0- */ 'None', 'Bind Sight', 'Mod Possess', 'Periodic Damage', 'Dummy',
/*5+ */ 'Mod Confuse', 'Mod Charm', 'Mod Fear', 'Periodic Heal', 'Mod Attack Speed',
'Mod Threat', 'Taunt', 'Stun', 'Mod Damage Done Flat', 'Mod Damage Taken Flat',
'Damage Shield', 'Mod Stealth', 'Mod Stealth Detection', 'Mod Invisibility', 'Mod Invisibility Detection',
'Mod Health Percent', 'Mod Power Percent', 'Mod Resistance Flat', 'Periodic Trigger Spell', 'Periodic Energize',
/*25+ */ 'Pacify', 'Root', 'Silence', 'Reflect Spells', 'Mod Stat Flat',
'Mod Skill', 'Mod Increase Speed', 'Mod Increase Mounted Speed', 'Mod Decrease Speed', 'Mod Increase Health',
'Mod Increase Power', 'Shapeshift', 'Spell Effect Immunity', 'Spell Aura Immunity', 'School Immunity',
'Damage Immunity', 'Dispel Immunity', 'Proc Trigger Spell', 'Proc Trigger Damage', 'Track Creatures',
'Track Resources', 'Mod Parry Skill', 'Mod Parry Percent', null, 'Mod Dodge Percent',
/*50+ */ 'Mod Critical Healing Amount', 'Mod Block Percent', 'Mod Physical Crit Percent', 'Periodic Health Leech', 'Mod Hit Chance',
'Mod Spell Hit Chance', 'Transform', 'Mod Spell Crit Chance', 'Mod Increase Swim Speed', 'Mod Damage Done Versus Creature',
'Pacify Silence', 'Mod Scale', 'Periodic Health Funnel', 'Periodic Mana Funnel', 'Periodic Mana Leech',
'Mod Casting Speed (not stacking)', 'Feign Death', 'Disarm', 'Stalked', 'School Absorb',
'Extra Attacks', 'Mod Spell Crit Chance School', 'Mod Power Cost School Percent', 'Mod Power Cost School Flat', 'Reflect Spells School',
/*75+ */ 'Language', 'Far Sight', 'Mechanic Immunity', 'Mounted', 'Mod Damage Done Percent',
'Mod Stat Percent', 'Split Damage Percent', 'Water Breathing', 'Mod Base Resistance Flat', 'Mod Health Regeneration',
'Mod Power Regeneration', 'Channel Death Item', 'Mod Damage Taken Percent', 'Mod Health Regeneration Percent', 'Periodic Damage Percent',
'Mod Resist Chance', 'Mod Detect Range', 'Prevent Fleeing', 'Unattackable', 'Interrupt Regeneration',
'Ghost', 'Spell Magnet', 'Mana Shield', 'Mod Skill Value', 'Mod Attack Power',
/*100+ */ 'Auras Visible', 'Mod Resistance Percent', 'Mod Melee Attack Power Versus', 'Mod Total Threat', 'Water Walk',
'Feather Fall', 'Hover', 'Add Flat Modifier', 'Add Percent Modifier', 'Add Target Trigger',
'Mod Power Regeneration Percent', 'Add Caster Hit Trigger', 'Override Class Scripts', 'Mod Ranged Damage Taken Flat', 'Mod Ranged Damage Taken Percent',
'Mod Healing', 'Mod Regeneration During Combat', 'Mod Mechanic Resistance', 'Mod Healing Taken Percent', 'Share Pet Tracking',
'Untrackable', 'Empathy', 'Mod Offhand Damage Percent', 'Mod Target Resistance', 'Mod Ranged Attack Power',
/*125+ */ 'Mod Melee Damage Taken Flat', 'Mod Melee Damage Taken Percent', 'Ranged Attack Power Attacker Bonus', 'Possess Pet', 'Mod Speed Always',
'Mod Mounted Speed Always', 'Mod Ranged Attack Power Versus', 'Mod Increase Energy Percent', 'Mod Increase Health Percent', 'Mod Mana Regeneration Interrupt',
'Mod Healing Done Flat', 'Mod Healing Done Percent', 'Mod Total Stat Percentage', 'Mod Melee Haste', 'Force Reaction',
'Mod Ranged Haste', 'Mod Ranged Ammo Haste', 'Mod Base Resistance Percent', 'Mod Resistance Exclusive', 'Safe Fall',
'Mod Pet Talent Points', 'Allow Tame Pet Type', 'Mechanic Immunity Mask', 'Retain Combo Points', 'Reduce Pushback',
/*150+ */ 'Mod Shield Blockvalue Percent', 'Track Stealthed', 'Mod Detected Range', 'Split Damage Flat', 'Mod Stealth Level',
'Mod Water Breathing', 'Mod Reputation Gain', 'Pet Damage Multi', 'Mod Shield Blockvalue', 'No PvP Credit',
'Mod AoE Avoidance', 'Mod Health Regeneration In Combat', 'Power Burn Mana', 'Mod Crit Damage Bonus', null,
'Melee Attack Power Attacker Bonus', 'Mod Attack Power Percent', 'Mod Ranged Attack Power Percent', 'Mod Damage Done Versus', 'Mod Crit Percent Versus',
'Change Model', 'Mod Speed (not stacking)', 'Mod Mounted Speed (not stacking)', null, 'Mod Spell Damage Of Stat Percent',
/*175+ */ 'Mod Spell Healing Of Stat Percent', 'Spirit Of Redemption', 'AoE Charm', 'Mod Debuff Resistance', 'Mod Attacker Spell Crit Chance',
'Mod Spell Damage Versus', null, 'Mod Resistance Of Stat Percent', 'Mod Critical Threat', 'Mod Attacker Melee Hit Chance',
'Mod Attacker Ranged Hit Chance', 'Mod Attacker Spell Hit Chance', 'Mod Attacker Melee Crit Chance', 'Mod Attacker Ranged Crit Chance', 'Mod Rating',
'Mod Faction Reputation Gain', 'Use Normal Movement Speed', 'Mod Melee Ranged Haste', 'Mod Haste', 'Mod Target Absorb School',
'Mod Target Ability Absorb School', 'Mod Cooldown', 'Mod Attacker Spell And Weapon Crit Chance', null, 'Mod Increases Spell Percent to Hit',
/*200+ */ 'Mod XP Percent', 'Fly', 'Ignore Combat Result', 'Mod Attacker Melee Crit Damage', 'Mod Attacker Ranged Crit Damage',
'Mod School Crit Damage Taken', 'Mod Increase Vehicle Flight Speed', 'Mod Increase Mounted Flight Speed', 'Mod Increase Flight Speed', 'Mod Mounted Flight Speed Always',
'Mod Vehicle Speed Always', 'Mod Flight Speed (not stacking)', 'Mod Ranged Attack Power Of Stat Percent', 'Mod Rage from Damage Dealt', 'Tamed Pet Passive',
'Arena Preparation', 'Haste Spells', 'Killing Spree', 'Haste Ranged', 'Mod Mana Regeneration from Stat',
'Mod Rating from Stat', 'Ignore Threat', null, 'Raid Proc from Charge', null,
/*225+ */ 'Raid Proc from Charge With Value', 'Periodic Dummy', 'Periodic Trigger Spell With Value', 'Detect Stealth', 'Mod AoE Damage Avoidance',
'Mod Increase Health', 'Proc Trigger Spell With Value', 'Mod Mechanic Duration', 'Mod Display Model', 'Mod Mechanic Duration (not stacking)',
'Mod Dispel Resist', 'Control Vehicle', 'Mod Spell Damage Of Attack Power', 'Mod Spell Healing Of Attack Power', 'Mod Scale 2',
'Mod Expertise', 'Force Move Forward', 'Mod Spell Damage from Healing', 'Mod Faction', 'Comprehend Language',
'Mod Aura Duration By Dispel', 'Mod Aura Duration By Dispel (not stacking)', 'Clone Caster', 'Mod Combat Result Chance', 'Convert Rune',
/*250+ */ 'Mod Increase Health 2', 'Mod Enemy Dodge', 'Mod Speed Slow All', 'Mod Block Crit Chance', 'Mod Disarm Offhand',
'Mod Mechanic Damage Taken Percent', 'No Reagent Use', 'Mod Target Resist By Spell Class', 'Mod Spell Visual', 'Mod HoT Percent',
'Screen Effect', 'Phase', 'Ability Ignore Aurastate', 'Allow Only Ability', null,
null, null, 'Mod Immune Aura Apply School', 'Mod Attack Power Of Stat Percent', 'Mod Ignore Target Resist',
'Mod Ability Ignore Target Resist', 'Mod Damage Taken Percent From Caster', 'Ignore Melee Reset', 'X Ray', 'Ability Consume No Ammo',
/*275+ */ 'Mod Ignore Shapeshift', 'Mod Mechanic Damage Done Percent', 'Mod Max Affected Targets', 'Mod Disarm Ranged', 'Initialize Images',
'Mod Armor Penetration Percent', 'Mod Honor Gain Percent', 'Mod Base Health Percent', 'Mod Healing Received', 'Linked',
'Mod Attack Power Of Armor', 'Ability Periodic Crit', 'Deflect Spells', 'Ignore Hit Direction', null,
'Mod Crit Percent', 'Mod XP Quest Percent', 'Open Stable', 'Override Spells', 'Prevent Power Regeneration',
null, 'Set Vehicle Id', 'Block Spell Family', 'Strangulate', null,
/*300+ */ 'Share Damage Percent', 'School Heal Absorb', null, 'Mod Damage Done Versus Aurastate', 'Mod Fake Inebriate',
'Mod Minimum Speed', null, 'Heal Absorb Test', 'Hunter Trap', null,
'Mod Creature AoE Damage Avoidance', null, null, null, 'Prevent Ressurection',
/* -316*/ 'Underwater Walking', 'Periodic Haste'
)
), ),
'item' => array( 'item' => array(
'notFound' => "Cet objet n'existe pas.", 'notFound' => "Cet objet n'existe pas.",
'armor' => "Armure : %s", 'armor' => "Armure : %s",
'block' => "Bloquer : %s", 'block' => "Bloquer : %s",
'charges' => "Charges", 'charges' => "%d |4charge:charges;",
'locked' => "Verrouillé", 'locked' => "Verrouillé",
'ratingString' => "%s&nbsp;@&nbsp;L%s", 'ratingString' => "%s&nbsp;@&nbsp;L%s",
'heroic' => "Héroïque", 'heroic' => "Héroïque",
'unique' => "Unique",
'uniqueEquipped'=> "Unique - Equipé",
'startQuest' => "Cet objet permet de lancer une quête", 'startQuest' => "Cet objet permet de lancer une quête",
'bagSlotString' => "%s %d emplacements", 'bagSlotString' => '%2$s %1$d |4emplacement:emplacements;',
'dps' => "dégâts par seconde",
'dps2' => "dégâts par seconde",
'addsDps' => "Ajoute",
'fap' => "puissance d'attaque en combat farouche", 'fap' => "puissance d'attaque en combat farouche",
'durability' => "Durabilité", 'durability' => "Durabilité %d / %d",
'realTime' => "temps réel", 'realTime' => "temps réel",
'conjured' => "Objet invoqué", 'conjured' => "Objet invoqué",
'damagePhys' => "Dégâts : %s",
'damageMagic' => "%s points de dégâts (%s)",
'speed' => "Vitesse",
'sellPrice' => "Prix de Vente", 'sellPrice' => "Prix de Vente",
'itemLevel' => "Niveau d'objet", 'itemLevel' => "Niveau d'objet %d",
'randEnchant' => "&lt;Enchantement aléatoire&gt", 'randEnchant' => "&lt;Enchantement aléatoire&gt",
'readClick' => "&lt;Clique Droit pour Lire&gt", 'readClick' => "&lt;Clique Droit pour Lire&gt",
'openClick' => "&lt;Clic Droit pour Ouvrir&gt", 'openClick' => "&lt;Clic Droit pour Ouvrir&gt",
'set' => "Set", 'setBonus' => "(%d) Ensemble : %s",
'setName' => "%s (%d/%d)",
'partyLoot' => "Butin de groupe", 'partyLoot' => "Butin de groupe",
'smartLoot' => "Butin intelligent", 'smartLoot' => "Butin intelligent",
'indestructible'=> "Ne peut être détruit", 'indestructible'=> "Ne peut être détruit",
@@ -891,7 +1073,7 @@ $lang = array(
'cost' => "Coût", 'cost' => "Coût",
'content' => "Contenu", 'content' => "Contenu",
'_transfer' => 'Cet objet sera converti en <a href="?item=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> si vous transférez en <span class="icon-%s">%s</span>.', '_transfer' => 'Cet objet sera converti en <a href="?item=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a> si vous transférez en <span class="icon-%s">%s</span>.',
'_unavailable' => "Este objeto no está disponible para los jugadores.", '_unavailable' => "Cet objet n'est pas disponible pour les joueurs.",
'_rndEnchants' => "Enchantements aléatoires", '_rndEnchants' => "Enchantements aléatoires",
'_chance' => "(%s%% de chance)", '_chance' => "(%s%% de chance)",
'slot' => "Emplacement", 'slot' => "Emplacement",
@@ -900,6 +1082,18 @@ $lang = array(
'buyout' => "Vente immédiate", 'buyout' => "Vente immédiate",
'each' => "chacun", 'each' => "chacun",
'tabOther' => "Autre", 'tabOther' => "Autre",
'reqMinLevel' => "Niveau %d requis",
'reqLevelRange' => "Niveau %d à %d (%s) requis",
'unique' => ["Unique", "Unique (%d)", "Unique: %s (%d)" ], // ITEM_UNIQUE, ITEM_UNIQUE_MULTIPLE, ITEM_LIMIT_CATEGORY
'uniqueEquipped'=> ["Unique - Equipé", null, "Unique - Equipé: %s (%d)"], // ITEM_UNIQUE_EQUIPPABLE, null, ITEM_LIMIT_CATEGORY_MULTIPLE
'speed' => "Vitesse",
'dps' => "(%.1f dégâts par seconde)",
'damage' => array( // *DAMAGE_TEMPLATE*
// basic, basic /w school, add basic, add basic /w school
'single' => ["%d Dégâts", "%d points de dégâts (%s)", "+ %d points de dégâts", "+ %d points de dégâts (%s)" ],
'range' => ["Dégâts : %d - %d", "%d - %d points de dégâts (%s)", "+ %d - %d points de dégâts", "+%d - %d points de dégâts (%s)" ],
'ammo' => ["Ajoute %g dégâts par seconde", "Ajoute %g points de dégâts (%s) par seconde", "+ %g points de dégâts par seconde", "+ %g points de dégâts (%s) par seconde" ]
),
'gems' => "Gemmes", 'gems' => "Gemmes",
'socketBonus' => "Bonus de châsse", 'socketBonus' => "Bonus de châsse",
'socket' => array( 'socket' => array(
@@ -909,22 +1103,22 @@ $lang = array(
"Méta", "rouge(s)", "jaune(s)", "bleue(s)" "Méta", "rouge(s)", "jaune(s)", "bleue(s)"
), ),
'gemConditions' => array( // ENCHANT_CONDITION_* 'gemConditions' => array( // ENCHANT_CONDITION_*
2 => ["moins de %d gemme %s", "moins de %d gemmes %s"], 2 => "moins de %d |4gemme:gemmes; %s",
3 => "plus de gemmes %s que de %s", // plus de gemmes %s que |2 %s 3 => "plus de gemmes %s que |2 %s",
5 => ["au moins %d gemme %s", "au moins %d gemmes %s"] 5 => "au moins %d |4gemme:gemmes; %s"
), ),
'reqRating' => array( // ITEM_REQ_ARENA_RATING* 'reqRating' => array( // ITEM_REQ_ARENA_RATING*
"Nécessite une cote d'arène personnelle et en équipe de %d", "Nécessite une cote d'arène personnelle et en équipe de %d",
"Nécessite une cote d'arène personnelle et en équipe de %d<br>en arène de 3c3 ou 5c5.", "Nécessite une cote d'arène personnelle et en équipe de %d|nen arène de 3c3 ou 5c5.",
"Nécessite une cote d'arène personnelle et en équipe de %d<br>en arène de 5c5." "Nécessite une cote d'arène personnelle et en équipe de %d|nen arène de 5c5."
), ),
'quality' => array( 'quality' => array(
"Médiocre", "Classique", "Bonne", "Rare", "Médiocre", "Classique", "Bonne", "Rare",
"Épique", "Légendaire", "Artefact", "Héritage" "Épique", "Légendaire", "Artefact", "Héritage"
), ),
'trigger' => array( 'trigger' => array(
"Utilise : ", "Équipé : ", "Chances quand vous touchez : ", null, null, "Utilise : ", "Équipé : ", "Chances quand vous touchez : ", "", "",
null, null "", ""
), ),
'bonding' => array( 'bonding' => array(
"Lié au compte", "Lié quand ramassé", "Lié quand équipé", "Lié au compte", "Lié quand ramassé", "Lié quand équipé",
@@ -1004,8 +1198,8 @@ $lang = array(
13 => "Clés", 13 => "Clés",
), ),
'statType' => array( 'statType' => array(
"Augmente vos points de mana de %d.", "Mana",
"Augmente vos points de vie de %d.", "Vie",
null, null,
"Agilité", "Agilité",
"Force", "Force",

View File

@@ -14,8 +14,7 @@ $lang = array(
'timeUnits' => array( 'timeUnits' => array(
'sg' => ["год", "месяц", "неделя", "день", "час", "минута", "секунда", "миллисекунда"], 'sg' => ["год", "месяц", "неделя", "день", "час", "минута", "секунда", "миллисекунда"],
'pl' => ["годы", "месяцы", "недели", "дн.", "часы", "мин", "секунды", "миллисекундах"], 'pl' => ["годы", "месяцы", "недели", "дн.", "часы", "мин", "секунды", "миллисекундах"],
'ab' => ["г.", "мес.", "нед.", "дн", "ч.", "мин", "сек.", "мс"], 'ab' => ["г.", "мес.", "нед.", "дн", "ч.", "мин", "сек.", "мс"]
'ago' => '%s назад'
), ),
'main' => array( 'main' => array(
'name' => "название", 'name' => "название",
@@ -47,10 +46,14 @@ $lang = array(
'forum' => "Форум", 'forum' => "Форум",
'n_a' => "нет", 'n_a' => "нет",
'siteRep' => "Репутация", 'siteRep' => "Репутация",
'yourRepHistory'=> "История вашей репутации",
'aboutUs' => "О Aowow", 'aboutUs' => "О Aowow",
'and' => " и ", 'and' => " и ",
'or' => " или ", 'or' => " или ",
'back' => "Назад", 'back' => "Назад",
'reputationTip' => "Очки репутации",
'byUserTimeAgo' => 'От <a href="'.HOST_URL.'/?user=%s">%1s</a> %s назад',
'help' => "Справка",
// filter // filter
'extSearch' => "Расширенный поиск", 'extSearch' => "Расширенный поиск",
@@ -107,22 +110,6 @@ $lang = array(
'chooseClass' => "Выберите класс", 'chooseClass' => "Выберите класс",
'chooseFamily' => "Выберите семейство питомцев", 'chooseFamily' => "Выберите семейство питомцев",
// profiler
'realm' => "Игровой мир",
'region' => "Регион",
'viewCharacter' => "Открыть персонажа",
'_cpHead' => "Профили персонажей",
'_cpHint' => "<b>Профили персонажей</b> позволяет вам редактировать своего персонажа, находить улучшения предметов и многое другое!",
'_cpHelp' => "Чтобы начать использовать профили персонажей, следуйте инструкциям ниже. Если вам потребуется помощь, вы можете обратиться к <a href=\"?help=profiler\">справке</a>.",
'_cpFooter' => "Если вам нужен более точный поиск, вы можете использовать <a href=\"?profiles\">дополнительные опции</a>. Также, вы можете создать <a href=\"?profile&amp;new\">новый собственный профиль</a>.",
// help
'help' => "Справка",
'helpTopics' => array(
"Комментарии и Вы", "3D просмотр", "Скриншоты: Секреты мастерства", "Значимость характеристик",
"Расчёт талантов", "Сравнение предметов", "Профили персонажей", "Markup Guide"
),
// search // search
'search' => "Поиск", 'search' => "Поиск",
'searchButton' => "Поиск", 'searchButton' => "Поиск",
@@ -143,29 +130,68 @@ $lang = array(
'bannedRating' => "Вам была заблокирована возможность оценивать комментарии.", # LANG.tooltip_banned_rating 'bannedRating' => "Вам была заблокирована возможность оценивать комментарии.", # LANG.tooltip_banned_rating
'tooManyVotes' => "Вы сегодня проголосовали слишком много раз! Вы сможете продолжить завтра.", # LANG.tooltip_too_many_votes 'tooManyVotes' => "Вы сегодня проголосовали слишком много раз! Вы сможете продолжить завтра.", # LANG.tooltip_too_many_votes
// screenshots 'moreTitles' => array(
'prepError' => "[An error occured preparing your screenshot]", 'reputation' => "Репутация на сайте",
'cropHint' => "[Crop the image by dragging the selection.<br>Please refer to <a href=\"?help=screenshots-tips-tricks\">Screenshots: Tips & Tricks</a> for an optimal layout.]", 'whats-new' => "Новости",
'searchbox' => "Окно поиска",
'tooltips' => "Всплывающие подсказки",
'faq' => "[Frequently Asked Questions]",
'aboutus' => "[What is AoWoW?]",
'searchplugins' => "Дополнения для браузеров",
'privileges' => "Привилегии",
'top-users' => "Лучшие пользователи",
'help' => array(
'commenting-and-you' => "Комментарии и Вы", 'modelviewer' => "3D просмотр", 'screenshots-tips-tricks' => "Скриншоты: Секреты мастерства",
'stat-weighting' => "Значимость характеристик", 'talent-calculator' => "Расчёт талантов", 'item-comparison' => "Сравнение предметов",
'profiler' => "Профили персонажей", 'markup-guide' => "Markup Guide"
)
)
),
'profiler' => array(
'realm' => "Игровой мир",
'region' => "Регион",
'viewCharacter' => "Открыть персонажа",
'_cpHint' => "<b>Профили персонажей</b> позволяет вам редактировать своего персонажа, находить улучшения предметов и многое другое!",
'_cpHelp' => "Чтобы начать использовать профили персонажей, следуйте инструкциям ниже. Если вам потребуется помощь, вы можете обратиться к <a href=\"?help=profiler\">справке</a>.",
'_cpFooter' => "Если вам нужен более точный поиск, вы можете использовать <a href=\"?profiles\">дополнительные опции</a>. Также, вы можете создать <a href=\"?profile&amp;new\">новый собственный профиль</a>.",
'firstUseTitle' => "%s", // yes, thats correct. No nonsense, just the name
'complexFilter' => "[Complex filter selected! Search results are limited to cached Characters.]",
'resync' => "Ресинхронизация",
'guildRoster' => "Список членов гильдии &lt;%s&gt",
'arenaRoster' => "[Arena Team Roster for &lt;%s&gt]", // string probably lost
'atCaptain' => "Капитан команды арены",
'profiler' => "Профили персонажей",
'arenaTeams' => "Команды арен",
'guilds' => "Гильдии",
'notFound' => array(
'profile' => "Этот персонаж не существует, либо еще не добавлен в базу данных.",
'arenateam' => "[This Arena Team doesn't exist or is not yet in the database.]",
'guild' => "Такая гильдия не существует, или еще не добавлена в базу данных."
),
'dummyNPCs' => array(
100001 => "Бой на Кораблях", 200001 => "Звери Нордскола", 200002 => "Чемпионы фракций", 200003 => "Валь'киры-близнецы"
),
),
'screenshot' => array(
'submission' => "Добавление изображения",
'selectAll' => "Выбрать всё",
'cropHint' => "Вы можете произвести кадрирование изображения и указать заголовок.",
'displayOn' => "[Displayed on:[br]%s - [%s=%d]]",
'caption' => "[Caption]", 'caption' => "[Caption]",
'originalSize' => "[Original size]", 'charLimit' => "Не обязательно, вплоть до 200 знаков",
'targetSize' => "[Target size]", 'thanks' => array(
'minSize' => "[Minimum size]", 'contrib' => "Спасибо за ваш вклад!",
'displayOn' => "[Displayed on: %s[br][%s=%d]]", 'goBack' => '<a href="?%s=%d">здесь</a> чтобы перейти к предыдущей странице.',
'ssEdit' => "[Edit uploaded screenshot]", 'note' => "Примечание: Перед появлением на сайте, ваше изображение должно быть одобрено. Это может занять до 72 часов."
'ssUpload' => "[Screenshot Upload]", ),
'ssSubmit' => "[Submit Screenshot]", 'error' => array(
'ssErrors' => array( 'unkFormat' => "неизвестный формат изображения.",
'noUpload' => "[The file was not uploaded!]", 'tooSmall' => "Изображение слишком маленькое. (&lt; ".CFG_SCREENSHOT_MIN_SIZE."x".CFG_SCREENSHOT_MIN_SIZE.").",
'maxSize' => "[The file exceeds the maximum size of %s!]", 'selectSS' => "Выберите изображение для загрузки.",
'interrupted' => "[The upload process was interrupted!]",
'noFile' => "[The file was not received!]",
'noDest' => "[The page this screenshot should be displayed on, does not exist!]",
'notAllowed' => "[You are not allowed to upload screenshots!]", 'notAllowed' => "[You are not allowed to upload screenshots!]",
'noImage' => "[The uploaded file is not an image file!]",
'wrongFormat' => "[The image file must be a png or jpg!]",
'load' => "[The image file could not be loaded!]",
'tooSmall' => "[The image size is too small! (lower than %d x %d)]",
'tooLarge' => "[The image size is too large! (greater than %d x %d)]"
) )
), ),
'game' => array( 'game' => array(
@@ -178,8 +204,12 @@ $lang = array(
'difficulty' => "Сложность", 'difficulty' => "Сложность",
'dispelType' => "Тип рассеивания", 'dispelType' => "Тип рассеивания",
'duration' => "Длительность", 'duration' => "Длительность",
'gameObject' => "объект", 'emote' => "Эмоция",
'gameObjects' => "Объекты", 'emotes' => "Эмоции",
'enchantment' => "улучшение",
'enchantments' => "Улучшения",
'object' => "объект",
'objects' => "Объекты",
'glyphType' => "Тип символа", 'glyphType' => "Тип символа",
'race' => "раса", 'race' => "раса",
'races' => "Расы", 'races' => "Расы",
@@ -191,6 +221,8 @@ $lang = array(
'faction' => "фракция", 'faction' => "фракция",
'factions' => "Фракции", 'factions' => "Фракции",
'cooldown' => "Восстановление: %s", 'cooldown' => "Восстановление: %s",
'icon' => "иконка",
'icons' => "Иконки",
'item' => "предмет", 'item' => "предмет",
'items' => "Предметы", 'items' => "Предметы",
'itemset' => "комплект", 'itemset' => "комплект",
@@ -209,12 +241,13 @@ $lang = array(
'requires' => "Требует %s", 'requires' => "Требует %s",
'requires2' => "Требуется:", 'requires2' => "Требуется:",
'reqLevel' => "Требуется уровень: %s", 'reqLevel' => "Требуется уровень: %s",
'reqLevelHlm' => "Требуется уровень: %s",
'reqSkillLevel' => "Требуется уровень навыка", 'reqSkillLevel' => "Требуется уровень навыка",
'level' => "Уровень", 'level' => "Уровень",
'school' => "Школа", 'school' => "Школа",
'skill' => "Уровень навыка", 'skill' => "Уровень навыка",
'skills' => "Умения", 'skills' => "Умения",
'sound' => "Звук",
'sounds' => "Звуки",
'spell' => "заклинание", 'spell' => "заклинание",
'spells' => "Заклинания", 'spells' => "Заклинания",
'type' => "Тип", 'type' => "Тип",
@@ -260,10 +293,10 @@ $lang = array(
'rep' => array("Ненависть", "Враждебность", "Неприязнь", "Равнодушие", "Дружелюбие", "Уважение", "Почтение", "Превознесение"), 'rep' => array("Ненависть", "Враждебность", "Неприязнь", "Равнодушие", "Дружелюбие", "Уважение", "Почтение", "Превознесение"),
'st' => array( 'st' => array(
"По-умолчанию", "Облик кошки", "TОблик Древа жизни", "Походный облик", "Водный облик", "Облик медведя", "По-умолчанию", "Облик кошки", "TОблик Древа жизни", "Походный облик", "Водный облик", "Облик медведя",
null, null, "Облик лютого медведя", null, null, null, "Фауна", "Вурдалак", "Облик лютого медведя", "Вурдалак Стива", "Скелет Тарон'джа", "Ярмарка Новолуния - испытание силы",
null, "Танец теней", null, null, "Призрачный волк", "Боевая стойка", "BLB Player", "Танец теней", "Существо: медведь", "Существо: кошка", "Призрачный волк", "Боевая стойка",
"Оборонительная стойка", "Стойка берсерка", null, null, "Метаморфоза", null, "Оборонительная стойка", "Стойка берсерка", "Тест", "Зомби", "Метаморфоза", null,
null, null, null, "Облик стремительной птицы", "Облик Тьмы", "Облик птицы", null, "Нежить", "Бешенство", "Облик стремительной птицы", "Облик Тьмы", "Облик птицы",
"Незаметность", "Облик лунного совуха", "Дух воздаяния" "Незаметность", "Облик лунного совуха", "Дух воздаяния"
), ),
'me' => array( 'me' => array(
@@ -302,7 +335,7 @@ $lang = array(
'groups' => array( 'groups' => array(
-1 => "Нет", "Тестер", "Администратор", "Редактор", "Модератор", "Бюрократ", -1 => "Нет", "Тестер", "Администратор", "Редактор", "Модератор", "Бюрократ",
"Разработчик", "VIP", "Блогер", "Учетная запись Премиум", "Переводчик", "Агент по продажам", "Разработчик", "VIP", "Блогер", "Учетная запись Премиум", "Переводчик", "Агент по продажам",
"Менеджер изображений", "Менеджер видео" "Менеджер изображений", "Менеджер видео", "API партнер", "Ожидающее"
), ),
// signIn // signIn
'doSignIn' => "Войти в вашу учетную запись Aowow", 'doSignIn' => "Войти в вашу учетную запись Aowow",
@@ -348,7 +381,7 @@ $lang = array(
'accActivated' => 'Your account has been activated.<br>Proceed to <a href="?account=signin&token=%s">sign in</a>', 'accActivated' => 'Your account has been activated.<br>Proceed to <a href="?account=signin&token=%s">sign in</a>',
'userNotFound' => "The username you entered does not exists.", 'userNotFound' => "The username you entered does not exists.",
'wrongPass' => "That password is not vaild.", 'wrongPass' => "That password is not vaild.",
'accInactive' => "That account has not yet been confirmed active.", // 'accInactive' => "That account has not yet been confirmed active.",
'loginExceeded' => "The maximum number of logins from this IP has been exceeded. Please try again in %s.", 'loginExceeded' => "The maximum number of logins from this IP has been exceeded. Please try again in %s.",
'signupExceeded'=> "The maximum number of signups from this IP has been exceeded. Please try again in %s.", 'signupExceeded'=> "The maximum number of signups from this IP has been exceeded. Please try again in %s.",
'errNameLength' => "Имя пользователя не должно быть короче 4 символов.", // message_usernamemin 'errNameLength' => "Имя пользователя не должно быть короче 4 символов.", // message_usernamemin
@@ -383,17 +416,37 @@ $lang = array(
'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."], 'recoverUser' => ["User Recovery", "Follow this link to log in.\r\n\r\n".HOST_URL."?account=signin&token=%s\r\n\r\nIf you did not request this mail simply ignore it."],
'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."] 'resetPass' => ["Password Reset", "Follow this link to reset your password.\r\n\r\n".HOST_URL."?account=forgotpassword&token=%s\r\n\r\nIf you did not request this mail simply ignore it."]
), ),
'emote' => array(
'notFound' => "[This Emote doesn't exist.]",
'self' => "[To Yourself]",
'target' => "[To others with a target]",
'noTarget' => "[To others without a target]",
'isAnimated' => "[Uses an animation]",
'aliases' => "[Aliases]",
'noText' => "[This Emote has no text.]",
),
'enchantment' => array(
'details' => "Подробности",
'activation' => "Активации",
'notFound' => "Такой улучшение не существует.",
'types' => array(
1 => "[Proc Spell]", 3 => "[Equip Spell]", 7 => "[Use Spell]", 8 => "Бесцветное гнездо",
5 => "Характеристики", 2 => "Урон оружия", 6 => "УВС", 4 => "Защита"
)
),
'gameObject' => array( 'gameObject' => array(
'notFound' => "Такой объект не существует.", 'notFound' => "Такой объект не существует.",
'cat' => [0 => "Другое", 9 => "Книги", 3 => "Контейнеры", -5 => "Сундуки", 25 => "Рыболовные лунки",-3 => "Травы", -4 => "Полезные ископаемые", -2 => "Задания", -6 => "Инструменты"], 'cat' => [0 => "Другое", 9 => "Книги", 3 => "Контейнеры", -5 => "Сундуки", 25 => "Рыболовные лунки",-3 => "Травы", -4 => "Полезные ископаемые", -2 => "Задания", -6 => "Инструменты"],
'type' => [ 9 => "Книга", 3 => "Контейнер", -5 => "Сундук", 25 => "", -3 => "Растение", -4 => "Полезное ископаемое", -2 => "Задание", -6 => ""], 'type' => [ 9 => "Книга", 3 => "Контейнер", -5 => "Сундук", 25 => "", -3 => "Растение", -4 => "Полезное ископаемое", -2 => "Задание", -6 => ""],
'unkPosition' => "Местонахождение этого объекта неизвестно.", 'unkPosition' => "Местонахождение этого объекта неизвестно.",
'npcLootPH' => '[The <b>%s</b> contains the loot from the fight against <a href="?npc=%d">%s</a>. It spawns after his death.]',
'key' => "Ключ", 'key' => "Ключ",
'focus' => "[Spell Focus]", 'focus' => "[Spell Focus]",
'focusDesc' => "[Spells requiring this Focus can be cast near this Object]", 'focusDesc' => "[Spells requiring this Focus can be cast near this Object]",
'trap' => "Ловушки", 'trap' => "Ловушки",
'triggeredBy' => "Срабатывает от", 'triggeredBy' => "Срабатывает от",
'capturePoint' => "Точка захвата", 'capturePoint' => "Точка захвата",
'foundIn' => "Этот НИП может быть найден в следующих зонах:",
'restock' => "[Restocks every %s.]" 'restock' => "[Restocks every %s.]"
), ),
'npc' => array( 'npc' => array(
@@ -416,6 +469,7 @@ $lang = array(
'melee' => "Ближнего боя", 'melee' => "Ближнего боя",
'ranged' => "Дальнего боя", 'ranged' => "Дальнего боя",
'armor' => "Броня", 'armor' => "Броня",
'foundIn' => "Этот объект может быть найден в следующих зонах:",
'tameable' => "Можно приручить (%s)", 'tameable' => "Можно приручить (%s)",
'waypoint' => "Путевой точки", 'waypoint' => "Путевой точки",
'wait' => "Период ожидания", 'wait' => "Период ожидания",
@@ -451,7 +505,7 @@ $lang = array(
'titleReward' => 'Наградное звание: "<a href="?title=%d">%s</a>"', 'titleReward' => 'Наградное звание: "<a href="?title=%d">%s</a>"',
'slain' => "убито", 'slain' => "убито",
'reqNumCrt' => "Требуется", 'reqNumCrt' => "Требуется",
'rfAvailable' => "[Available on realm]:", 'rfAvailable' => "[Available on realm]: ",
'_transfer' => 'Этот предмет превратится в <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a>, если вы перейдете за <span class="icon-%s">%s</span>.', '_transfer' => 'Этот предмет превратится в <a href="?achievement=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a>, если вы перейдете за <span class="icon-%s">%s</span>.',
), ),
'chrClass' => array( 'chrClass' => array(
@@ -479,6 +533,20 @@ $lang = array(
'Azeroth' => "Азерот", 'Azeroth' => "Азерот",
'CosmicMap' => "Звёздная карта", 'CosmicMap' => "Звёздная карта",
), ),
'privileges' => array(
'main' => "Здесь на AoWoW вы можете зарабатывать <a href=\"?reputation\">репутацию</a>. Основной источник получения репутации — увеличение рейтинга ваших комментариев другими пользователями.<br><br>Репутация примерно измеряет количество вашего вклада в сообщество.<br><br>По мере того, как вы зарабатываете репутацию, вы получаете доверие сообщества и особые привилегии. Полный список привилегий расположен ниже.",
'privilege' => "Привилегия",
'privileges' => "Привилегии",
'requiredRep' => "Необходима репутация",
'reqPoints' => "Для этой привилегии требуется <b>%s</b> очков репутации.",
'_privileges' => array(
null, "Оставлять комментарии", "Оставлять внешние ссылки", null,
"Нет CAPTCHA", "Более сильные голоса", null, null,
null, "Больше голосов в день", "Голосовать за комментарии", "Голосовать против комментариев",
"Отвечать на комментарии", "Граница: Необычный", "Граница: Редкий", "Граница: Эпический",
"Граница: Легендарный", "AoWoW Premium"
)
),
'zone' => array( 'zone' => array(
'notFound' => "Такая местность не существует.", 'notFound' => "Такая местность не существует.",
'attunement' => ["[Attunement]", "[Heroic attunement]"], 'attunement' => ["[Attunement]", "[Heroic attunement]"],
@@ -556,8 +624,9 @@ $lang = array(
'receiveAlso' => "Вы также получите", 'receiveAlso' => "Вы также получите",
'spellCast' => "Следующее заклинание будет наложено на вас", 'spellCast' => "Следующее заклинание будет наложено на вас",
'spellLearn' => "Вы изучите", 'spellLearn' => "Вы изучите",
'bonusTalents' => "очков талантов", 'bonusTalents' => "%d |4очко талантов:очка талантов:очков талантов;",
'spellDisplayed'=> ' (показано: <a href="?spell=%d">%s</a>)', 'spellDisplayed'=> ' (показано: <a href="?spell=%d">%s</a>)',
'attachment' => "[Attachment]",
'questInfo' => array( 'questInfo' => array(
0 => "Обычный", 1 => "Группа", 21 => "Жизнь", 41 => "PvP", 62 => "Рейд", 81 => "Подземелье", 82 => "Игровое событие", 0 => "Обычный", 1 => "Группа", 21 => "Жизнь", 41 => "PvP", 62 => "Рейд", 81 => "Подземелье", 82 => "Игровое событие",
83 => "Легенда", 84 => "Сопровождение", 85 => "Героическое", 88 => "Рейд (10)", 89 => "Рейд (25)" 83 => "Легенда", 84 => "Сопровождение", 85 => "Героическое", 88 => "Рейд (10)", 89 => "Рейд (25)"
@@ -633,6 +702,9 @@ $lang = array(
-2 => "Разное" -2 => "Разное"
) )
), ),
'icon' => array(
'notFound' => "Этой иконки не существует"
),
'title' => array( 'title' => array(
'notFound' => "Такое звание не существует.", 'notFound' => "Такое звание не существует.",
'_transfer' => 'Этот предмет превратится в <a href="?title=%d" class="q1">%s</a>, если вы перейдете за <span class="icon-%s">%s</span>.', '_transfer' => 'Этот предмет превратится в <a href="?title=%d" class="q1">%s</a>, если вы перейдете за <span class="icon-%s">%s</span>.',
@@ -654,10 +726,25 @@ $lang = array(
1 => "Разное", 2 => "PvP", 4 => "World of Warcraft", 21 => "Wrath of the Lich King", 22 => "Подземелья и рейды", 23 => "Burning Crusade", 41 => "Test", 3 => "Неактивно" 1 => "Разное", 2 => "PvP", 4 => "World of Warcraft", 21 => "Wrath of the Lich King", 22 => "Подземелья и рейды", 23 => "Burning Crusade", 41 => "Test", 3 => "Неактивно"
) )
), ),
'sound' => array(
'notFound' => "Этот звук не существует.",
'foundIn' => "Этот Звук может быть найден в следующих зонах:",
'goToPlaylist' => "Перейти к плейлисту",
'music' => "Музыка",
'intro' => "Начальная музыка",
'ambience' => "Атмосфера",
'cat' => array(
null, "Spells", "User Interface", "Footsteps", "Weapons Impacts", null, "Weapons Misses", null, null, "Pick Up/Put Down",
"NPC Combat", null, "Errors", "Nature", "Objects", null, "Death", "NPC Greetings", null, "Armor",
"Footstep Splash", "Water (Character)", "Water", "Tradeskills", "Misc Ambience", "Doodads", "Spell Fizzle", "NPC Loops", "Zone Music", "Emotes",
"Narration Music", "Narration", 50 => "Zone Ambience", 52 => "Emitters", 53 => "Vehicles", 1000 => "Мой плейлист"
)
),
'pet' => array( 'pet' => array(
'notFound' => "Такой породы питомцев не существует.", 'notFound' => "Такой породы питомцев не существует.",
'exotic' => "Экзотический", 'exotic' => "Экзотический",
'cat' => ["Свирепость", "Упорство", "Хитрость"] 'cat' => ["Свирепость", "Упорство", "Хитрость"],
'food' => ["Мясо", "Рыба", "Сыр", "Хлеб", "Грибы", "Фрукты", "Сырое мясо", "Сырая рыба"]
), ),
'faction' => array( 'faction' => array(
'notFound' => "Такая фракция не существует.", 'notFound' => "Такая фракция не существует.",
@@ -721,7 +808,6 @@ $lang = array(
'_collapseAll' => "Свернуть все", '_collapseAll' => "Свернуть все",
'_expandAll' => "Развернуть все", '_expandAll' => "Развернуть все",
'_transfer' => 'Этот предмет превратится в <a href="?spell=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a>, если вы перейдете за <span class="icon-%s">%s</span>.', '_transfer' => 'Этот предмет превратится в <a href="?spell=%d" class="q%d icontiny tinyspecial" style="background-image: url('.STATIC_URL.'/images/wow/icons/tiny/%s.gif)">%s</a>, если вы перейдете за <span class="icon-%s">%s</span>.',
'discovered' => "Изучается путём освоения местности", 'discovered' => "Изучается путём освоения местности",
'ppm' => "Срабатывает %s раз в минуту", 'ppm' => "Срабатывает %s раз в минуту",
'procChance' => "Шанс срабатывания", 'procChance' => "Шанс срабатывания",
@@ -742,6 +828,8 @@ $lang = array(
'pctCostOf' => "от базовой %s", 'pctCostOf' => "от базовой %s",
'costPerSec' => ", плюс %s в секунду", 'costPerSec' => ", плюс %s в секунду",
'costPerLevel' => ", плюс %s за уровень", 'costPerLevel' => ", плюс %s за уровень",
'stackGroup' => "[Stack Group]",
'linkedWith' => "[Linked with]",
'_scaling' => "[Scaling]", '_scaling' => "[Scaling]",
'scaling' => array( 'scaling' => array(
'directSP' => "[+%.2f%% of spell power to direct component]", 'directAP' => "[+%.2f%% of attack power to direct component]", 'directSP' => "[+%.2f%% of spell power to direct component]", 'directAP' => "[+%.2f%% of attack power to direct component]",
@@ -814,7 +902,8 @@ $lang = array(
), ),
'traitShort' => array( 'traitShort' => array(
'atkpwr' => "СА", 'rgdatkpwr' => "Сил", 'splpwr' => "СЗ", 'arcsplpwr' => "Урон", 'firsplpwr' => "Урон", 'atkpwr' => "СА", 'rgdatkpwr' => "Сил", 'splpwr' => "СЗ", 'arcsplpwr' => "Урон", 'firsplpwr' => "Урон",
'frosplpwr' => "Урон", 'holsplpwr' => "Урон", 'natsplpwr' => "Урон", 'shasplpwr' => "Урон", 'splheal' => "Исцеление" 'frosplpwr' => "Урон", 'holsplpwr' => "Урон", 'natsplpwr' => "Урон", 'shasplpwr' => "Урон", 'splheal' => "Исцеление",
'str' => "Сила", 'agi' => "Ловк", 'sta' => "Выно", 'int' => "Инт", 'spi' => "Дух"
), ),
'spellModOp' => array( 'spellModOp' => array(
"DAMAGE", "DURATION", "THREAT", "EFFECT1", "CHARGES", "DAMAGE", "DURATION", "THREAT", "EFFECT1", "CHARGES",
@@ -840,36 +929,127 @@ $lang = array(
"Начертание", "Открыть на ходу" "Начертание", "Открыть на ходу"
), ),
'stealthType' => ["GENERAL", "TRAP"], 'stealthType' => ["GENERAL", "TRAP"],
'invisibilityType' => ["GENERAL", 3 => "TRAP", 6 => "DRUNK"] 'invisibilityType' => ["GENERAL", 3 => "TRAP", 6 => "DRUNK"],
'unkEffect' => 'Unknown Effect',
'effects' => array(
/*0-5 */ 'None', 'Instakill', 'School Damage', 'Dummy', 'Portal Teleport', 'Teleport Units',
/*6+ */ 'Apply Aura', 'Environmental Damage', 'Power Drain', 'Health Leech', 'Heal', 'Bind',
/*12+ */ 'Portal', 'Ritual Base', 'Ritual Specialize', 'Ritual Activate Portal', 'Quest Complete', 'Weapon Damage NoSchool',
/*18+ */ 'Resurrect', 'Add Extra Attacks', 'Dodge', 'Evade', 'Parry', 'Block',
/*24+ */ 'Create Item', 'Can Use Weapon', 'Defense', 'Persistent Area Aura', 'Summon', 'Leap',
/*30+ */ 'Energize', 'Weapon Damage Percent', 'Trigger Missile', 'Open Lock', 'Summon Change Item', 'Apply Area Aura Party',
/*36+ */ 'Learn Spell', 'Spell Defense', 'Dispel', 'Language', 'Dual Wield', 'Jump',
/*42+ */ 'Jump Dest', 'Teleport Units Face Caster','Skill Step', 'Add Honor', 'Spawn', 'Trade Skill',
/*48+ */ 'Stealth', 'Detect', 'Trans Door', 'Force Critical Hit', 'Guarantee Hit', 'Enchant Item Permanent',
/*54+ */ 'Enchant Item Temporary', 'Tame Creature', 'Summon Pet', 'Learn Pet Spell', 'Weapon Damage Flat', 'Create Random Item',
/*60+ */ 'Proficiency', 'Send Event', 'Power Burn', 'Threat', 'Trigger Spell', 'Apply Area Aura Raid',
/*66+ */ 'Create Mana Gem', 'Heal Max Health', 'Interrupt Cast', 'Distract', 'Pull', 'Pickpocket',
/*72+ */ 'Add Farsight', 'Untrain Talents', 'Apply Glyph', 'Heal Mechanical', 'Summon Object Wild', 'Script Effect',
/*78+ */ 'Attack', 'Sanctuary', 'Add Combo Points', 'Create House', 'Bind Sight', 'Duel',
/*84+ */ 'Stuck', 'Summon Player', 'Activate Object', 'WMO Damage', 'WMO Repair', 'WMO Change',
/*90+ */ 'Kill Credit', 'Threat All', 'Enchant Held Item', 'Force Deselect', 'Self Resurrect', 'Skinning',
/*96+ */ 'Charge', 'Cast Button', 'Knock Back', 'Disenchant', 'Inebriate', 'Feed Pet',
/*102+ */ 'Dismiss Pet', 'Reputation', 'Summon Object Slot1', 'Summon Object Slot2', 'Summon Object Slot3', 'Summon Object Slot4',
/*108+ */ 'Dispel Mechanic', 'Summon Dead Pet', 'Destroy All Totems', 'Durability Damage', 'Summon Demon', 'Resurrect Flat',
/*114+ */ 'Attack Me', 'Durability Damage Percent','Skin Player Corpse', 'Spirit Heal', 'Skill', 'Apply Area Aura Pet',
/*120+ */ 'Teleport Graveyard', 'Weapon Damage Normalized', null, 'Send Taxi', 'Pull Towards', 'Modify Threat Percent',
/*126+ */ 'Steal Beneficial Buff', 'Prospecting', 'Apply Area Aura Friend', 'Apply Area Aura Enemy', 'Redirect Threat', 'Play Sound',
/*132+ */ 'Play Music', 'Unlearn Specialization', 'Kill Credit2', 'Call Pet', 'Heal Percent', 'Energize Percent',
/*138+ */ 'Leap Back', 'Clear Quest', 'Force Cast', 'Force Cast With Value', 'Trigger Spell With Value', 'Apply Area Aura Owner',
/*144+ */ 'Knock Back Dest', 'Pull Towards Dest', 'Activate Rune', 'Quest Fail', null, 'Charge Dest',
/*150+ */ 'Quest Start', 'Trigger Spell 2', null, 'Create Tamed Pet', 'Discover Taxi', 'Dual Wield 2H Weapons',
/*156+ */ 'Enchant Item Prismatic', 'Create Item 2', 'Milling', 'Allow Rename Pet', null, 'Talent Spec Count',
/*162-164*/ 'Talent Spec Select', null, 'Remove Aura'
),
'unkAura' => 'Unknown Aura',
'auras' => array(
/*0- */ 'None', 'Bind Sight', 'Mod Possess', 'Periodic Damage', 'Dummy',
/*5+ */ 'Mod Confuse', 'Mod Charm', 'Mod Fear', 'Periodic Heal', 'Mod Attack Speed',
'Mod Threat', 'Taunt', 'Stun', 'Mod Damage Done Flat', 'Mod Damage Taken Flat',
'Damage Shield', 'Mod Stealth', 'Mod Stealth Detection', 'Mod Invisibility', 'Mod Invisibility Detection',
'Mod Health Percent', 'Mod Power Percent', 'Mod Resistance Flat', 'Periodic Trigger Spell', 'Periodic Energize',
/*25+ */ 'Pacify', 'Root', 'Silence', 'Reflect Spells', 'Mod Stat Flat',
'Mod Skill', 'Mod Increase Speed', 'Mod Increase Mounted Speed', 'Mod Decrease Speed', 'Mod Increase Health',
'Mod Increase Power', 'Shapeshift', 'Spell Effect Immunity', 'Spell Aura Immunity', 'School Immunity',
'Damage Immunity', 'Dispel Immunity', 'Proc Trigger Spell', 'Proc Trigger Damage', 'Track Creatures',
'Track Resources', 'Mod Parry Skill', 'Mod Parry Percent', null, 'Mod Dodge Percent',
/*50+ */ 'Mod Critical Healing Amount', 'Mod Block Percent', 'Mod Physical Crit Percent', 'Periodic Health Leech', 'Mod Hit Chance',
'Mod Spell Hit Chance', 'Transform', 'Mod Spell Crit Chance', 'Mod Increase Swim Speed', 'Mod Damage Done Versus Creature',
'Pacify Silence', 'Mod Scale', 'Periodic Health Funnel', 'Periodic Mana Funnel', 'Periodic Mana Leech',
'Mod Casting Speed (not stacking)', 'Feign Death', 'Disarm', 'Stalked', 'School Absorb',
'Extra Attacks', 'Mod Spell Crit Chance School', 'Mod Power Cost School Percent', 'Mod Power Cost School Flat', 'Reflect Spells School',
/*75+ */ 'Language', 'Far Sight', 'Mechanic Immunity', 'Mounted', 'Mod Damage Done Percent',
'Mod Stat Percent', 'Split Damage Percent', 'Water Breathing', 'Mod Base Resistance Flat', 'Mod Health Regeneration',
'Mod Power Regeneration', 'Channel Death Item', 'Mod Damage Taken Percent', 'Mod Health Regeneration Percent', 'Periodic Damage Percent',
'Mod Resist Chance', 'Mod Detect Range', 'Prevent Fleeing', 'Unattackable', 'Interrupt Regeneration',
'Ghost', 'Spell Magnet', 'Mana Shield', 'Mod Skill Value', 'Mod Attack Power',
/*100+ */ 'Auras Visible', 'Mod Resistance Percent', 'Mod Melee Attack Power Versus', 'Mod Total Threat', 'Water Walk',
'Feather Fall', 'Hover', 'Add Flat Modifier', 'Add Percent Modifier', 'Add Target Trigger',
'Mod Power Regeneration Percent', 'Add Caster Hit Trigger', 'Override Class Scripts', 'Mod Ranged Damage Taken Flat', 'Mod Ranged Damage Taken Percent',
'Mod Healing', 'Mod Regeneration During Combat', 'Mod Mechanic Resistance', 'Mod Healing Taken Percent', 'Share Pet Tracking',
'Untrackable', 'Empathy', 'Mod Offhand Damage Percent', 'Mod Target Resistance', 'Mod Ranged Attack Power',
/*125+ */ 'Mod Melee Damage Taken Flat', 'Mod Melee Damage Taken Percent', 'Ranged Attack Power Attacker Bonus', 'Possess Pet', 'Mod Speed Always',
'Mod Mounted Speed Always', 'Mod Ranged Attack Power Versus', 'Mod Increase Energy Percent', 'Mod Increase Health Percent', 'Mod Mana Regeneration Interrupt',
'Mod Healing Done Flat', 'Mod Healing Done Percent', 'Mod Total Stat Percentage', 'Mod Melee Haste', 'Force Reaction',
'Mod Ranged Haste', 'Mod Ranged Ammo Haste', 'Mod Base Resistance Percent', 'Mod Resistance Exclusive', 'Safe Fall',
'Mod Pet Talent Points', 'Allow Tame Pet Type', 'Mechanic Immunity Mask', 'Retain Combo Points', 'Reduce Pushback',
/*150+ */ 'Mod Shield Blockvalue Percent', 'Track Stealthed', 'Mod Detected Range', 'Split Damage Flat', 'Mod Stealth Level',
'Mod Water Breathing', 'Mod Reputation Gain', 'Pet Damage Multi', 'Mod Shield Blockvalue', 'No PvP Credit',
'Mod AoE Avoidance', 'Mod Health Regeneration In Combat', 'Power Burn Mana', 'Mod Crit Damage Bonus', null,
'Melee Attack Power Attacker Bonus', 'Mod Attack Power Percent', 'Mod Ranged Attack Power Percent', 'Mod Damage Done Versus', 'Mod Crit Percent Versus',
'Change Model', 'Mod Speed (not stacking)', 'Mod Mounted Speed (not stacking)', null, 'Mod Spell Damage Of Stat Percent',
/*175+ */ 'Mod Spell Healing Of Stat Percent', 'Spirit Of Redemption', 'AoE Charm', 'Mod Debuff Resistance', 'Mod Attacker Spell Crit Chance',
'Mod Spell Damage Versus', null, 'Mod Resistance Of Stat Percent', 'Mod Critical Threat', 'Mod Attacker Melee Hit Chance',
'Mod Attacker Ranged Hit Chance', 'Mod Attacker Spell Hit Chance', 'Mod Attacker Melee Crit Chance', 'Mod Attacker Ranged Crit Chance', 'Mod Rating',
'Mod Faction Reputation Gain', 'Use Normal Movement Speed', 'Mod Melee Ranged Haste', 'Mod Haste', 'Mod Target Absorb School',
'Mod Target Ability Absorb School', 'Mod Cooldown', 'Mod Attacker Spell And Weapon Crit Chance', null, 'Mod Increases Spell Percent to Hit',
/*200+ */ 'Mod XP Percent', 'Fly', 'Ignore Combat Result', 'Mod Attacker Melee Crit Damage', 'Mod Attacker Ranged Crit Damage',
'Mod School Crit Damage Taken', 'Mod Increase Vehicle Flight Speed', 'Mod Increase Mounted Flight Speed', 'Mod Increase Flight Speed', 'Mod Mounted Flight Speed Always',
'Mod Vehicle Speed Always', 'Mod Flight Speed (not stacking)', 'Mod Ranged Attack Power Of Stat Percent', 'Mod Rage from Damage Dealt', 'Tamed Pet Passive',
'Arena Preparation', 'Haste Spells', 'Killing Spree', 'Haste Ranged', 'Mod Mana Regeneration from Stat',
'Mod Rating from Stat', 'Ignore Threat', null, 'Raid Proc from Charge', null,
/*225+ */ 'Raid Proc from Charge With Value', 'Periodic Dummy', 'Periodic Trigger Spell With Value', 'Detect Stealth', 'Mod AoE Damage Avoidance',
'Mod Increase Health', 'Proc Trigger Spell With Value', 'Mod Mechanic Duration', 'Mod Display Model', 'Mod Mechanic Duration (not stacking)',
'Mod Dispel Resist', 'Control Vehicle', 'Mod Spell Damage Of Attack Power', 'Mod Spell Healing Of Attack Power', 'Mod Scale 2',
'Mod Expertise', 'Force Move Forward', 'Mod Spell Damage from Healing', 'Mod Faction', 'Comprehend Language',
'Mod Aura Duration By Dispel', 'Mod Aura Duration By Dispel (not stacking)', 'Clone Caster', 'Mod Combat Result Chance', 'Convert Rune',
/*250+ */ 'Mod Increase Health 2', 'Mod Enemy Dodge', 'Mod Speed Slow All', 'Mod Block Crit Chance', 'Mod Disarm Offhand',
'Mod Mechanic Damage Taken Percent', 'No Reagent Use', 'Mod Target Resist By Spell Class', 'Mod Spell Visual', 'Mod HoT Percent',
'Screen Effect', 'Phase', 'Ability Ignore Aurastate', 'Allow Only Ability', null,
null, null, 'Mod Immune Aura Apply School', 'Mod Attack Power Of Stat Percent', 'Mod Ignore Target Resist',
'Mod Ability Ignore Target Resist', 'Mod Damage Taken Percent From Caster', 'Ignore Melee Reset', 'X Ray', 'Ability Consume No Ammo',
/*275+ */ 'Mod Ignore Shapeshift', 'Mod Mechanic Damage Done Percent', 'Mod Max Affected Targets', 'Mod Disarm Ranged', 'Initialize Images',
'Mod Armor Penetration Percent', 'Mod Honor Gain Percent', 'Mod Base Health Percent', 'Mod Healing Received', 'Linked',
'Mod Attack Power Of Armor', 'Ability Periodic Crit', 'Deflect Spells', 'Ignore Hit Direction', null,
'Mod Crit Percent', 'Mod XP Quest Percent', 'Open Stable', 'Override Spells', 'Prevent Power Regeneration',
null, 'Set Vehicle Id', 'Block Spell Family', 'Strangulate', null,
/*300+ */ 'Share Damage Percent', 'School Heal Absorb', null, 'Mod Damage Done Versus Aurastate', 'Mod Fake Inebriate',
'Mod Minimum Speed', null, 'Heal Absorb Test', 'Hunter Trap', null,
'Mod Creature AoE Damage Avoidance', null, null, null, 'Prevent Ressurection',
/* -316*/ 'Underwater Walking', 'Periodic Haste'
)
), ),
'item' => array( 'item' => array(
'notFound' => "Такой предмет не существует.", 'notFound' => "Такой предмет не существует.",
'armor' => "Броня: %s", 'armor' => "Броня: %s",
'block' => "Блок: %s", 'block' => "Блок: %s",
'charges' => "зарядов", 'charges' => "%d |4заряд:заряда:зарядов;",
'locked' => "Заперт", 'locked' => "Заперто",
'ratingString' => "%s&nbsp;@&nbsp;L%s", 'ratingString' => "%s&nbsp;@&nbsp;L%s",
'heroic' => "Героический", 'heroic' => "Героический",
'unique' => "Уникальный",
'uniqueEquipped'=> "Не более 1 в вооружении",
'startQuest' => "Этот предмет позволяет получить задание.", 'startQuest' => "Этот предмет позволяет получить задание.",
'bagSlotString' => "%s (ячеек: %d)", 'bagSlotString' => '%2$s (%1$d |4ячейка:ячейки:ячеек;)',
'dps' => "ед. урона в секунду",
'dps2' => "урон в секунду",
'addsDps' => "Добавляет",
'fap' => "Сила атаки зверя", 'fap' => "Сила атаки зверя",
'durability' => "Прочность:", 'durability' => "Прочность: %d / %d",
'realTime' => "реальное время", 'realTime' => "реальное время",
'conjured' => "Сотворенный предмет", 'conjured' => "Сотворенный предмет",
'damagePhys' => "Урон: %s",
'damageMagic' => "Урон: %s (%s)",
'speed' => "Скорость",
'sellPrice' => "Цена продажи", 'sellPrice' => "Цена продажи",
'itemLevel' => "Уровень предмета:", 'itemLevel' => "Уровень предмета: %d",
'randEnchant' => "&lt;Случайное зачарование&gt", 'randEnchant' => "&lt;Случайное зачарование&gt",
'readClick' => "&lt;Щелкните правой кнопкой мыши, чтобы прочитать.&gt", 'readClick' => "&lt;Щелкните правой кнопкой мыши, чтобы прочитать.&gt",
'openClick' => "&lt;Щелкните правой кнопкой мыши, чтобы открыть.&gt", 'openClick' => "&lt;Щелкните правой кнопкой мыши, чтобы открыть.&gt",
'set' => "Набор", 'setBonus' => "Комплект (%d |4предмет:предмета:предметов;): %s",
'setName' => "%s (%d/%d)",
'partyLoot' => "Добыча группы", 'partyLoot' => "Добыча группы",
'smartLoot' => "Умное распределение добычи", 'smartLoot' => "Умное распределение добычи",
'indestructible'=> "Невозможно выбросить", 'indestructible'=> "Невозможно выбросить",
@@ -902,6 +1082,18 @@ $lang = array(
'buyout' => "Цена выкупа", 'buyout' => "Цена выкупа",
'each' => "каждый", 'each' => "каждый",
'tabOther' => "Другое", 'tabOther' => "Другое",
'reqMinLevel' => "Требуется уровень: %d",
'reqLevelRange' => "Требуемый уровень: %d %d (%d)",
'unique' => ["Уникальный", "Уникальный (%d)", "Уникальный: %s (%d)" ],
'uniqueEquipped'=> ["Уникальный использующийся", null, "Уникальный использующийся предмет: %s (%d)"],
'speed' => "Скорость",
'dps' => "(%.1f ед. урона в секунду)",
'damage' => array( // *DAMAGE_TEMPLATE*
// basic, basic /w school, add basic, add basic /w school
'single' => ["Урон: %d", "%d ед. |3-6(%s)", "+ %d ед. урона", "+%d ед. урона (%s)" ],
'range' => ["Урон: %d - %d", "%d - %d ед. |3-6(%s)", "+ %d - %d ед. урона", "+%d - %d ед. урона (%s)" ],
'ammo' => ["Добавляет %g ед. урона в секунду", "Добавляет %g ед. урона (%s) в секунду", "+ ед. урона в секунду от боеприпасов (%g)", "+ %g %s ед. урона в секунду" ]
),
'gems' => "Самоцветы", 'gems' => "Самоцветы",
'socketBonus' => "При соответствии цвета", 'socketBonus' => "При соответствии цвета",
'socket' => array( 'socket' => array(
@@ -911,14 +1103,14 @@ $lang = array(
"Особый", "Красный", "Желтый", "Синий" "Особый", "Красный", "Желтый", "Синий"
), ),
'gemConditions' => array( // ENCHANT_CONDITION_* so whats that pipe-code..? 'gemConditions' => array( // ENCHANT_CONDITION_* so whats that pipe-code..?
2 => ["меньше, чем %d камень %s цвета", "меньше, чем %d камня %s цвета"], // меньше, чем %d |4камень:камня:камней; |3-1(%s) цвета 2 => "меньше, чем %d |4камень:камня:камней; |3-1(%s) цвета",
3 => "больше %s, чем %s камней", // больше |3-7(%s), чем |3-7(%s) камней 3 => "больше |3-7(%s), чем |3-7(%s) камней",
5 => ["хотя бы %d камень %s цвета", "хотя бы %d камня %s цвета"] // хотя бы %d |4камень:камня:камней; |3-1(%s) цвета; same here 5 => "хотя бы %d |4камень:камня:камней; |3-1(%s) цвета"
), ),
'reqRating' => array( // ITEM_REQ_ARENA_RATING* 'reqRating' => array( // ITEM_REQ_ARENA_RATING*
"Требуется личный и командный рейтинг на арене не ниже %d", "Требуется личный и командный рейтинг на арене не ниже %d",
"Требуется личный рейтинг и рейтинг команды Арены %d<br>в команде 3 на 3 или 5 на 5", "Требуется личный рейтинг и рейтинг команды Арены %d|nв команде 3 на 3 или 5 на 5",
"Требуется личный рейтинг и рейтинг команды Арены %d<br>в команде 5 на 5" "Требуется личный рейтинг и рейтинг команды Арены %d|nв команде 5 на 5"
), ),
'quality' => array( 'quality' => array(
"Низкий", "Обычный", "Необычный", "Редкий", "Низкий", "Обычный", "Необычный", "Редкий",
@@ -926,7 +1118,7 @@ $lang = array(
), ),
'trigger' => array( 'trigger' => array(
"Использование: ", "Если на персонаже: ", "Возможный эффект при попадании: ", "Использование: ", "Если на персонаже: ", "Возможный эффект при попадании: ",
null, null, null, null "", "", "", ""
), ),
'bonding' => array( 'bonding' => array(
"Привязано к учетной записи", "Персональный при поднятии", "Становится персональным при надевании", "Привязано к учетной записи", "Персональный при поднятии", "Становится персональным при надевании",
@@ -1005,8 +1197,8 @@ $lang = array(
13 => "Ключи", 13 => "Ключи",
), ),
'statType' => array( 'statType' => array(
"Увеличение запаса маны на %d ед.", "к мане",
"Увеличение максимального запаса здоровья на %d ед.", "к здоровью",
null, null,
"к ловкости", "к ловкости",
"к силе", "к силе",

View File

@@ -73,8 +73,13 @@ class AccountPage extends GenericPage
switch ($this->category[0]) switch ($this->category[0])
{ {
case 'forgotpassword': case 'forgotpassword':
if (CFG_AUTH_MODE != AUTH_MODE_SELF) // only recover own accounts if (CFG_ACC_AUTH_MODE != AUTH_MODE_SELF)
$this->error(); {
if (CFG_ACC_EXT_RECOVER_URL)
header('Location: '.CFG_ACC_EXT_RECOVER_URL, true, 302);
else
$this->error();
}
$this->tpl = 'acc-recover'; $this->tpl = 'acc-recover';
$this->resetPass = false; $this->resetPass = false;
@@ -85,8 +90,13 @@ class AccountPage extends GenericPage
$this->head = sprintf(Lang::account('recoverPass'), $nStep); $this->head = sprintf(Lang::account('recoverPass'), $nStep);
break; break;
case 'forgotusername': case 'forgotusername':
if (CFG_AUTH_MODE != AUTH_MODE_SELF) // only recover own accounts if (CFG_ACC_AUTH_MODE != AUTH_MODE_SELF)
$this->error(); {
if (CFG_ACC_EXT_RECOVER_URL)
header('Location: '.CFG_ACC_EXT_RECOVER_URL, true, 302);
else
$this->error();
}
$this->tpl = 'acc-recover'; $this->tpl = 'acc-recover';
$this->resetPass = false; $this->resetPass = false;
@@ -123,9 +133,17 @@ class AccountPage extends GenericPage
break; break;
case 'signup': case 'signup':
if (!CFG_ALLOW_REGISTER || CFG_AUTH_MODE != AUTH_MODE_SELF) if (!CFG_ACC_ALLOW_REGISTER)
$this->error(); $this->error();
if (CFG_ACC_AUTH_MODE != AUTH_MODE_SELF)
{
if (CFG_ACC_EXT_CREATE_URL)
header('Location: '.CFG_ACC_EXT_CREATE_URL, true, 302);
else
$this->error();
}
$this->tpl = 'acc-signUp'; $this->tpl = 'acc-signUp';
$nStep = 1; $nStep = 1;
if ($this->_post['username'] || $this->_post['password'] || $this->_post['c_password'] || $this->_post['email']) if ($this->_post['username'] || $this->_post['password'] || $this->_post['c_password'] || $this->_post['email'])
@@ -141,10 +159,8 @@ class AccountPage extends GenericPage
else if (!empty($_GET['token']) && ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE status = ?d AND token = ?', ACC_STATUS_NEW, $_GET['token']))) else if (!empty($_GET['token']) && ($newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE status = ?d AND token = ?', ACC_STATUS_NEW, $_GET['token'])))
{ {
$nStep = 2; $nStep = 2;
DB::Aowow()->query('UPDATE ?_account SET status = ?d WHERE token = ?', ACC_STATUS_OK, $_GET['token']); DB::Aowow()->query('UPDATE ?_account SET status = ?d, statusTimer = 0, token = 0, userGroups = ?d WHERE token = ?', ACC_STATUS_OK, U_GROUP_NONE, $_GET['token']);
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, ?d + 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_FAILED_AUTH_COUNT, CFG_FAILED_AUTH_EXCLUSION); DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, ?d + 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_ACC_FAILED_AUTH_COUNT, CFG_ACC_FAILED_AUTH_BLOCK);
Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
$this->text = sprintf(Lang::account('accActivated'), $_GET['token']); $this->text = sprintf(Lang::account('accActivated'), $_GET['token']);
} }
@@ -218,7 +234,6 @@ class AccountPage extends GenericPage
/* Listview */ /* Listview */
/************/ /************/
$this->lvTabs = [];
$this->forceTabs = true; $this->forceTabs = true;
// Reputation changelog (params only for comment-events) // Reputation changelog (params only for comment-events)
@@ -227,11 +242,7 @@ class AccountPage extends GenericPage
foreach ($repData as &$r) foreach ($repData as &$r)
$r['when'] = date(Util::$dateFormatInternal, $r['when']); $r['when'] = date(Util::$dateFormatInternal, $r['when']);
$this->lvTabs[] = array( $this->lvTabs[] = ['reputationhistory', ['data' => $repData]];
'file' => 'reputationhistory',
'data' => $repData,
'params' => []
);
} }
// comments // comments
@@ -241,14 +252,11 @@ class AccountPage extends GenericPage
// _totalCount: 377, // _totalCount: 377,
// note: $WH.sprintf(LANG.lvnote_usercomments, 377), // note: $WH.sprintf(LANG.lvnote_usercomments, 377),
$this->lvTabs[] = array( $this->lvTabs[] = ['commentpreview', array(
'file' => 'commentpreview', 'data' => $_,
'data' => $_, 'hiddenCols' => ['author'],
'params' => array( 'onBeforeCreate' => '$Listview.funcBox.beforeUserComments'
'hiddenCols' => "$['author']", )];
'onBeforeCreate' => '$Listview.funcBox.beforeUserComments'
)
);
} }
// replies // replies
@@ -259,13 +267,10 @@ class AccountPage extends GenericPage
// _totalCount: 377, // _totalCount: 377,
// note: $WH.sprintf(LANG.lvnote_usercomments, 377), // note: $WH.sprintf(LANG.lvnote_usercomments, 377),
$this->lvTabs[] = array( $this->lvTabs[] = ['replypreview', array(
'file' => 'replypreview', 'data' => $_,
'data' => $_, 'hiddenCols' => ['author']
'params' => array( )];
'hiddenCols' => "$['author']"
)
);
} }
/* /*
@@ -346,9 +351,10 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
return Lang::main('intError'); return Lang::main('intError');
// reset account status, update expiration // reset account status, update expiration
DB::Aowow()->query('UPDATE ?_account SET prevIP = IF(curIp = ?, prevIP, curIP), curIP = IF(curIp = ?, curIP, ?), allowExpire = ?d, status = 0, statusTimer = 0, token = "" WHERE user = ?', DB::Aowow()->query('UPDATE ?_account SET prevIP = IF(curIp = ?, prevIP, curIP), curIP = IF(curIp = ?, curIP, ?), allowExpire = ?d, status = IF(status = ?d, status, 0), statusTimer = IF(status = ?d, statusTimer, 0), token = IF(status = ?d, token, "") WHERE user = ?',
User::$ip, User::$ip, User::$ip, User::$ip, User::$ip, User::$ip,
$this->_post['remember_me'] != 'yes', $this->_post['remember_me'] != 'yes',
ACC_STATUS_NEW, ACC_STATUS_NEW, ACC_STATUS_NEW,
$this->_post['username'] $this->_post['username']
); );
@@ -366,12 +372,9 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
case AUTH_WRONGPASS: case AUTH_WRONGPASS:
User::destroy(); User::destroy();
return Lang::account('wrongPass'); return Lang::account('wrongPass');
case AUTH_ACC_INACTIVE:
User::destroy();
return Lang::account('accInactive');
case AUTH_IPBANNED: case AUTH_IPBANNED:
User::destroy(); User::destroy();
return sprintf(Lang::account('loginExceeded'), Util::formatTime(CFG_FAILED_AUTH_EXCLUSION * 1000)); return sprintf(Lang::account('loginExceeded'), Util::formatTime(CFG_ACC_FAILED_AUTH_BLOCK * 1000));
case AUTH_INTERNAL_ERR: case AUTH_INTERNAL_ERR:
User::destroy(); User::destroy();
return Lang::main('intError'); return Lang::main('intError');
@@ -403,10 +406,10 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
// limit account creation // limit account creation
$ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 1 AND ip = ?', User::$ip); $ip = DB::Aowow()->selectRow('SELECT ip, count, unbanDate FROM ?_account_bannedips WHERE type = 1 AND ip = ?', User::$ip);
if ($ip && $ip['count'] >= CFG_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time()) if ($ip && $ip['count'] >= CFG_ACC_FAILED_AUTH_COUNT && $ip['unbanDate'] >= time())
{ {
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_FAILED_AUTH_EXCLUSION, User::$ip); DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_ACC_FAILED_AUTH_BLOCK, User::$ip);
return sprintf(Lang::account('signupExceeded'), Util::formatTime(CFG_FAILED_AUTH_EXCLUSION * 1000)); return sprintf(Lang::account('signupExceeded'), Util::formatTime(CFG_ACC_FAILED_AUTH_BLOCK * 1000));
} }
// username taken // username taken
@@ -415,7 +418,7 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
// create.. // create..
$token = Util::createHash(); $token = Util::createHash();
$id = DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, email, joindate, curIP, allowExpire, locale, status, statusTimer, token) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)', $ok = DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, email, joindate, curIP, allowExpire, locale, userGroups, status, statusTimer, token) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), ?, ?d, ?d, ?d, ?d, UNIX_TIMESTAMP() + ?d, ?)',
$this->_post['username'], $this->_post['username'],
User::hashCrypt($this->_post['password']), User::hashCrypt($this->_post['password']),
Util::ucFirst($this->_post['username']), Util::ucFirst($this->_post['username']),
@@ -423,19 +426,23 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
User::$ip, User::$ip,
$this->_post['remember_me'] != 'yes', $this->_post['remember_me'] != 'yes',
User::$localeId, User::$localeId,
U_GROUP_PENDING,
ACC_STATUS_NEW, ACC_STATUS_NEW,
CFG_ACCOUNT_CREATE_SAVE_DECAY, CFG_ACC_CREATE_SAVE_DECAY,
$token $token
); );
if (!$id) // something went wrong if (!$ok)
return Lang::main('intError'); return Lang::main('intError');
else if ($_ = $this->sendMail(Lang::mail('accConfirm', 0), sprintf(Lang::mail('accConfirm', 1), $token), CFG_ACCOUNT_CREATE_SAVE_DECAY)) else if ($_ = $this->sendMail(Lang::mail('accConfirm', 0), sprintf(Lang::mail('accConfirm', 1), $token), CFG_ACC_CREATE_SAVE_DECAY))
{ {
if ($id = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE token = ?', $token))
Util::gainSiteReputation($id, SITEREP_ACTION_REGISTER);
// success:: update ip-bans // success:: update ip-bans
if (!$ip || $ip['unbanDate'] < time()) if (!$ip || $ip['unbanDate'] < time())
DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_FAILED_AUTH_EXCLUSION); DB::Aowow()->query('REPLACE INTO ?_account_bannedips (ip, type, count, unbanDate) VALUES (?, 1, 1, UNIX_TIMESTAMP() + ?d)', User::$ip, CFG_ACC_FAILED_AUTH_BLOCK);
else else
DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_FAILED_AUTH_EXCLUSION, User::$ip); DB::Aowow()->query('UPDATE ?_account_bannedips SET count = count + 1, unbanDate = UNIX_TIMESTAMP() + ?d WHERE ip = ? AND type = 1', CFG_ACC_FAILED_AUTH_BLOCK, User::$ip);
return $_; return $_;
} }
@@ -443,11 +450,11 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
private function doRecoverPass() private function doRecoverPass()
{ {
if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_PASS, CFG_ACCOUNT_RECOVERY_DECAY, $token)) if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_PASS, CFG_ACC_RECOVERY_DECAY, $token))
return $_; return $_;
// send recovery mail // send recovery mail
return $this->sendMail(Lang::mail('resetPass', 0), sprintf(Lang::mail('resetPass', 1), $token), CFG_ACCOUNT_RECOVERY_DECAY); return $this->sendMail(Lang::mail('resetPass', 0), sprintf(Lang::mail('resetPass', 1), $token), CFG_ACC_RECOVERY_DECAY);
} }
private function doResetPass() private function doResetPass()
@@ -475,11 +482,11 @@ Markup.printHtml("description text here", "description-generic", { allow: Markup
private function doRecoverUser() private function doRecoverUser()
{ {
if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_USER, CFG_ACCOUNT_RECOVERY_DECAY, $token)) if ($_ = $this->initRecovery(ACC_STATUS_RECOVER_USER, CFG_ACC_RECOVERY_DECAY, $token))
return $_; return $_;
// send recovery mail // send recovery mail
return $this->sendMail(Lang::mail('recoverUser', 0), sprintf(Lang::mail('recoverUser', 1), $token), CFG_ACCOUNT_RECOVERY_DECAY); return $this->sendMail(Lang::mail('recoverUser', 0), sprintf(Lang::mail('recoverUser', 1), $token), CFG_ACC_RECOVERY_DECAY);
} }
private function initRecovery($type, $delay, &$token) private function initRecovery($type, $delay, &$token)

View File

@@ -103,14 +103,9 @@ class AchievementPage extends GenericPage
if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH)) if ($this->subject->getField('flags') & 0x100 && DB::isConnectable(DB_AUTH))
{ {
$avlb = []; $avlb = [];
foreach (DB::Auth()->selectCol('SELECT id AS ARRAY_KEY, name FROM realmlist WHERE allowedSecurityLevel = 0 AND gamebuild = ?d', WOW_VERSION) AS $rId => $name) foreach (Profiler::getRealms() AS $rId => $rData)
{
if (!DB::isConnectable(DB_CHARACTERS . $rId))
continue;
if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId)) if (!DB::Characters($rId)->selectCell('SELECT 1 FROM character_achievement WHERE achievement = ?d LIMIT 1', $this->typeId))
$avlb[] = $name; $avlb[] = Util::ucWords($rData['name']);
}
if ($avlb) if ($avlb)
$infobox[] = Lang::achievement('rfAvailable').implode(', ', $avlb); $infobox[] = Lang::achievement('rfAvailable').implode(', ', $avlb);
@@ -151,8 +146,14 @@ class AchievementPage extends GenericPage
$this->series = $series ? [[$series, null]] : null; $this->series = $series ? [[$series, null]] : null;
$this->description = $this->subject->getField('description', true); $this->description = $this->subject->getField('description', true);
$this->redButtons = array( $this->redButtons = array(
BUTTON_LINKS => ['color' => 'ffffff00', 'linkId' => Util::$typeStrings[TYPE_ACHIEVEMENT].':'.$this->typeId.':&quot;..UnitGUID(&quot;player&quot;)..&quot;:0:0:0:0:0:0:0:0'], BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE),
BUTTON_WOWHEAD => !($this->subject->getField('cuFlags') & CUSTOM_SERVERSIDE) BUTTON_LINKS => array(
'linkColor' => 'ffffff00',
'linkId' => Util::$typeStrings[TYPE_ACHIEVEMENT].':'.$this->typeId.':&quot;..UnitGUID(&quot;player&quot;)..&quot;:0:0:0:0:0:0:0:0',
'linkName' => $this->name,
'type' => $this->type,
'typeId' => $this->typeId
)
); );
$this->criteria = array( $this->criteria = array(
'reqQty' => $this->subject->getField('reqCriteriaCount'), 'reqQty' => $this->subject->getField('reqCriteriaCount'),
@@ -224,15 +225,12 @@ class AchievementPage extends GenericPage
['id', $this->typeId, '!'] ['id', $this->typeId, '!']
); );
$saList = new AchievementList($conditions); $saList = new AchievementList($conditions);
$this->lvTabs[] = array( $this->lvTabs[] = ['achievement', array(
'file' => 'achievement', 'data' => array_values($saList->getListviewData()),
'data' => $saList->getListviewData(), 'id' => 'see-also',
'params' => array( 'name' => '$LANG.tab_seealso',
'id' => 'see-also', 'visibleCols' => ['category']
'name' => '$LANG.tab_seealso', )];
'visibleCols' => "$['category']"
)
);
$this->extendGlobalData($saList->getJSGlobals()); $this->extendGlobalData($saList->getJSGlobals());
// tab: criteria of // tab: criteria of
@@ -243,15 +241,12 @@ class AchievementPage extends GenericPage
if (!empty($refs)) if (!empty($refs))
{ {
$coList = new AchievementList(array(['id', $refs])); $coList = new AchievementList(array(['id', $refs]));
$this->lvTabs[] = array( $this->lvTabs[] = ['achievement', array(
'file' => 'achievement', 'data' => array_values($coList->getListviewData()),
'data' => $coList->getListviewData(), 'id' => 'criteria-of',
'params' => array( 'name' => '$LANG.tab_criteriaof',
'id' => 'criteria-of', 'visibleCols' => ['category']
'name' => '$LANG.tab_criteriaof', )];
'visibleCols' => "$['category']"
)
);
$this->extendGlobalData($coList->getJSGlobals()); $this->extendGlobalData($coList->getJSGlobals());
} }
@@ -261,6 +256,12 @@ class AchievementPage extends GenericPage
$iconId = 1; $iconId = 1;
$rightCol = []; $rightCol = [];
$scripts = [];
// serverside extra-Data
$crtIds = array_column($this->subject->getCriteria(), 'id');
Util::checkNumeric($crtIds);
$crtExtraData = DB::World()->select('SELECT criteria_id AS ARRAY_KEY, type AS ARRAY_KEY2, value1, value2, ScriptName FROM achievement_criteria_data WHERE criteria_id IN (?a)', $crtIds);
foreach ($this->subject->getCriteria() as $i => $crt) foreach ($this->subject->getCriteria() as $i => $crt)
{ {
@@ -418,11 +419,84 @@ class AchievementPage extends GenericPage
'text' => $crtName, 'text' => $crtName,
); );
break; break;
// link to emote
case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE:
$tmp['link'] = array(
'href' => '?emote='.$obj,
'text' => $crtName,
);
break;
default: default:
// Add a gold coin icon if required // Add a gold coin icon if required
$tmp['extraText'] = $displayMoney ? Util::formatMoney($qty) : $crtName; $tmp['extraText'] = $displayMoney ? Util::formatMoney($qty) : $crtName;
break; break;
} }
if (!empty($crtExtraData[$crt['id']]))
{
$tmp['extraData'] = [];
foreach ($crtExtraData[$crt['id']] as $xType => $xData)
{
// just pick stuff, that can actually be linked
switch ($xType)
{
case 1: // TYPE_T_CREATURE
$tmp['extraData'][] = ['?npc='.$xData['value1'], CreatureList::getName($xData['value1'])];
break;
case 2: // TYPE_T_PLAYER_CLASS_RACE
case 21: // TYPE_S_PLAYER_CLASS_RACE
if ($xData['value1'])
$tmp['extraData'][] = ['?class='.$xData['value1'], (new CharClassList(array(['id', $xData['value1']])))->getField('name', true)];
if ($xData['value2'])
$tmp['extraData'][] = ['?race='.$xData['value2'], (new CharRaceList(array(['id', $xData['value2']])))->getField('name', true)];
break;
// case 3: // TYPE_T_PLAYER_LESS_HEALTH
// case 4: // TYPE_T_PLAYER_DEAD
case 5: // TYPE_S_AURA
case 7: // TYPE_T_AURA
$tmp['extraData'][] = ['?spell='.$xData['value1'], SpellList::getName($xData['value1'])];
break;
case 6: // TYPE_S_AREA
$tmp['extraData'][] = ['?zone='.$xData['value1'], ZoneList::getName($xData['value1'])];
break;
// case 8: // TYPE_VALUE
// case 9: // TYPE_T_LEVEL
// case 10: // TYPE_T_GENDER
case 11: // TYPE_SCRIPT
if ($xData['ScriptName'])
$scripts[] = $xData['ScriptName'];
break;
// case 12: // TYPE_MAP_DIFFICULTY
// case 13: // TYPE_MAP_PLAYER_COUNT
// case 14: // TYPE_T_TEAM
// case 15: // TYPE_S_DRUNK
case 16: // TYPE_HOLIDAY
if ($we = new WorldEventList(array(['holidayId', $xData['value1']])))
$tmp['extraData'][] = ['?event='.$we->id, $we->getField('name', true)];
break;
// case 17: // TYPE_BG_LOSS_TEAM_SCORE
// case 18: // TYPE_INSTANCE_SCRIPT
// case 19: // TYPE_S_EQUIPED_ITEM
case 20: // TYPE_MAP_ID
if ($z = new ZoneList(array(['mapIdBak', $xData['value1']])))
$tmp['extraData'][] = ['?zone='.$z->id, $z->getField('name', true)];
break;
// case 22: // TYPE_NTH_BIRTHDAY
case 23: // TYPE_S_KNOWN_TITLE
$tmp['extraData'][] = ['?title='.$xData['value1'], trim(str_replace('%s', '', (new TitleList(array(['id', $xData['value1']])))->getField('male', true)))];
break;
}
// moar stuffz
}
}
// If the right column // If the right column
if ($i % 2) if ($i % 2)
$this->criteria['data'][] = $tmp; $this->criteria['data'][] = $tmp;
@@ -433,6 +507,13 @@ class AchievementPage extends GenericPage
// If you found the second column - merge data from it to the end of the main body // If you found the second column - merge data from it to the end of the main body
if ($rightCol) if ($rightCol)
$this->criteria['data'] = array_merge($this->criteria['data'], $rightCol); $this->criteria['data'] = array_merge($this->criteria['data'], $rightCol);
// criteria have scripts
if (User::isInGroup(U_GROUP_EMPLOYEE) && $scripts)
{
$s = '[li]Script'.Lang::main('colon').'[ul][li]'.implode('[/li][li]', array_unique($scripts)).'[/li][/ul][/li]';
$this->infobox = substr_replace($this->infobox, $s, -5, 0);
}
} }
protected function generateTooltip($asError = false) protected function generateTooltip($asError = false)
@@ -442,7 +523,7 @@ class AchievementPage extends GenericPage
$x = '$WowheadPower.registerAchievement('.$this->typeId.', '.User::$localeId.",{\n"; $x = '$WowheadPower.registerAchievement('.$this->typeId.', '.User::$localeId.",{\n";
$x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n"; $x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n";
$x .= "\ticon: '".urlencode($this->subject->getField('iconString'))."',\n"; $x .= "\ticon: '".rawurlencode($this->subject->getField('iconString', true, true))."',\n";
$x .= "\ttooltip_".User::$localeString.": '".$this->subject->renderTooltip()."'\n"; $x .= "\ttooltip_".User::$localeString.": '".$this->subject->renderTooltip()."'\n";
$x .= "});"; $x .= "});";
@@ -464,10 +545,10 @@ class AchievementPage extends GenericPage
die($tt); die($tt);
} }
public function notFound() public function notFound($title = '', $msg = '')
{ {
if ($this->mode != CACHE_TYPE_TOOLTIP) if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::notFound(Lang::game('achievement'), Lang::achievement('notFound')); return parent::notFound($title ?: Lang::game('achievement'), $msg ?: Lang::achievement('notFound'));
header('Content-type: application/x-javascript; charset=utf-8'); header('Content-type: application/x-javascript; charset=utf-8');
echo $this->generateTooltip(true); echo $this->generateTooltip(true);

View File

@@ -42,8 +42,8 @@ class AchievementsPage extends GenericPage
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
{ {
$this->filterObj = new AchievementListFilter();
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($pageParam);
$this->filterObj = new AchievementListFilter(false, $this->category);
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
@@ -63,9 +63,12 @@ class AchievementsPage extends GenericPage
$conditions[] = ['category', (int)end($this->category)]; $conditions[] = ['category', (int)end($this->category)];
// recreate form selection // recreate form selection
$this->filter = $this->filterObj->getForm('form'); $this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['fi'] = $this->filterObj->getForm(); $this->filter['initData'] = ['init' => 'achievements'];
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
if ($fiCnd = $this->filterObj->getConditions()) if ($fiCnd = $this->filterObj->getConditions())
$conditions[] = $fiCnd; $conditions[] = $fiCnd;
@@ -88,37 +91,33 @@ class AchievementsPage extends GenericPage
$acvList = new AchievementList($conditions); $acvList = new AchievementList($conditions);
} }
$params = $data = []; $tabData = [];
if (!$acvList->error) if (!$acvList->error)
{ {
$data = $acvList->getListviewData(); $tabData['data'] = array_values($acvList->getListviewData());
// fill g_items, g_titles, g_achievements // fill g_items, g_titles, g_achievements
$this->extendGlobalData($acvList->getJSGlobals()); $this->extendGlobalData($acvList->getJSGlobals());
// if we are have different cats display field // if we are have different cats display field
if ($acvList->hasDiffFields(['category'])) if ($acvList->hasDiffFields(['category']))
$params['visibleCols'] = "$['category']"; $tabData['visibleCols'] = ['category'];
if (!empty($this->filter['fi']['extraCols'])) if (!empty($this->filter['fi']['extraCols']))
$params['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
// create note if search limit was exceeded // create note if search limit was exceeded
if ($acvList->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($acvList->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{ {
$params['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), CFG_SQL_LIMIT_DEFAULT); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_achievementsfound', $acvList->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$params['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
if ($this->filterObj->error) if ($this->filterObj->error)
$params['_errors'] = '$1'; $tabData['_errors'] = 1;
} }
$this->lvTabs[] = array( $this->lvTabs[] = ['achievement', $tabData];
'file' => 'achievement',
'data' => $data,
'params' => $params
);
// sort for dropdown-menus in filter // sort for dropdown-menus in filter
Lang::sort('game', 'si'); Lang::sort('game', 'si');

View File

@@ -20,7 +20,7 @@ class AdminPage extends GenericPage
switch ($pageParam) switch ($pageParam)
{ {
case 'screenshots': case 'screenshots':
$this->reqUGroup = U_GROUP_STAFF | U_GROUP_SCREENSHOT; $this->reqUGroup = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_SCREENSHOT;
$this->generator = 'handleScreenshots'; $this->generator = 'handleScreenshots';
$this->tpl = 'admin/screenshots'; $this->tpl = 'admin/screenshots';
@@ -38,11 +38,19 @@ class AdminPage extends GenericPage
case 'siteconfig': case 'siteconfig':
$this->reqUGroup = U_GROUP_ADMIN | U_GROUP_DEV; $this->reqUGroup = U_GROUP_ADMIN | U_GROUP_DEV;
$this->generator = 'handleConfig'; $this->generator = 'handleConfig';
$this->tpl = 'list-page-generic'; $this->tpl = 'admin/siteconfig';
array_push($this->path, 2, 18); array_push($this->path, 2, 18);
$this->name = 'Site Configuration'; $this->name = 'Site Configuration';
break; break;
case 'weight-presets':
$this->reqUGroup = U_GROUP_ADMIN | U_GROUP_DEV | U_GROUP_BUREAU;
$this->generator = 'handleWeightPresets';
$this->tpl = 'admin/weight-presets';
array_push($this->path, 2, 16);
$this->name = 'Weight Presets';
break;
default: // error out through unset template default: // error out through unset template
} }
@@ -60,321 +68,51 @@ class AdminPage extends GenericPage
private function handleConfig() private function handleConfig()
{ {
$this->addCSS(array( $this->addCSS(array(
['string' => '.grid input[type=\'text\'] { width:250px; }'], ['string' => '.grid input[type=\'text\'], .grid input[type=\'number\'] { width:250px; text-align:left; }'],
['string' => '.grid input[type=\'button\'] { width:65px; padding:2px; }'], ['string' => '.grid input[type=\'button\'] { width:65px; padding:2px; }'],
['string' => '.disabled { opacity:0.4 !important; }'],
['string' => '.grid a.tip { margin:0px 5px; opacity:0.8; }'], ['string' => '.grid a.tip { margin:0px 5px; opacity:0.8; }'],
['string' => '.grid a.tip:hover { opacity:1; }'], ['string' => '.grid a.tip:hover { opacity:1; }'],
['string' => '.status { position:absolute; right:5px; }'], ['string' => '.grid tr { height:30px; }'],
['string' => '.grid .disabled { opacity:0.4 !important; }'],
['string' => '.grid .status { position:absolute; right:5px; }'],
)); ));
// well .. fuck!
ob_start();
?>
<script type="text/javascript">
function createStatusIcon(errTxt)
{
function fadeout()
{
$(this).animate({ opacity: '0.0' }, 250, null, function() {
$WH.de(this);
$WH.Tooltip.hide()
});
}
var a = $WH.ce('a');
a.style.opacity = 0;
a.className = errTxt ? 'icon-report' : 'icon-tick';
g_addTooltip(a, errTxt || 'success', 'q');
a.onclick = fadeout.bind(a);
setTimeout(function () { $(a).animate({ opacity: '1.0' }, 250); }, 50);
setTimeout(fadeout.bind(a), 10000);
return a;
}
function cfg_add(el)
{
_self = el.parentNode.parentNode;
var tr = $WH.ce('tr');
tr.style.position = 'relative';
var td = $WH.ce('td'),
key = $WH.ce('input');
key.type = 'text';
key.name = 'key';
$WH.ae(td, key);
$WH.ae(tr, td);
var td = $WH.ce('td'),
val = $WH.ce('input');
val.type = 'text';
val.name = 'value';
$WH.ae(td, val);
$WH.ae(tr, td);
var td = $WH.ce('td'),
aCancel = $WH.ce('a'),
aSubmit = $WH.ce('a'),
status = $WH.ce('span');
aSubmit.className = 'icon-save tip';
g_addTooltip(aSubmit, 'Submit Setting', 'q');
aCancel.className = 'icon-delete tip';
g_addTooltip(aCancel, 'Cancel', 'q');
aSubmit.onclick = cfg_new.bind(aSubmit, key, val);
aCancel.onclick = function () {
$WH.Tooltip.hide();
$WH.de(this.parentNode.parentNode);
};
status.className = 'status';
$WH.ae(td, aSubmit);
$WH.ae(td, $WH.ct('|'));
$WH.ae(td, aCancel);
$WH.ae(td, status);
$WH.ae(tr, td);
_self.parentNode.insertBefore(tr, _self);
key.focus();
}
function cfg_new(elKey, elVal)
{
var
_td = this.parentNode,
_row = this.parentNode.parentNode,
_status = $(_td).find('.status')[0];
// already performing action
if (_status.lastChild && _status.lastChild.tagName == 'IMG')
return;
else if (_status.lastChild && _status.lastChild.tagName == 'A')
$WH.ee(_status);
if (!elKey.value || !elVal.value)
{
$WH.ae(_status, createStatusIcon('key or value are empty'));
return;
}
var
key = elKey.value.toLowerCase().trim(),
value = elVal.value.trim();
$(_status).append(CreateAjaxLoader());
new Ajax('?admin=siteconfig&action=add&id=' + key + '&val=' + value, {
method: 'get',
onSuccess: function(xhr) {
$WH.ee(_status);
if (!xhr.responseText) {
$WH.ee(_row);
$(_row).append($('<td>' + key + '</td>')).append($('<td><input id="' + key + '" type="text" name="' + key + '" value="' + value + '" /></td>'));
var
td = $WH.ce('td'),
a = $WH.ce('a'),
sp = $WH.ce('span');
g_addTooltip(a, 'Save Changes', 'q');
a.onclick = cfg_submit.bind(a, key);
a.className = 'icon-save tip';
$WH.ae(td, a);
a = $WH.ce('a');
a.className = 'icon-refresh tip disabled';
$WH.ae(td, $WH.ct('|'));
$WH.ae(td, a);
a = $WH.ce('a');
g_addTooltip(a, 'Remove Setting', 'q');
a.onclick = cfg_remove.bind(a, key);
a.className = 'icon-delete tip';
$WH.ae(td, $WH.ct('|'));
$WH.ae(td, a);
sp.className = 'status';
$WH.ae(sp, createStatusIcon());
$WH.ae(td, sp);
$WH.ae(_row, td);
}
else {
$WH.ae(_status, createStatusIcon(xhr.responseText));
}
}
});
}
function cfg_submit(id)
{
var
node = $WH.ge(id),
_td = this.parentNode,
_status = $(_td).find('.status')[0];
if (!node)
return;
var value = 0;
// already performing action
if (_status.lastChild && _status.lastChild.tagName == 'IMG')
return;
else if (_status.lastChild && _status.lastChild.tagName == 'A')
$WH.ee(_status);
if (node.tagName == 'DIV')
{
// bitmask
$(node).find('input[type="checkbox"]').each(function(idx, opt) {
if (opt.checked)
value |= (1 << opt.value);
});
// boolean
$(node).find('input[type="radio"]').each(function(idx, opt) {
if (opt.checked)
value = opt.value;
});
}
else if (node.tagName == 'SELECT') // opt-list
{
$(node).find('option').each(function(idx, opt) {
if (opt.selected)
value = opt.value;
});
}
else if (node.tagName == 'INPUT') // string or numeric
{
if (node.value.search(/[^\d\s\/\*\-\+\.]/i) == -1)
node.value = eval(node.value);
value = node.value;
}
value = value.toString().trim();
if (!value.length)
{
$WH.ae(_status, createStatusIcon('value is empty'));
return;
}
$(_status).append(CreateAjaxLoader());
new Ajax('?admin=siteconfig&action=update&id=' + id + '&val=' + value, {
method: 'get',
onSuccess: function(xhr) {
$WH.ee(_status);
$WH.ae(_status, createStatusIcon(xhr.responseText));
}
});
}
function cfg_default(id, val)
{
var node = $WH.ge(id);
if (!node)
return;
if (node.tagName == 'DIV')
{
// bitmask
$(node).find('input[type="checkbox"]').each(function(idx, opt) { opt.checked = !!(val & (1 << opt.value)); });
// boolean
$(node).find('input[type="radio"]').each(function(idx, opt) { opt.checked = !!opt.value == !!val; });
}
else if (node.tagName == 'SELECT') // opt-list
$(node).find('option').each(function(idx, opt) { opt.selected = opt.value == val; });
else if (node.tagName == 'INPUT') // string or numeric
node.value = val;
}
function cfg_remove(id)
{
var
_td = this.parentNode,
_status = $(_td).find('.status')[0];
// already performing action
if (_status.lastChild && _status.lastChild.tagName == 'IMG')
return;
else if (_status.lastChild && _status.lastChild.tagName == 'A')
$WH.ee(_status);
if (!confirm('Confirm remove'))
return;
$(_status).append(CreateAjaxLoader());
new Ajax('?admin=siteconfig&action=remove&id=' + id, {
method: 'get',
onSuccess: function(xhr) {
if (!xhr.responseText)
$WH.de(_td.parentNode);
else {
$WH.ee(_status);
$WH.ae(_status, createStatusIcon(xhr.responseText));
}
}
});
}
</script>
<?php
$this->extraHTML = ob_get_contents();
ob_end_clean();
// eof (end of fuckup)
$head = '<table class="grid"><tr><th><b>Key</b></th><th><b>Value</b></th><th style="width:150px;"><b>Options</b></th></tr>'; $head = '<table class="grid"><tr><th><b>Key</b></th><th><b>Value</b></th><th style="width:150px;"><b>Options</b></th></tr>';
$mainTab = [];
// for aowow $miscTab = [];
if ($rows = DB::Aowow()->select('SELECT * FROM ?_config WHERE (flags & ?d) = 0 ORDER BY `key` ASC', CON_FLAG_PHP)) foreach (Util::$configCats as $idx => $catName)
{ {
$buff = $head; if ($rows = DB::Aowow()->select('SELECT * FROM ?_config WHERE cat = ?d ORDER BY `flags` DESC, `key` ASC', $idx))
foreach ($rows as $r) {
$buff .= $this->configAddRow($r); $buff = $head;
foreach ($rows as $r)
$buff .= $this->configAddRow($r);
$buff .= '</table>'; if (!$idx) //cat: misc
$buff .= '<tr><td colspan="3"><a class="icon-add" onclick="cfg_add(this)">new configuration</a></td></tr>';
$this->lvTabs[] = array( $buff .= '</table>';
'file' => null,
'data' => $buff, if ($idx)
'params' => array( $mainTab[$catName] = $buff;
'name' => 'Aowow', else
'id' => 'aowow' $miscTab[$catName] = $buff;
) }
);
} }
// for php foreach ($mainTab as $n => $t)
$rows = DB::Aowow()->select('SELECT * FROM ?_config WHERE flags & ?d ORDER BY `key` ASC', CON_FLAG_PHP); $this->lvTabs[] = [null, array(
$buff = $head; 'data' => $t,
foreach ($rows as $r) 'name' => $n,
$buff .= $this->configAddRow($r); 'id' => Profiler::urlize($n)
)];
$buff .= '<tr><td colspan="3"><a class="icon-add" onclick="cfg_add(this)">new configuration</a></td></tr>'; foreach ($miscTab as $n => $t)
$buff .= '</table>'; $this->lvTabs[] = [null, array(
'data' => $t,
$this->lvTabs[] = array( 'name' => $n,
'file' => null, 'id' => Profiler::urlize($n)
'data' => $buff, )];
'params' => array(
'name' => 'PHP',
'id' => 'php'
)
);
} }
private function handlePhpInfo() private function handlePhpInfo()
@@ -423,26 +161,20 @@ class AdminPage extends GenericPage
else else
$name .= $p[0]; $name .= $p[0];
$this->lvTabs[] = array( $this->lvTabs[] = [null, array(
'file' => null, 'data' => $body,
'data' => $body, 'id' => strtolower(strtr($name, [' ' => ''])),
'params' => array( 'name' => $name
'id' => strtolower(strtr($name, [' ' => ''])), )];
'name' => $name
)
);
} }
} }
else else
{ {
$this->lvTabs[] = array( $this->lvTabs[] = [null, array(
'file' => null, 'data' => $buff,
'data' => $buff, 'id' => strtolower($names[$i]),
'params' => array( 'name' => $names[$i]
'id' => strtolower($names[$i]), )];
'name' => $names[$i]
)
);
} }
} }
} }
@@ -468,7 +200,7 @@ class AdminPage extends GenericPage
else if (!empty($_GET['user'])) else if (!empty($_GET['user']))
{ {
$name = urldecode($_GET['user']); $name = urldecode($_GET['user']);
if (strlen($name) > 3) if (mb_strlen($name) >= 3)
{ {
if ($uId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE displayName = ?', ucFirst($name))) if ($uId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE displayName = ?', ucFirst($name)))
{ {
@@ -486,11 +218,38 @@ class AdminPage extends GenericPage
$this->ssNFound = $nMatches; // ssm_numPagesFound $this->ssNFound = $nMatches; // ssm_numPagesFound
} }
private function handleWeightPresets()
{
$this->addCSS(['string' => '.wt-edit {display:inline-block; vertical-align:top; width:350px;}']);
$this->addJS('filters.js');
$head = $body = '';
$scales = DB::Aowow()->select('SELECT class AS ARRAY_KEY, id AS ARRAY_KEY2, name, icon FROM ?_account_weightscales WHERE userId = 0');
$weights = DB::Aowow()->selectCol('SELECT awd.id AS ARRAY_KEY, awd.field AS ARRAY_KEY2, awd.val FROM ?_account_weightscale_data awd JOIN ?_account_weightscales ad ON awd.id = ad.id WHERE ad.userId = 0');
foreach ($scales as $cl => $data)
{
$ul = '';
foreach ($data as $id => $s)
{
$weights[$id]['__icon'] = $s['icon'];
$ul .= '[url=# onclick="loadScale.bind(this, '.$id.')();"]'.$s['name'].'[/url][br]';
}
$head .= '[td=header]'.Lang::game('cl', $cl).'[/td]';
$body .= '[td valign=top]'.$ul.'[/td]';
}
$this->extraText = '[table class=grid][tr]'.$head.'[/tr][tr]'.$body.'[/tr][/table]';
$this->extraHTML = '<script type="text/javascript">var wt_presets = '.Util::toJSON($weights).";</script>\n\n";
}
private function configAddRow($r) private function configAddRow($r)
{ {
$buff = '<tr>'; $buff = '<tr>';
$info = explode(' - ', $r['comment']); $info = explode(' - ', $r['comment']);
$key = $r['flags'] & CON_FLAG_PHP ? strtolower($r['key']) : 'CFG_'.strtoupper($r['key']); $key = $r['flags'] & CON_FLAG_PHP ? strtolower($r['key']) : strtoupper($r['key']);
// name // name
if (!empty($info[1])) if (!empty($info[1]))
@@ -522,7 +281,7 @@ class AdminPage extends GenericPage
$buff .= '</div></td>'; $buff .= '</div></td>';
} }
else else
$buff .= '<td><input id="'.$key.'" type="text" name="'.$key.'" value="'.$r['value'].'" /></td>'; $buff .= '<td><input id="'.$key.'" type="'.($r['flags'] & CON_FLAG_TYPE_STRING ? 'text" placeholder="<empty>' : 'number'.($r['flags'] & CON_FLAG_TYPE_FLOAT ? '" step="any' : '')).'" name="'.$key.'" value="'.$r['value'].'" /></td>';
// actions // actions
$buff .= '<td style="position:relative;">'; $buff .= '<td style="position:relative;">';

139
pages/arenateam.php Normal file
View File

@@ -0,0 +1,139 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 5: Profiler g_initPath()
// tabId 1: Tools g_initHeader()
class ArenaTeamPage extends GenericPage
{
use TrProfiler;
protected $lvTabs = [];
protected $tabId = 1;
protected $path = [1, 5, 3];
protected $tpl = 'roster';
protected $js = ['profile_all.js', 'profile.js'];
protected $css = [['path' => 'Profiler.css']];
public function __construct($pageCall, $pageParam)
{
$params = array_map('urldecode', explode('.', $pageParam));
if ($params[0])
$params[0] = Profiler::urlize($params[0]);
if (isset($params[1]))
$params[1] = Profiler::urlize($params[1]);
parent::__construct($pageCall, $pageParam);
if (count($params) == 1 && intval($params[0]))
{
$this->subject = new LocalArenaTeamList(array(['at.id', intval($params[0])]));
if ($this->subject->error)
$this->notFound();
header('Location: '.$this->subject->getProfileUrl(), true, 302);
}
else if (count($params) == 3)
{
$this->getSubjectFromUrl($pageParam);
if (!$this->subjectName)
$this->notFound();
// 3 possibilities
// 1) already synced to aowow
if ($subject = DB::Aowow()->selectRow('SELECT id, realmGUID, cuFlags FROM ?_profiler_arena_team WHERE realm = ?d AND nameUrl = ?', $this->realmId, Profiler::urlize($this->subjectName)))
{
if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC)
{
$this->handleIncompleteData($subject['realmGUID']);
return;
}
$this->subjectGUID = $subject['id'];
$this->subject = new LocalArenaTeamList(array(['id', $subject['id']]));
if ($this->subject->error)
$this->notFound();
$this->profile = $params;
$this->name = sprintf(Lang::profiler('arenaRoster'), $this->subject->getField('name'));
}
// 2) not yet synced but exists on realm (wont work if we get passed an urlized name, but there is nothing we can do about it)
else if ($team = DB::Characters($this->realmId)->selectRow('SELECT at.arenaTeamId AS realmGUID, at.name, at.type FROM arena_team at WHERE at.name = ?', Util::ucFirst($this->subjectName)))
{
$team['realm'] = $this->realmId;
$team['cuFlags'] = PROFILER_CU_NEEDS_RESYNC;
// create entry from realm with basic info
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_arena_team (?#) VALUES (?a)', array_keys($team), array_values($team));
$this->handleIncompleteData($team['realmGUID']);
}
// 3) does not exist at all
else
$this->notFound();
}
else
$this->notFound();
}
protected function generateTitle()
{
$team = !empty($this->subject) ? $this->subject->getField('name') : $this->subjectName;
$team .= ' ('.$this->realm.' - '.Lang::profiler('regions', $this->region).')';
array_unshift($this->title, $team, Util::ucFirst(Lang::profiler('profiler')));
}
protected function generateContent()
{
if ($this->doResync)
return;
$this->addJS('?data=realms.weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$this->redButtons[BUTTON_RESYNC] = [$this->subjectGUID, 'arena-team'];
/****************/
/* Main Content */
/****************/
// statistic calculations here
/**************/
/* Extra Tabs */
/**************/
// tab: members
$member = new LocalProfileList(array(['atm.arenaTeamId', $this->subjectGUID]));
if (!$member->error)
{
$this->lvTabs[] = ['profile', array(
'data' => array_values($member->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_ARENA)),
'sort' => [-15],
'visibleCols' => ['race', 'classs', 'level', 'talents', 'gearscore', 'rating', 'wins', 'losses'],
'hiddenCols' => ['guild', 'location']
)];
}
}
public function notFound($title = '', $msg = '')
{
return parent::notFound($title ?: Util::ucFirst(Lang::profiler('profiler')), $msg ?: Lang::profiler('notFound', 'arenateam'));
}
private function handleIncompleteData($teamGuid)
{
//display empty page and queue status
$newId = Profiler::scheduleResync(TYPE_ARENA_TEAM, $this->realmId, $teamGuid);
$this->doResync = ['arena-team', $newId];
$this->initialSync();
}
}
?>

116
pages/arenateams.php Normal file
View File

@@ -0,0 +1,116 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 5: Profiler g_initPath()
// tabId 1: Tools g_initHeader()
class ArenaTeamsPage extends GenericPage
{
use TrProfiler;
protected $tabId = 1;
protected $path = [1, 5, 3];
protected $tpl = 'arena-teams';
protected $js = ['filters.js', 'profile_all.js', 'profile.js'];
public function __construct($pageCall, $pageParam)
{
$this->getSubjectFromUrl($pageParam);
$this->filterObj = new ArenaTeamListFilter();
foreach (Profiler::getRealms() as $idx => $r)
{
if ($this->region && $r['region'] != $this->region)
continue;
if ($this->realm && $r['name'] != $this->realm)
continue;
$this->sumSubjects += DB::Characters($idx)->selectCell('SELECT count(*) FROM arena_team');
}
parent::__construct($pageCall, $pageParam);
$this->name = Lang::profiler('arenaTeams');
$this->subCat = $pageParam ? '='.$pageParam : '';
}
protected function generateTitle()
{
if ($this->realm)
array_unshift($this->title, $this->realm,/* CFG_BATTLEGROUP,*/ Lang::profiler('regions', $this->region), Lang::profiler('arenaTeams'));
else if ($this->region)
array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::profiler('arenaTeams'));
else
array_unshift($this->title, Lang::profiler('arenaTeams'));
}
protected function generateContent()
{
$this->addJS('?data=realms&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = ['at.rating', 1000, '>'];
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['initData'] = ['type' => 'arenateams'];
$tabData = array(
'id' => 'arena-teams',
'hideCount' => 1,
'sort' => [-16],
'extraCols' => ['$Listview.extraCols.members'],
'visibleCols' => ['rank', 'wins', 'losses', 'rating'],
'hiddenCols' => ['arenateam', 'guild'],
);
if (empty($this->filter['sz']))
$tabData['visibleCols'][] = 'size';
$miscParams = [];
if ($this->realm)
$miscParams['sv'] = $this->realm;
if ($this->region)
$miscParams['rg'] = $this->region;
$teams = new RemoteArenaTeamList($conditions, $miscParams);
if (!$teams->error)
{
$teams->initializeLocalEntries();
$dFields = $teams->hasDiffFields(['faction', 'type']);
if (!($dFields & 0x1))
$tabData['hiddenCols'][] = 'faction';
$tabData['data'] = array_values($teams->getListviewData());
// create note if search limit was exceeded
if ($this->filter['query'] && $teams->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound2', $this->sumSubjects, $teams->getMatches());
$tabData['_truncated'] = 1;
}
else if ($teams->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_arenateamsfound', $this->sumSubjects, 0);
if ($this->filterObj->error)
$tabData['_errors'] = 1;
}
$this->lvTabs[] = ['profile', $tabData, 'membersCol'];
Lang::sort('game', 'cl');
Lang::sort('game', 'ra');
}
}
?>

View File

@@ -96,7 +96,7 @@ class ClassPage extends GenericPage
$this->expansion = Util::$expansionString[$this->subject->getField('expansion')]; $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
$this->headIcons = ['class_'.strtolower($this->subject->getField('fileString'))]; $this->headIcons = ['class_'.strtolower($this->subject->getField('fileString'))];
$this->redButtons = array( $this->redButtons = array(
BUTTON_LINKS => ['color' => '', 'linkId' => ''], BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => true, BUTTON_WOWHEAD => true,
BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[$tcClassId[$this->typeId] * 3], 'pet' => false], BUTTON_TALENT => ['href' => '?talent#'.Util::$tcEncoding[$tcClassId[$this->typeId] * 3], 'pet' => false],
BUTTON_FORUM => false // todo (low): CFG_BOARD_URL + X BUTTON_FORUM => false // todo (low): CFG_BOARD_URL + X
@@ -126,27 +126,25 @@ class ClassPage extends GenericPage
'OR', 'OR',
['s.cuFlags', SPELL_CU_LAST_RANK, '&'], ['s.cuFlags', SPELL_CU_LAST_RANK, '&'],
['s.rankNo', 0] ['s.rankNo', 0]
] ],
CFG_SQL_LIMIT_NONE
); );
$genSpells = new SpellList($conditions); $genSpells = new SpellList($conditions);
if (!$genSpells->error) if (!$genSpells->error)
{ {
$this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($genSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$this->lvTabs[] = array( $this->lvTabs[] = ['spell', array(
'file' => 'spell', 'data' => array_values($genSpells->getListviewData()),
'data' => $genSpells->getListviewData(), 'id' => 'spells',
'params' => array( 'name' => '$LANG.tab_spells',
'id' => 'spells', 'visibleCols' => ['level', 'schools', 'type', 'classes'],
'name' => '$LANG.tab_spells', 'hiddenCols' => ['reagents', 'skill'],
'visibleCols' => "$['level', 'schools', 'type', 'classes']", 'sort' => ['-level', 'type', 'name'],
'hiddenCols' => "$['reagents', 'skill']", 'computeDataFunc' => '$Listview.funcBox.initSpellFilter',
'sort' => "$['-level', 'type', 'name']", 'onAfterCreate' => '$Listview.funcBox.addSpellIndicator'
'computeDataFunc' => '$Listview.funcBox.initSpellFilter', )];
'onAfterCreate' => '$Listview.funcBox.addSpellIndicator'
)
);
} }
// Tab: Items (grouped) // Tab: Items (grouped)
@@ -155,7 +153,7 @@ class ClassPage extends GenericPage
['requiredClass', $_mask, '&'], ['requiredClass', $_mask, '&'],
[['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!'], [['requiredClass', CLASS_MASK_ALL, '&'], CLASS_MASK_ALL, '!'],
['itemset', 0], // hmm, do or dont..? ['itemset', 0], // hmm, do or dont..?
0 CFG_SQL_LIMIT_NONE
); );
$items = new ItemList($conditions); $items = new ItemList($conditions);
@@ -163,23 +161,21 @@ class ClassPage extends GenericPage
{ {
$this->extendGlobalData($items->getJSGlobals()); $this->extendGlobalData($items->getJSGlobals());
if (!$items->hasDiffFields(['requiredRace'])) $hiddenCols = null;
$hidden = "$['side']"; if ($items->hasDiffFields(['requiredRace']))
$hiddenCols = ['side'];
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($items->getListviewData()),
'data' => $items->getListviewData(), 'id' => 'items',
'params' => array( 'name' => '$LANG.tab_items',
'id' => 'items', 'visibleCols' => ['dps', 'armor', 'slot'],
'name' => '$LANG.tab_items', 'hiddenCols' => $hiddenCols,
'visibleCols' => "$['dps', 'armor', 'slot']", 'computeDataFunc' => '$Listview.funcBox.initSubclassFilter',
'hiddenCols' => isset($hidden) ? $hidden : null, 'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator',
'computeDataFunc' => '$Listview.funcBox.initSubclassFilter', 'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'),
'onAfterCreate' => '$Listview.funcBox.addSubclassIndicator', '_truncated' => 1
'note' => sprintf(Util::$filterResultString, '?items&filter=cr=152;crs='.$this->typeId.';crv=0'), )];
'_truncated' => 1
)
);
} }
// Tab: Quests // Tab: Quests
@@ -193,11 +189,10 @@ class ClassPage extends GenericPage
{ {
$this->extendGlobalData($quests->getJSGlobals()); $this->extendGlobalData($quests->getJSGlobals());
$this->lvTabs[] = array( $this->lvTabs[] = ['quest', array(
'file' => 'quest', 'data' => array_values($quests->getListviewData()),
'data' => $quests->getListviewData(), 'sort' => ['reqlevel', 'name']
'params' => ['sort' => "$['reqlevel', 'name']"] )];
);
} }
// Tab: Itemsets // Tab: Itemsets
@@ -206,15 +201,12 @@ class ClassPage extends GenericPage
{ {
$this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array( $this->lvTabs[] = ['itemset', array(
'file' => 'itemset', 'data' => array_values($sets->getListviewData()),
'data' => $sets->getListviewData(), 'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId),
'params' => array( 'hiddenCols' => ['classes'],
'note' => sprintf(Util::$filterResultString, '?itemsets&filter=cl='.$this->typeId), 'sort' => ['-level', 'name']
'hiddenCols' => "$['classes']", )];
'sort' => "$['-level', 'name']"
)
);
} }
// Tab: Trainer // Tab: Trainer
@@ -227,26 +219,17 @@ class ClassPage extends GenericPage
$trainer = new CreatureList($conditions); $trainer = new CreatureList($conditions);
if (!$trainer->error) if (!$trainer->error)
{ {
$this->lvTabs[] = array( $this->lvTabs[] = ['creature', array(
'file' => 'creature', 'data' => array_values($trainer->getListviewData()),
'data' => $trainer->getListviewData(), 'id' => 'trainers',
'params' => array( 'name' => '$LANG.tab_trainers'
'id' => 'trainers', )];
'name' => '$LANG.tab_trainers'
)
);
} }
// Tab: Races // Tab: Races
$races = new CharRaceList(array(['classMask', $_mask, '&'])); $races = new CharRaceList(array(['classMask', $_mask, '&']));
if (!$races->error) if (!$races->error)
{ $this->lvTabs[] = ['race', ['data' => array_values($races->getListviewData())]];
$this->lvTabs[] = array(
'file' => 'race',
'data' => $races->getListviewData(),
'params' => []
);
}
} }
} }

View File

@@ -27,13 +27,7 @@ class ClassesPage extends GenericPage
{ {
$classes = new CharClassList(); $classes = new CharClassList();
if (!$classes->error) if (!$classes->error)
{ $this->lvTabs[] = ['class', ['data' => array_values($classes->getListviewData())]];
$this->lvTabs[] = array(
'file' => 'class',
'data' => $classes->getListviewData(),
'params' => []
);
}
} }
protected function generateTitle() protected function generateTitle()

View File

@@ -1,7 +1,7 @@
<?php <?php
if (!defined('AOWOW_REVISION')) if (!defined('AOWOW_REVISION'))
die('invalid access'); die('illegal access');
// tabId 1: Tools g_initHeader() // tabId 1: Tools g_initHeader()
@@ -43,6 +43,12 @@ class ComparePage extends GenericPage
// add conditional js // add conditional js
$this->addJS('?data=weight-presets.gems.enchants.itemsets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']); $this->addJS('?data=weight-presets.gems.enchants.itemsets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$this->summary = array(
'template' => 'compare',
'id' => 'compare',
'parent' => 'compare-generic'
);
if (!$this->compareString) if (!$this->compareString)
return; return;
@@ -64,7 +70,8 @@ class ComparePage extends GenericPage
$outSet[] = $outString; $outSet[] = $outString;
} }
$this->summary = $outSet;
$this->summary['groups'] = $outSet;
$iList = new ItemList(array(['i.id', $items])); $iList = new ItemList(array(['i.id', $items]));
$data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON); $data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON);
@@ -74,12 +81,15 @@ class ComparePage extends GenericPage
if (empty($data[$itemId])) if (empty($data[$itemId]))
continue; continue;
$this->cmpItems[] = [ if (!empty($data[$itemId]['subitems']))
$itemId, foreach ($data[$itemId]['subitems'] as &$si)
$iList->getField('name', true), $si['enchantment'] = implode(', ', $si['enchantment']);
$iList->getField('quality'),
$iList->getField('iconString'), $this->cmpItems[$itemId] = [
$data[$itemId] 'name_'.User::$localeString => $iList->getField('name', true),
'quality' => $iList->getField('quality'),
'icon' => $iList->getField('iconString'),
'jsonequip' => $data[$itemId]
]; ];
} }
} }

View File

@@ -37,11 +37,7 @@ class CurrenciesPage extends GenericPage
$conditions[] = ['category', (int)$this->category[0]]; $conditions[] = ['category', (int)$this->category[0]];
$money = new CurrencyList($conditions); $money = new CurrencyList($conditions);
$this->lvTabs[] = array( $this->lvTabs[] = ['currency', ['data' => array_values($money->getListviewData())]];
'file' => 'currency',
'data' => $money->getListviewData(),
'params' => []
);
} }
protected function generateTitle() protected function generateTitle()

View File

@@ -21,11 +21,15 @@ class CurrencyPage extends GenericPage
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && isset($_GET['domain']))
Util::powerUseLocale($_GET['domain']);
$this->typeId = intVal($id); $this->typeId = intVal($id);
$this->subject = new CurrencyList(array(['id', $this->typeId])); $this->subject = new CurrencyList(array(['id', $this->typeId]));
if ($this->subject->error) if ($this->subject->error)
$this->notFound(Lang::game('currency'), Lang::currency('notFound')); $this->notFound();
$this->name = $this->subject->getField('name', true); $this->name = $this->subject->getField('name', true);
} }
@@ -50,12 +54,10 @@ class CurrencyPage extends GenericPage
/* Infobox */ /* Infobox */
/**********/ /**********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); $infobox = Lang::getInfoBoxForFlags(intval($this->subject->getField('cuFlags')));
if ($this->typeId == 103) // Arena Points if ($_ = $this->subject->getField('cap'))
$infobox[] = Lang::currency('cap').Lang::main('colon').'10\'000'; $infobox[] = Lang::currency('cap').Lang::main('colon').Lang::nf($_);
else if ($this->typeId == 104) // Honor
$infobox[] = Lang::currency('cap').Lang::main('colon').'75\'000';
/****************/ /****************/
/* Main Content */ /* Main Content */
@@ -69,6 +71,9 @@ class CurrencyPage extends GenericPage
BUTTON_LINKS => true BUTTON_LINKS => true
); );
if ($_ = $this->subject->getField('description', true))
$this->extraText = $_;
/**************/ /**************/
/* Extra Tabs */ /* Extra Tabs */
/**************/ /**************/
@@ -82,20 +87,8 @@ class CurrencyPage extends GenericPage
{ {
$this->extendGlobalData($lootTabs->jsGlobals); $this->extendGlobalData($lootTabs->jsGlobals);
foreach ($lootTabs->iterate() as $tab) foreach ($lootTabs->iterate() as list($file, $tabData))
{ $this->lvTabs[] = [$file, $tabData];
$this->lvTabs[] = array(
'file' => $tab[0],
'data' => $tab[1],
'params' => [
'name' => $tab[2],
'id' => $tab[3],
'extraCols' => $tab[4] ? '$['.implode(', ', array_unique($tab[4])).']' : null,
'hiddenCols' => $tab[5] ? '$['.implode(', ', array_unique($tab[5])).']' : null,
'visibleCols' => $tab[6] ? '$'. Util::toJSON( array_unique($tab[6])) : null
]
);
}
} }
// tab: sold by // tab: sold by
@@ -109,7 +102,7 @@ class CurrencyPage extends GenericPage
if (!$soldBy->error) if (!$soldBy->error)
{ {
$sbData = $soldBy->getListviewData(); $sbData = $soldBy->getListviewData();
$extraCols = ['Listview.extraCols.stock', "Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", 'Listview.extraCols.cost']; $extraCols = ['$Listview.extraCols.stock', "\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'];
$holidays = []; $holidays = [];
foreach ($sbData as $k => &$row) foreach ($sbData as $k => &$row)
@@ -130,7 +123,7 @@ class CurrencyPage extends GenericPage
if ($vendors[$k]['event']) if ($vendors[$k]['event'])
{ {
if (count($extraCols) == 3) // not already pushed if (count($extraCols) == 3) // not already pushed
$extraCols[] = 'Listview.extraCols.condition'; $extraCols[] = '$Listview.extraCols.condition';
$this->extendGlobalIds(TYPE_WORLDEVENT, $vendors[$k]['event']); $this->extendGlobalIds(TYPE_WORLDEVENT, $vendors[$k]['event']);
$row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $vendors[$k]['event']]]; $row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $vendors[$k]['event']]];
@@ -145,16 +138,13 @@ class CurrencyPage extends GenericPage
); );
} }
$this->lvTabs[] = array( $this->lvTabs[] = ['creature', array(
'file' => 'creature', 'data' => array_values($sbData),
'data' => $sbData, 'name' => '$LANG.tab_soldby',
'params' => [ 'id' => 'sold-by-npc',
'name' => '$LANG.tab_soldby', 'extraCols' => $extraCols,
'id' => 'sold-by-npc', 'hiddenCols' => ['level', 'type']
'extraCols' => '$['.implode(', ', $extraCols).']', )];
'hiddenCols' => "$['level', 'type']"
]
);
} }
} }
} }
@@ -167,18 +157,16 @@ class CurrencyPage extends GenericPage
{ {
$this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($createdBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
if ($createdBy->hasSetFields(['reagent1'])) $tabData = array(
$visCols = ['reagents']; 'data' => array_values($createdBy->getListviewData()),
'name' => '$LANG.tab_createdby',
$this->lvTabs[] = array( 'id' => 'created-by',
'file' => 'spell',
'data' => $createdBy->getListviewData(),
'params' => [
'name' => '$LANG.tab_createdby',
'id' => 'created-by',
'visibleCols' => isset($visCols) ? '$'.Util::toJSON($visCols) : null
]
); );
if ($createdBy->hasSetFields(['reagent1']))
$tabData['visibleCols'] = ['reagents'];
$this->lvTabs[] = ['spell', $tabData];
} }
} }
@@ -206,24 +194,61 @@ class CurrencyPage extends GenericPage
$boughtBy = new ItemList(array(['id', $boughtBy])); $boughtBy = new ItemList(array(['id', $boughtBy]));
if (!$boughtBy->error) if (!$boughtBy->error)
{ {
if ($boughtBy->getMatches() <= CFG_SQL_LIMIT_DEFAULT) $tabData = array(
$n = null; 'data' => array_values($boughtBy->getListviewData(ITEMINFO_VENDOR, [TYPE_CURRENCY => $this->typeId])),
'name' => '$LANG.tab_currencyfor',
$this->lvTabs[] = array( 'id' => 'currency-for',
'file' => 'item', 'extraCols' => ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')"],
'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, [TYPE_CURRENCY => $this->typeId]),
'params' => [
'name' => '$LANG.tab_currencyfor',
'id' => 'currency-for',
'extraCols' => "$[Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')]",
'note' => $n ? sprintf(Util::$filterResultString, $n) : null
]
); );
if ($boughtBy->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tabData['note'] = sprintf(Util::$filterResultString, $n);
$this->lvTabs[] = ['item', $tabData];
$this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
} }
} }
} }
protected function generateTooltip($asError = false)
{
if ($asError)
return '$WowheadPower.registerCurrency('.$this->typeId.', '.User::$localeId.', {});';
$x = '$WowheadPower.registerCurrency('.$this->typeId.', '.User::$localeId.", {\n";
$x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n";
$x .= "\ticon: '".rawurlencode($this->subject->getField('iconString', true, true))."',\n";
$x .= "\ttooltip_".User::$localeString.": '".$this->subject->renderTooltip()."'\n";
$x .= "});";
return $x;
}
public function display($override = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::display($override);
if (!$this->loadCache($tt))
{
$tt = $this->generateTooltip();
$this->saveCache($tt);
}
header('Content-type: application/x-javascript; charset=utf-8');
die($tt);
}
public function notFound($title = '', $msg = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::notFound($title ?: Lang::game('currency'), $msg ?: Lang::currency('notFound'));
header('Content-type: application/x-javascript; charset=utf-8');
echo $this->generateTooltip(true);
exit();
}
} }
?> ?>

128
pages/emote.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 100: Emotes g_initPath()
// tabid 0: Database g_initHeader()
class EmotePage extends GenericPage
{
use DetailPage;
protected $type = TYPE_EMOTE;
protected $typeId = 0;
protected $tpl = 'detail-page-generic';
protected $path = [0, 100];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $id)
{
parent::__construct($pageCall, $id);
$this->typeId = intVal($id);
$this->subject = new EmoteList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Util::ucFirst(Lang::game('emote')), Lang::emote('notFound'));
$this->name = Util::ucFirst($this->subject->getField('cmd'));
}
protected function generatePath() { }
protected function generateTitle()
{
array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('emote')));
}
protected function generateContent()
{
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// has Animation
if ($this->subject->getField('isAnimated'))
$infobox[] = Lang::emote('isAnimated');
/****************/
/* Main Content */
/****************/
$text = '';
if ($aliasses = DB::Aowow()->selectCol('SELECT command FROM ?_emotes_aliasses WHERE id = ?d AND locales & ?d', $this->typeId, 1 << User::$localeId))
{
$text .= '[h3]'.Lang::emote('aliases').'[/h3][ul]';
foreach ($aliasses as $a)
$text .= '[li]/'.$a.'[/li]';
$text .= '[/ul][br][br]';
}
$texts = [];
if ($_ = $this->subject->getField('self', true))
$texts[Lang::emote('self')] = $_;
if ($_ = $this->subject->getField('target', true))
$texts[Lang::emote('target')] = $_;
if ($_ = $this->subject->getField('noTarget', true))
$texts[Lang::emote('noTarget')] = $_;
if (!$texts)
$text .= '[div][i class=q0]'.Lang::emote('noText').'[/i][/div]';
else
foreach ($texts as $h => $t)
$text .= '[pad][b]'.$h.'[/b][ul][li][span class=s4]'.preg_replace('/%\d?\$?s/', '<'.Util::ucFirst(Lang::main('name')).'>', $t).'[/span][/li][/ul]';
$this->extraText = $text;
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => false
);
/**************/
/* Extra Tabs */
/**************/
// tab: achievement
$condition = array(
['ac.type', ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE],
['ac.value1', $this->typeId],
);
$acv = new AchievementList($condition);
$this->lvTabs[] = ['achievement', ['data' => array_values($acv->getListviewData())]];
$this->extendGlobalData($acv->getJsGlobals());
// tab: sound
if ($em = DB::Aowow()->select('SELECT soundId AS ARRAY_KEY, BIT_OR(1 << (raceId - 1)) AS raceMask, BIT_OR(1 << (gender - 1)) AS gender FROM aowow_emotes_sounds WHERE emoteId = ?d GROUP BY soundId', $this->typeId))
{
$sounds = new SoundList(array(['id', array_keys($em)]));
if (!$sounds->error)
{
$this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
$data = $sounds->getListviewData();
foreach($data as $id => &$d)
{
$d['races'] = $em[$id]['raceMask'];
$d['gender'] = $em[$id]['gender'];
}
$this->lvTabs[] = ['sound', array(
'data' => array_values($data),
// gender races
'extraCols' => ['$Listview.templates.title.columns[1]', '$Listview.templates.classs.columns[1]']
)];
}
}
}
}
?>

44
pages/emotes.php Normal file
View File

@@ -0,0 +1,44 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 100: Emotes g_initPath()
// tabid 0: Database g_initHeader()
class EmotesPage extends GenericPage
{
use ListPage;
protected $type = TYPE_EMOTE;
protected $tpl = 'list-page-generic';
protected $path = [0, 100];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $pageParam)
{
parent::__construct($pageCall, $pageParam);
$this->name = Util::ucFirst(Lang::game('emotes'));
}
protected function generateContent()
{
$tabData = array(
'data' => array_values((new EmoteList())->getListviewData()),
'name' => Util::ucFirst(Lang::game('emotes'))
);
$this->lvTabs[] = ['emote', $tabData, 'emote'];
}
protected function generateTitle()
{
array_unshift($this->title, $this->name);
}
protected function generatePath() { }
}
?>

324
pages/enchantment.php Normal file
View File

@@ -0,0 +1,324 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 101: Enchantment g_initPath()
// tabId 0: Database g_initHeader()
class EnchantmentPage extends GenericPage
{
use DetailPage;
protected $type = TYPE_ENCHANTMENT;
protected $typeId = 0;
protected $tpl = 'enchantment';
protected $path = [0, 101];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $id)
{
parent::__construct($pageCall, $id);
$this->typeId = intVal($id);
$this->subject = new EnchantmentList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Util::ucFirst(Lang::game('enchantment')), Lang::enchantment('notFound'));
$this->extendGlobalData($this->subject->getJSGlobals());
$this->name = Util::ucFirst($this->subject->getField('name', true));
}
private function getDistinctType()
{
$type = 0;
for ($i = 1; $i < 4; $i++)
{
if ($_ = $this->subject->getField('type'.$i))
{
if ($type) // already set
return 0;
else
$type = $_;
}
}
return $type;
}
protected function generateContent()
{
/***********/
/* Infobox */
/***********/
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// reqLevel
if ($_ = $this->subject->getField('requiredLevel'))
$infobox[] = sprintf(Lang::game('reqLevel'), $_);
// reqskill
if ($_ = $this->subject->getField('skillLine'))
{
$this->extendGlobalIds(TYPE_SKILL, $_);
$foo = sprintf(Lang::game('requires'), '&nbsp;[skill='.$_.']');
if ($_ = $this->subject->getField('skillLevel'))
$foo .= ' ('.$_.')';
$infobox[] = $foo;
}
/****************/
/* Main Content */
/****************/
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => false
);
$this->effects = [];
// 3 effects
for ($i = 1; $i < 4; $i++)
{
$_ty = $this->subject->getField('type'.$i);
$_qty = $this->subject->getField('amount'.$i);
$_obj = $this->subject->getField('object'.$i);
switch ($_ty)
{
case 1:
case 3:
case 7:
$sArr = $this->subject->getField('spells')[$i];
$spl = $this->subject->getRelSpell($sArr[0]);
$this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::item('trigger', $sArr[1])) : Lang::item('trigger', $sArr[1]);
$this->effects[$i]['proc'] = $sArr[3];
$this->effects[$i]['value'] = $_qty ?: null;
$this->effects[$i]['icon'] = array(
'name' => !$spl ? Util::ucFirst(Lang::game('spell')).' #'.$sArr[0] : Util::localizedString($spl, 'name'),
'id' => $sArr[0],
'count' => $sArr[2]
);
break;
case 5:
if ($_obj < 2) // [mana, health] are on [0, 1] respectively and are expected on [1, 2] ..
$_obj++; // 0 is weaponDmg .. ehh .. i messed up somewhere
$this->effects[$i]['tip'] = [$_obj, Game::$itemMods[$_obj]];
// DO NOT BREAK!
case 2:
case 6:
case 8:
case 4:
$this->effects[$i]['name'] = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Type: '.$_ty, Lang::enchantment('types', $_ty)) : Lang::enchantment('types', $_ty);
$this->effects[$i]['value'] = $_qty;
if ($_ty == 4)
$this->effects[$i]['name'] .= Lang::main('colon').'('.(User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'Object: '.$_obj, Lang::getMagicSchools(1 << $_obj)) : Lang::getMagicSchools(1 << $_obj)).')';
}
}
// activation conditions
if ($_ = $this->subject->getField('conditionId'))
{
$x = '';
if ($gemCnd = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantmentcondition WHERE id = ?d', $_))
{
for ($i = 1; $i < 6; $i++)
{
if (!$gemCnd['color'.$i])
continue;
$fiColors = function ($idx)
{
$foo = '';
switch ($idx)
{
case 2: $foo = '0:3:5'; break; // red
case 3: $foo = '2:4:5'; break; // yellow
case 4: $foo = '1:3:4'; break; // blue
}
return $foo;
};
$bLink = $gemCnd['color'.$i] ? '<a class="tip" href="?items=3&filter=ty='.$fiColors($gemCnd['color'.$i]).'">'.Lang::item('gemColors', $gemCnd['color'.$i] - 1).'</a>' : '';
$cLink = $gemCnd['cmpColor'.$i] ? '<a class="tip" href="?items=3&filter=ty='.$fiColors($gemCnd['cmpColor'.$i]).'">'.Lang::item('gemColors', $gemCnd['cmpColor'.$i] - 1).'</a>' : '';
switch ($gemCnd['comparator'.$i])
{
case 2: // requires less <color> than (<value> || <comparecolor>) gems
case 5: // requires at least <color> than (<value> || <comparecolor>) gems
$sp = (int)$gemCnd['value'.$i] > 1;
$x .= '<span class="q0">'.Lang::achievement('reqNumCrt').' '.Lang::item('gemConditions', $gemCnd['comparator'.$i], [$gemCnd['value'.$i], $bLink]).'</span><br />';
break;
case 3: // requires more <color> than (<value> || <comparecolor>) gems
$link = '<a href="?items=3&filter=ty='.$fiColors($gemCnd['cmpColor'.$i]).'">'.Lang::item('gemColors', $gemCnd['cmpColor'.$i] - 1).'</a>';
$x .= '<span class="q0">'.Lang::achievement('reqNumCrt').' '.Lang::item('gemConditions', $gemCnd['comparator'.$i], [$bLink, $cLink]).'</span><br />';
break;
}
}
}
$this->activateCondition = $x;
}
/**************/
/* Extra Tabs */
/**************/
// used by gem
$gemList = new ItemList(array(['gemEnchantmentId', $this->typeId]));
if (!$gemList->error)
{
$this->lvTabs[] = ['item', array(
'data' => array_values($gemList->getListviewData()),
'name' => '$LANG.tab_usedby + \' \' + LANG.gems',
'id' => 'used-by-gem',
)];
$this->extendGlobalData($gemList->getJsGlobals());
}
// used by socket bonus
$socketsList = new ItemList(array(['socketBonus', $this->typeId]));
if (!$socketsList->error)
{
$this->lvTabs[] = ['item', array(
'data' => array_values($socketsList->getListviewData()),
'name' => '$LANG.tab_usedby + \' \' + \''.Lang::item('socketBonus').'\'',
'id' => 'used-by-socketbonus',
)];
$this->extendGlobalData($socketsList->getJsGlobals());
}
// used by spell
// used by useItem
$cnd = array(
'OR',
['AND', ['effect1Id', [53, 54, 156, 92]], ['effect1MiscValue', $this->typeId]],
['AND', ['effect2Id', [53, 54, 156, 92]], ['effect2MiscValue', $this->typeId]],
['AND', ['effect3Id', [53, 54, 156, 92]], ['effect3MiscValue', $this->typeId]],
);
$spellList = new SpellList($cnd);
if (!$spellList->error)
{
$spellData = $spellList->getListviewData();
$this->extendGlobalData($spellList->getJsGlobals());
$spellIds = $spellList->getFoundIDs();
$conditions = array(
'OR', // [use, useUndelayed]
['AND', ['spellTrigger1', [0, 5]], ['spellId1', $spellIds]],
['AND', ['spellTrigger2', [0, 5]], ['spellId2', $spellIds]],
['AND', ['spellTrigger3', [0, 5]], ['spellId3', $spellIds]],
['AND', ['spellTrigger4', [0, 5]], ['spellId4', $spellIds]],
['AND', ['spellTrigger5', [0, 5]], ['spellId5', $spellIds]]
);
$ubItems = new ItemList($conditions);
if (!$ubItems->error)
{
$this->lvTabs[] = ['item', array(
'data' => array_values($ubItems->getListviewData()),
'name' => '$LANG.tab_usedby + \' \' + LANG.types[3][0]',
'id' => 'used-by-item',
)];
$this->extendGlobalData($ubItems->getJSGlobals(GLOBALINFO_SELF));
}
// remove found spells if they are used by an item
if (!$ubItems->error)
{
foreach ($spellList->iterate() as $sId => $__)
{
// if Perm. Enchantment has a createItem its a Scroll of Enchantment (display both)
for ($i = 1; $i < 4; $i++)
if ($spellList->getField('effect'.$i.'Id') == 53 && $spellList->getField('effect'.$i.'CreateItemId'))
continue 2;
foreach ($ubItems->iterate() as $__)
{
for ($i = 1; $i < 6; $i++)
{
if ($ubItems->getField('spellId'.$i) == $sId)
{
unset($spellData[$sId]);
break 2;
}
}
}
}
}
$this->lvTabs[] = ['spell', array(
'data' => array_values($spellData),
'name' => '$LANG.tab_usedby + \' \' + LANG.types[6][0]',
'id' => 'used-by-spell',
)];
}
// used by randomAttrItem
$ire = DB::Aowow()->select(
'SELECT *, ABS(id) AS ARRAY_KEY FROM ?_itemrandomenchant WHERE enchantId1 = ?d OR enchantId2 = ?d OR enchantId3 = ?d OR enchantId4 = ?d OR enchantId5 = ?d',
$this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId
);
if ($ire)
{
if ($iet = DB::World()->select('SELECT entry AS ARRAY_KEY, ench, chance FROM item_enchantment_template WHERE ench IN (?a)', array_keys($ire)))
{
$randIds = []; // transform back to signed format
foreach ($iet as $tplId => $data)
$randIds[$ire[$data['ench']]['id'] > 0 ? $tplId : -$tplId] = $ire[$data['ench']]['id'];
$randItems = new ItemList(array(CFG_SQL_LIMIT_NONE, ['randomEnchant', array_keys($randIds)]));
if (!$randItems->error)
{
$data = $randItems->getListviewData();
foreach ($randItems->iterate() as $iId => $__)
{
$re = $randItems->getField('randomEnchant');
$data[$iId]['percent'] = $iet[abs($re)]['chance'];
$data[$iId]['count'] = 1; // expected by js or the pct-col becomes unsortable
$data[$iId]['rel'] = 'rand='.$ire[$iet[abs($re)]['ench']]['id'];
$data[$iId]['name'] .= ' '.Util::localizedString($ire[$iet[abs($re)]['ench']], 'name');
}
$this->lvTabs[] = ['item', array(
'data' => array_values($data),
'id' => 'used-by-rand',
'name' => '$LANG.tab_usedby + \' \' + \''.Lang::item('_rndEnchants').'\'',
'extraCols' => ['$Listview.extraCols.percent']
)];
$this->extendGlobalData($randItems->getJSGlobals(GLOBALINFO_SELF));
}
}
}
}
protected function generateTitle()
{
array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('enchantment')));
}
protected function generatePath()
{
if ($_ = $this->getDistinctType())
$this->path[] = $_;
}
}
?>

108
pages/enchantments.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 101: Enchantment g_initPath()
// tabId 0: Database g_initHeader()
class EnchantmentsPage extends GenericPage
{
use ListPage;
protected $type = TYPE_ENCHANTMENT;
protected $tpl = 'enchantments';
protected $path = [0, 101];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
protected $js = ['filters.js'];
public function __construct($pageCall, $pageParam)
{
$this->getCategoryFromUrl($pageParam);;
$this->filterObj = new EnchantmentListFilter(false, ['parentCats' => $this->category]);
parent::__construct($pageCall, $pageParam);
$this->name = Util::ucFirst(Lang::game('enchantments'));
$this->subCat = $pageParam !== null ? '='.$pageParam : '';
}
protected function generateContent()
{
$tabData = array(
'data' => [],
'name' => Util::ucFirst(Lang::game('enchantments'))
);
$conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
$ench = new EnchantmentList($conditions);
$tabData['data'] = array_values($ench->getListviewData());
$this->extendGlobalData($ench->getJSGlobals());
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL;
$this->filter['initData'] = ['init' => 'enchantments'];
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
$xCols = $this->filterObj->getExtraCols();
foreach (Util::$itemFilter as $fiId => $str)
if (array_column($tabData['data'], $str))
$xCols[] = $fiId;
if (array_column($tabData['data'], 'dmg'))
$xCols[] = 34;
if ($xCols)
$this->filter['initData']['ec'] = array_values(array_unique($xCols));
if ($xCols)
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
if ($ench->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_enchantmentsfound', $ench->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$tabData['_truncated'] = 1;
}
if (array_filter(array_column($tabData['data'], 'spells')))
$tabData['visibleCols'] = ['trigger'];
if (!$ench->hasSetFields(['skillLine']))
$tabData['hiddenCols'] = ['skill'];
if ($this->filterObj->error)
$tabData['_errors'] = '$1';
$this->lvTabs[] = ['enchantment', $tabData, 'enchantment'];
}
protected function generateTitle()
{
$form = $this->filterObj->getForm('form');
if (!empty($form['ty']) && intVal($form['ty']) && $form['ty'] > 0 && $form['ty'] < 9)
array_unshift($this->title, Lang::enchantment('types', $form['ty']));
array_unshift($this->title, $this->name);
}
protected function generatePath()
{
$form = $this->filterObj->getForm('form');
if (isset($form['ty']) && !is_array($form['ty']))
$this->path[] = $form['ty'];
}
}
?>

View File

@@ -4,8 +4,8 @@ if (!defined('AOWOW_REVISION'))
die('illegal access'); die('illegal access');
// menuId 11: Object g_initPath() // menuId 11: Worldevent g_initPath()
// tabId 0: Database g_initHeader() // tabId 0: Database g_initHeader()
class EventPage extends GenericPage class EventPage extends GenericPage
{ {
use DetailPage; use DetailPage;
@@ -24,22 +24,25 @@ class EventPage extends GenericPage
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && isset($_GET['domain']))
Util::powerUseLocale($_GET['domain']);
$this->typeId = intVal($id); $this->typeId = intVal($id);
$conditions = $this->typeId < 0 ? [['id', -$this->typeId]] : [['holidayId', $this->typeId]]; $this->subject = new WorldEventList(array(['id', $this->typeId]));
$this->subject = new WorldEventList($conditions);
if ($this->subject->error) if ($this->subject->error)
$this->notFound(Lang::game('event'), Lang::event('notFound')); $this->notFound();
$this->hId = $this->subject->getField('holidayId'); $this->hId = $this->subject->getField('holidayId');
$this->eId = $this->subject->getField('eventBak'); $this->eId = $this->typeId;
$this->name = $this->subject->getField('name', true);
// redirect if associated with a holiday $this->dates = array(
if ($this->hId && $this->typeId != $this->hId) 'firstDate' => $this->subject->getField('startTime'),
header('Location: '.HOST_URL.'?event='.$this->hId, true, 302); 'lastDate' => $this->subject->getField('endTime'),
'length' => $this->subject->getField('length'),
$this->name = $this->subject->getField('name', true); 'rec' => $this->subject->getField('occurence')
);
} }
protected function generatePath() protected function generatePath()
@@ -84,16 +87,14 @@ class EventPage extends GenericPage
/* Main Content */ /* Main Content */
/****************/ /****************/
// no entry in ?_articles? use default HolidayDescription
if ($this->hId && empty($this->article))
$this->article = ['text' => Util::jsEscape($this->subject->getField('description', true)), 'params' => []];
$this->headIcons = [$this->subject->getField('iconString')]; $this->headIcons = [$this->subject->getField('iconString')];
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => $this->typeId > 0, BUTTON_WOWHEAD => $this->hId > 0,
BUTTON_LINKS => true BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
);
$this->dates = array(
'firstDate' => $this->subject->getField('startTime'),
'lastDate' => $this->subject->getField('endTime'),
'length' => $this->subject->getField('length'),
'rec' => $this->subject->getField('occurence')
); );
/**************/ /**************/
@@ -112,11 +113,12 @@ class EventPage extends GenericPage
foreach ($data as &$d) foreach ($data as &$d)
$d['method'] = $npcIds[$d['id']]; $d['method'] = $npcIds[$d['id']];
$this->lvTabs[] = array( $tabData = ['data' => array_values($data)];
'file' => CreatureList::$brickFile,
'data' => $data, if ($hasFilter)
'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?npcs&filter=cr=38;crs='.$this->hId.';crv=0') : null] $tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=38;crs='.$this->hId.';crv=0');
);
$this->lvTabs[] = ['creature', $tabData];
} }
} }
@@ -130,11 +132,12 @@ class EventPage extends GenericPage
foreach ($data as &$d) foreach ($data as &$d)
$d['method'] = $objectIds[$d['id']]; $d['method'] = $objectIds[$d['id']];
$this->lvTabs[] = array( $tabData = ['data' => array_values($data)];
'file' => GameObjectList::$brickFile,
'data' => $data, if ($hasFilter)
'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?objects&filter=cr=16;crs='.$this->hId.';crv=0') : null] $tabData['note'] = sprintf(Util::$filterResultString, '?objects&filter=cr=16;crs='.$this->hId.';crv=0');
);
$this->lvTabs[] = ['object', $tabData];
} }
} }
@@ -147,14 +150,15 @@ class EventPage extends GenericPage
{ {
$this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$this->lvTabs[] = array( $tabData = array(
'file' => AchievementList::$brickFile, 'data' => array_values($acvs->getListviewData()),
'data' => $acvs->getListviewData(), 'visibleCols' => ['category']
'params' => array(
'note' => $hasFilter ? sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$this->hId.';crv=0') : null,
'visibleCols' => "$['category']"
)
); );
if ($hasFilter)
$tabData['note'] = sprintf(Util::$filterResultString, '?achievements&filter=cr=11;crs='.$this->hId.';crv=0');
$this->lvTabs[] = ['achievement', $tabData];
} }
} }
@@ -163,20 +167,21 @@ class EventPage extends GenericPage
{ {
$itemCnd = array( $itemCnd = array(
'OR', 'OR',
['holidayId', $this->hId], // direct requirement on item ['eventId', $this->eId], // direct requirement on item
); );
// tab: quests (by table, go & creature) // tab: quests (by table, go & creature)
$quests = new QuestList(array(['holidayId', $this->hId])); $quests = new QuestList(array(['eventId', $this->eId]));
if (!$quests->error) if (!$quests->error)
{ {
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$this->lvTabs[] = array( $tabData = ['data'=> array_values($quests->getListviewData())];
'file' => QuestList::$brickFile,
'data' => $quests->getListviewData(), if ($hasFilter)
'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$this->hId.';crv=0') : null] $tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=33;crs='.$this->hId.';crv=0');
);
$this->lvTabs[] = ['quest', $tabData];
$questItems = []; $questItems = [];
foreach (array_column($quests->rewards, TYPE_ITEM) as $arr) foreach (array_column($quests->rewards, TYPE_ITEM) as $arr)
@@ -208,23 +213,24 @@ class EventPage extends GenericPage
{ {
$this->extendGlobalData($eventItems->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($eventItems->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array( $tabData = ['data'=> array_values($eventItems->getListviewData())];
'file' => ItemList::$brickFile,
'data' => $eventItems->getListviewData(), if ($hasFilter)
'params' => ['note' => $hasFilter ? sprintf(Util::$filterResultString, '?items&filter=cr=160;crs='.$this->hId.';crv=0') : null] $tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=160;crs='.$this->hId.';crv=0');
);
$this->lvTabs[] = ['item', $tabData];
} }
} }
// tab: see also (event conditions) // tab: see also (event conditions)
if ($rel = DB::World()->selectCol('SELECT IF(eventEntry = prerequisite_event, NULL, IF(eventEntry = ?d, -prerequisite_event, eventEntry)) FROM game_event_prerequisite WHERE prerequisite_event = ?d OR eventEntry = ?d', $this->eId, $this->eId, $this->eId)) if ($rel = DB::World()->selectCol('SELECT IF(eventEntry = prerequisite_event, NULL, IF(eventEntry = ?d, prerequisite_event, -eventEntry)) FROM game_event_prerequisite WHERE prerequisite_event = ?d OR eventEntry = ?d', $this->eId, $this->eId, $this->eId))
{ {
$list = []; $list = [];
array_walk($rel, function($v, $k) use (&$list) { array_walk($rel, function($v, $k) use (&$list) {
if ($v > 0) if ($v > 0)
$list[] = $v; $list[] = $v;
else if ($v === null) else if ($v === null)
Util::addNote(U_GROUP_EMPLOYEE, 'game_event_prerequisite: this event has itself as prerequisite'); trigger_error('game_event_prerequisite: this event has itself as prerequisite', E_USER_WARNING);
}); });
if ($list) if ($list)
@@ -233,82 +239,136 @@ class EventPage extends GenericPage
$this->extendGlobalData($relEvents->getJSGlobals()); $this->extendGlobalData($relEvents->getJSGlobals());
$relData = $relEvents->getListviewData(); $relData = $relEvents->getListviewData();
foreach ($relEvents->getFoundIDs() as $id) foreach ($relEvents->getFoundIDs() as $id)
$relData[$id]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, -$this->eId]]; $relData[$id]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, $this->eId]];
$this->extendGlobalData($this->subject->getJSGlobals()); $this->extendGlobalData($this->subject->getJSGlobals());
foreach ($rel as $r) foreach ($rel as $r)
{ {
if ($r >= 0) if ($r <= 0)
continue; continue;
$this->extendGlobalIds(TYPE_WORLDEVENT, $r); $this->extendGlobalIds(TYPE_WORLDEVENT, $r);
$d = $this->subject->getListviewData(); $d = $this->subject->getListviewData();
$d[-$this->eId]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, $r]]; $d[$this->eId]['condition'][0][$this->typeId][] = [[-CND_ACTIVE_EVENT, $r]];
$relData = array_merge($relData, $d); $relData = array_merge($relData, $d);
} }
$this->lvTabs[] = array( $this->lvTabs[] = ['event', array(
'file' => WorldEventList::$brickFile, 'data' => array_values($relData),
'data' => $relData, 'id' => 'see-also',
'params' => array( 'name' => '$LANG.tab_seealso',
'id' => 'see-also', 'hiddenCols' => ['date'],
'name' => '$LANG.tab_seealso', 'extraCols' => ['$Listview.extraCols.condition']
'hiddenCols' => "$['date']", )];
'extraCols' => '$[Listview.extraCols.condition]'
)
);
} }
} }
} }
protected function postCache() protected function postCache()
{ {
/********************/
/* finalize infobox */
/********************/
// update dates to now() // update dates to now()
$updated = WorldEventList::updateDates($this->dates); $updated = WorldEventList::updateDates($this->dates);
// start if ($this->mode == CACHE_TYPE_TOOLTIP)
if ($updated['start'])
array_push($this->infobox, Lang::event('start').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['start']));
// end
if ($updated['end'])
array_push($this->infobox, Lang::event('end').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['end']));
// occurence
if ($updated['rec'] > 0)
array_push($this->infobox, Lang::event('interval').Lang::main('colon').Util::formatTime($updated['rec'] * 1000));
// in progress
if ($updated['start'] < time() && $updated['end'] > time())
array_push($this->infobox, '[span class=q2]'.Lang::event('inProgress').'[/span]');
$this->infobox = '[ul][li]'.implode('[/li][li]', $this->infobox).'[/li][/ul]';
/***************************/
/* finalize related events */
/***************************/
foreach ($this->lvTabs as &$view)
{ {
if ($view['file'] != WorldEventList::$brickFile) return array(
continue; date(Lang::main('dateFmtLong'), $updated['start']),
date(Lang::main('dateFmtLong'), $updated['end'])
foreach ($view['data'] as &$data) );
{
$updated = WorldEventList::updateDates($data['_date']);
unset($data['_date']);
$data['startDate'] = $updated['start'] ? date(Util::$dateFormatInternal, $updated['start']) : false;
$data['endDate'] = $updated['end'] ? date(Util::$dateFormatInternal, $updated['end']) : false;
$data['rec'] = $updated['rec'];
}
} }
else
{
if ($this->hId)
Util::$wowheadLink = 'http://'.Util::$subDomains[User::$localeId].'.wowhead.com/event='.$this->hId;
/********************/
/* finalize infobox */
/********************/
// start
if ($updated['start'])
array_push($this->infobox, Lang::event('start').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['start']));
// end
if ($updated['end'])
array_push($this->infobox, Lang::event('end').Lang::main('colon').date(Lang::main('dateFmtLong'), $updated['end']));
// occurence
if ($updated['rec'] > 0)
array_push($this->infobox, Lang::event('interval').Lang::main('colon').Util::formatTime($updated['rec'] * 1000));
// in progress
if ($updated['start'] < time() && $updated['end'] > time())
array_push($this->infobox, '[span class=q2]'.Lang::event('inProgress').'[/span]');
$this->infobox = '[ul][li]'.implode('[/li][li]', $this->infobox).'[/li][/ul]';
/***************************/
/* finalize related events */
/***************************/
foreach ($this->lvTabs as &$view)
{
if ($view[0] != WorldEventList::$brickFile)
continue;
foreach ($view[1]['data'] as &$data)
{
$updated = WorldEventList::updateDates($data['_date']);
unset($data['_date']);
$data['startDate'] = $updated['start'] ? date(Util::$dateFormatInternal, $updated['start']) : false;
$data['endDate'] = $updated['end'] ? date(Util::$dateFormatInternal, $updated['end']) : false;
$data['rec'] = $updated['rec'];
}
}
}
}
protected function generateTooltip($asError = false)
{
if ($asError)
return '$WowheadPower.registerHoliday('.$this->typeId.', '.User::$localeId.', {});';
$x = '$WowheadPower.registerHoliday('.$this->typeId.', '.User::$localeId.", {\n";
$x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n";
if ($this->subject->getField('iconString') != 'trade_engineering')
$x .= "\ticon: '".rawurlencode($this->subject->getField('iconString', true, true))."',\n";
$x .= "\ttooltip_".User::$localeString.": '".$this->subject->renderTooltip()."'\n";
$x .= "});";
return $x;
}
public function display($override = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::display($override);
if (!$this->loadCache($tt))
{
$tt = $this->generateTooltip();
$this->saveCache($tt);
}
list($start, $end) = $this->postCache();
header('Content-type: application/x-javascript; charset=utf-8');
die(sprintf($tt, $start, $end));
}
public function notFound($title = '', $msg = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::notFound($title ?: Lang::game('event'), $msg ?: Lang::event('notFound'));
header('Content-type: application/x-javascript; charset=utf-8');
echo $this->generateTooltip(true);
exit();
} }
} }

View File

@@ -52,19 +52,16 @@ class EventsPage extends GenericPage
if ($d = $events->getField('requires')) if ($d = $events->getField('requires'))
$this->deps[$events->id] = $d; $this->deps[$events->id] = $d;
$this->lvTabs[] = array( $data = array_values($events->getListviewData());
'file' => 'event',
'data' => $events->getListviewData(),
'params' => []
);
if ($_ = array_filter($events->getListviewData(), function($x) {return $x['id'] > 0;})) $this->lvTabs[] = ['event', ['data' => $data]];
if ($_ = array_values(array_filter($data, function($x) {return $x['category'] > 0;})))
{ {
$this->lvTabs[] = array( $this->lvTabs[] = ['calendar', array(
'file' => 'calendar', 'data' => $_,
'data' => $_, 'hideCount' => 1
'params' => ['hideCount' => 1] )];
);
} }
} }
@@ -86,7 +83,7 @@ class EventsPage extends GenericPage
// recalculate dates with now() // recalculate dates with now()
foreach ($this->lvTabs as &$views) foreach ($this->lvTabs as &$views)
{ {
foreach ($views['data'] as &$data) foreach ($views[1]['data'] as &$data)
{ {
// is a followUp-event // is a followUp-event
if (!empty($this->deps[$data['id']])) if (!empty($this->deps[$data['id']]))

View File

@@ -88,7 +88,7 @@ class FactionPage extends GenericPage
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => true, BUTTON_WOWHEAD => true,
BUTTON_LINKS => true BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
); );
// Spillover Effects // Spillover Effects
@@ -134,12 +134,15 @@ class FactionPage extends GenericPage
switch ($k) switch ($k)
{ {
case 'quest_rate': $buff .= '[tr][td]'.Lang::game('quests') .Lang::main('colon').'[/td]'; break; case 'quest_rate': $buff .= '[tr][td]'.Lang::game('quests') .Lang::main('colon').'[/td]'; break;
case 'quest_daily_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('daily').')' .Lang::main('colon').'[/td]'; break; case 'quest_daily_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('daily').')' .Lang::main('colon').'[/td]'; break;
case 'quest_weekly_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('weekly').')' .Lang::main('colon').'[/td]'; break; case 'quest_weekly_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('weekly').')' .Lang::main('colon').'[/td]'; break;
case 'quest_monthly_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('monthly').')'.Lang::main('colon').'[/td]'; break; case 'quest_monthly_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('monthly').')' .Lang::main('colon').'[/td]'; break;
case 'creature_rate': $buff .= '[tr][td]'.Lang::game('npcs') .Lang::main('colon').'[/td]'; break; case 'quest_repeatable_rate': $buff .= '[tr][td]'.Lang::game('quests').' ('.Lang::quest('repeatable').')'.Lang::main('colon').'[/td]'; break;
case 'spell_rate': $buff .= '[tr][td]'.Lang::game('spells') .Lang::main('colon').'[/td]'; break; case 'creature_rate': $buff .= '[tr][td]'.Lang::game('npcs') .Lang::main('colon').'[/td]'; break;
case 'spell_rate': $buff .= '[tr][td]'.Lang::game('spells') .Lang::main('colon').'[/td]'; break;
default:
continue;
} }
$buff .= '[td width=35px align=right][span class=q'.($v < 1 ? '10]' : '2]+').intVal(($v - 1) * 100).'%[/span][/td][/tr]'; $buff .= '[td width=35px align=right][span class=q'.($v < 1 ? '10]' : '2]+').intVal(($v - 1) * 100).'%[/span][/td][/tr]';
@@ -175,49 +178,49 @@ class FactionPage extends GenericPage
{ {
$this->extendGlobalData($items->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($items->getJSGlobals(GLOBALINFO_SELF));
$tab = array( $tabData = array(
'file' => 'item', 'data' => array_values($items->getListviewData()),
'data' => $items->getListviewData(), 'extraCols' => '$_',
'showRep' => true, 'sort' => ['standing', 'name']
'params' => array(
'extraCols' => '$_',
'sort' => "$['standing', 'name']"
)
); );
if ($items->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($items->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tab['params']['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=17;crs='.$this->typeId.';crv=0'); $tabData['note'] = sprintf(Util::$filterResultString, '?items&filter=cr=17;crs='.$this->typeId.';crv=0');
$this->lvTabs[] = $tab; $this->lvTabs[] = ['item', $tabData, 'itemStandingCol'];
} }
// tab: creatures with onKill reputation // tab: creatures with onKill reputation
if ($this->subject->getField('reputationIndex') != -1) // only if you can actually gain reputation by kills if ($this->subject->getField('reputationIndex') != -1) // only if you can actually gain reputation by kills
{ {
// inherit siblings/children from $spillover // inherit siblings/children from $spillover
$cIds = DB::World()->selectCol('SELECT DISTINCT creature_id FROM creature_onkill_reputation WHERE $cRep = DB::World()->selectCol('SELECT DISTINCT creature_id AS ARRAY_KEY, qty FROM (
(RewOnKillRepValue1 > 0 AND (RewOnKillRepFaction1 = ?d{ OR (RewOnKillRepFaction1 IN (?a) AND IsTeamAward1 <> 0)})) OR SELECT creature_id, RewOnKillRepValue1 as qty FROM creature_onkill_reputation WHERE RewOnKillRepValue1 > 0 AND (RewOnKillRepFaction1 = ?d{ OR (RewOnKillRepFaction1 IN (?a) AND IsTeamAward1 <> 0)}) UNION
(RewOnKillRepValue2 > 0 AND (RewOnKillRepFaction2 = ?d{ OR (RewOnKillRepFaction2 IN (?a) AND IsTeamAward2 <> 0)}))', SELECT creature_id, RewOnKillRepValue2 as qty FROM creature_onkill_reputation WHERE RewOnKillRepValue2 > 0 AND (RewOnKillRepFaction2 = ?d{ OR (RewOnKillRepFaction2 IN (?a) AND IsTeamAward2 <> 0)})
) x',
$this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP, $this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP,
$this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP $this->typeId, $spillover->getFoundIDs() ?: DBSIMPLE_SKIP
); );
if ($cIds) if ($cRep)
{ {
$killCreatures = new CreatureList(array(['id', $cIds])); $killCreatures = new CreatureList(array(['id', array_keys($cRep)]));
if (!$killCreatures->error) if (!$killCreatures->error)
{ {
$tab = array( $data = $killCreatures->getListviewData();
'file' => 'creature', foreach ($data as $id => &$d)
'data' => $killCreatures->getListviewData(), $d['reputation'] = $cRep[$id];
'showRep' => true,
'params' => [] $tabData = array(
'data' => array_values($data),
'extraCols' => '$_',
'sort' => ['-reputation', 'name']
); );
if ($killCreatures->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($killCreatures->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tab['params']['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=42;crs='.$this->typeId.';crv=0'); $tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=42;crs='.$this->typeId.';crv=0');
$this->lvTabs[] = $tab; $this->lvTabs[] = ['creature', $tabData, 'npcRepCol'];
} }
} }
} }
@@ -228,20 +231,16 @@ class FactionPage extends GenericPage
$members = new CreatureList(array(['faction', $_])); $members = new CreatureList(array(['faction', $_]));
if (!$members->error) if (!$members->error)
{ {
$tab = array( $tabData = array(
'file' => 'creature', 'data' => array_values($members->getListviewData()),
'data' => $members->getListviewData(), 'id' => 'member',
'showRep' => true, 'name' => '$LANG.tab_members'
'params' => array(
'id' => 'member',
'name' => '$LANG.tab_members'
)
); );
if ($members->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($members->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tab['params']['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=3;crs='.$this->typeId.';crv=0'); $tabData['note'] = sprintf(Util::$filterResultString, '?npcs&filter=cr=3;crs='.$this->typeId.';crv=0');
$this->lvTabs[] = $tab; $this->lvTabs[] = ['creature', $tabData];
} }
} }
@@ -250,13 +249,7 @@ class FactionPage extends GenericPage
{ {
$objects = new GameObjectList(array(['faction', $_])); $objects = new GameObjectList(array(['faction', $_]));
if (!$objects->error) if (!$objects->error)
{ $this->lvTabs[] = ['object', ['data' => array_values($objects->getListviewData())]];
$this->lvTabs[] = array(
'file' => 'object',
'data' => $objects->getListviewData(),
'params' => []
);
}
} }
// tab: quests // tab: quests
@@ -273,17 +266,15 @@ class FactionPage extends GenericPage
{ {
$this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_ANY)); $this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_ANY));
$tab = array( $tabData = array(
'file' => 'quest', 'data' => array_values($quests->getListviewData($this->typeId)),
'data' => $quests->getListviewData($this->typeId), 'extraCols' => '$_'
'showRep' => true,
'params' => ['extraCols' => '$_']
); );
if ($quests->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($quests->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tab['params']['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=1;crs='.$this->typeId.';crv=0'); $tabData['note'] = sprintf(Util::$filterResultString, '?quests&filter=cr=1;crs='.$this->typeId.';crv=0');
$this->lvTabs[] = $tab; $this->lvTabs[] = ['quest', $tabData, 'questRepCol'];
} }
// tab: achievements // tab: achievements
@@ -296,15 +287,12 @@ class FactionPage extends GenericPage
{ {
$this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_ANY)); $this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_ANY));
$this->lvTabs[] = array( $this->lvTabs[] = ['achievement', array(
'file' => 'achievement', 'data' => array_values($acvs->getListviewData()),
'data' => $acvs->getListviewData(), 'id' => 'criteria-of',
'params' => array( 'name' => '$LANG.tab_criteriaof',
'id' => 'criteria-of', 'visibleCols' => ['category']
'name' => '$LANG.tab_criteriaof', )];
'visibleCols' => "$['category']"
)
);
} }
} }
} }

View File

@@ -53,13 +53,9 @@ class FactionsPage extends GenericPage
$data = []; $data = [];
$factions = new FactionList($conditions); $factions = new FactionList($conditions);
if (!$factions->error) if (!$factions->error)
$data = $factions->getListviewData(); $data = array_values($factions->getListviewData());
$this->lvTabs[] = array( $this->lvTabs[] = ['faction', ['data' => $data]];
'file' => 'faction',
'data' => $data,
'params' => []
);
} }
protected function generateTitle() protected function generateTitle()

View File

@@ -10,7 +10,13 @@ trait DetailPage
protected $category = null; // not used on detail pages protected $category = null; // not used on detail pages
protected $lvTabs = []; // most pages have this protected $lvTabs = []; // most pages have this
private $subject = null; // so it will not get cached protected $ssError = null;
protected $coError = null;
protected $viError = null;
protected $subject = null; // so it will not get cached
protected $contribute = CONTRIBUTE_ANY;
protected function generateCacheKey($withStaff = true) protected function generateCacheKey($withStaff = true)
{ {
@@ -25,6 +31,20 @@ trait DetailPage
return implode('_', $key); return implode('_', $key);
} }
protected function applyCCErrors()
{
if (!empty($_SESSION['error']['co']))
$this->coError = $_SESSION['error']['co'];
if (!empty($_SESSION['error']['ss']))
$this->ssError = $_SESSION['error']['ss'];
if (!empty($_SESSION['error']['vi']))
$this->viError = $_SESSION['error']['vi'];
unset($_SESSION['error']);
}
} }
@@ -53,6 +73,88 @@ trait ListPage
} }
} }
trait TrProfiler
{
protected $region = '';
protected $realm = '';
protected $realmId = 0;
protected $battlegroup = ''; // not implemented, since no pserver supports it
protected $subjectName = '';
protected $subjectGUID = 0;
protected $sumSubjects = 0;
protected $doResync = null;
protected function getSubjectFromUrl($str)
{
if (!$str)
return;
// cat[0] is always region
// cat[1] is realm or bGroup (must be realm if cat[2] is set)
// cat[2] is arena-team, guild or player
$cat = explode('.', $str, 3);
$cat = array_map('urldecode', $cat);
if (count($cat) > 3)
return;
if ($cat[0] !== 'eu' && $cat[0] !== 'us')
return;
$this->region = $cat[0];
// if ($cat[1] == Profiler::urlize(CFG_BATTLEGROUP))
// $this->battlegroup = CFG_BATTLEGROUP;
if (isset($cat[1]))
{
foreach (Profiler::getRealms() as $rId => $r)
{
if (Profiler::urlize($r['name']) == $cat[1])
{
$this->realm = $r['name'];
$this->realmId = $rId;
if (isset($cat[2]) && mb_strlen($cat[2]) >= 3)
$this->subjectName = $cat[2]; // cannot reconstruct original name from urlized form; match against special name field
break;
}
}
}
}
protected function initialSync()
{
$this->prepareContent();
$this->notFound = array(
'title' => sprintf(Lang::profiler('firstUseTitle'), $this->subjectName, $this->realm),
'msg' => ''
);
$this->hasComContent = false;
Util::arraySumByKey($this->mysql, DB::Aowow()->getStatistics(), DB::World()->getStatistics());
if (isset($this->tabId))
$this->pageTemplate['activeTab'] = $this->tabId;
$this->display('text-page-generic');
exit();
}
protected function generatePath()
{
if ($this->region)
{
$this->path[] = $this->region;
if ($this->realm)
$this->path[] = Profiler::urlize($this->realm);
// else
// $this->path[] = Profiler::urlize(CFG_BATTLEGROUP);
}
}
}
class GenericPage class GenericPage
{ {
@@ -84,10 +186,53 @@ class GenericPage
private $memcached = null; private $memcached = null;
private $mysql = ['time' => 0, 'count' => 0]; private $mysql = ['time' => 0, 'count' => 0];
public function __construct($pageCall/*, $pageParam */) private $headerLogo = '';
private $fullParams = '';
private $lvTemplates = array(
'achievement' => ['template' => 'achievement', 'id' => 'achievements', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_achievements' ],
'calendar' => ['template' => 'holidaycal', 'id' => 'calendar', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_calendar' ],
'class' => ['template' => 'classs', 'id' => 'classes', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_classes' ],
'commentpreview' => ['template' => 'commentpreview', 'id' => 'comments', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_comments' ],
'creature' => ['template' => 'npc', 'id' => 'npcs', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_npcs' ],
'currency' => ['template' => 'currency', 'id' => 'currencies', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_currencies' ],
'emote' => ['template' => 'emote', 'id' => 'emotes', 'parent' => 'lv-generic', 'data' => [] ],
'enchantment' => ['template' => 'enchantment', 'id' => 'enchantments', 'parent' => 'lv-generic', 'data' => [] ],
'event' => ['template' => 'holiday', 'id' => 'holidays', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_holidays' ],
'faction' => ['template' => 'faction', 'id' => 'factions', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_factions' ],
'genericmodel' => ['template' => 'genericmodel', 'id' => 'same-model-as', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_samemodelas' ],
'icongallery' => ['template' => 'icongallery', 'id' => 'icons', 'parent' => 'lv-generic', 'data' => [] ],
'item' => ['template' => 'item', 'id' => 'items', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_items' ],
'itemset' => ['template' => 'itemset', 'id' => 'itemsets', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_itemsets' ],
'model' => ['template' => 'model', 'id' => 'gallery', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_gallery' ],
'object' => ['template' => 'object', 'id' => 'objects', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_objects' ],
'pet' => ['template' => 'pet', 'id' => 'hunter-pets', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_pets' ],
'profile' => ['template' => 'profile', 'id' => 'profiles', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_profiles' ],
'quest' => ['template' => 'quest', 'id' => 'quests', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_quests' ],
'race' => ['template' => 'race', 'id' => 'races', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_races' ],
'replypreview' => ['template' => 'replypreview', 'id' => 'comment-replies', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_commentreplies'],
'reputationhistory' => ['template' => 'reputationhistory', 'id' => 'reputation', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_reputation' ],
'screenshot' => ['template' => 'screenshot', 'id' => 'screenshots', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_screenshots' ],
'skill' => ['template' => 'skill', 'id' => 'skills', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_skills' ],
'sound' => ['template' => 'sound', 'id' => 'sounds', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.types[19][2]' ],
'spell' => ['template' => 'spell', 'id' => 'spells', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_spells' ],
'title' => ['template' => 'title', 'id' => 'titles', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_titles' ],
'topusers' => ['template' => 'topusers', 'id' => 'topusers', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.topusers' ],
'video' => ['template' => 'video', 'id' => 'videos', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_videos' ],
'zone' => ['template' => 'zone', 'id' => 'zones', 'parent' => 'lv-generic', 'data' => [], 'name' => '$LANG.tab_zones' ]
);
public function __construct($pageCall, $pageParam = null)
{ {
$this->time = microtime(true); $this->time = microtime(true);
$this->fullParams = $pageCall;
if ($pageParam)
$this->fullParams .= '='.$pageParam;
if (CFG_CACHE_DIR && Util::writeDir(CFG_CACHE_DIR))
$this->cacheDir = mb_substr(CFG_CACHE_DIR, -1) != '/' ? CFG_CACHE_DIR.'/' : CFG_CACHE_DIR;
// force page refresh // force page refresh
if (isset($_GET['refresh']) && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_DEV)) if (isset($_GET['refresh']) && User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_DEV))
{ {
@@ -106,6 +251,10 @@ class GenericPage
$this->mode = CACHE_TYPE_XML; $this->mode = CACHE_TYPE_XML;
else else
{ {
// get alt header logo
if ($ahl = DB::Aowow()->selectCell('SELECT altHeaderLogo FROM ?_home_featuredbox WHERE ?d BETWEEN startDate AND endDate ORDER BY id DESC', time()))
$this->headerLogo = Util::defStatic($ahl);
$this->gUser = User::getUserGlobals(); $this->gUser = User::getUserGlobals();
$this->pageTemplate['pageName'] = strtolower($pageCall); $this->pageTemplate['pageName'] = strtolower($pageCall);
@@ -130,6 +279,10 @@ class GenericPage
$this->maintenance(); $this->maintenance();
else if (CFG_MAINTENANCE && User::isInGroup(U_GROUP_EMPLOYEE)) else if (CFG_MAINTENANCE && User::isInGroup(U_GROUP_EMPLOYEE))
Util::addNote(U_GROUP_EMPLOYEE, 'Maintenance mode enabled!'); Util::addNote(U_GROUP_EMPLOYEE, 'Maintenance mode enabled!');
// get errors from previous page from session and apply to template
if (method_exists($this, 'applyCCErrors'))
$this->applyCCErrors();
} }
/**********/ /**********/
@@ -152,23 +305,26 @@ class GenericPage
if (!isset($this->category) || empty($this->validCats)) if (!isset($this->category) || empty($this->validCats))
return true; return true;
switch (count($this->category)) $c = $this->category; // shorthand
switch (count($c))
{ {
case 0: // no params works always case 0: // no params works always
return true; return true;
case 1: // null is valid || value in a 1-dim-array || key for a n-dim-array case 1: // null is valid || value in a 1-dim-array || (key for a n-dim-array && ( has more subcats || no further subCats ))
return $this->category[0] === null || in_array($this->category[0], $this->validCats) || !empty($this->validCats[$this->category[0]]); $filtered = array_filter($this->validCats, function ($x) { return is_int($x); });
return $c[0] === null || in_array($c[0], $filtered) || (!empty($this->validCats[$c[0]]) && (is_array($this->validCats[$c[0]]) || $this->validCats[$c[0]] === true));
case 2: // first param has to be a key. otherwise invalid case 2: // first param has to be a key. otherwise invalid
if (!isset($this->validCats[$this->category[0]])) if (!isset($this->validCats[$c[0]]))
return false; return false;
// check if the sub-array is n-imensional // check if the sub-array is n-imensional
if (count($this->validCats[$this->category[0]]) == count($this->validCats[$this->category[0]], COUNT_RECURSIVE)) if (is_array($this->validCats[$c[0]]) && count($this->validCats[$c[0]]) == count($this->validCats[$c[0]], COUNT_RECURSIVE))
return in_array($this->category[1], $this->validCats[$this->category[0]]); // second param is value in second level array return in_array($c[1], $this->validCats[$c[0]]); // second param is value in second level array
else else
return isset($this->validCats[$this->category[0]][$this->category[1]]); // check if params is key of another array return isset($this->validCats[$c[0]][$c[1]]); // check if params is key of another array
case 3: // 3 params MUST point to a specific value case 3: // 3 params MUST point to a specific value
return isset($this->validCats[$this->category[0]][$this->category[1]]) && in_array($this->category[2], $this->validCats[$this->category[0]][$this->category[1]]); return isset($this->validCats[$c[0]][$c[1]]) && in_array($c[2], $this->validCats[$c[0]][$c[1]]);
} }
return false; return false;
@@ -178,15 +334,15 @@ class GenericPage
/* Prepare Page */ /* Prepare Page */
/****************/ /****************/
private function prepareContent() // get from cache ?: run generators protected function prepareContent() // get from cache ?: run generators
{ {
if (!$this->loadCache()) if (!$this->loadCache())
{ {
$this->addArticle(); $this->addArticle();
$this->generateContent();
$this->generatePath(); $this->generatePath();
$this->generateTitle(); $this->generateTitle();
$this->generateContent();
$this->applyGlobals(); $this->applyGlobals();
@@ -194,11 +350,20 @@ class GenericPage
} }
if (isset($this->type) && isset($this->typeId)) if (isset($this->type) && isset($this->typeId))
{
$this->gPageInfo = array( // varies slightly for special pages like maps, user-dashboard or profiler $this->gPageInfo = array( // varies slightly for special pages like maps, user-dashboard or profiler
'type' => $this->type, 'type' => $this->type,
'typeId' => $this->typeId, 'typeId' => $this->typeId,
'name' => $this->name 'name' => $this->name
); );
}
else if (!empty($this->articleUrl))
{
$this->gPageInfo = array(
'articleUrl' => $this->fullParams, // is actually be the url-param
'editAccess' => isset($this->editAccess) ? $this->editAccess : (U_GROUP_ADMIN | U_GROUP_EDITOR | U_GROUP_BUREAU)
);
}
if (!empty($this->path)) if (!empty($this->path))
$this->pageTemplate['breadcrumb'] = $this->path; $this->pageTemplate['breadcrumb'] = $this->path;
@@ -209,6 +374,13 @@ class GenericPage
if (method_exists($this, 'postCache')) // e.g. update dates for events and such if (method_exists($this, 'postCache')) // e.g. update dates for events and such
$this->postCache(); $this->postCache();
// determine contribute tabs
if (isset($this->subject))
{
$x = get_class($this->subject);
$this->contribute = $x::$contribute;
}
if (!empty($this->hasComContent)) // get comments, screenshots, videos if (!empty($this->hasComContent)) // get comments, screenshots, videos
{ {
$this->community = CommunityContent::getAll($this->type, $this->typeId, $jsGlobals); $this->community = CommunityContent::getAll($this->type, $this->typeId, $jsGlobals);
@@ -254,38 +426,57 @@ class GenericPage
private function addArticle() // get article & static infobox (run before processing jsGlobals) private function addArticle() // get article & static infobox (run before processing jsGlobals)
{ {
if (empty($this->type) || !isset($this->typeId)) $article = [];
return; if (!empty($this->type) && isset($this->typeId))
{
$article = DB::Aowow()->selectRow( $article = DB::Aowow()->selectRow('SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE type = ?d AND typeId = ?d AND locale = ?d UNION ALL SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE type = ?d AND typeId = ?d AND locale = 0 ORDER BY locale DESC LIMIT 1',
'SELECT article, quickInfo, locale FROM ?_articles WHERE type = ?d AND typeId = ?d AND locale = ?d UNION ALL '. $this->type, $this->typeId, User::$localeId, $this->type, $this->typeId
'SELECT article, quickInfo, locale FROM ?_articles WHERE type = ?d AND typeId = ?d AND locale = 0 ORDER BY locale DESC LIMIT 1', );
$this->type, $this->typeId, User::$localeId, }
$this->type, $this->typeId else if (!empty($this->articleUrl))
); {
$article = DB::Aowow()->selectRow('SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE url = ? AND locale = ?d UNION ALL SELECT article, quickInfo, locale, editAccess FROM ?_articles WHERE url = ? AND locale = 0 ORDER BY locale DESC LIMIT 1',
$this->articleUrl, User::$localeId, $this->articleUrl
);
}
if ($article) if ($article)
{ {
foreach ($article as $text) if ($article['article'])
(new Markup($text))->parseGlobalsFromText($this->jsgBuffer); (new Markup($article['article']))->parseGlobalsFromText($this->jsgBuffer);
if ($article['quickInfo'])
$replace = array( (new Markup($article['quickInfo']))->parseGlobalsFromText($this->jsgBuffer);
'<script' => '<scr"+"ipt',
'script>' => 'scr"+"ipt>',
'HOST_URL' => HOST_URL,
'STATIC_URL' => STATIC_URL
);
$this->article = array( $this->article = array(
'text' => strtr($article['article'], $replace), 'text' => Util::jsEscape(Util::defStatic($article['article'])),
'params' => [] 'params' => []
); );
if (!empty($this->type) && isset($this->typeId))
$this->article['params']['dbpage'] = true;
// convert U_GROUP_* to MARKUP.CLASS_* (as seen in js-object Markup)
if($article['editAccess'] & (U_GROUP_ADMIN | U_GROUP_VIP | U_GROUP_DEV))
$this->article['params']['allow'] = '$Markup.CLASS_ADMIN';
else if($article['editAccess'] & U_GROUP_STAFF)
$this->article['params']['allow'] = '$Markup.CLASS_STAFF';
else if($article['editAccess'] & U_GROUP_PREMIUM)
$this->article['params']['allow'] = '$Markup.CLASS_PREMIUM';
else if($article['editAccess'] & U_GROUP_PENDING)
$this->article['params']['allow'] = '$Markup.CLASS_PENDING';
else
$this->article['params']['allow'] = '$Markup.CLASS_USER';
$this->editAccess = $article['editAccess'];
if (empty($this->infobox) && !empty($article['quickInfo'])) if (empty($this->infobox) && !empty($article['quickInfo']))
$this->infobox = $article['quickInfo']; $this->infobox = $article['quickInfo'];
if ($article['locale'] != User::$localeId) if ($article['locale'] != User::$localeId)
$this->article['params'] = ['prepend' => Util::jsEscape('<div class="notice-box"><span class="icon-bubble">'.Lang::main('englishOnly').'</span></div>')]; $this->article['params']['prepend'] = '<div class="notice-box"><span class="icon-bubble">'.Lang::main('englishOnly').'</span></div>';
if (method_exists($this, 'postArticle')) // e.g. update variables in article
$this->postArticle();
} }
} }
@@ -297,14 +488,16 @@ class GenericPage
// display occured notices // display occured notices
if ($_ = Util::getNotes()) if ($_ = Util::getNotes())
{ {
array_unshift($_, 'One or more errors occured, while generating this page.');
$this->announcements[0] = array( $this->announcements[0] = array(
'parent' => 'announcement-0', 'parent' => 'announcement-0',
'id' => 0, 'id' => 0,
'mode' => 1, 'mode' => 1,
'status' => 1, 'status' => 1,
'name' => 'internal error', 'name' => 'internal error',
'style' => 'padding-left: 40px; background-image: url('.STATIC_URL.'/images/announcements/warn-small.png); background-size: 15px 15px; background-position: 12px center; border: dashed 2px #C03030;', 'style' => 'color: #ff3333; font-weight: bold; font-size: 14px; padding-left: 40px; background-image: url('.STATIC_URL.'/images/announcements/warn-small.png); background-size: 15px 15px; background-position: 12px center; border: dashed 2px #C03030;',
'text' => '[span id=inputbox-error]'.implode("[br]", $_).'[/span]', 'text' => '[span]'.implode("[br]", $_).'[/span]'
); );
} }
@@ -316,22 +509,17 @@ class GenericPage
{ {
if ($t = Util::localizedString($v, 'text')) if ($t = Util::localizedString($v, 'text'))
{ {
$replace = array(
'HOST_URL' => HOST_URL,
'STATIC_URL' => STATIC_URL
);
$_ = array( $_ = array(
'parent' => 'announcement-'.$k, 'parent' => 'announcement-'.$k,
'id' => $v['id'], 'id' => $v['id'],
'mode' => $v['mode'], 'mode' => $v['mode'],
'status' => $v['status'], 'status' => $v['status'],
'name' => $v['name'], 'name' => $v['name'],
'text' => strtr($t, $replace) 'text' => Util::defStatic($t)
); );
if ($v['style']) // may be empty if ($v['style']) // may be empty
$_['style'] = strtr($v['style'], $replace); $_['style'] = Util::defStatic($v['style']);
$this->announcements[$k] = $_; $this->announcements[$k] = $_;
} }
@@ -366,8 +554,8 @@ class GenericPage
array_unshift($this->title, Lang::main('nfPageTitle')); array_unshift($this->title, Lang::main('nfPageTitle'));
$this->notFound = array( $this->notFound = array(
'title' => $this->typeId ? Util::ucFirst($title).' #'.$this->typeId : $title, 'title' => isset($this->typeId) ? Util::ucFirst($title).' #'.$this->typeId : $title,
'msg' => !$msg && $this->typeId ? sprintf(Lang::main('pageNotFound'), $title) : $msg 'msg' => !$msg && isset($this->typeId) ? sprintf(Lang::main('pageNotFound'), $title) : $msg
); );
$this->hasComContent = false; $this->hasComContent = false;
Util::arraySumByKey($this->mysql, DB::Aowow()->getStatistics(), DB::World()->getStatistics()); Util::arraySumByKey($this->mysql, DB::Aowow()->getStatistics(), DB::World()->getStatistics());
@@ -375,29 +563,36 @@ class GenericPage
if (isset($this->tabId)) if (isset($this->tabId))
$this->pageTemplate['activeTab'] = $this->tabId; $this->pageTemplate['activeTab'] = $this->tabId;
$this->display('text-page-generic'); header('HTTP/1.0 404 Not Found', true, 404);
$this->display('list-page-generic');
exit(); exit();
} }
public function error() // unknown page public function error() // unknown page
{ {
$this->path = null; $this->path = null;
$this->tabId = null; $this->tabId = null;
$this->type = -99; // get error-article $this->articleUrl = 'page-not-found';
$this->typeId = 0; $this->title[] = Lang::main('errPageTitle');
$this->title[] = Lang::main('errPageTitle'); $this->name = Lang::main('errPageTitle');
$this->name = Lang::main('errPageTitle'); $this->lvTabs = [];
$this->addArticle(); $this->addArticle();
Util::arraySumByKey($this->mysql, DB::Aowow()->getStatistics(), DB::World()->getStatistics()); Util::arraySumByKey($this->mysql, DB::Aowow()->getStatistics(), DB::World()->getStatistics());
$this->display('text-page-generic'); header('HTTP/1.0 404 Not Found', true, 404);
$this->display('list-page-generic');
exit(); exit();
} }
public function maintenance() // display brb gnomes public function maintenance() // display brb gnomes
{ {
header('HTTP/1.0 503 Service Temporarily Unavailable', true, 503);
header('Retry-After: '.(3 * HOUR));
$this->display('maintenance'); $this->display('maintenance');
exit(); exit();
} }
@@ -427,7 +622,10 @@ class GenericPage
$this->prepareContent(); $this->prepareContent();
if (!$this->isSaneInclude('template/pages/', $this->tpl)) if (!$this->isSaneInclude('template/pages/', $this->tpl))
die(User::isInGroup(U_GROUP_EMPLOYEE) ? 'Error: nonexistant template requested: template/pages/'.$this->tpl.'.tpl.php' : null); {
trigger_error('Error: nonexistant template requested: template/pages/'.$this->tpl.'.tpl.php', E_USER_ERROR);
$this->error();
}
$this->addAnnouncements(); $this->addAnnouncements();
@@ -490,22 +688,32 @@ class GenericPage
$$n = $v; $$n = $v;
if (!$this->isSaneInclude('template/bricks/', $file)) if (!$this->isSaneInclude('template/bricks/', $file))
echo User::isInGroup(U_GROUP_EMPLOYEE) ? "\n\nError: nonexistant template requested: template/bricks/".$file.".tpl.php\n\n" : null; trigger_error('Nonexistant template requested: template/bricks/'.$file.'.tpl.php', E_USER_ERROR);
else else
include('template/bricks/'.$file.'.tpl.php'); include('template/bricks/'.$file.'.tpl.php');
} }
public function lvBrick($file, array $localVars = []) // load listview public function lvBrick($file) // load listview addIns
{ {
foreach ($localVars as $n => $v)
$$n = $v;
if (!$this->isSaneInclude('template/listviews/', $file)) if (!$this->isSaneInclude('template/listviews/', $file))
echo User::isInGroup(U_GROUP_EMPLOYEE) ? "\n\nError: nonexistant template requested: template/listviews/".$file.".tpl.php\n\n" : null; trigger_error('Nonexistant Listview addin requested: template/listviews/'.$file.'.tpl.php', E_USER_ERROR);
else else
include('template/listviews/'.$file.'.tpl.php'); include('template/listviews/'.$file.'.tpl.php');
} }
public function localizedBrick($file, $loc = LOCALE_EN) // load brick with more text then vars
{
if (!$this->isSaneInclude('template/localized/', $file.'_'.$loc))
{
if ($loc == LOCALE_EN || !$this->isSaneInclude('template/localized/', $file.'_'.LOCALE_EN))
trigger_error('Nonexistant template requested: template/localized/'.$file.'_'.$loc.'.tpl.php', E_USER_ERROR);
else
include('template/localized/'.$file.'_'.LOCALE_EN.'.tpl.php');
}
else
include('template/localized/'.$file.'_'.$loc.'.tpl.php');
}
/**********************/ /**********************/
/* Prepare js-Globals */ /* Prepare js-Globals */
/**********************/ /**********************/
@@ -577,8 +785,12 @@ class GenericPage
case TYPE_RACE: $jsg[TYPE_RACE] = ['g_races', [], []]; break; case TYPE_RACE: $jsg[TYPE_RACE] = ['g_races', [], []]; break;
case TYPE_SKILL: $jsg[TYPE_SKILL] = ['g_skills', [], []]; break; case TYPE_SKILL: $jsg[TYPE_SKILL] = ['g_skills', [], []]; break;
case TYPE_CURRENCY: $jsg[TYPE_CURRENCY] = ['g_gatheredcurrencies', [], []]; break; case TYPE_CURRENCY: $jsg[TYPE_CURRENCY] = ['g_gatheredcurrencies', [], []]; break;
case TYPE_SOUND: $jsg[TYPE_SOUND] = ['g_sounds', [], []]; break;
case TYPE_ICON: $jsg[TYPE_ICON] = ['g_icons', [], []]; break;
// well, this is awkward // well, this is awkward
case TYPE_USER: $jsg[TYPE_USER] = ['g_users', [], []]; break; case TYPE_USER: $jsg[TYPE_USER] = ['g_users', [], []]; break;
case TYPE_EMOTE: $jsg[TYPE_EMOTE] = ['g_emotes', [], []]; break;
case TYPE_ENCHANTMENT: $jsg[TYPE_ENCHANTMENT] = ['g_enchantments', [], []]; break;
} }
} }
@@ -595,27 +807,7 @@ class GenericPage
$this->initJSGlobal($type); $this->initJSGlobal($type);
// todo (med): properly distinguish holidayId and eventId $cnd = [CFG_SQL_LIMIT_NONE, ['id', array_unique($ids, SORT_NUMERIC)]];
$cnd = [CFG_SQL_LIMIT_NONE];
if ($type == TYPE_WORLDEVENT)
{
$hIds = array_filter($ids, function($v) { return $v > 0; });
$eIds = array_filter($ids, function($v) { return $v < 0; });
if ($hIds)
$cnd[] = ['holidayId', array_unique($hIds, SORT_NUMERIC)];
if ($eIds)
{
array_walk($eIds, function(&$v) { $v = abs($v);});
$cnd[] = ['e.id', array_unique($eIds, SORT_NUMERIC)];
}
if ($eIds && $hIds)
$cnd[] = 'OR';
}
else
$cnd [] = ['id', array_unique($ids, SORT_NUMERIC)];
switch ($type) switch ($type)
{ {
@@ -635,8 +827,11 @@ class GenericPage
case TYPE_RACE: $obj = new CharRaceList($cnd); break; case TYPE_RACE: $obj = new CharRaceList($cnd); break;
case TYPE_SKILL: $obj = new SkillList($cnd); break; case TYPE_SKILL: $obj = new SkillList($cnd); break;
case TYPE_CURRENCY: $obj = new CurrencyList($cnd); break; case TYPE_CURRENCY: $obj = new CurrencyList($cnd); break;
case TYPE_SOUND: $obj = new SoundList($cnd); break;
// "um, eh":, he ums and ehs. // "um, eh":, he ums and ehs.
case TYPE_USER: $obj = new UserList($cnd); break; case TYPE_USER: $obj = new UserList($cnd); break;
case TYPE_EMOTE: $obj = new EmoteList($cnd); break;
case TYPE_ENCHANTMENT: $obj = new EnchantmentList($cnd); break;
default: continue; default: continue;
} }
@@ -659,8 +854,9 @@ class GenericPage
if (!CFG_CACHE_MODE || CFG_DEBUG) if (!CFG_CACHE_MODE || CFG_DEBUG)
return; return;
$cKey = $this->generateCacheKey(); $noCache = ['coError', 'ssError', 'viError'];
$cache = []; $cKey = $this->generateCacheKey();
$cache = [];
if (!$saveString) if (!$saveString)
{ {
foreach ($this as $key => $val) foreach ($this as $key => $val)
@@ -669,7 +865,8 @@ class GenericPage
{ {
// public, protected and an undocumented flag added to properties created on the fly..? // public, protected and an undocumented flag added to properties created on the fly..?
if ((new ReflectionProperty($this, $key))->getModifiers() & 0x1300) if ((new ReflectionProperty($this, $key))->getModifiers() & 0x1300)
$cache[$key] = $val; if (!in_array($key, $noCache))
$cache[$key] = $val;
} }
catch (ReflectionException $e) { } // shut up! catch (ReflectionException $e) { } // shut up!
} }
@@ -748,7 +945,7 @@ class GenericPage
$type = $cache['isString']; $type = $cache['isString'];
$data = $cache['data']; $data = $cache['data'];
if ($cache['timestamp'] + CFG_CACHE_DECAY <= time() || $cache['revision'] < AOWOW_REVISION) if ($cache['timestamp'] + CFG_CACHE_DECAY <= time() || $cache['revision'] != AOWOW_REVISION)
$cache = null; $cache = null;
else else
$this->cacheLoaded = [CACHE_MODE_MEMCACHED, $cache['timestamp']]; $this->cacheLoaded = [CACHE_MODE_MEMCACHED, $cache['timestamp']];
@@ -771,7 +968,7 @@ class GenericPage
list($time, $rev, $type) = explode(' ', $cache[0]); list($time, $rev, $type) = explode(' ', $cache[0]);
if ($time + CFG_CACHE_DECAY <= time() || $rev < AOWOW_REVISION) if ($time + CFG_CACHE_DECAY <= time() || $rev != AOWOW_REVISION)
$cache = null; $cache = null;
else else
{ {

143
pages/guild.php Normal file
View File

@@ -0,0 +1,143 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 5: Profiler g_initPath()
// tabId 1: Tools g_initHeader()
class GuildPage extends GenericPage
{
use TrProfiler;
protected $lvTabs = [];
protected $tabId = 1;
protected $path = [1, 5, 2];
protected $tpl = 'roster';
protected $js = ['profile_all.js', 'profile.js'];
protected $css = [['path' => 'Profiler.css']];
public function __construct($pageCall, $pageParam)
{
$params = array_map('urldecode', explode('.', $pageParam));
if ($params[0])
$params[0] = Profiler::urlize($params[0]);
if (isset($params[1]))
$params[1] = Profiler::urlize($params[1]);
parent::__construct($pageCall, $pageParam);
if (count($params) == 1 && intval($params[0]))
{
$this->subject = new LocalGuildList(array(['g.id', intval($params[0])]));
if ($this->subject->error)
$this->notFound();
header('Location: '.$this->subject->getProfileUrl(), true, 302);
}
else if (count($params) == 3)
{
$this->getSubjectFromUrl($pageParam);
if (!$this->subjectName)
$this->notFound();
// 3 possibilities
// 1) already synced to aowow
if ($subject = DB::Aowow()->selectRow('SELECT id, realmGUID, cuFlags FROM ?_profiler_guild WHERE realm = ?d AND nameUrl = ?', $this->realmId, Profiler::urlize($this->subjectName)))
{
if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC)
{
$this->handleIncompleteData($subject['realmGUID']);
return;
}
$this->subjectGUID = $subject['id'];
$this->subject = new LocalGuildList(array(['id', $subject['id']]));
if ($this->subject->error)
$this->notFound();
$this->profile = $params;
$this->name = sprintf(Lang::profiler('guildRoster'), $this->subject->getField('name'));
}
// 2) not yet synced but exists on realm (wont work if we get passed an urlized name, but there is nothing we can do about it)
else if ($team = DB::Characters($this->realmId)->selectRow('SELECT guildid AS realmGUID, name FROM guild WHERE name = ?', Util::ucFirst($this->subjectName)))
{
$team['realm'] = $this->realmId;
$team['cuFlags'] = PROFILER_CU_NEEDS_RESYNC;
// create entry from realm with basic info
DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES (?a)', array_keys($team), array_values($team));
$this->handleIncompleteData($team['realmGUID']);
}
// 3) does not exist at all
else
$this->notFound();
}
else
$this->notFound();
}
protected function generateTitle()
{
$team = !empty($this->subject) ? $this->subject->getField('name') : $this->subjectName;
$team .= ' ('.$this->realm.' - '.Lang::profiler('regions', $this->region).')';
array_unshift($this->title, $team, Util::ucFirst(Lang::profiler('profiler')));
}
protected function generateContent()
{
if ($this->doResync)
return;
$this->addJS('?data=realms.weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$this->redButtons[BUTTON_RESYNC] = [$this->subjectGUID, 'guild'];
/****************/
/* Main Content */
/****************/
// statistic calculations here
// smuggle the guild ranks into the html
if ($ranks = DB::Aowow()->selectCol('SELECT rank AS ARRAY_KEY, name FROM ?_profiler_guild_rank WHERE guildId = ?d', $this->subjectGUID))
$this->extraHTML = '<script type="text/javascript">var guild_ranks = '.Util::toJSON($ranks).';</script>';
/**************/
/* Extra Tabs */
/**************/
// tab: members
$member = new LocalProfileList(array(['p.guild', $this->subjectGUID]));
if (!$member->error)
{
$this->lvTabs[] = ['profile', array(
'data' => array_values($member->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_ARENA)),
'sort' => [-15],
'visibleCols' => ['race', 'classs', 'level', 'talents', 'gearscore', 'achievementpoints', 'guildrank'],
'hiddenCols' => ['guild', 'location']
)];
}
}
public function notFound($title = '', $msg = '')
{
return parent::notFound($title ?: Util::ucFirst(Lang::profiler('profiler')), $msg ?: Lang::profiler('notFound', 'guild'));
}
private function handleIncompleteData($teamGuid)
{
//display empty page and queue status
$newId = Profiler::scheduleResync(TYPE_GUILD, $this->realmId, $teamGuid);
$this->doResync = ['guild', $newId];
$this->initialSync();
}
}
?>

116
pages/guilds.php Normal file
View File

@@ -0,0 +1,116 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 5: Profiler g_initPath()
// tabId 1: Tools g_initHeader()
class GuildsPage extends GenericPage
{
use TrProfiler;
protected $tabId = 1;
protected $path = [1, 5, 2];
protected $tpl = 'guilds';
protected $js = ['filters.js', 'profile_all.js', 'profile.js'];
public function __construct($pageCall, $pageParam)
{
$this->getSubjectFromUrl($pageParam);
$this->filterObj = new GuildListFilter();
foreach (Profiler::getRealms() as $idx => $r)
{
if ($this->region && $r['region'] != $this->region)
continue;
if ($this->realm && $r['name'] != $this->realm)
continue;
$this->sumSubjects += DB::Characters($idx)->selectCell('SELECT COUNT(*) FROM guild');
}
parent::__construct($pageCall, $pageParam);
$this->name = Lang::profiler('guilds');
$this->subCat = $pageParam ? '='.$pageParam : '';
}
protected function generateTitle()
{
if ($this->realm)
array_unshift($this->title, $this->realm,/* CFG_BATTLEGROUP,*/ Lang::profiler('regions', $this->region), Lang::profiler('guilds'));
else if ($this->region)
array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::profiler('guilds'));
else
array_unshift($this->title, Lang::profiler('guilds'));
}
protected function generateContent()
{
$this->addJS('?data=realms&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$conditions = array(
['c.deleteInfos_Account', null],
['c.level', MAX_LEVEL, '<='], // prevents JS errors
[['c.extra_flags', Profiler::CHAR_GMFLAGS, '&'], 0]
);
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['initData'] = ['type' => 'guilds'];
$tabData = array(
'id' => 'guilds',
'hideCount' => 1,
'sort' => [-3],
'visibleCols' => ['members', 'achievementpoints', 'gearscore'],
'hiddenCols' => ['guild'],
);
$miscParams = [];
if ($this->realm)
$miscParams['sv'] = $this->realm;
if ($this->region)
$miscParams['rg'] = $this->region;
$guilds = new RemoteGuildList($conditions, $miscParams);
if (!$guilds->error)
{
$guilds->initializeLocalEntries();
$dFields = $guilds->hasDiffFields(['faction', 'type']);
if (!($dFields & 0x1))
$tabData['hiddenCols'][] = 'faction';
if (($dFields & 0x2))
$tabData['visibleCols'][] = 'size';
$tabData['data'] = array_values($guilds->getListviewData());
// create note if search limit was exceeded
if ($this->filter['query'] && $guilds->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_guildsfound2', $this->sumSubjects, $guilds->getMatches());
$tabData['_truncated'] = 1;
}
else if ($guilds->getMatches() > CFG_SQL_LIMIT_DEFAULT)
$tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_guildsfound', $this->sumSubjects, 0);
if ($this->filterObj->error)
$tabData['_errors'] = 1;
}
$this->lvTabs[] = ['profile', $tabData, 'membersCol'];
Lang::sort('game', 'cl');
Lang::sort('game', 'ra');
}
}
?>

View File

@@ -6,11 +6,12 @@ if (!defined('AOWOW_REVISION'))
class HomePage extends GenericPage class HomePage extends GenericPage
{ {
protected $tpl = 'home'; protected $tpl = 'home';
protected $js = ['home.js']; protected $js = ['home.js'];
protected $css = [['path' => 'home.css']]; protected $css = [['path' => 'home.css']];
protected $news = []; protected $featuredBox = [];
protected $oneliner = '';
public function __construct() public function __construct()
{ {
@@ -21,31 +22,40 @@ class HomePage extends GenericPage
{ {
$this->addCSS(['string' => '.announcement { margin: auto; max-width: 1200px; padding: 0px 15px 15px 15px }']); $this->addCSS(['string' => '.announcement { margin: auto; max-width: 1200px; padding: 0px 15px 15px 15px }']);
// load news // load oneliner
$this->news = DB::Aowow()->selectRow('SELECT id as ARRAY_KEY, n.* FROM ?_news n WHERE active = 1 ORDER BY id DESC LIMIT 1'); if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_home_oneliner WHERE active = 1 LIMIT 1'))
if (!$this->news) $this->oneliner = Util::jsEscape(Util::localizedString($_, 'text'));
// load featuredBox (user web server time)
$this->featuredBox = DB::Aowow()->selectRow('SELECT id as ARRAY_KEY, n.* FROM ?_home_featuredbox n WHERE ?d BETWEEN startDate AND endDate ORDER BY id DESC LIMIT 1', time());
if (!$this->featuredBox)
return; return;
$this->news['text'] = Util::localizedString($this->news, 'text', true); $this->featuredBox = Util::defStatic($this->featuredBox);
if ($_ = (new Markup($this->news['text']))->parseGlobalsFromText()) $this->featuredBox['text'] = Util::localizedString($this->featuredBox, 'text', true);
if ($_ = (new Markup($this->featuredBox['text']))->parseGlobalsFromText())
$this->extendGlobalData($_); $this->extendGlobalData($_);
if (empty($this->news['bgImgUrl'])) if (empty($this->featuredBox['boxBG']))
$this->news['bgImgUrl'] = STATIC_URL.'/images/'.User::$localeString.'/mainpage-bg-news.jpg'; $this->featuredBox['boxBG'] = STATIC_URL.'/images/'.User::$localeString.'/mainpage-bg-news.jpg';
else
$this->news['bgImgUrl'] = strtr($this->news['bgImgUrl'], ['HOST_URL' => HOST_URL, 'STATIC_URL' => STATIC_URL]);
// load overlay links // load overlay links
$this->news['overlays'] = DB::Aowow()->select('SELECT * FROM ?_news_overlay WHERE newsId = ?d', $this->news['id']); $this->featuredBox['overlays'] = DB::Aowow()->select('SELECT * FROM ?_home_featuredbox_overlay WHERE featureId = ?d', $this->featuredBox['id']);
foreach ($this->news['overlays'] as &$o) foreach ($this->featuredBox['overlays'] as &$o)
{ {
$o['title'] = Util::localizedString($o, 'title', true); $o['title'] = Util::localizedString($o, 'title', true);
$o['title'] = strtr($o['title'], ['HOST_URL' => HOST_URL, 'STATIC_URL' => STATIC_URL]); $o['title'] = Util::defStatic($o['title']);
} }
} }
protected function generateTitle() {} protected function generateTitle()
{
if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_home_titles WHERE active = 1 AND title_loc?d <> "" ORDER BY RAND() LIMIT 1', User::$localeId))
$this->title[0] .= Lang::main('colon').Util::localizedString($_, 'title');
}
protected function generatePath() {} protected function generatePath() {}
} }

116
pages/icon.php Normal file
View File

@@ -0,0 +1,116 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 31: Icons g_initPath()
// tabId 0: Database g_initHeader()
class IconPage extends GenericPage
{
use DetailPage;
protected $type = TYPE_ICON;
protected $typeId = 0;
protected $tpl = 'icon';
protected $path = [0, 31];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
public function __construct($pageCall, $id)
{
parent::__construct($pageCall, $id);
$this->typeId = intVal($id);
$this->subject = new IconList(array(['id', $this->typeId]));
if ($this->subject->error)
$this->notFound(Util::ucFirst(Lang::game('icon')), Lang::icon('notFound'));
$this->extendGlobalData($this->subject->getJSGlobals());
$this->name = Util::ucFirst($this->subject->getField('name'));
$this->icon = $this->subject->getField('name', true, true);
}
protected function generateContent()
{
/****************/
/* Main Content */
/****************/
$this->redButtons = array(
BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_WOWHEAD => false
);
/**************/
/* Extra Tabs */
/**************/
// used by: spell
$ubSpells = new SpellList(array(['iconId', $this->typeId]));
if (!$ubSpells->error)
{
$this->extendGlobalData($ubSpells->getJsGlobals());
$this->lvTabs[] = [SpellList::$brickFile, array(
'data' => array_values($ubSpells->getListviewData()),
'id' => 'used-by-spell'
)];
}
// used by: item
$ubItems = new ItemList(array(['iconId', $this->typeId]));
if (!$ubItems->error)
{
$this->extendGlobalData($ubItems->getJsGlobals());
$this->lvTabs[] = [ItemList::$brickFile, array(
'data' => array_values($ubItems->getListviewData()),
'id' => 'used-by-item'
)];
}
// used by: achievement
$ubAchievements = new AchievementList(array(['iconId', $this->typeId]));
if (!$ubAchievements->error)
{
$this->extendGlobalData($ubAchievements->getJsGlobals());
$this->lvTabs[] = [AchievementList::$brickFile, array(
'data' => array_values($ubAchievements->getListviewData()),
'id' => 'used-by-achievement'
)];
}
// used by: currency
$ubCurrencies = new CurrencyList(array(['iconId', $this->typeId]));
if (!$ubCurrencies->error)
{
$this->extendGlobalData($ubCurrencies->getJsGlobals());
$this->lvTabs[] = [CurrencyList::$brickFile, array(
'data' => array_values($ubCurrencies->getListviewData()),
'id' => 'used-by-currency'
)];
}
// used by: hunter pet
$ubPets = new PetList(array(['iconId', $this->typeId]));
if (!$ubPets->error)
{
$this->extendGlobalData($ubPets->getJsGlobals());
$this->lvTabs[] = [PetList::$brickFile, array(
'data' => array_values($ubPets->getListviewData()),
'id' => 'used-by-pet'
)];
}
}
protected function generateTitle()
{
array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('icon')));
}
protected function generatePath() { }
}
?>

110
pages/icons.php Normal file
View File

@@ -0,0 +1,110 @@
<?php
if (!defined('AOWOW_REVISION'))
die('illegal access');
// menuId 31: Icons g_initPath()
// tabId 0: Database g_initHeader()
class IconsPage extends GenericPage
{
use ListPage;
protected $type = TYPE_ICON;
protected $tpl = 'icons';
protected $path = [0, 31];
protected $tabId = 0;
protected $mode = CACHE_TYPE_PAGE;
protected $js = ['filters.js'];
public function __construct($pageCall)
{
$this->filterObj = new IconListFilter();
parent::__construct($pageCall);
$this->name = Util::ucFirst(Lang::game('icons'));
}
protected function generateContent()
{
$tabData = array(
'data' => [],
);
$sqlLimit = 600; // fits better onto the grid
$conditions = [$sqlLimit];
if (!User::isInGroup(U_GROUP_EMPLOYEE))
$conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
if ($_ = $this->filterObj->getConditions())
$conditions[] = $_;
$icons = new IconList($conditions);
$tabData['data'] = array_values($icons->getListviewData());
$this->extendGlobalData($icons->getJSGlobals());
// recreate form selection
$this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['initData'] = ['init' => 'icons'];
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
if ($icons->getMatches() > $sqlLimit)
{
$tabData['note'] = sprintf(Util::$tryFilteringEntityString, $icons->getMatches(), 'LANG.types[29][3]', $sqlLimit);
$tabData['_truncated'] = 1;
}
if ($this->filterObj->error)
$tabData['_errors'] = '$1';
$this->lvTabs[] = ['icongallery', $tabData];
}
protected function generateTitle()
{
$setCrt = $this->filterObj->getSetCriteria();
$title = $this->name;
if (isset($setCrt['cr']) && count($setCrt['cr']) == 1)
{
switch ($setCrt['cr'][0])
{
case 1:
$title = Util::ucFirst(Lang::game('item')).' '.$title;
break;
case 2:
$title = Util::ucFirst(Lang::game('spell')).' '.$title;
break;
case 3:
$title = Util::ucFirst(Lang::game('achievement')).' '.$title;
break;
case 6:
$title = Util::ucFirst(Lang::game('currency')).' '.$title;
break;
case 9:
$title = Util::ucFirst(Lang::game('pet')).' '.$title;
break;
case 11:
$title = Util::ucFirst(Lang::game('class')).' '.$title;
break;
}
}
array_unshift($this->title, $title);
}
protected function generatePath()
{
$setCrt = $this->filterObj->getSetCriteria();
if (isset($setCrt['cr']) && count($setCrt['cr']) == 1)
$this->path[] = $setCrt['cr'][0];
}
}
?>

View File

@@ -54,7 +54,7 @@ class ItemPage extends genericPage
// allow lookup by name for xml // allow lookup by name for xml
if (!is_numeric($param)) if (!is_numeric($param))
$conditions = [['name_loc'.User::$localeId, utf8_encode(urldecode($param))]]; $conditions = [['name_loc'.User::$localeId, urldecode($param)]];
} }
$this->subject = new ItemList($conditions); $this->subject = new ItemList($conditions);
@@ -167,9 +167,11 @@ class ItemPage extends genericPage
} }
// related holiday // related holiday
if ($hId = $this->subject->getField('holidayId')) if ($eId = $this->subject->getField('eventId'))
if ($hName = DB::Aowow()->selectRow('SELECT * FROM ?_holidays WHERE id = ?d', $hId)) {
$infobox[] = Lang::game('eventShort').Lang::main('colon').'[url=?event='.$hId.']'.Util::localizedString($hName, 'name').'[/url]'; $this->extendGlobalIds(TYPE_WORLDEVENT, $eId);
$infobox[] = Lang::game('eventShort').Lang::main('colon').'[event='.$eId.']';
}
// tool // tool
if ($tId = $this->subject->getField('totemCategory')) if ($tId = $this->subject->getField('totemCategory'))
@@ -259,7 +261,7 @@ class ItemPage extends genericPage
// avg auction buyout // avg auction buyout
if (in_array($this->subject->getField('bonding'), [0, 2, 3])) if (in_array($this->subject->getField('bonding'), [0, 2, 3]))
if ($_ = Util::getBuyoutForItem($this->typeId)) if ($_ = Profiler::getBuyoutForItem($this->typeId))
$infobox[] = '[tooltip=tooltip_buyoutprice]'.Lang::item('buyout.').'[/tooltip]'.Lang::main('colon').'[money='.$_.']'.$each; $infobox[] = '[tooltip=tooltip_buyoutprice]'.Lang::item('buyout.').'[/tooltip]'.Lang::main('colon').'[money='.$_.']'.$each;
// avg money contained // avg money contained
@@ -326,34 +328,34 @@ class ItemPage extends genericPage
$_cu = in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]) || $this->subject->getField('gemEnchantmentId'); $_cu = in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]) || $this->subject->getField('gemEnchantmentId');
// pageText
$pageText = [];
if ($this->pageText = Game::getPageText($this->subject->getField('pageTextId')))
{
$this->addJS('Book.js');
$this->addCSS(['path' => 'Book.css']);
}
$this->headIcons = [$this->subject->getField('iconString'), $this->subject->getField('stackable')]; $this->headIcons = [$this->subject->getField('iconString'), $this->subject->getField('stackable')];
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->tooltip = $this->subject->renderTooltip(true); $this->tooltip = $this->subject->renderTooltip(true);
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => true, BUTTON_WOWHEAD => true,
BUTTON_LINKS => ['color' => 'ff'.Util::$rarityColorStings[$this->subject->getField('quality')], 'linkId' => 'item:'.$this->typeId.':0:0:0:0:0:0:0:0'],
BUTTON_VIEW3D => in_array($_slot, $_visSlots) && $_model ? ['displayId' => $this->subject->getField('displayId'), 'slot' => $_slot, 'type' => TYPE_ITEM, 'typeId' => $this->typeId] : false, BUTTON_VIEW3D => in_array($_slot, $_visSlots) && $_model ? ['displayId' => $this->subject->getField('displayId'), 'slot' => $_slot, 'type' => TYPE_ITEM, 'typeId' => $this->typeId] : false,
BUTTON_COMPARE => $_cu, BUTTON_COMPARE => $_cu,
BUTTON_EQUIP => in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]), BUTTON_EQUIP => in_array($_class, [ITEM_CLASS_WEAPON, ITEM_CLASS_ARMOR]),
BUTTON_UPGRADE => $_cu ? ['class' => $_class, 'slot' => $_slot] : false BUTTON_UPGRADE => ($_cu ? ['class' => $_class, 'slot' => $_slot] : false),
BUTTON_LINKS => array(
'linkColor' => 'ff'.Game::$rarityColorStings[$this->subject->getField('quality')],
'linkId' => 'item:'.$this->typeId.':0:0:0:0:0:0:0:0',
'linkName' => $this->name,
'type' => $this->type,
'typeId' => $this->typeId
)
); );
// availablility // availablility
$this->disabled = false; // todo (med): get itemSources (which are not yet in DB :x) or $this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE;
// pageText
if ($next = $this->subject->getField('pageTextId'))
{
$this->addJS('Book.js');
$this->addCSS(['path' => 'Book.css']);
while ($next)
{
$row = DB::World()->selectRow('SELECT *, text as Text_loc0 FROM page_text pt LEFT JOIN locales_page_text lpt ON pt.entry = lpt.entry WHERE pt.entry = ?d', $next);
$next = $row['next_page'];
$this->pageText[] = Util::parseHtmlText(Util::localizedString($row, 'Text'));
}
}
// subItems // subItems
$this->subject->initSubItems(); $this->subject->initSubItems();
@@ -362,10 +364,12 @@ class ItemPage extends genericPage
uaSort($this->subject->subItems[$this->typeId], function($a, $b) { return strcmp($a['name'], $b['name']); }); uaSort($this->subject->subItems[$this->typeId], function($a, $b) { return strcmp($a['name'], $b['name']); });
$this->subItems = array( $this->subItems = array(
'data' => array_values($this->subject->subItems[$this->typeId]), 'data' => array_values($this->subject->subItems[$this->typeId]),
'randIds' => array_keys($this->subject->subItems[$this->typeId]),
'quality' => $this->subject->getField('quality') 'quality' => $this->subject->getField('quality')
); );
// merge identical stats and names for normal users (e.g. spellPower of a specific school became generel spellPower with 3.0) // merge identical stats and names for normal users (e.g. spellPower of a specific school became generel spellPower with 3.0)
if (!User::isInGroup(U_GROUP_EMPLOYEE)) if (!User::isInGroup(U_GROUP_EMPLOYEE))
{ {
for ($i = 1; $i < count($this->subItems['data']); $i++) for ($i = 1; $i < count($this->subItems['data']); $i++)
@@ -376,6 +380,7 @@ class ItemPage extends genericPage
{ {
$prev['chance'] += $cur['chance']; $prev['chance'] += $cur['chance'];
array_splice($this->subItems['data'], $i , 1); array_splice($this->subItems['data'], $i , 1);
array_splice($this->subItems['randIds'], $i , 1);
$i = 1; $i = 1;
} }
} }
@@ -404,6 +409,31 @@ class ItemPage extends genericPage
/* Extra Tabs */ /* Extra Tabs */
/**************/ /**************/
// tab: createdBy (perfect item specific)
if ($perfItem = DB::World()->select('SELECT *, spellId AS ARRAY_KEY FROM skill_perfect_item_template WHERE perfectItemType = ?d', $this->typeId))
{
$perfSpells = new SpellList(array(['id', array_column($perfItem, 'spellId')]));
if (!$perfSpells->error)
{
$lvData = $perfSpells->getListviewData();
$this->extendGlobalData($perfSpells->getJSGlobals(GLOBALINFO_RELATED));
foreach ($lvData as $sId => &$data)
{
$data['percent'] = $perfItem[$sId]['perfectCreateChance'];
$data['condition'][0][$this->typeId] = [[[CND_SPELL, $perfItem[$sId]['requiredSpecialization']]]];
$this->extendGlobalIDs(TYPE_SPELL, $perfItem[$sId]['requiredSpecialization']);
}
$this->lvTabs[] = ['spell', array(
'data' => array_values($lvData),
'name' => '$LANG.tab_createdby',
'id' => 'created-by', // should by exclusive with created-by from spell_loot
'extraCols' => ['$Listview.extraCols.percent', '$Listview.extraCols.condition']
)];
}
}
// tabs: this item is contained in.. // tabs: this item is contained in..
$lootTabs = new Loot(); $lootTabs = new Loot();
$createdBy = []; $createdBy = [];
@@ -411,34 +441,24 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($lootTabs->jsGlobals); $this->extendGlobalData($lootTabs->jsGlobals);
foreach ($lootTabs->iterate() as $idx => $tab) foreach ($lootTabs->iterate() as $idx => list($file, $tabData))
{ {
if (!$tab[1]) if (!$tabData['data'])
continue; continue;
if ($idx == 16) if ($idx == 16)
$createdBy = array_column($tab[1], 'id'); $createdBy = array_column($tabData['data'], 'id');
$this->lvTabs[] = array( $this->lvTabs[] = [$file, $tabData];
'file' => $tab[0],
'data' => $tab[1],
'params' => [
'name' => $tab[2],
'id' => $tab[3],
'extraCols' => $tab[4] ? '$['.implode(', ', array_unique($tab[4])).']' : null,
'hiddenCols' => $tab[5] ? '$ '.Util::toJSON( array_unique($tab[5])) : null,
'visibleCols' => $tab[6] ? '$'. Util::toJSON( array_unique($tab[6])) : null
]
);
} }
} }
// tabs: this item contains.. // tabs: this item contains..
$sourceFor = array( $sourceFor = array(
[LOOT_ITEM, $this->subject->id, '$LANG.tab_contains', 'contains', ['Listview.extraCols.percent'], [] , []], [LOOT_ITEM, $this->subject->id, '$LANG.tab_contains', 'contains', ['$Listview.extraCols.percent'], [] , []],
[LOOT_PROSPECTING, $this->subject->id, '$LANG.tab_prospecting', 'prospecting', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], [LOOT_PROSPECTING, $this->subject->id, '$LANG.tab_prospecting', 'prospecting', ['$Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []],
[LOOT_MILLING, $this->subject->id, '$LANG.tab_milling', 'milling', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], [LOOT_MILLING, $this->subject->id, '$LANG.tab_milling', 'milling', ['$Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []],
[LOOT_DISENCHANT, $this->subject->getField('disenchantId'), '$LANG.tab_disenchanting', 'disenchanting', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []] [LOOT_DISENCHANT, $this->subject->getField('disenchantId'), '$LANG.tab_disenchanting', 'disenchanting', ['$Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []]
); );
$reqQuest = []; $reqQuest = [];
@@ -462,17 +482,22 @@ class ItemPage extends genericPage
$lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]]; $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
} }
$this->lvTabs[] = array( $tabData = array(
'file' => 'item', 'data' => array_values($lootTab->getResult()),
'data' => $lootTab->getResult(), 'name' => $sf[2],
'params' => [ 'id' => $sf[3],
'name' => $sf[2],
'id' => $sf[3],
'extraCols' => $sf[4] ? "$[".implode(', ', array_unique($sf[4]))."]" : null,
'hiddenCols' => $sf[5] ? "$".Util::toJSON($sf[5]) : null,
'visibleCols' => $sf[6] ? '$'.Util::toJSON($sf[6]) : null
]
); );
if ($sf[4])
$tabData['extraCols'] = array_unique($sf[4]);
if ($sf[5])
$tabData['hiddenCols'] = array_unique($sf[5]);
if ($sf[6])
$tabData['visibleCols'] = array_unique($sf[6]);
$this->lvTabs[] = ['item', $tabData];
} }
} }
@@ -512,15 +537,12 @@ class ItemPage extends genericPage
if (!$contains->hasSetFields(['slot'])) if (!$contains->hasSetFields(['slot']))
$hCols[] = 'slot'; $hCols[] = 'slot';
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($contains->getListviewData()),
'data' => $contains->getListviewData(), 'name' => '$LANG.tab_cancontain',
'params' => [ 'id' => 'can-contain',
'name' => '$LANG.tab_cancontain', 'hiddenCols' => $hCols
'id' => 'can-contain', )];
'hiddenCols' => '$'.Util::toJSON($hCols)
]
);
} }
} }
@@ -532,15 +554,12 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($contains->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($contains->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($contains->getListviewData()),
'data' => $contains->getListviewData(), 'name' => '$LANG.tab_canbeplacedin',
'params' => [ 'id' => 'can-be-placed-in',
'name' => '$LANG.tab_canbeplacedin', 'hiddenCols' => ['side']
'id' => 'can-be-placed-in', )];
'hiddenCols' => "$['side']"
]
);
} }
} }
@@ -553,22 +572,19 @@ class ItemPage extends genericPage
$criteriaOf = new AchievementList($conditions); $criteriaOf = new AchievementList($conditions);
if (!$criteriaOf->error) if (!$criteriaOf->error)
{ {
$this->extendGlobalData($criteriaOf->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $this->extendGlobalData($criteriaOf->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$hCols = []; $tabData = array(
if (!$criteriaOf->hasSetFields(['reward_loc0'])) 'data' => array_values($criteriaOf->getListviewData()),
$hCols = ['rewards']; 'name' => '$LANG.tab_criteriaof',
'id' => 'criteria-of',
'visibleCols' => ['category']
);
$this->lvTabs[] = array( if (!$criteriaOf->hasSetFields(['reward_loc0']))
'file' => 'achievement', $tabData['hiddenCols'] = ['rewards'];
'data' => $criteriaOf->getListviewData(),
'params' => [ $this->lvTabs[] = ['achievement', $tabData];
'name' => '$LANG.tab_criteriaof',
'id' => 'criteria-of',
'visibleCols' => "$['category']",
'hiddenCols' => '$'.Util::toJSON($hCols)
]
);
} }
// tab: reagent for // tab: reagent for
@@ -583,15 +599,12 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($reagent->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($reagent->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
$this->lvTabs[] = array( $this->lvTabs[] = ['spell', array(
'file' => 'spell', 'data' => array_values($reagent->getListviewData()),
'data' => $reagent->getListviewData(), 'name' => '$LANG.tab_reagentfor',
'params' => [ 'id' => 'reagent-for',
'name' => '$LANG.tab_reagentfor', 'visibleCols' => ['reagents']
'id' => 'reagent-for', )];
'visibleCols' => "$['reagents']"
]
);
} }
// tab: unlocks (object or item) // tab: unlocks (object or item)
@@ -608,14 +621,11 @@ class ItemPage extends genericPage
$lockedObj = new GameObjectList(array(['lockId', $lockIds])); $lockedObj = new GameObjectList(array(['lockId', $lockIds]));
if (!$lockedObj->error) if (!$lockedObj->error)
{ {
$this->lvTabs[] = array( $this->lvTabs[] = ['object', array(
'file' => 'object', 'data' => array_values($lockedObj->getListviewData()),
'data' => $lockedObj->getListviewData(), 'name' => '$LANG.tab_unlocks',
'params' => [ 'id' => 'unlocks-object'
'name' => '$LANG.tab_unlocks', )];
'id' => 'unlocks-object'
]
);
} }
// items (generally unused. It's the spell on the item, that unlocks stuff) // items (generally unused. It's the spell on the item, that unlocks stuff)
@@ -624,14 +634,11 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($lockedItm->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($lockedItm->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($lockedItm->getListviewData()),
'data' => $lockedItm->getListviewData(), 'name' => '$LANG.tab_unlocks',
'params' => [ 'id' => 'unlocks-item'
'name' => '$LANG.tab_unlocks', )];
'id' => 'unlocks-item'
]
);
} }
} }
@@ -659,14 +666,11 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($saItems->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($saItems->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($saItems->getListviewData()),
'data' => $saItems->getListviewData(), 'name' => '$LANG.tab_seealso',
'params' => [ 'id' => 'see-also'
'name' => '$LANG.tab_seealso', )];
'id' => 'see-also'
]
);
} }
// tab: starts (quest) // tab: starts (quest)
@@ -677,14 +681,11 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($starts->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $this->extendGlobalData($starts->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$this->lvTabs[] = array( $this->lvTabs[] = ['quest', array(
'file' => 'quest', 'data' => array_values($starts->getListviewData()),
'data' => $starts->getListviewData(), 'name' => '$LANG.tab_starts',
'params' => [ 'id' => 'starts-quest'
'name' => '$LANG.tab_starts', )];
'id' => 'starts-quest'
]
);
} }
} }
@@ -699,14 +700,11 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($objective->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $this->extendGlobalData($objective->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$this->lvTabs[] = array( $this->lvTabs[] = ['quest', array(
'file' => 'quest', 'data' => array_values($objective->getListviewData()),
'data' => $objective->getListviewData(), 'name' => '$LANG.tab_objectiveof',
'params' => [ 'id' => 'objective-of-quest'
'name' => '$LANG.tab_objectiveof', )];
'id' => 'objective-of-quest'
]
);
} }
// tab: provided for (quest) // tab: provided for (quest)
@@ -720,14 +718,11 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($provided->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS)); $this->extendGlobalData($provided->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_REWARDS));
$this->lvTabs[] = array( $this->lvTabs[] = ['quest', array(
'file' => 'quest', 'data' => array_values($provided->getListviewData()),
'data' => $provided->getListviewData(), 'name' => '$LANG.tab_providedfor',
'params' => [ 'id' => 'provided-for-quest'
'name' => '$LANG.tab_providedfor', )];
'id' => 'provided-for-quest'
]
);
} }
// tab: same model as // tab: same model as
@@ -739,15 +734,12 @@ class ItemPage extends genericPage
{ {
$this->extendGlobalData($sameModel->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($sameModel->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array( $this->lvTabs[] = ['genericmodel', array(
'file' => 'genericmodel', 'data' => array_values($sameModel->getListviewData(ITEMINFO_MODEL)),
'data' => $sameModel->getListviewData(ITEMINFO_MODEL), 'name' => '$LANG.tab_samemodelas',
'params' => [ 'id' => 'same-model-as',
'name' => '$LANG.tab_samemodelas', 'genericlinktype' => 'item'
'id' => 'same-model-as', )];
'genericlinktype' => 'item'
]
);
} }
} }
@@ -761,7 +753,7 @@ class ItemPage extends genericPage
$sbData = $soldBy->getListviewData(); $sbData = $soldBy->getListviewData();
$this->extendGlobalData($soldBy->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($soldBy->getJSGlobals(GLOBALINFO_SELF));
$extraCols = ['Listview.extraCols.stock', "Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", 'Listview.extraCols.cost']; $extraCols = ['$Listview.extraCols.stock', "\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'];
$holidays = []; $holidays = [];
foreach ($sbData as $k => &$row) foreach ($sbData as $k => &$row)
@@ -791,7 +783,7 @@ class ItemPage extends genericPage
if ($e = $vendors[$k]['event']) if ($e = $vendors[$k]['event'])
{ {
if (count($extraCols) == 3) if (count($extraCols) == 3)
$extraCols[] = 'Listview.extraCols.condition'; $extraCols[] = '$Listview.extraCols.condition';
$this->extendGlobalIds(TYPE_WORLDEVENT, $e); $this->extendGlobalIds(TYPE_WORLDEVENT, $e);
$row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $e]]; $row['condition'][0][$this->typeId][] = [[CND_ACTIVE_EVENT, $e]];
@@ -814,16 +806,13 @@ class ItemPage extends genericPage
} }
$this->lvTabs[] = array( $this->lvTabs[] = ['creature', array(
'file' => 'creature', 'data' => array_values($sbData),
'data' => $sbData, 'name' => '$LANG.tab_soldby',
'params' => [ 'id' => 'sold-by-npc',
'name' => '$LANG.tab_soldby', 'extraCols' => $extraCols,
'id' => 'sold-by-npc', 'hiddenCols' => ['level', 'type']
'extraCols' => '$['.implode(', ', $extraCols).']', )];
'hiddenCols' => "$['level', 'type']"
]
);
} }
} }
@@ -852,23 +841,22 @@ class ItemPage extends genericPage
$boughtBy = new ItemList(array(['id', $boughtBy])); $boughtBy = new ItemList(array(['id', $boughtBy]));
if (!$boughtBy->error) if (!$boughtBy->error)
{ {
if ($boughtBy->getMatches() <= CFG_SQL_LIMIT_DEFAULT)
$n = null;
$iCur = new CurrencyList(array(['itemId', $this->typeId])); $iCur = new CurrencyList(array(['itemId', $this->typeId]));
$filter = $iCur->error ? [TYPE_ITEM => $this->typeId] : [TYPE_CURRENCY => $iCur->id]; $filter = $iCur->error ? [TYPE_ITEM => $this->typeId] : [TYPE_CURRENCY => $iCur->id];
$this->lvTabs[] = array( $tabData = array(
'file' => 'item', 'data' => array_values($boughtBy->getListviewData(ITEMINFO_VENDOR, $filter)),
'data' => $boughtBy->getListviewData(ITEMINFO_VENDOR, $filter), 'name' => '$LANG.tab_currencyfor',
'params' => [ 'id' => 'currency-for',
'name' => '$LANG.tab_currencyfor', 'extraCols' => ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'],
'id' => 'currency-for',
'extraCols' => "$[Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack'), Listview.extraCols.cost]",
'note' => $n ? sprintf(Util::$filterResultString, $n) : null
]
); );
$this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_ANY));
if ($boughtBy->getMatches() > CFG_SQL_LIMIT_DEFAULT && $n)
$tabData['note'] = sprintf(Util::$filterResultString, $n);
$this->lvTabs[] = ['item', $tabData];
$this->extendGlobalData($boughtBy->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
} }
} }
@@ -891,7 +879,7 @@ class ItemPage extends genericPage
foreach ($_ as $idx) foreach ($_ as $idx)
$ids[] = $indirectSpells->getField('effect'.$idx.'TriggerSpell'); $ids[] = $indirectSpells->getField('effect'.$idx.'TriggerSpell');
$ids = array_merge($ids, Util::getTaughtSpells($indirect)); $ids = array_merge($ids, Game::getTaughtSpells($indirect));
} }
if ($ids) if ($ids)
@@ -905,15 +893,12 @@ class ItemPage extends genericPage
if ($taughtSpells->hasSetFields(['reagent1'])) if ($taughtSpells->hasSetFields(['reagent1']))
$visCols[] = 'reagents'; $visCols[] = 'reagents';
$this->lvTabs[] = array( $this->lvTabs[] = ['spell', array(
'file' => 'spell', 'data' => array_values($taughtSpells->getListviewData()),
'data' => $taughtSpells->getListviewData(), 'name' => '$LANG.tab_teaches',
'params' => [ 'id' => 'teaches',
'name' => '$LANG.tab_teaches', 'visibleCols' => $visCols
'id' => 'teaches', )];
'visibleCols' => '$'.Util::toJSON($visCols)
]
);
} }
} }
@@ -926,29 +911,68 @@ class ItemPage extends genericPage
if ($cdCats) if ($cdCats)
{ {
$conditions = array( $conditions = array(
'OR', ['id', $this->typeId, '!'],
['spellCategory1', $cdCats], [
['spellCategory2', $cdCats], 'OR',
['spellCategory3', $cdCats], ['spellCategory1', $cdCats],
['spellCategory4', $cdCats], ['spellCategory2', $cdCats],
['spellCategory5', $cdCats] ['spellCategory3', $cdCats],
['spellCategory4', $cdCats],
['spellCategory5', $cdCats],
]
); );
$cdItems = new ItemList($conditions); $cdItems = new ItemList($conditions);
if (!$cdItems->error) if (!$cdItems->error)
{ {
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($cdItems->getListviewData()),
'data' => $cdItems->getListviewData(), 'name' => '$LANG.tab_sharedcooldown',
'params' => [ 'id' => 'shared-cooldown'
'name' => '$LANG.tab_sharedcooldown', )];
'id' => 'shared-cooldown'
]
);
$this->extendGlobalData($cdItems->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($cdItems->getJSGlobals(GLOBALINFO_SELF));
} }
} }
// tab: sounds
$soundIds = [];
if ($_class == ITEM_CLASS_WEAPON)
{
$scm = (1 << $_subClass);
if ($this->subject->getField('soundOverrideSubclass') > 0)
$scm = (1 << $this->subject->getField('soundOverrideSubclass'));
$soundIds = DB::Aowow()->selectCol('SELECT soundId FROM ?_items_sounds WHERE subClassMask & ?d', $scm);
}
$fields = ['pickUpSoundId', 'dropDownSoundId', 'sheatheSoundId', 'unsheatheSoundId'];
foreach ($fields as $f)
if ($x = $this->subject->getField($f))
$soundIds[] = $x;
if ($x = $this->subject->getField('spellVisualId'))
{
if ($spellSounds = DB::Aowow()->selectRow('SELECT * FROM ?_spell_sounds WHERE id = ?d', $x))
{
array_shift($spellSounds); // bye 'id'-field
foreach ($spellSounds as $ss)
if ($ss)
$soundIds[] = $ss;
}
}
if ($soundIds)
{
$sounds = new SoundList(array(['id', $soundIds]));
if (!$sounds->error)
{
$this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = ['sound', ['data' => array_values($sounds->getListviewData())]];
}
}
// // todo - tab: taught by // // todo - tab: taught by
// use var $createdBy to find source of this spell // use var $createdBy to find source of this spell
// id: 'taught-by-X', // id: 'taught-by-X',
@@ -965,9 +989,9 @@ class ItemPage extends genericPage
return '$WowheadPower.registerItem(\''.$itemString.'\', '.User::$localeId.', {})'; return '$WowheadPower.registerItem(\''.$itemString.'\', '.User::$localeId.', {})';
$x = '$WowheadPower.registerItem(\''.$itemString.'\', '.User::$localeId.", {\n"; $x = '$WowheadPower.registerItem(\''.$itemString.'\', '.User::$localeId.", {\n";
$x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n"; $x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true, false, $this->enhancedTT))."',\n";
$x .= "\tquality: ".$this->subject->getField('quality').",\n"; $x .= "\tquality: ".$this->subject->getField('quality').",\n";
$x .= "\ticon: '".urlencode($this->subject->getField('iconString'))."',\n"; $x .= "\ticon: '".rawurlencode($this->subject->getField('iconString', true, true))."',\n";
$x .= "\ttooltip_".User::$localeString.": '".Util::jsEscape($this->subject->renderTooltip(false, 0, $this->enhancedTT))."'\n"; $x .= "\ttooltip_".User::$localeString.": '".Util::jsEscape($this->subject->renderTooltip(false, 0, $this->enhancedTT))."'\n";
$x .= "});"; $x .= "});";
@@ -984,7 +1008,7 @@ class ItemPage extends genericPage
{ {
// item root // item root
$xml = $root->addChild('item'); $xml = $root->addChild('item');
$xml->addAttribute('id', $this->subject->id); $xml->addAttribute('id', $this->typeId);
// name // name
$xml->addChild('name')->addCData($this->subject->getField('name', true)); $xml->addChild('name')->addCData($this->subject->getField('name', true));
@@ -1008,50 +1032,62 @@ class ItemPage extends genericPage
$this->subject->extendJsonStats(); $this->subject->extendJsonStats();
// json // json
$fields = ["classs", "displayid", "dps", "id", "level", "name", "reqlevel", "slot", "slotbak", "source", "sourcemore", "speed", "subclass"]; $fields = ['classs', 'displayid', 'dps', 'id', 'level', 'name', 'reqlevel', 'slot', 'slotbak', 'speed', 'subclass'];
$json = ''; $json = [];
foreach ($fields as $f) foreach ($fields as $f)
{ {
if (isset($this->subject->json[$this->subject->id][$f])) if (isset($this->subject->json[$this->typeId][$f]))
{ {
$_ = $this->subject->json[$this->subject->id][$f]; $_ = $this->subject->json[$this->typeId][$f];
if ($f == 'name') if ($f == 'name')
$_ = (7 - $this->subject->getField('quality')).$_; $_ = (7 - $this->subject->getField('quality')).$_;
$json .= ',"'.$f.'":'.$_; $json[$f] = $_;
} }
} }
$xml->addChild('json')->addCData(substr($json, 1));
// jsonEquip missing: avgbuyout, cooldown, source, sourcemore // itemsource
$json = ''; if ($this->subject->getSources($s, $m))
{
$json['source'] = $s;
if ($m)
$json['sourcemore'] = $m;
}
$xml->addChild('json')->addCData(substr(json_encode($json), 1, -1));
// jsonEquip missing: avgbuyout
$json = [];
if ($_ = $this->subject->getField('sellPrice')) // sellprice if ($_ = $this->subject->getField('sellPrice')) // sellprice
$json .= ',"sellprice":'.$_; $json['sellprice'] = $_;
if ($_ = $this->subject->getField('requiredLevel')) // reqlevel if ($_ = $this->subject->getField('requiredLevel')) // reqlevel
$json .= ',"reqlevel":'.$_; $json['reqlevel'] = $_;
if ($_ = $this->subject->getField('requiredSkill')) // reqskill if ($_ = $this->subject->getField('requiredSkill')) // reqskill
$json .= ',"reqskill":'.$_; $json['reqskill'] = $_;
if ($_ = $this->subject->getField('requiredSkillRank')) // reqskillrank if ($_ = $this->subject->getField('requiredSkillRank')) // reqskillrank
$json .= ',"reqskillrank":'.$_; $json['reqskillrank'] = $_;
foreach ($this->subject->itemMods[$this->subject->id] as $mod => $qty) if ($_ = $this->subject->getField('cooldown')) // cooldown
$json .= ',"'.$mod.'":'.$qty; $json['cooldown'] = $_ / 1000;
foreach ($_ = $this->subject->json[$this->subject->id] as $name => $qty) foreach ($this->subject->itemMods[$this->typeId] as $mod => $qty)
$json[$mod] = $qty;
foreach ($this->subject->json[$this->typeId] as $name => $qty)
if (in_array($name, Util::$itemFilter)) if (in_array($name, Util::$itemFilter))
$json .= ',"'.$name.'":'.$qty; $json[$name] = $qty;
$xml->addChild('jsonEquip')->addCData(substr($json, 1)); $xml->addChild('jsonEquip')->addCData(substr(json_encode($json), 1, -1));
// jsonUse // jsonUse
if ($onUse = $this->subject->getOnUseStats()) if ($onUse = $this->subject->getOnUseStats())
{ {
$j = ''; $j = '';
foreach ($onUse as $idx => $qty) foreach ($onUse as $idx => $qty)
$j .= ',"'.Util::$itemMods[$idx].'":'.$qty; $j .= ',"'.Game::$itemMods[$idx].'":'.$qty;
$xml->addChild('jsonUse')->addCData(substr($j, 1)); $xml->addChild('jsonUse')->addCData(substr($j, 1));
} }
@@ -1059,9 +1095,9 @@ class ItemPage extends genericPage
// reagents // reagents
$cnd = array( $cnd = array(
'OR', 'OR',
['AND', ['effect1CreateItemId', $this->subject->id], ['OR', ['effect1Id', SpellList::$effects['itemCreate']], ['effect1AuraId', SpellList::$auras['itemCreate']]]], ['AND', ['effect1CreateItemId', $this->typeId], ['OR', ['effect1Id', SpellList::$effects['itemCreate']], ['effect1AuraId', SpellList::$auras['itemCreate']]]],
['AND', ['effect2CreateItemId', $this->subject->id], ['OR', ['effect2Id', SpellList::$effects['itemCreate']], ['effect2AuraId', SpellList::$auras['itemCreate']]]], ['AND', ['effect2CreateItemId', $this->typeId], ['OR', ['effect2Id', SpellList::$effects['itemCreate']], ['effect2AuraId', SpellList::$auras['itemCreate']]]],
['AND', ['effect3CreateItemId', $this->subject->id], ['OR', ['effect3Id', SpellList::$effects['itemCreate']], ['effect3AuraId', SpellList::$auras['itemCreate']]]], ['AND', ['effect3CreateItemId', $this->typeId], ['OR', ['effect3Id', SpellList::$effects['itemCreate']], ['effect3AuraId', SpellList::$auras['itemCreate']]]],
); );
$spellSource = new SpellList($cnd); $spellSource = new SpellList($cnd);
@@ -1073,7 +1109,7 @@ class ItemPage extends genericPage
{ {
foreach ($spellSource->canCreateItem() as $idx) foreach ($spellSource->canCreateItem() as $idx)
{ {
if ($spellSource->getField('effect'.$idx.'CreateItemId') != $this->subject->id) if ($spellSource->getField('effect'.$idx.'CreateItemId') != $this->typeId)
continue; continue;
$splNode = $cbNode->addChild('spell'); $splNode = $cbNode->addChild('spell');
@@ -1136,7 +1172,7 @@ class ItemPage extends genericPage
return parent::display($override); return parent::display($override);
} }
public function notFound() public function notFound($title = '', $msg = '')
{ {
if ($this->mode == CACHE_TYPE_TOOLTIP) if ($this->mode == CACHE_TYPE_TOOLTIP)
{ {
@@ -1151,7 +1187,7 @@ class ItemPage extends genericPage
exit(); exit();
} }
else else
return parent::notFound(Lang::game('item'), Lang::item('notFound')); return parent::notFound($title ?: Lang::game('item'), $msg ?: Lang::item('notFound'));
} }
} }

View File

@@ -81,13 +81,14 @@ class ItemsPage extends GenericPage
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
{ {
$this->filterObj = new ItemListFilter();
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($pageParam);
$this->filterObj = new ItemListFilter(false, ['parentCats' => $this->category]);
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
$this->name = Util::ucFirst(Lang::game('items')); $this->name = Util::ucFirst(Lang::game('items'));
$this->subCat = $pageParam ? '='.$pageParam : ''; $this->subCat = $pageParam !== null ? '='.$pageParam : '';
} }
protected function generateContent() protected function generateContent()
@@ -107,12 +108,21 @@ class ItemsPage extends GenericPage
if ($_ = $this->filterObj->getConditions()) if ($_ = $this->filterObj->getConditions())
$conditions[] = $_; $conditions[] = $_;
$this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); $this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null; $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['fi'] = $this->filterObj->getForm(); $this->filter['initData'] = ['init' => 'items'];
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
$xCols = $this->filterObj->getExtraCols();
if ($xCols)
$this->filter['initData']['ec'] = $xCols;
if ($x = $this->filterObj->getSetWeights())
$this->filter['initData']['sw'] = $x;
$menu = $this->createExtraMenus(); $menu = $this->createExtraMenus();
foreach ($menu['type'][0] as $k => $str) foreach ($menu['type'][0] as $k => $str)
if ($str && (!$menu['type'][1] || ($menu['type'][1] & (1 << $k)))) if ($str && (!$menu['type'][1] || ($menu['type'][1] & (1 << $k))))
$this->filter['type'][$k] = $str; $this->filter['type'][$k] = $str;
@@ -124,13 +134,12 @@ class ItemsPage extends GenericPage
if (isset($this->filter['slot'][INVTYPE_SHIELD])) // "Off Hand" => "Shield" if (isset($this->filter['slot'][INVTYPE_SHIELD])) // "Off Hand" => "Shield"
$this->filter['slot'][INVTYPE_SHIELD] = Lang::item('armorSubClass', 6); $this->filter['slot'][INVTYPE_SHIELD] = Lang::item('armorSubClass', 6);
$xCols = $this->filterObj->getForm('extraCols', true);
$infoMask = ITEMINFO_JSON; $infoMask = ITEMINFO_JSON;
if (array_intersect([63, 64, 125], $xCols)) // 63:buyPrice; 64:sellPrice; 125:reqarenartng if (array_intersect([63, 64, 125], $xCols)) // 63:buyPrice; 64:sellPrice; 125:reqarenartng
$infoMask |= ITEMINFO_VENDOR; $infoMask |= ITEMINFO_VENDOR;
if (!empty($this->filter['fi']['extraCols'])) if ($xCols)
$this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, '.(isset($this->filter['gm']) ? $this->filter['gm'] : 0).', '.(array_intersect([63], $xCols) ? 1 : 0).')'; $this->sharedLV['extraCols'] = '$fi_getExtraCols(fi_extraCols, '.(isset($this->filter['gm']) ? $this->filter['gm'] : 0).', '.(array_intersect([63], $xCols) ? 1 : 0).')';
if ($this->filterObj->error) if ($this->filterObj->error)
@@ -158,7 +167,7 @@ class ItemsPage extends GenericPage
/*************************/ /*************************/
$upgItemData = []; $upgItemData = [];
if (!empty($this->filter['upg']) && !empty($this->filter['fi']['setWeights'])) if (!empty($this->filter['upg']) && !empty($this->filterObj->getSetWeights()))
{ {
$upgItems = new ItemList(array(['id', array_keys($this->filter['upg'])]), ['extraOpts' => $this->filterObj->extraOpts]); $upgItems = new ItemList(array(['id', array_keys($this->filter['upg'])]), ['extraOpts' => $this->filterObj->extraOpts]);
if (!$upgItems->error) if (!$upgItems->error)
@@ -232,7 +241,7 @@ class ItemsPage extends GenericPage
if (isset($this->filter['sl'])) // skip lookups for unselected slots if (isset($this->filter['sl'])) // skip lookups for unselected slots
$groups = array_intersect($groups, (array)$this->filter['sl']); $groups = array_intersect($groups, (array)$this->filter['sl']);
if (isset($this->filter['upg'])) // skip lookups for slots we dont have items to upgrade for if (!empty($this->filter['upg'])) // skip lookups for slots we dont have items to upgrade for
$groups = array_intersect($groups, (array)$this->filter['upg']); $groups = array_intersect($groups, (array)$this->filter['upg']);
if ($groups) if ($groups)
@@ -249,7 +258,6 @@ class ItemsPage extends GenericPage
$extraOpts = array_merge($this->filterOpts, ['i' => ['g' => ['itemlevel'], 'o' => ['itemlevel DESC']]]); $extraOpts = array_merge($this->filterOpts, ['i' => ['g' => ['itemlevel'], 'o' => ['itemlevel DESC']]]);
$levelRef = new ItemList(array_merge($conditions, [10]), ['extraOpts' => $extraOpts]); $levelRef = new ItemList(array_merge($conditions, [10]), ['extraOpts' => $extraOpts]);
foreach ($levelRef->iterate() as $_) foreach ($levelRef->iterate() as $_)
{ {
$l = $levelRef->getField('itemLevel'); $l = $levelRef->getField('itemLevel');
@@ -306,10 +314,9 @@ class ItemsPage extends GenericPage
continue; continue;
$this->extendGlobalData($items->getJSGlobals()); $this->extendGlobalData($items->getJSGlobals());
$tab = array( $tabData = array_merge(
'file' => 'item', ['data' => $items->getListviewData($infoMask)],
'data' => $items->getListviewData($infoMask), $this->sharedLV
'params' => $this->sharedLV
); );
$upg = []; $upg = [];
@@ -322,17 +329,17 @@ class ItemsPage extends GenericPage
})); }));
foreach ($upg as $uId) foreach ($upg as $uId)
$tab['data'][$uId] = $upgItemData[$uId]; $tabData['data'][$uId] = $upgItemData[$uId];
if ($upg) if ($upg)
$tab['params']['_upgradeIds'] = '$'.Util::toJSON($upg); $tabData['_upgradeIds'] = $upg;
} }
else if ($grouping) else if ($grouping)
{ {
$upg = array_keys($this->filter['upg']); $upg = array_keys($this->filter['upg']);
$tab['params']['_upgradeIds'] = '$'.Util::toJSON($upg); $tabData['_upgradeIds'] = $upg;
foreach ($upgItemData as $uId => $data) // using numeric keys => cant use array_merge foreach ($upgItemData as $uId => $data) // using numeric keys => cant use array_merge
$tab['data'][$uId] = $data; $tabData['data'][$uId] = $data;
} }
} }
@@ -341,28 +348,28 @@ class ItemsPage extends GenericPage
switch ($grouping) switch ($grouping)
{ {
case 1: case 1:
$tab['params']['id'] = 'slot-'.$group; $tabData['id'] = 'slot-'.$group;
break; break;
case 2: case 2:
$tab['params']['id'] = $group > 0 ? 'level-'.$group : 'other'; $tabData['id'] = $group > 0 ? 'level-'.$group : 'other';
break; break;
case 3: case 3:
$tab['params']['id'] = $group ? 'source-'.$group : 'unknown'; $tabData['id'] = $group ? 'source-'.$group : 'unknown';
break; break;
} }
$tab['params']['name'] = $nameSource[$group]; $tabData['name'] = $nameSource[$group];
$tab['params']['tabs'] = '$tabsGroups'; $tabData['tabs'] = '$tabsGroups';
} }
if (!empty($this->filter['fi']['setWeights'])) if (!empty($this->filterObj->getSetWeights()))
if ($items->hasSetFields(['armor'])) if ($items->hasSetFields(['armor']))
$tab['params']['visibleCols'][] = 'armor'; $tabData['visibleCols'][] = 'armor';
// create note if search limit was exceeded; overwriting 'note' is intentional // create note if search limit was exceeded; overwriting 'note' is intentional
if ($items->getMatches() > $maxResults && count($groups) > 1) if ($items->getMatches() > $maxResults && count($groups) > 1)
{ {
$tab['params']['_truncated'] = 1; $tabData['_truncated'] = 1;
$cls = isset($this->category[0]) ? '='.$this->category[0] : ''; $cls = isset($this->category[0]) ? '='.$this->category[0] : '';
$override = ['gb' => '']; $override = ['gb' => ''];
@@ -373,7 +380,7 @@ class ItemsPage extends GenericPage
{ {
case 1: case 1:
$override['sl'] = $group; $override['sl'] = $group;
$tab['params']['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \''.$cls.'\', \''.$this->filterObj->urlize($override).'\')'; $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoreslot, \''.$cls.'\', \''.$this->filterObj->getFilterString($override).'\')';
break; break;
case 2: case 2:
if ($group > 0) if ($group > 0)
@@ -384,46 +391,42 @@ class ItemsPage extends GenericPage
else else
$override['maxle'] = abs($group) - 1; $override['maxle'] = abs($group) - 1;
$tab['params']['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \''.$cls.'\', \''.$this->filterObj->urlize($override).'\')'; $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmorelevel, \''.$cls.'\', \''.$this->filterObj->getFilterString($override).'\')';
break; break;
case 3: case 3:
if ($_ = [null, 3, 4, 5, 6, 7, 9, 10, 11][$group]) if ($_ = [null, 3, 4, 5, 6, 7, 9, 10, 11][$group])
$tab['params']['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \''.$cls.'\', \''.$this->filterObj->urlize($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]).'\')'; $tabData['note'] = '$$WH.sprintf(LANG.lvnote_viewmoresource, \''.$cls.'\', \''.$this->filterObj->getFilterString($override, ['cr' => 128, 'crs' => $_, 'crv' => 0]).'\')';
break; break;
} }
} }
else if ($items->getMatches() > $maxResults) else if ($items->getMatches() > $maxResults)
{ {
$tab['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsfound', $items->getMatches(), CFG_SQL_LIMIT_DEFAULT); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsfound', $items->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$tab['params']['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
if (!empty($tab['params']['hiddenCols'])) foreach ($tabData as $k => $p)
$tab['params']['hiddenCols'] = '$'.Util::toJSON($tab['params']['hiddenCols']); if (!$p && $k != 'data')
unset($tabData[$k]);
if (!empty($tab['params']['visibleCols']))
$tab['params']['visibleCols'] = '$'.Util::toJSON($tab['params']['visibleCols']);
foreach ($tab['params'] as $k => $p)
if (!$p)
unset($tab['params'][$k]);
if ($grouping) if ($grouping)
$tab['params']['hideCount'] = '$1'; $tabData['hideCount'] = 1;
$this->lvTabs[] = $tab; $tabData['data'] = array_values($tabData['data']);
$this->lvTabs[] = ['item', $tabData];
} }
// reformat for use in template // reformat for use in template
if (isset($this->filter['upg'])) if (!empty($this->filter['upg']))
$this->filter['upg'] = implode(':', array_keys($this->filter['upg'])); $this->filter['upg'] = implode(':', array_keys($this->filter['upg']));
// whoops, we have no data? create emergency content // whoops, we have no data? create emergency content
if (empty($this->lvTabs)) if (empty($this->lvTabs))
{ {
$this->forceTabs = false; $this->forceTabs = false;
$this->lvTabs[] = ['file' => 'item', 'data' => [], 'params' => []]; $this->lvTabs[] = ['item', ['data' => []]];
} }
// sort for dropdown-menus // sort for dropdown-menus
@@ -442,6 +445,8 @@ class ItemsPage extends GenericPage
$tPart = Lang::item('cat', $this->category[0], 1, $this->category[1], 1, $this->category[2]); $tPart = Lang::item('cat', $this->category[0], 1, $this->category[1], 1, $this->category[2]);
else if (isset($this->category[1]) && is_array(Lang::item('cat', $this->category[0]))) else if (isset($this->category[1]) && is_array(Lang::item('cat', $this->category[0])))
$tPart = Lang::item('cat', $this->category[0], 1, $this->category[1]); $tPart = Lang::item('cat', $this->category[0], 1, $this->category[1]);
else if ($this->category[0] == 0 && isset($this->filter['ty']) && !is_array($this->filter['ty']))
$tPart = Lang::item('cat', 0, 1, $this->filter['ty']);
else else
$tPart = Lang::item('cat', $this->category[0]); $tPart = Lang::item('cat', $this->category[0]);
@@ -454,9 +459,11 @@ class ItemsPage extends GenericPage
$this->path[] = $c; $this->path[] = $c;
// if slot-dropdown is available && Armor && $path points to Armor-Class // if slot-dropdown is available && Armor && $path points to Armor-Class
$form = $this->filterObj->getForm('form'); $form = $this->filterObj->getForm();
if (count($this->path) == 4 && $this->category[0] == 4 && isset($form['sl']) && !is_array($form['sl'])) if (count($this->path) == 4 && $this->category[0] == 4 && isset($form['sl']) && !is_array($form['sl']))
$this->path[] = $form['sl']; $this->path[] = $form['sl'];
else if (!empty($this->category[0]) && $this->category[0] == 0 && isset($form['ty']) && !is_array($form['ty']))
$this->path[] = $form['ty'];
} }
// fetch best possible gems for chosen weights // fetch best possible gems for chosen weights
@@ -464,7 +471,7 @@ class ItemsPage extends GenericPage
{ {
$gemScores = []; $gemScores = [];
if (empty($this->filter['fi']['setWeights'])) if (empty($this->filterObj->getSetWeights()))
return []; return [];
if (!empty($this->filter['gm'])) if (!empty($this->filter['gm']))
@@ -502,7 +509,7 @@ class ItemsPage extends GenericPage
$this->sharedLV['onBeforeCreate'] = '$fi_initWeightedListview'; $this->sharedLV['onBeforeCreate'] = '$fi_initWeightedListview';
$this->sharedLV['onAfterCreate'] = '$fi_addUpgradeIndicator'; $this->sharedLV['onAfterCreate'] = '$fi_addUpgradeIndicator';
$this->sharedLV['sort'] = "$['-score', 'name']"; $this->sharedLV['sort'] = ['-score', 'name'];
array_push($this->sharedLV['hiddenCols'], 'type', 'source'); array_push($this->sharedLV['hiddenCols'], 'type', 'source');
@@ -534,8 +541,7 @@ class ItemsPage extends GenericPage
switch ($this->category[0]) switch ($this->category[0])
{ {
case 0: case 0:
if (!isset($this->category[1])) $menu['type'] = [Lang::item('cat', 0, 1), null];
$menu['type'] = [Lang::item('cat', 0, 1), null];
if (!isset($this->category[1]) || in_array($this->category[1], [6, -3])) if (!isset($this->category[1]) || in_array($this->category[1], [6, -3]))
{ {

View File

@@ -25,11 +25,15 @@ class ItemsetPage extends GenericPage
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
// temp locale
if ($this->mode == CACHE_TYPE_TOOLTIP && isset($_GET['domain']))
Util::powerUseLocale($_GET['domain']);
$this->typeId = intVal($id); $this->typeId = intVal($id);
$this->subject = new ItemsetList(array(['id', $this->typeId])); $this->subject = new ItemsetList(array(['id', $this->typeId]));
if ($this->subject->error) if ($this->subject->error)
$this->notFound(Lang::game('itemset'), Lang::itemset('notFound')); $this->notFound();
$this->name = $this->subject->getField('name', true); $this->name = $this->subject->getField('name', true);
$this->extendGlobalData($this->subject->getJSGlobals()); $this->extendGlobalData($this->subject->getJSGlobals());
@@ -68,11 +72,11 @@ class ItemsetPage extends GenericPage
if ($this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE) if ($this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE)
$infobox[] = Lang::main('unavailable'); $infobox[] = Lang::main('unavailable');
// holiday // worldevent
if ($h = $this->subject->getField('holidayId')) if ($e = $this->subject->getField('eventId'))
{ {
$infobox[] = Lang::game('eventShort').Lang::main('colon').'[event='.$h.']'; $infobox[] = Lang::game('eventShort').Lang::main('colon').'[event='.$e.']';
$this->extendGlobalIds(TYPE_WORLDEVENT, $h); $this->extendGlobalIds(TYPE_WORLDEVENT, $e);
} }
// itemLevel // itemLevel
@@ -135,54 +139,14 @@ class ItemsetPage extends GenericPage
$compare[] = $itemId; $compare[] = $itemId;
$pieces[] = array( $pieces[$itemId] = array(
'id' => $itemId, 'name_'.User::$localeString => $iList->getField('name', true),
'name' => $iList->getField('name', true), 'quality' => $iList->getField('quality'),
'quality' => $iList->getField('quality'), 'icon' => $iList->getField('iconString'),
'icon' => $iList->getField('iconString'), 'jsonequip' => $data[$itemId]
'json' => $data[$itemId]
); );
} }
// spells
$foo = [];
$spells = [];
for ($i = 1; $i < 9; $i++)
{
$spl = $this->subject->getField('spell'.$i);
$qty = $this->subject->getField('bonus'.$i);
if ($spl && $qty)
{
$foo[] = $spl;
$spells[] = array( // cant use spell as index, would change order
'id' => $spl,
'bonus' => $qty,
'desc' => ''
);
}
}
// 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', $foo]));
foreach ($setSpells->iterate() as $spellId => $__)
{
foreach ($spells as &$s)
{
if ($spellId != $s['id'])
continue;
$s['desc'] = $setSpells->parseText('description')[0];
}
}
$skill = ''; $skill = '';
if ($_sk = $this->subject->getField('skillId')) if ($_sk = $this->subject->getField('skillId'))
{ {
@@ -195,19 +159,20 @@ class ItemsetPage extends GenericPage
$this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE; $this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE;
$this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null; $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
$this->pieces = $pieces; $this->pieces = $pieces;
$this->spells = $spells; $this->spells = $this->subject->getBonuses();
$this->expansion = 0; $this->expansion = 0;
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => $this->typeId > 0, // bool only BUTTON_WOWHEAD => $this->typeId > 0, // bool only
BUTTON_LINKS => ['color' => '', 'linkId' => ''], BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_VIEW3D => ['type' => TYPE_ITEMSET, 'typeId' => $this->typeId, 'equipList' => $eqList], BUTTON_VIEW3D => ['type' => TYPE_ITEMSET, 'typeId' => $this->typeId, 'equipList' => $eqList],
BUTTON_COMPARE => ['eqList' => implode(':', $compare), 'qty' => $_cnt] BUTTON_COMPARE => ['eqList' => implode(':', $compare), 'qty' => $_cnt]
); );
$this->compare = array( $this->summary = array(
'level' => $this->subject->getField('reqLevel'), 'id' => 'itemset',
'items' => array_map(function ($v) { 'template' => 'itemset',
return [[$v]]; 'parent' => 'summary-generic',
}, $compare) 'groups' => array_map(function ($v) { return [[$v]]; }, $compare),
'level' => $this->subject->getField('reqLevel'),
); );
/**************/ /**************/
@@ -223,10 +188,10 @@ class ItemsetPage extends GenericPage
$rel[] = ['classMask', 1 << (end($this->path) - 1), '&']; $rel[] = ['classMask', 1 << (end($this->path) - 1), '&'];
$rel[] = ['contentGroup', (int)$_ta]; $rel[] = ['contentGroup', (int)$_ta];
} }
else if ($this->subject->getField('holidayId')) else if ($this->subject->getField('eventId'))
{ {
$rel[] = ['id', $this->typeId, '!']; $rel[] = ['id', $this->typeId, '!'];
$rel[] = ['holidayId', 0, '!']; $rel[] = ['eventId', 0, '!'];
} }
else if ($this->subject->getField('skillId')) else if ($this->subject->getField('skillId'))
{ {
@@ -248,24 +213,59 @@ class ItemsetPage extends GenericPage
$relSets = new ItemsetList($rel); $relSets = new ItemsetList($rel);
if (!$relSets->error) if (!$relSets->error)
{ {
$lv = array( $tabData = array(
'file' => 'itemset', 'data' => array_values($relSets->getListviewData()),
'data' => $relSets->getListviewData(), 'id' => 'see-also',
'params' => array( 'name' => '$LANG.tab_seealso'
'id' => 'see-also',
'name' => '$LANG.tab_seealso'
)
); );
if (!$relSets->hasDiffFields(['classMask'])) if (!$relSets->hasDiffFields(['classMask']))
$lv['params']['hiddenCols'] = "$['classes']"; $tabData['hiddenCols'] = ['classes'];
$this->lvTabs[] = $lv; $this->lvTabs[] = ['itemset', $tabData];
$this->extendGlobalData($relSets->getJSGlobals()); $this->extendGlobalData($relSets->getJSGlobals());
} }
} }
} }
protected function generateTooltip($asError = false)
{
if ($asError)
return '$WowheadPower.registerItemSet('.$this->typeId.', '.User::$localeId.', {});';
$x = '$WowheadPower.registerItemSet('.$this->typeId.', '.User::$localeId.", {\n";
$x .= "\tname_".User::$localeString.": '".Util::jsEscape($this->subject->getField('name', true))."',\n";
$x .= "\ttooltip_".User::$localeString.": '".$this->subject->renderTooltip()."'\n";
$x .= "});";
return $x;
}
public function display($override = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::display($override);
if (!$this->loadCache($tt))
{
$tt = $this->generateTooltip();
$this->saveCache($tt);
}
header('Content-type: application/x-javascript; charset=utf-8');
die($tt);
}
public function notFound($title = '', $msg = '')
{
if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::notFound($title ?: Lang::game('itemset'), $msg ?: Lang::itemset('notFound'));
header('Content-type: application/x-javascript; charset=utf-8');
echo $this->generateTooltip(true);
exit();
}
} }

View File

@@ -19,8 +19,8 @@ class ItemsetsPage extends GenericPage
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
{ {
$this->filterObj = new ItemsetListFilter();
$this->getCategoryFromUrl($pageParam); $this->getCategoryFromUrl($pageParam);
$this->filterObj = new ItemsetListFilter(false, ['parentCats' => $this->category]);
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
@@ -29,6 +29,8 @@ class ItemsetsPage extends GenericPage
protected function generateContent() protected function generateContent()
{ {
$this->addJS('?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$conditions = []; $conditions = [];
if (!User::isInGroup(U_GROUP_EMPLOYEE)) if (!User::isInGroup(U_GROUP_EMPLOYEE))
@@ -41,32 +43,33 @@ class ItemsetsPage extends GenericPage
$this->extendGlobalData($itemsets->getJSGlobals()); $this->extendGlobalData($itemsets->getJSGlobals());
// recreate form selection // recreate form selection
$this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); $this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL;
$this->filter['fi'] = $this->filterObj->getForm(); $this->filter['initData'] = ['init' => 'itemsets'];
$this->addJS('?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']); if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
$lv = array( $xCols = $this->filterObj->getExtraCols();
'file' => 'itemset', if ($xCols)
'data' => $itemsets->getListviewData(), // listview content $this->filter['initData']['ec'] = $xCols;
'params' => []
);
if (!empty($this->filter['fi']['extraCols'])) $tabData = ['data' => array_values($itemsets->getListviewData())];
$lv['params']['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
if ($xCols)
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
// create note if search limit was exceeded // create note if search limit was exceeded
if ($itemsets->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($itemsets->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{ {
$lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsetsfound', $itemsets->getMatches(), CFG_SQL_LIMIT_DEFAULT); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsetsfound', $itemsets->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$lv['params']['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
if ($this->filterObj->error) if ($this->filterObj->error)
$lv['params']['_errors'] = '$1'; $tabData['_errors'] = 1;
$this->lvTabs[] = $lv; $this->lvTabs[] = ['itemset', $tabData];
// sort for dropdown-menus // sort for dropdown-menus
Lang::sort('itemset', 'notes', SORT_NATURAL); Lang::sort('itemset', 'notes', SORT_NATURAL);

View File

@@ -10,21 +10,41 @@ if (!defined('AOWOW_REVISION'))
class MorePage extends GenericPage class MorePage extends GenericPage
{ {
protected $tpl = 'text-page-generic'; protected $tpl = 'list-page-generic';
protected $path = [2]; protected $path = [];
protected $tabId = 2; protected $tabId = 0;
protected $mode = CACHE_TYPE_NONE; protected $mode = CACHE_TYPE_NONE;
protected $js = ['swfobject.js']; protected $js = ['swfobject.js'];
private $subPages = [ -13 => ['commenting-and-you', 'modelviewer', 'screenshots-tips-tricks', 'stat-weighting', 'talent-calculator', 'item-comparison', 'profiler', 'markup-guide']]; private $page = [];
private $validPages = array( // [type, typeId, name] private $req2priv = array(
'whats-new' => [ -7, 0, "What's New"], 1 => CFG_REP_REQ_COMMENT, // write comments
'searchbox' => [-16, 0, 'Search Box'], 2 => 0, // NYI post external links
'tooltips' => [-10, 0, 'Tooltips'], 4 => 0, // NYI no captcha
'faq' => [ -3, 0, 'Frequently Asked Questions'], 5 => CFG_REP_REQ_SUPERVOTE, // votes count for more
'aboutus' => [ -1, 0, 'What is AoWoW?'], 9 => CFG_REP_REQ_VOTEMORE_BASE, // more votes per day
'searchplugins' => [ -8, 0, 'Search Plugins'], 10 => CFG_REP_REQ_UPVOTE, // can upvote
'help' => [-13, null, ''] 11 => CFG_REP_REQ_DOWNVOTE, // can downvote
12 => CFG_REP_REQ_REPLY, // can reply
13 => 0, // avatar border [NYI: checked by js, avatars not in use]
14 => 0, // avatar border [NYI: checked by js, avatars not in use]
15 => 0, // avatar border [NYI: checked by js, avatars not in use]
16 => 0, // avatar border [NYI: checked by js, avatars not in use]
17 => CFG_REP_REQ_PREMIUM // premium status
);
private $validPages = array( // [tabId, path[, subPaths]]
'whats-new' => [2, [2, 7]],
'searchbox' => [2, [2, 16]],
'tooltips' => [2, [2, 10]],
'faq' => [2, [2, 3]],
'aboutus' => [2, [2, 0]],
'searchplugins' => [2, [2, 8]],
'help' => [2, [2, 13], ['commenting-and-you', 'modelviewer', 'screenshots-tips-tricks', 'stat-weighting', 'talent-calculator', 'item-comparison', 'profiler', 'markup-guide']],
'reputation' => [1, [3, 10]],
'privilege' => [1, [3, 10], [1, 2, 4, 5, 9, 10, 11, 12, 13, 14, 15, 16, 17]],
'privileges' => [1, [3, 10, 0]],
'top-users' => [1, [3, 11]]
); );
public function __construct($pageCall, $subPage) public function __construct($pageCall, $subPage)
@@ -34,44 +54,186 @@ class MorePage extends GenericPage
// chack if page is valid // chack if page is valid
if (isset($this->validPages[$pageCall])) if (isset($this->validPages[$pageCall]))
{ {
$_ = $this->validPages[$pageCall]; $pageData = $this->validPages[$pageCall];
// check if subpage is valid $this->tab = $pageData[0];
if (!isset($_[1])) $this->path = $pageData[1];
$this->page = [$pageCall, $subPage];
if ($subPage && isset($pageData[2]))
{ {
if (($_[1] = array_search($subPage, $this->subPages[$_[0]])) === false) $exists = array_search($subPage, $pageData[2]);
if ($exists === false)
$this->error(); $this->error();
if ($pageCall == 'help') // ye.. hack .. class definitions only allow static values if (is_numeric($subPage))
$_[2] = Lang::main('helpTopics', $_[1]); $this->articleUrl = $pageCall.'='.$subPage;
else
$this->articleUrl = $subPage;
$this->path[] = $subPage;
$this->name = Lang::main('moreTitles', $pageCall, $subPage);
}
else
{
$this->articleUrl = $pageCall;
$this->name = Lang::main('moreTitles', $pageCall);
} }
$this->type = $_[0];
$this->typeId = $_[1];
$this->name = $_[2];
$this->gPageInfo = array(
'type' => $this->type,
'typeId' => $this->typeId,
'name' => $this->name
);
} }
else else
$this->error(); $this->error();
// order by requirement ASC
asort($this->req2priv);
} }
protected function generatePath() protected function generateContent()
{ {
$this->path[] = abs($this->type); switch ($this->page[0])
{
if ($this->typeId > -1) case 'reputation':
$this->path[] = $this->typeId; $this->handleReputationPage();
return;
case 'privileges':
$this->handlePrivilegesPage();
return;
case 'privilege':
$this->tpl = 'privilege';
$this->privReqPoints = sprintf(Lang::privileges('reqPoints'), Lang::nf($this->req2priv[$this->page[1]]));
return;
case 'top-users':
$this->handleTopUsersPage();
return;
default:
return;
}
} }
protected function postArticle()
{
if ($this->page[0] != 'reputation' &&
$this->page[0] != 'privileges' &&
$this->page[0] != 'privilege')
return;
$txt = &$this->article['text'];
$consts = get_defined_constants(true);
foreach ($consts['user'] as $k => $v)
{
if (strstr($k, 'CFG_REP_'))
$txt = str_replace($k, Lang::nf($v), $txt);
else if ($k == 'CFG_USER_MAX_VOTES' || $k == 'CFG_BOARD_URL')
$txt = str_replace($k, $v, $txt);
}
}
protected function generatePath() { }
protected function generateTitle() protected function generateTitle()
{ {
array_unshift($this->title, $this->name); array_unshift($this->title, $this->name);
} }
protected function generateContent() {} // its just articles here private function handleReputationPage()
{
if (!User::$id)
return;
if ($repData = DB::Aowow()->select('SELECT action, amount, date AS \'when\', IF(action IN (3, 4, 5), sourceA, 0) AS param FROM ?_account_reputation WHERE userId = ?d', User::$id))
{
foreach ($repData as &$r)
$r['when'] = date(Util::$dateFormatInternal, $r['when']);
$this->tabsTitle = Lang::main('yourRepHistory');
$this->forceTabs = true;
$this->lvTabs[] = ['reputationhistory', array(
'id' => 'reputation-history',
'name' => '$LANG.reputationhistory',
'data' => $repData
)];
}
}
private function handlePrivilegesPage()
{
$this->tpl = 'privileges';
$this->privileges = [];
foreach ($this->req2priv as $id => $val)
if ($val)
$this->privileges[$id] = array(
User::getReputation() >= $val,
Lang::privileges('_privileges', $id),
$val
);
}
private function handleTopUsersPage()
{
$tabs = array(
[0, 'top-users-alltime', '$LANG.alltime_stc' ],
[time() - MONTH, 'top-users-monthly', '$LANG.lastmonth_stc'],
[time() - WEEK, 'top-users-weekly', '$LANG.lastweek_stc' ]
);
$nullFields = array(
'uploads' => 0,
'posts' => 0,
'gold' => 0,
'silver' => 0,
'copper' => 0
);
foreach ($tabs as list($t, $tabId, $tabName))
{
// stuff received
$res = DB::Aowow()->select('
SELECT
a.id AS ARRAY_KEY,
a.displayName AS username,
a.userGroups AS groups,
a.joinDate AS creation,
SUM(r.amount) AS reputation,
SUM(IF(r.`action` = 3, 1, 0)) AS comments,
SUM(IF(r.`action` = 6, 1, 0)) AS screenshots,
SUM(IF(r.`action` = 9, 1, 0)) AS reports
FROM ?_account_reputation r
JOIN ?_account a ON a.id = r.userId
{WHERE r.date > ?d}
GROUP BY a.id
ORDER BY reputation DESC
LIMIT ?d
', $t ?: DBSIMPLE_SKIP, CFG_SQL_LIMIT_SEARCH);
$data = [];
if ($res)
{
// stuff given
$votes = DB::Aowow()->selectCol(
'SELECT sourceB AS ARRAY_KEY, SUM(1) FROM ?_account_reputation WHERE action IN (4, 5) AND sourceB IN (?a) {AND date > ?d} GROUP BY sourceB',
array_keys($res),
$t ?: DBSIMPLE_SKIP
);
foreach ($res as $uId => &$r)
{
$r['creation'] = date('c', $r['creation']);
$r['votes'] = empty($votes[$uId]) ? 0 : $votes[$uId];
$r = array_merge($r, $nullFields);
}
$data = array_values($res);
}
$this->lvTabs[] = ['topusers', array(
'hiddenCols' => ['achievements', 'posts', 'uploads'],
'visibleCols' => ['created'],
'name' => '$LANG.lastweek_stc',
'name' => $tabName,
'id' => $tabId,
'data' => $data
)];
}
}
} }
?> ?>

View File

@@ -18,6 +18,8 @@ class NpcPage extends GenericPage
protected $mode = CACHE_TYPE_PAGE; protected $mode = CACHE_TYPE_PAGE;
protected $js = ['swfobject.js']; protected $js = ['swfobject.js'];
private $soundIds = [];
public function __construct($pageCall, $id) public function __construct($pageCall, $id)
{ {
parent::__construct($pageCall, $id); parent::__construct($pageCall, $id);
@@ -53,15 +55,15 @@ class NpcPage extends GenericPage
{ {
$this->addJS('?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']); $this->addJS('?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']);
$_typeFlags = $this->subject->getField('typeFlags'); $_typeFlags = $this->subject->getField('typeFlags');
$_altIds = []; $_altIds = [];
$_altNPCs = null; $_altNPCs = null;
$position = null; $placeholder = null;
$accessory = []; $accessory = [];
// difficulty entries of self // difficulty entries of self
if ($this->subject->getField('cuFlags') & NPC_CU_DIFFICULTY_DUMMY) if ($this->subject->getField('cuFlags') & NPC_CU_DIFFICULTY_DUMMY)
$position = [$this->subject->getField('parentId'), $this->subject->getField('parent', true)]; $placeholder = [$this->subject->getField('parentId'), $this->subject->getField('parent', true)];
else else
{ {
for ($i = 1; $i < 4; $i++) for ($i = 1; $i < 4; $i++)
@@ -85,7 +87,7 @@ class NpcPage extends GenericPage
{ {
if (count($maps) == 1) // should only exist in one instance if (count($maps) == 1) // should only exist in one instance
{ {
switch ((new ZoneList(array(['id', $maps], 1)))->getField('type')) switch (DB::Aowow()->selectCell('SELECT `type` FROM ?_zones WHERE id = ?d', $maps[0]))
{ {
case 2: case 2:
case 5: $mapType = 1; break; case 5: $mapType = 1; break;
@@ -103,6 +105,7 @@ class NpcPage extends GenericPage
$mapType = 1; $mapType = 1;
} }
/***********/ /***********/
/* Infobox */ /* Infobox */
/***********/ /***********/
@@ -110,7 +113,7 @@ class NpcPage extends GenericPage
$infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
// Event (ignore events, where the object only gets removed) // Event (ignore events, where the object only gets removed)
if ($_ = DB::World()->selectCol('SELECT DISTINCT IF(ge.holiday, ge.holiday, -ge.eventEntry) FROM game_event ge, game_event_creature gec, creature c WHERE ge.eventEntry = gec.eventEntry AND c.guid = gec.guid AND c.id = ?d', $this->typeId)) if ($_ = DB::World()->selectCol('SELECT DISTINCT ge.eventEntry FROM game_event ge, game_event_creature gec, creature c WHERE ge.eventEntry = gec.eventEntry AND c.guid = gec.guid AND c.id = ?d', $this->typeId))
{ {
$this->extendGlobalIds(TYPE_WORLDEVENT, $_); $this->extendGlobalIds(TYPE_WORLDEVENT, $_);
$ev = []; $ev = [];
@@ -136,7 +139,7 @@ class NpcPage extends GenericPage
// Classification // Classification
if ($_ = $this->subject->getField('rank')) // != NPC_RANK_NORMAL if ($_ = $this->subject->getField('rank')) // != NPC_RANK_NORMAL
{ {
$str = $_typeFlags & 0x4 ? '[span class=icon-boss]'.Lang::npc('rank', $_).'[/span]' : Lang::npc('rank', $_); $str = $this->subject->isBoss() ? '[span class=icon-boss]'.Lang::npc('rank', $_).'[/span]' : Lang::npc('rank', $_);
$infobox[] = Lang::npc('classification').Lang::main('colon').$str; $infobox[] = Lang::npc('classification').Lang::main('colon').$str;
} }
@@ -227,33 +230,32 @@ class NpcPage extends GenericPage
} }
// > Stats // > Stats
$_nf = function ($num) { return number_format($num, 0, '', '.'); };
$stats = []; $stats = [];
$modes = []; // get difficulty versions if set $modes = []; // get difficulty versions if set
$hint = '[tooltip name=%3$s][table cellspacing=10][tr]%1s[/tr][/table][/tooltip][span class=tip tooltip=%3$s]%2s[/span]'; $hint = '[tooltip name=%3$s][table cellspacing=10][tr]%1s[/tr][/table][/tooltip][span class=tip tooltip=%3$s]%2s[/span]';
$modeRow = '[tr][td]%s&nbsp;&nbsp;[/td][td]%s[/td][/tr]'; $modeRow = '[tr][td]%s&nbsp;&nbsp;[/td][td]%s[/td][/tr]';
// Health // Health
$health = $this->subject->getBaseStats('health'); $health = $this->subject->getBaseStats('health');
$stats['health'] = Util::ucFirst(Lang::spell('powerTypes', -2)).Lang::main('colon').($health[0] < $health[1] ? $_nf($health[0]).' - '.$_nf($health[1]) : $_nf($health[0])); $stats['health'] = Util::ucFirst(Lang::spell('powerTypes', -2)).Lang::main('colon').($health[0] < $health[1] ? Lang::nf($health[0]).' - '.Lang::nf($health[1]) : Lang::nf($health[0]));
// Mana (may be 0) // Mana (may be 0)
$mana = $this->subject->getBaseStats('power'); $mana = $this->subject->getBaseStats('power');
$stats['mana'] = $mana[0] ? Lang::spell('powerTypes', 0).Lang::main('colon').($mana[0] < $mana[1] ? $_nf($mana[0]).' - '.$_nf($mana[1]) : $_nf($mana[0])) : null; $stats['mana'] = $mana[0] ? Lang::spell('powerTypes', 0).Lang::main('colon').($mana[0] < $mana[1] ? Lang::nf($mana[0]).' - '.Lang::nf($mana[1]) : Lang::nf($mana[0])) : null;
// Armor // Armor
$armor = $this->subject->getBaseStats('armor'); $armor = $this->subject->getBaseStats('armor');
$stats['armor'] = Lang::npc('armor').Lang::main('colon').($armor[0] < $armor[1] ? $_nf($armor[0]).' - '.$_nf($armor[1]) : $_nf($armor[0])); $stats['armor'] = Lang::npc('armor').Lang::main('colon').($armor[0] < $armor[1] ? Lang::nf($armor[0]).' - '.Lang::nf($armor[1]) : Lang::nf($armor[0]));
// Melee Damage // Melee Damage
$melee = $this->subject->getBaseStats('melee'); $melee = $this->subject->getBaseStats('melee');
if ($_ = $this->subject->getField('dmgSchool')) // magic damage if ($_ = $this->subject->getField('dmgSchool')) // magic damage
$stats['melee'] = Lang::npc('melee').Lang::main('colon').$_nf($melee[0]).' - '.$_nf($melee[1]).' ('.Lang::game('sc', $_).')'; $stats['melee'] = Lang::npc('melee').Lang::main('colon').Lang::nf($melee[0]).' - '.Lang::nf($melee[1]).' ('.Lang::game('sc', $_).')';
else // phys. damage else // phys. damage
$stats['melee'] = Lang::npc('melee').Lang::main('colon').$_nf($melee[0]).' - '.$_nf($melee[1]); $stats['melee'] = Lang::npc('melee').Lang::main('colon').Lang::nf($melee[0]).' - '.Lang::nf($melee[1]);
// Ranged Damage // Ranged Damage
$ranged = $this->subject->getBaseStats('ranged'); $ranged = $this->subject->getBaseStats('ranged');
$stats['ranged'] = Lang::npc('ranged').Lang::main('colon').$_nf($ranged[0]).' - '.$_nf($ranged[1]); $stats['ranged'] = Lang::npc('ranged').Lang::main('colon').Lang::nf($ranged[0]).' - '.Lang::nf($ranged[1]);
if (in_array($mapType, [1, 2])) // Dungeon or Raid if (in_array($mapType, [1, 2])) // Dungeon or Raid
{ {
@@ -268,26 +270,26 @@ class NpcPage extends GenericPage
// Health // Health
$health = $_altNPCs->getBaseStats('health'); $health = $_altNPCs->getBaseStats('health');
$modes['health'][] = sprintf($modeRow, $m, $health[0] < $health[1] ? $_nf($health[0]).' - '.$_nf($health[1]) : $_nf($health[0])); $modes['health'][] = sprintf($modeRow, $m, $health[0] < $health[1] ? Lang::nf($health[0]).' - '.Lang::nf($health[1]) : Lang::nf($health[0]));
// Mana (may be 0) // Mana (may be 0)
$mana = $_altNPCs->getBaseStats('power'); $mana = $_altNPCs->getBaseStats('power');
$modes['mana'][] = $mana[0] ? sprintf($modeRow, $m, $mana[0] < $mana[1] ? $_nf($mana[0]).' - '.$_nf($mana[1]) : $_nf($mana[0])) : null; $modes['mana'][] = $mana[0] ? sprintf($modeRow, $m, $mana[0] < $mana[1] ? Lang::nf($mana[0]).' - '.Lang::nf($mana[1]) : Lang::nf($mana[0])) : null;
// Armor // Armor
$armor = $_altNPCs->getBaseStats('armor'); $armor = $_altNPCs->getBaseStats('armor');
$modes['armor'][] = sprintf($modeRow, $m, $armor[0] < $armor[1] ? $_nf($armor[0]).' - '.$_nf($armor[1]) : $_nf($armor[0])); $modes['armor'][] = sprintf($modeRow, $m, $armor[0] < $armor[1] ? Lang::nf($armor[0]).' - '.Lang::nf($armor[1]) : Lang::nf($armor[0]));
// Melee Damage // Melee Damage
$melee = $_altNPCs->getBaseStats('melee'); $melee = $_altNPCs->getBaseStats('melee');
if ($_ = $_altNPCs->getField('dmgSchool')) // magic damage if ($_ = $_altNPCs->getField('dmgSchool')) // magic damage
$modes['melee'][] = sprintf($modeRow, $m, $_nf($melee[0]).' - '.$_nf($melee[1]).' ('.Lang::game('sc', $_).')'); $modes['melee'][] = sprintf($modeRow, $m, Lang::nf($melee[0]).' - '.Lang::nf($melee[1]).' ('.Lang::game('sc', $_).')');
else // phys. damage else // phys. damage
$modes['melee'][] = sprintf($modeRow, $m, $_nf($melee[0]).' - '.$_nf($melee[1])); $modes['melee'][] = sprintf($modeRow, $m, Lang::nf($melee[0]).' - '.Lang::nf($melee[1]));
// Ranged Damage // Ranged Damage
$ranged = $_altNPCs->getBaseStats('ranged'); $ranged = $_altNPCs->getBaseStats('ranged');
$modes['ranged'][] = sprintf($modeRow, $m, $_nf($ranged[0]).' - '.$_nf($ranged[1])); $modes['ranged'][] = sprintf($modeRow, $m, Lang::nf($ranged[0]).' - '.Lang::nf($ranged[1]));
} }
} }
} }
@@ -301,6 +303,7 @@ class NpcPage extends GenericPage
if ($stats) if ($stats)
$infobox[] = Lang::npc('stats').($modes ? ' ('.Lang::npc('modes', $mapType, 0).')' : null).Lang::main('colon').'[ul][li]'.implode('[/li][li]', $stats).'[/li][/ul]'; $infobox[] = Lang::npc('stats').($modes ? ' ('.Lang::npc('modes', $mapType, 0).')' : null).Lang::main('colon').'[ul][li]'.implode('[/li][li]', $stats).'[/li][/ul]';
/****************/ /****************/
/* Main Content */ /* Main Content */
/****************/ /****************/
@@ -318,16 +321,20 @@ class NpcPage extends GenericPage
$this->map = $map; $this->map = $map;
$this->infobox = '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]'; $this->infobox = '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]';
$this->position = $position; $this->placeholder = $placeholder;
$this->accessory = $accessory; $this->accessory = $accessory;
$this->quotes = $this->getQuotes(); $this->quotes = $this->getQuotes();
$this->reputation = $this->getOnKillRep($_altIds, $mapType); $this->reputation = $this->getOnKillRep($_altIds, $mapType);
$this->redButtons = array( $this->redButtons = array(
BUTTON_WOWHEAD => true, BUTTON_WOWHEAD => true,
BUTTON_LINKS => true, BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
BUTTON_VIEW3D => ['type' => TYPE_NPC, 'typeId' => $this->typeId, 'displayId' => $this->subject->getRandomModelId()] BUTTON_VIEW3D => ['type' => TYPE_NPC, 'typeId' => $this->typeId, 'displayId' => $this->subject->getRandomModelId()]
); );
if ($this->subject->getField('humanoid'))
$this->redButtons[BUTTON_VIEW3D]['humanoid'] = 1;
/**************/ /**************/
/* Extra Tabs */ /* Extra Tabs */
/**************/ /**************/
@@ -356,7 +363,7 @@ class NpcPage extends GenericPage
{ {
$skill = 0; $skill = 0;
$mask = 0x0; $mask = 0x0;
foreach (Util::$skillLineMask[-1] as $idx => $pair) foreach (Game::$skillLineMask[-1] as $idx => $pair)
{ {
if ($pair[0] != $_) if ($pair[0] != $_)
continue; continue;
@@ -404,24 +411,18 @@ class NpcPage extends GenericPage
} }
if ($normal) if ($normal)
$this->lvTabs[] = array( $this->lvTabs[] = ['spell', array(
'file' => 'spell', 'data' => array_values($normal),
'data' => $normal, 'name' => '$LANG.tab_abilities',
'params' => array( 'id' => 'abilities'
'name' => '$LANG.tab_abilities', )];
'id' => 'abilities'
)
);
if ($controled) if ($controled)
$this->lvTabs[] = array( $this->lvTabs[] = ['spell', array(
'file' => 'spell', 'data' => array_values($controled),
'data' => $controled, 'name' => '$LANG.tab_controlledabilities',
'params' => array( 'id' => 'controlled-abilities'
'name' => '$LANG.tab_controlledabilities', )];
'id' => 'controlled-abilities'
)
);
} }
} }
@@ -438,28 +439,25 @@ class NpcPage extends GenericPage
{ {
$this->extendGlobalData($summoned->getJSGlobals()); $this->extendGlobalData($summoned->getJSGlobals());
$this->lvTabs[] = array( $this->lvTabs[] = ['spell', array(
'file' => 'spell', 'data' => array_values($summoned->getListviewData()),
'data' => $summoned->getListviewData(), 'name' => '$LANG.tab_summonedby',
'params' => array( 'id' => 'summoned-by'
'name' => '$LANG.tab_summonedby', )];
'id' => 'summoned-by'
)
);
} }
// tab: teaches // tab: teaches
if ($this->subject->getField('npcflag') & NPC_FLAG_TRAINER) if ($this->subject->getField('npcflag') & NPC_FLAG_TRAINER)
{ {
$teachQuery = ' $teachQuery = '
SELECT IFNULL(t2.spell, t1.spell) AS ARRAY_KEY, SELECT IFNULL(t2.SpellID, t1.SpellID) AS ARRAY_KEY,
IFNULL(t2.spellcost, t1.spellcost) AS cost, IFNULL(t2.MoneyCost, t1.MoneyCost) AS cost,
IFNULL(t2.reqskill, t1.reqskill) AS reqSkillId, IFNULL(t2.ReqSkillLine, t1.ReqSkillLine) AS reqSkillId,
IFNULL(t2.reqskillvalue, t1.reqskillvalue) AS reqSkillValue, IFNULL(t2.ReqSkillRank, t1.ReqSkillRank) AS reqSkillValue,
IFNULL(t2.reqlevel, t1.reqlevel) AS reqLevel IFNULL(t2.ReqLevel, t1.ReqLevel) AS reqLevel
FROM npc_trainer t1 FROM npc_trainer t1
LEFT JOIN npc_trainer t2 ON t2.entry = IF(t1.spell < 0, -t1.spell, null) LEFT JOIN npc_trainer t2 ON t2.ID = IF(t1.SpellID < 0, -t1.SpellID, null)
WHERE t1.entry = ?d WHERE t1.ID = ?d
'; ';
if ($tSpells = DB::World()->select($teachQuery, $this->typeId)) if ($tSpells = DB::World()->select($teachQuery, $this->typeId))
@@ -480,7 +478,7 @@ class NpcPage extends GenericPage
{ {
$this->extendGlobalIds(TYPE_SKILL, $_); $this->extendGlobalIds(TYPE_SKILL, $_);
if (!isset($extra[0])) if (!isset($extra[0]))
$extra[0] = 'Listview.extraCols.condition'; $extra[0] = '$Listview.extraCols.condition';
$data[$sId]['condition'][0][$this->typeId][] = [[CND_SKILL, $_, $train['reqSkillValue']]]; $data[$sId]['condition'][0][$this->typeId][] = [[CND_SKILL, $_, $train['reqSkillValue']]];
} }
@@ -488,7 +486,7 @@ class NpcPage extends GenericPage
if ($_ = $train['reqLevel']) if ($_ = $train['reqLevel'])
{ {
if (!isset($extra[1])) if (!isset($extra[1]))
$extra[1] = "Listview.funcBox.createSimpleCol('reqLevel', LANG.tooltip_reqlevel, '7%', 'reqLevel')"; $extra[1] = "\$Listview.funcBox.createSimpleCol('reqLevel', LANG.tooltip_reqlevel, '7%', 'reqLevel')";
$data[$sId]['reqLevel'] = $_; $data[$sId]['reqLevel'] = $_;
} }
@@ -497,20 +495,21 @@ class NpcPage extends GenericPage
$data[$sId]['trainingcost'] = $_; $data[$sId]['trainingcost'] = $_;
} }
$this->lvTabs[] = array( $tabData = array(
'file' => 'spell', 'data' => array_values($data),
'data' => $data, 'name' => '$LANG.tab_teaches',
'params' => array( 'id' => 'teaches',
'name' => '$LANG.tab_teaches', 'visibleCols' => ['trainingcost']
'id' => 'teaches',
'visibleCols' => "$['trainingcost']",
'extraCols' => $extra ? '$['.implode(', ', $extra).']' : null
)
); );
if ($extra)
$tabData['extraCols'] = $extra;
$this->lvTabs[] = ['spell', $tabData];
} }
} }
else else
Util::addNote(U_GROUP_EMPLOYEE, 'NPC '.$this->typeId.' is flagged as trainer, but doesn\'t have any spells set'); trigger_error('NPC '.$this->typeId.' is flagged as trainer, but doesn\'t have any spells set', E_USER_WARNING);
} }
// tab: sells // tab: sells
@@ -519,9 +518,9 @@ class NpcPage extends GenericPage
$soldItems = new ItemList(array(['id', $sells])); $soldItems = new ItemList(array(['id', $sells]));
if (!$soldItems->error) if (!$soldItems->error)
{ {
$extraCols = ["Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", 'Listview.extraCols.cost']; $extraCols = ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'];
if ($soldItems->hasSetFields(['condition'])) if ($soldItems->hasSetFields(['condition']))
$extraCols[] = 'Listview.extraCols.condition'; $extraCols[] = '$Listview.extraCols.condition';
$lvData = $soldItems->getListviewData(ITEMINFO_VENDOR, [TYPE_NPC => [$this->typeId]]); $lvData = $soldItems->getListviewData(ITEMINFO_VENDOR, [TYPE_NPC => [$this->typeId]]);
@@ -530,8 +529,7 @@ class NpcPage extends GenericPage
{ {
$this->extendGlobalData($sc[1]); $this->extendGlobalData($sc[1]);
if (!array_search('Listview.extraCols.condition', $extraCols)) $extraCols[] = '$Listview.extraCols.condition';
$extraCols[] = 'Listview.extraCols.condition';
foreach ($lvData as $id => &$row) foreach ($lvData as $id => &$row)
foreach ($sc[0] as $srcType => $cndData) foreach ($sc[0] as $srcType => $cndData)
@@ -539,15 +537,12 @@ class NpcPage extends GenericPage
$row['condition'][0][$id.':'.$this->typeId] = $cndData[$id.':'.$this->typeId]; $row['condition'][0][$id.':'.$this->typeId] = $cndData[$id.':'.$this->typeId];
} }
$this->lvTabs[] = array( $this->lvTabs[] = ['item', array(
'file' => 'item', 'data' => array_values($lvData),
'data' => $lvData, 'name' => '$LANG.tab_sells',
'params' => array( 'id' => 'currency-for',
'name' => '$LANG.tab_sells', 'extraCols' => array_unique($extraCols)
'id' => 'currency-for', )];
'extraCols' => '$['.implode(', ', $extraCols).']'
)
);
$this->extendGlobalData($soldItems->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED)); $this->extendGlobalData($soldItems->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
} }
@@ -572,9 +567,9 @@ class NpcPage extends GenericPage
*/ */
$sourceFor = array( $sourceFor = array(
[LOOT_CREATURE, $this->subject->getField('lootId'), '$LANG.tab_drops', 'drops', ['Listview.extraCols.percent'], [] , []], [LOOT_CREATURE, $this->subject->getField('lootId'), '$LANG.tab_drops', 'drops', [] ],
[LOOT_PICKPOCKET, $this->subject->getField('pickpocketLootId'), '$LANG.tab_pickpocketing', 'pickpocketing', ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []], [LOOT_PICKPOCKET, $this->subject->getField('pickpocketLootId'), '$LANG.tab_pickpocketing', 'pickpocketing', ['side', 'slot', 'reqlevel']],
[LOOT_SKINNING, $this->subject->getField('skinLootId'), '$LANG.'.$skinTab[0], $skinTab[1], ['Listview.extraCols.percent'], ['side', 'slot', 'reqlevel'], []] [LOOT_SKINNING, $this->subject->getField('skinLootId'), '$LANG.'.$skinTab[0], $skinTab[1], ['side', 'slot', 'reqlevel']]
); );
// temp: manually add loot for difficulty-versions // temp: manually add loot for difficulty-versions
@@ -589,22 +584,29 @@ class NpcPage extends GenericPage
if ($_altIds) if ($_altIds)
{ {
$sourceFor[0][2] = $langref[1]; $sourceFor[0][2] = $mapType == 1 ? $langref[-1] : $langref[1];
foreach ($_altNPCs->iterate() as $id => $__) foreach ($_altNPCs->iterate() as $id => $__)
{ {
$mode = $_altIds[$id]; $mode = ($_altIds[$id] + 1) * ($mapType == 1 ? -1 : 1);
array_splice($sourceFor, 1, 0, [[LOOT_CREATURE, $_altNPCs->getField('lootId'), $langref[$mode + 1], 'drops-'.$mode, ['Listview.extraCols.percent'], [], []]]); if ($lootGO = DB::Aowow()->selectRow('SELECT o.id, o.lootId, o.name_loc0, o.name_loc2, o.name_loc3, o.name_loc6, o.name_loc8 FROM ?_loot_link l JOIN ?_objects o ON o.id = l.objectId WHERE l.npcId = ?d', $id))
array_splice($sourceFor, 1, 0, [[LOOT_GAMEOBJECT, $lootGO['lootId'], $langref[$mode], 'drops-object-'.abs($mode), [], 'note' => '$$WH.sprintf(LANG.lvnote_npcobjectsource, '.$lootGO['id'].', "'.Util::localizedString($lootGO, 'name').'")']]);
if ($lootId = $_altNPCs->getField('lootId'))
array_splice($sourceFor, 1, 0, [[LOOT_CREATURE, $lootId, $langref[$mode], 'drops-'.abs($mode), []]]);
} }
} }
if ($lootGOs = DB::Aowow()->select('SELECT o.id, IF(npcId < 0, 1, 0) AS modeDummy, o.lootId, o.name_loc0, o.name_loc2, o.name_loc3, o.name_loc6, o.name_loc8 FROM ?_loot_link l JOIN ?_objects o ON o.id = l.objectId WHERE ABS(l.npcId) = ?d', $this->typeId))
foreach ($lootGOs as $idx => $lgo)
array_splice($sourceFor, 1, 0, [[LOOT_GAMEOBJECT, $lgo['lootId'], $mapType ? $langref[($mapType == 1 ? -1 : 1) + ($lgo['modeDummy'] ? 1 : 0)] : '$LANG.tab_drops', 'drops-object-'.$idx, [], 'note' => '$$WH.sprintf(LANG.lvnote_npcobjectsource, '.$lgo['id'].', "'.Util::localizedString($lgo, 'name').'")']]);
$reqQuest = []; $reqQuest = [];
foreach ($sourceFor as $sf) foreach ($sourceFor as $sf)
{ {
$creatureLoot = new Loot(); $creatureLoot = new Loot();
if ($creatureLoot->getByContainer($sf[0], $sf[1])) if ($creatureLoot->getByContainer($sf[0], $sf[1]))
{ {
if ($_ = $creatureLoot->extraCols) $extraCols = $creatureLoot->extraCols;
$sf[4] = array_merge($sf[4], $_); $extraCols[] = '$Listview.extraCols.percent';
$this->extendGlobalData($creatureLoot->jsGlobals); $this->extendGlobalData($creatureLoot->jsGlobals);
@@ -613,23 +615,26 @@ class NpcPage extends GenericPage
if (!$lv['quest']) if (!$lv['quest'])
continue; continue;
$sf[4][] = 'Listview.extraCols.condition'; $extraCols[] = '$Listview.extraCols.condition';
$reqQuest[$lv['id']] = 0; $reqQuest[$lv['id']] = 0;
$lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]]; $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
} }
$this->lvTabs[] = array( $tabData = array(
'file' => 'item', 'data' => array_values($creatureLoot->getResult()),
'data' => $creatureLoot->getResult(), 'name' => $sf[2],
'params' => array( 'id' => $sf[3],
'name' => $sf[2], 'extraCols' => array_unique($extraCols),
'id' => $sf[3], 'sort' => ['-percent', 'name'],
'extraCols' => $sf[4] ? "$[".implode(', ', array_unique($sf[4]))."]" : null,
'hiddenCols' => $sf[5] ? "$".Util::toJSON($sf[5]) : null,
'visibleCols' => $sf[6] ? '$'.Util::toJSON($sf[6]) : null,
'sort' => "$['-percent', 'name']",
)
); );
if (!empty($sf['note']))
$tabData['note'] = $sf['note'];
if ($sf[4])
$tabData['hiddenCols'] = $sf[4];
$this->lvTabs[] = ['item', $tabData];
} }
} }
@@ -676,28 +681,18 @@ class NpcPage extends GenericPage
} }
if ($_[0]) if ($_[0])
{ $this->lvTabs[] = ['quest', array(
$this->lvTabs[] = array( 'data' => array_values($_[0]),
'file' => 'quest', 'name' => '$LANG.tab_starts',
'data' => $_[0], 'id' => 'starts'
'params' => array( )];
'name' => '$LANG.tab_starts',
'id' => 'starts'
)
);
}
if ($_[1]) if ($_[1])
{ $this->lvTabs[] = ['quest', array(
$this->lvTabs[] = array( 'data' => array_values($_[1]),
'file' => 'quest', 'name' => '$LANG.tab_ends',
'data' => $_[1], 'id' => 'ends'
'params' => array( )];
'name' => '$LANG.tab_ends',
'id' => 'ends'
)
);
}
} }
// tab: objective of quest // tab: objective of quest
@@ -714,14 +709,11 @@ class NpcPage extends GenericPage
{ {
$this->extendGlobalData($objectiveOf->getJSGlobals()); $this->extendGlobalData($objectiveOf->getJSGlobals());
$this->lvTabs[] = array( $this->lvTabs[] = ['quest', array(
'file' => 'quest', 'data' => array_values($objectiveOf->getListviewData()),
'data' => $objectiveOf->getListviewData(), 'name' => '$LANG.tab_objectiveof',
'params' => array( 'id' => 'objective-of'
'name' => '$LANG.tab_objectiveof', )];
'id' => 'objective-of'
)
);
} }
// tab: criteria of [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE have no data set to check for] // tab: criteria of [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE have no data set to check for]
@@ -735,14 +727,11 @@ class NpcPage extends GenericPage
{ {
$this->extendGlobalData($crtOf->getJSGlobals()); $this->extendGlobalData($crtOf->getJSGlobals());
$this->lvTabs[] = array( $this->lvTabs[] = ['achievement', array(
'file' => 'achievement', 'data' => array_values($crtOf->getListviewData()),
'data' => $crtOf->getListviewData(), 'name' => '$LANG.tab_criteriaof',
'params' => array( 'id' => 'criteria-of'
'name' => '$LANG.tab_criteriaof', )];
'id' => 'criteria-of'
)
);
} }
// tab: passengers // tab: passengers
@@ -753,25 +742,74 @@ class NpcPage extends GenericPage
{ {
$data = $passengers->getListviewData(); $data = $passengers->getListviewData();
$xCols = null;
if (User::isInGroup(U_GROUP_STAFF)) if (User::isInGroup(U_GROUP_STAFF))
{
foreach ($data as $id => &$d) foreach ($data as $id => &$d)
$d['seat'] = str_replace(',', ', ', $_[$id]); $d['seat'] = str_replace(',', ', ', $_[$id]);
$xCols = "$[Listview.funcBox.createSimpleCol('seat', '".Lang::npc('seat')."', '10%', 'seat')]";
}
$this->extendGlobalData($passengers->getJSGlobals(GLOBALINFO_SELF)); $this->extendGlobalData($passengers->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = array(
'file' => 'creature', $tabData = array(
'data' => $data, 'data' => array_values($data),
'params' => array( 'name' => Lang::npc('accessory'),
'extraCols' => $xCols, 'id' => 'accessory'
'name' => Lang::npc('accessory'),
'id' => 'accessory'
)
); );
if (User::isInGroup(U_GROUP_STAFF))
$tabData['extraCols'] = ["\$Listview.funcBox.createSimpleCol('seat', '".Lang::npc('seat')."', '10%', 'seat')"];
$this->lvTabs[] = ['creature', $tabData];
}
}
/* tab sounds:
* activity sounds => CreatureDisplayInfo.dbc => (CreatureModelData.dbc => ) CreatureSoundData.dbc
* AI => smart_scripts
* Dialogue VO => creature_text
* onClick VO => CreatureDisplayInfo.dbc => NPCSounds.dbc
*/
$ssActionLists = DB::World()->select('SELECT action_type, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6 FROM smart_scripts WHERE entryorguid = ?d AND source_type = 0 AND action_type IN (80, 87, 88)', $this->typeId);
$actionListIds = [];
foreach ($ssActionLists as $sal)
{
$iMax = 0;
switch ($sal['action_type'])
{
case 80: $iMax = 1; break;
case 87: $iMax = 6; break;
case 88: $iMax = 2; break;
default: continue;
}
for ($i = 1; $i <= $iMax; $i++)
if ($sal['action_param'.$i])
$actionListIds[] = $sal['action_param'.$i];
}
// not going for a per guid basis. The infos are nested enough as is.
$smartScripts = DB::World()->selectCol('SELECT action_param1 FROM smart_scripts WHERE action_type = 4 AND ((source_type = 0 AND entryorguid = ?d) { OR (source_type = 9 AND entryorguid IN (?a)) } )', $this->typeId, $actionListIds ?: DBSIMPLE_SKIP);
$this->soundIds = array_merge($this->soundIds, $smartScripts);
// up to 4 possible displayIds .. for the love of things betwixt, just use the first!
$activitySounds = DB::Aowow()->selectRow('SELECT * FROM ?_creature_sounds WHERE id = ?d', $this->subject->getField('displayId1'));
array_shift($activitySounds); // remove id-column
$this->soundIds = array_merge($this->soundIds, array_values($activitySounds));
if ($this->soundIds)
{
$sounds = new SoundList(array(['id', $this->soundIds]));
if (!$sounds->error)
{
$data = $sounds->getListviewData();
foreach ($activitySounds as $activity => $id)
if (isset($data[$id]))
$data[$id]['activity'] = $activity; // no index, js wants a string :(
$tabData = ['data' => array_values($data)];
if ($activitySounds)
$tabData['visibleCols'] = ['activity'];
$this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
$this->lvTabs[] = ['sound', $tabData];
} }
} }
} }
@@ -807,10 +845,10 @@ class NpcPage extends GenericPage
die($tt); die($tt);
} }
public function notFound() public function notFound($title = '', $msg = '')
{ {
if ($this->mode != CACHE_TYPE_TOOLTIP) if ($this->mode != CACHE_TYPE_TOOLTIP)
return parent::notFound(Lang::game('npc'), Lang::npc('notFound')); return parent::notFound($title ?: Lang::game('npc'), $msg ?: Lang::npc('notFound'));
header('Content-type: application/x-javascript; charset=utf-8'); header('Content-type: application/x-javascript; charset=utf-8');
echo $this->generateTooltip(true); echo $this->generateTooltip(true);
@@ -837,15 +875,22 @@ class NpcPage extends GenericPage
$set = array( $set = array(
'id' => $row['faction'], 'id' => $row['faction'],
'qty' => $row['qty'], 'qty' => [$row['qty'], 0],
'name' => $factions->getField('name', true), 'name' => $factions->getField('name', true),
'npc' => $row['npc'], 'npc' => $row['npc'],
'cap' => $row['maxRank'] && $row['maxRank'] < REP_EXALTED ? Lang::game('rep', $row['maxRank']) : null 'cap' => $row['maxRank'] && $row['maxRank'] < REP_EXALTED ? Lang::game('rep', $row['maxRank']) : null
); );
$cuRate = DB::World()->selectCell('SELECT creature_rate FROM reputation_reward_rate WHERE creature_rate <> 1 AND faction = ?d', $row['faction']);
if ($cuRate !== null)
$set['qty'][1] = $set['qty'][0] * ($cuRate - 1);
if ($row['spillover']) if ($row['spillover'])
{ {
$spillover[$factions->getField('cat')] = [intVal($row['qty'] / 2), $row['maxRank']]; $spillover[$factions->getField('cat')] = array(
[ $set['qty'][0] / 2, $set['qty'][1] / 2 ],
$row['maxRank']
);
$set['spillover'] = $factions->getField('cat'); $set['spillover'] = $factions->getField('cat');
} }
@@ -923,24 +968,25 @@ class NpcPage extends GenericPage
$quotes = []; $quotes = [];
$quoteSrc = DB::World()->select(' $quoteSrc = DB::World()->select('
SELECT SELECT
ct.groupid AS ARRAY_KEY, ct.id as ARRAY_KEY2, ct.`type`, ct.GroupID AS ARRAY_KEY, ct.ID as ARRAY_KEY2, ct.`Type`,
ct.TextRange AS `range`, ct.TextRange AS `range`,
IFNULL(bct.`Language`, ct.`language`) AS lang, IFNULL(bct.`Language`, ct.`Language`) AS lang,
IFNULL(NULLIF(bct.MaleText, ""), IFNULL(NULLIF(bct.FemaleText, ""), IFNULL(ct.`text`, ""))) AS text_loc0, IFNULL(NULLIF(bct.MaleText, ""), IFNULL(NULLIF(bct.FemaleText, ""), IFNULL(ct.`Text`, ""))) AS text_loc0,
IFNULL(NULLIF(lbct.MaleText_loc2, ""), IFNULL(NULLIF(lbct.FemaleText_loc2, ""), IFNULL(lct.text_loc2, ""))) AS text_loc2, {IFNULL(NULLIF(bctl.MaleText, ""), IFNULL(NULLIF(bctl.FemaleText, ""), IFNULL(ctl.Text, ""))) AS text_loc?d,}
IFNULL(NULLIF(lbct.MaleText_loc3, ""), IFNULL(NULLIF(lbct.FemaleText_loc3, ""), IFNULL(lct.text_loc3, ""))) AS text_loc3, IF(bct.SoundId > 0, bct.SoundId, ct.Sound) AS soundId
IFNULL(NULLIF(lbct.MaleText_loc6, ""), IFNULL(NULLIF(lbct.FemaleText_loc6, ""), IFNULL(lct.text_loc6, ""))) AS text_loc6,
IFNULL(NULLIF(lbct.MaleText_loc8, ""), IFNULL(NULLIF(lbct.FemaleText_loc8, ""), IFNULL(lct.text_loc8, ""))) AS text_loc8
FROM FROM
creature_text ct creature_text ct
LEFT JOIN {LEFT JOIN
locales_creature_text lct ON ct.entry = lct.entry AND ct.groupid = lct.groupid AND ct.id = lct.id creature_text_locale ctl ON ct.CreatureID = ctl.CreatureID AND ct.GroupID = ctl.GroupID AND ct.ID = ctl.ID AND ctl.Locale = ?}
LEFT JOIN LEFT JOIN
broadcast_text bct ON ct.BroadcastTextId = bct.ID broadcast_text bct ON ct.BroadcastTextId = bct.ID
LEFT JOIN {LEFT JOIN
locales_broadcast_text lbct ON ct.BroadcastTextId = lbct.ID broadcast_text_locale bctl ON ct.BroadcastTextId = bctl.ID AND bctl.locale = ?}
WHERE WHERE
ct.entry = ?d', ct.CreatureID = ?d',
User::$localeId ?: DBSIMPLE_SKIP,
User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
User::$localeId ? Util::$localeStrings[User::$localeId] : DBSIMPLE_SKIP,
$this->typeId $this->typeId
); );
@@ -949,22 +995,28 @@ class NpcPage extends GenericPage
$group = []; $group = [];
foreach ($text as $t) foreach ($text as $t)
{ {
if ($t['soundId'])
$this->soundIds[] = $t['soundId'];
$msg = Util::localizedString($t, 'text'); $msg = Util::localizedString($t, 'text');
if (!$msg) if (!$msg)
continue; continue;
// fixup .. either set %s for emotes or dont >.< // fixup .. either set %s for emotes or dont >.<
if (in_array($t['type'], [2, 16]) && strpos($msg, '%s') === false) if (in_array($t['Type'], [2, 16]) && strpos($msg, '%s') === false)
$msg = '%s '.$msg; $msg = '%s '.$msg;
// fixup: bad case-insensivity
$msg = str_replace('%S', '%s', $msg);
$line = array( $line = array(
'range' => $t['range'], 'range' => $t['range'],
'type' => 2, // [type: 0, 12] say: yellow-ish 'type' => 2, // [type: 0, 12] say: yellow-ish
'lang' => !empty($t['language']) ? Lang::game('languages', $t['language']) : null, 'lang' => !empty($t['lang']) ? Lang::game('languages', $t['lang']) : null,
'text' => sprintf(Util::parseHtmlText(htmlentities($msg)), $this->name), 'text' => sprintf(Util::parseHtmlText(htmlentities($msg)), $this->name),
); );
switch ($t['type']) switch ($t['Type'])
{ {
case 1: // yell: case 1: // yell:
case 14: $line['type'] = 1; break; // - dark red case 14: $line['type'] = 1; break; // - dark red

View File

@@ -20,8 +20,8 @@ class NpcsPage extends GenericPage
public function __construct($pageCall, $pageParam) public function __construct($pageCall, $pageParam)
{ {
$this->filterObj = new CreatureListFilter();
$this->getCategoryFromUrl($pageParam);; $this->getCategoryFromUrl($pageParam);;
$this->filterObj = new CreatureListFilter(false, ['parentCats' => $this->category]);
parent::__construct($pageCall, $pageParam); parent::__construct($pageCall, $pageParam);
@@ -53,33 +53,42 @@ class NpcsPage extends GenericPage
$npcs = new CreatureList($conditions, ['extraOpts' => $this->filterObj->extraOpts]); $npcs = new CreatureList($conditions, ['extraOpts' => $this->filterObj->extraOpts]);
// recreate form selection // recreate form selection
$this->filter = array_merge($this->filterObj->getForm('form'), $this->filter); $this->filter = $this->filterObj->getForm();
$this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : NULL; $this->filter['query'] = isset($_GET['filter']) ? $_GET['filter'] : null;
$this->filter['fi'] = $this->filterObj->getForm(); $this->filter['initData'] = ['init' => 'npcs'];
$lv = array( $rCols = $this->filterObj->getReputationCols();
'file' => 'creature', $xCols = $this->filterObj->getExtraCols();
'data' => $npcs->getListviewData(), // listview content if ($rCols)
'params' => [] $this->filter['initData']['rc'] = $rCols;
);
if (!empty($this->filter['fi']['extraCols'])) if ($xCols)
$lv['params']['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)'; $this->filter['initData']['ec'] = $xCols;
if ($x = $this->filterObj->getSetCriteria())
$this->filter['initData']['sc'] = $x;
$tabData = ['data' => array_values($npcs->getListviewData($rCols ? NPCINFO_REP : 0x0))];
if ($rCols) // never use pretty-print
$tabData['extraCols'] = '$fi_getReputationCols('.Util::toJSON($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')';
else if ($xCols)
$tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
if ($this->category) if ($this->category)
$lv['params']['hiddenCols'] = "$['type']"; $tabData['hiddenCols'] = ['type'];
// create note if search limit was exceeded // create note if search limit was exceeded
if ($npcs->getMatches() > CFG_SQL_LIMIT_DEFAULT) if ($npcs->getMatches() > CFG_SQL_LIMIT_DEFAULT)
{ {
$lv['params']['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_npcsfound', $npcs->getMatches(), CFG_SQL_LIMIT_DEFAULT); $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_npcsfound', $npcs->getMatches(), CFG_SQL_LIMIT_DEFAULT);
$lv['params']['_truncated'] = 1; $tabData['_truncated'] = 1;
} }
if ($this->filterObj->error) if ($this->filterObj->error)
$lv['params']['_errors'] = '$1'; $tabData['_errors'] = 1;
$this->lvTabs[] = $lv; $this->lvTabs[] = ['creature', $tabData];
// sort for dropdown-menus // sort for dropdown-menus
Lang::sort('game', 'fa'); Lang::sort('game', 'fa');
@@ -90,6 +99,10 @@ class NpcsPage extends GenericPage
array_unshift($this->title, $this->name); array_unshift($this->title, $this->name);
if ($this->category) if ($this->category)
array_unshift($this->title, Lang::npc('cat', $this->category[0])); array_unshift($this->title, Lang::npc('cat', $this->category[0]));
$form = $this->filterObj->getForm();
if (isset($form['fa']) && !is_array($form['fa']))
array_unshift($this->title, Lang::game('fa', $form['fa']));
} }
protected function generatePath() protected function generatePath()
@@ -97,7 +110,7 @@ class NpcsPage extends GenericPage
if ($this->category) if ($this->category)
$this->path[] = $this->category[0]; $this->path[] = $this->category[0];
$form = $this->filterObj->getForm('form'); $form = $this->filterObj->getForm();
if (isset($form['fa']) && !is_array($form['fa'])) if (isset($form['fa']) && !is_array($form['fa']))
$this->path[] = $form['fa']; $this->path[] = $form['fa'];
} }

Some files were not shown because too many files have changed in this diff Show More