198 Commits

Author SHA1 Message Date
Eduardo Bart
59c3e4467a Update v0.6.5 download link in README 2014-01-06 19:16:50 -02:00
Eduardo Bart
117833addc Update to version 0.6.5 2014-01-06 19:02:45 -02:00
Ahmed Samy
edf17a44ca Code cleanup 2014-01-06 14:35:57 +02:00
Sam
6db2458c1d Adding 10.31 to version list 2014-01-01 15:47:51 +01:00
Ahmed Samy
2556bfcbf2 Tab -> space 2013-12-30 18:13:49 +02:00
Ahmed Samy
9bdce800d8 Bind Container item code to lua 2013-12-30 18:10:45 +02:00
Ahmed Samy
9adab5f9bf Item serialization: Write description & text 2013-12-27 12:59:50 +02:00
Ahmed Samy
a6f998d406 Fix typo & bind ItemType::isWritable 2013-12-27 11:32:19 +02:00
Ahmed Samy
ab21558c47 Do not test for TILESTATE_NONE
This flag is ALWAYS set, cause it's always 0 i put there by mistake.
It causes an issue by simplying drawing everything dark, cause of
the opacity...
2013-12-27 05:10:00 +02:00
Sam
17c4d59b15 Enabling versions 7.8 to 8.0 2013-12-26 23:20:34 +01:00
Sam
d53f4dfa1e Merge pull request #419 from dominique120/master
Spanish translation improvement.
2013-12-26 13:46:08 -08:00
Sam
da959aca28 Support for protocol 7.7/7.72 / Tiny fix 2013-12-26 21:31:55 +01:00
Sam
91c182d93d Tiny typo fix :) 2013-12-26 19:39:40 +01:00
dominique120@live.com
6ecd49abe1 Fix recommended by @Bahamuntxd 2013-12-26 12:17:57 -05:00
Ahmed Samy
6745bff132 Introduce g_resources.getDirectoryFiles
This can recursively find files in a directory that's not in physfs search
path, this is needed for mapeditor needs, esp the file browser.

We do not want to limit users to the application run directory
so we have to use this.

This function uses boost filesystem.
Prototype:
	g_resources.getDirectoryFiles(directory STRING, filenameOnly BOOL,
	recursive BOOL)

filenameOnly is there if we want to just get the filenames in the
directory.
recursive if we want to loop directories in the directory.
2013-12-25 21:32:49 +02:00
Sam
27db78567b Fix writables in 7.6, fixes #416 2013-12-25 15:11:12 +01:00
Sam
83dd92cfec Merge pull request #417 from ronpetit/master
fixed outfit for 7.6 fix #415
2013-12-24 16:01:10 -08:00
dominique120@live.com
5605061435 fixed a little typo. 2013-12-24 17:47:08 -05:00
dominique120@live.com
241b178ebc Improved the Spanish translation by making some statements sound more formal and correcting a few other ones that where incorrect or that missed a few words. 2013-12-24 17:39:51 -05:00
Ron
d0288e23d8 Update outfit.lua
Fixed outfits for 7.6
2013-12-24 14:07:52 -04:30
Sam
9d8cf33acc Fix 7.60 login protocol 2013-12-23 23:45:49 +01:00
Sam
6a4868f2b5 Merge pull request #412 from LordHepipud/patch-1
Fix 7.70 RSA / XTEA encryption
2013-12-23 14:45:17 -08:00
LordHepipud
aa84ee4362 Fix 7.70 RSA / XTEA encryption
RSA / XTEA encryption was implemented with Client 7.70

~~~~Lord Hepipud~~~~
2013-12-23 23:31:59 +01:00
Henrique Santiago
1e17919a26 Use of createTable on containers 2013-12-22 17:36:43 -02:00
Ahmed Samy
df3546b073 lua: now we have a function to static allocate an array
lua_newtable() definition:
	#define lua_newtable(L) 	lua_createtable(L, 0, 0)

This simply allocates space for 0 fields, meaning, if we push any
values into this array, it will re-allocate the array, which is bad.

This function statically allocates an array, so it's ready to have
X fields into it.

Performance tests:

lua_newtable
1428161
1426992
1413513

lua_createtable
2004544
1974117
1957533

These tests were done on an AMD 8350fx CPU, single thread used.

narr: This is for fields that just have an index, e.g. arr[0] etc.
nrec: For fields which needs like arr.a, arr.b etc.

This is how many times each of the functions can run per second, as
you can see about 1.7x the calls to lua_newtable.

All credits goes to @dalkon, he was too lazy to do it by himself, and
asked me to do it for him.
2013-12-21 16:13:24 +02:00
Ahmed Samy
24e26e190b Also unbind g_minimap.updateTile 2013-12-18 04:40:53 +02:00
Ahmed Samy
ad7cda534d Fix minimap tile update for item removal 2013-12-18 04:40:52 +02:00
Sam
863bece70b VC12 fixes by @dalkon 2013-12-15 19:09:03 +01:00
Ahmed Samy
22f97aa00b More binds to lua 2013-12-15 04:59:45 +02:00
Ahmed Samy
e32607b05c Tiny fixes 2013-12-15 04:33:43 +02:00
Ahmed Samy
1790eee1c6 Add selection to tile 2013-12-14 23:06:52 +02:00
Ahmed Samy
ec2518525a Add more tile flags to be checked for 2013-12-14 19:44:54 +02:00
Ahmed Samy
eb4fb4ff41 Animations are now optional
The default behaviour is still there though.
2013-12-14 18:48:18 +02:00
Ahmed Samy
4f997a3826 Only request tile cache to be updated once for both x & y 2013-12-12 16:24:50 +02:00
Ahmed Samy
c54015a60d Fixup movePixels a little bit
Thanks to @dalkon for dis.
2013-12-12 16:21:22 +02:00
Ahmed Samy
57c432d3ea Increase maximum move offset to be 64 instead of 32 2013-12-12 03:23:13 +02:00
Ahmed Samy
bd8c883e13 Take out debug message 2013-12-12 02:53:57 +02:00
Ahmed Samy
b5c3b37b3c Tiny fix for movePixels 2013-12-12 02:46:20 +02:00
Ahmed Samy
ece6507df2 Bind g_minimap.updateTile to lua
This is needed to update tile removals from minimap widget.
2013-12-11 19:08:12 +02:00
Ahmed Samy
04515ffbb8 Fix map move pixels function
Thanks to @dalkon for this.
2013-12-11 18:36:31 +02:00
Ahmed Samy
ed24a0735a Bind {un,}lockVisibleFloor to lua 2013-12-09 20:15:39 +02:00
Ahmed Samy
2498129ff3 mapio: don't add saved with line in description 2013-12-07 02:52:51 +02:00
Ahmed Samy
066638e7c0 mapio: don't add description twice 2013-12-07 02:27:21 +02:00
Ahmed Samy
95610de5f3 bind set{Width,Height} of g_map to lua 2013-12-07 02:00:31 +02:00
Ahmed Samy
9154c0a678 mapio: fix load on new maps 2013-12-06 23:08:08 +02:00
Ahmed Samy
772ec208c0 don't throw error on fail 2013-12-05 22:23:46 +02:00
Eduardo Bart
7914832be9 Update readme 2013-12-04 14:00:09 -02:00
Sam
99500c9c91 Protocol 8.40-.42 fixes, closes #272 2013-12-04 16:02:51 +01:00
Ahmed Samy
b9b9a32b83 Spawns: add function to find a spawn to place a creature on 2013-12-04 14:30:26 +02:00
Sam
f615b80fbc Navigation for large tab amounts, closes #211 2013-12-03 20:25:56 +01:00
Henrique Santiago
319f32cdaa Merge pull request #387 from conde2/master
Fix compilation under MSVC
2013-11-30 19:52:57 -08:00
conde2
ba091c9f5b Indentation 2013-12-01 01:30:48 -02:00
conde2
3288711d16 Mistake 2013-12-01 01:25:34 -02:00
conde2
8b0509a5c3 Try to fix compilation 2013-12-01 01:19:29 -02:00
conde2
d7612e9304 Little mistake 2013-12-01 01:12:06 -02:00
conde2
4dcb30110f Use of clamp 2013-12-01 00:48:36 -02:00
conde2
7ff73d1064 Fix indentation 2013-11-30 23:34:21 -02:00
conde2
ca2fe9cf45 Fix compilation under MSVC
Use template for std::min and std::max
2013-11-30 23:08:43 -02:00
Sam
b61f509755 Checkboxes fix 2013-11-19 14:54:17 +01:00
Sam
ec012cb137 Fix 7.6 protocol issue / Typo 2013-11-19 01:09:31 +01:00
Sam
69ae324676 Tiny setPVPMode fix 2013-11-19 00:55:42 +01:00
Sam
4d656f8bd1 New pvp modes to combat controls 2013-11-19 00:50:00 +01:00
Sam
c98439ae65 onOutfitChange fix 2013-11-18 23:18:48 +01:00
Sam
4f9f04206d Added enter / escape button to minimap flag window 2013-11-18 20:57:50 +01:00
Sam
fc54a6e418 Full modal dialog functionality 2013-11-18 18:58:15 +01:00
Ahmed Samy
25d3019d1a force use of template version of std::min/max 2013-11-14 00:02:47 +02:00
Ahmed Samy
1711d8bdaf Revert "replace all std::min/std::max with just min/max"
This reverts commit 28d967ccce.
2013-11-13 23:53:54 +02:00
Ahmed Samy
28d967ccce replace all std::min/std::max with just min/max
This errors out in MSVC since the inclusion of winsock2.h, CL uses the
macro not the template version.
2013-11-13 23:48:10 +02:00
Ahmed Samy
f02d5e7975 Merge https://github.com/BrunoDCC/otclient
# By BrunoDCC
# Via BrunoDCC
* https://github.com/BrunoDCC/otclient:
  Fix houses and monsters
2013-11-13 22:51:00 +02:00
BrunoDCC
4bfe9b640e Fix houses and monsters 2013-11-13 18:48:37 -02:00
Eduardo Bart
ab1cc8c890 Update download links 2013-11-13 15:25:23 -02:00
Eduardo Bart
d657a0482a Add download links to README 2013-11-13 15:17:38 -02:00
Eduardo Bart
8562d38ca4 Update make_snapshop.sh [2] 2013-11-13 15:09:33 -02:00
Eduardo Bart
28786a3570 Update make_snapshop.sh 2013-11-13 14:33:29 -02:00
Eduardo Bart
b07a77f705 Changes to compile for Win64 2013-11-13 13:55:34 -02:00
Eduardo Bart
0d44942e8e Version 0.6.4 2013-11-13 13:07:37 -02:00
Eduardo Bart
dd829395e3 Merge pull request #376 from dalkon/master
Item phases & Fix party "bug" & Additions
2013-11-12 15:56:54 -08:00
dalkon
18fa54181a Format fix 2013-11-13 00:50:46 +01:00
dalkon
5b8ff29fa3 Fix party "bug" & Additions 2013-11-13 00:48:41 +01:00
dalkon
59e90e8f1e Item phases 2013-11-13 00:18:15 +01:00
Sam
5db37857bf Fix game zoom issue 2013-11-12 23:35:19 +01:00
Eduardo Bart
fc0297d6db Fix compile errors in ubuntu 2013-11-12 20:07:46 -02:00
Eduardo Bart
1060c6f78c Compilation for MSVC2013, thanks @dalkon
OTclient now compiles in "Microsoft Visual Studio 2013 Express for Windows Desktop"
All the needed libraries you can download at https://www.dropbox.com/s/2yfb1c763io8efy/otclient-msvc13-libs.zip
NOTE: You have to change VC++ Directories to the properly directories
NOTE: Latested MSVC 2013 or greated is required
2013-11-12 16:32:39 -02:00
dalkon
c9597d6682 Visual Studio 2013 Compatibility 2013-11-12 16:32:39 -02:00
dalkon
d3e97d33c7 Update .gitignore 2013-11-12 16:32:39 -02:00
Ahmed Samy
2b015d49d7 don't set bits twice 2013-11-12 20:06:59 +02:00
Ahmed Samy
f04b4a0b42 small fix 2013-11-12 20:02:24 +02:00
Ahmed Samy
ff5c22d4c0 Tiny fix
Saving newly made houses by the map editor is not even supported, so this
is not a bug
2013-11-12 17:15:09 +02:00
Sam
d6105a98d1 Fixed issue with deathwindow 2013-11-12 15:21:10 +01:00
Eduardo Bart
0938e22eb9 Fixes in stdext::format 2013-11-11 23:07:25 -02:00
Eduardo Bart
1868b235cb Implement creature marks 2013-11-11 01:45:18 -02:00
Eduardo Bart
9a30050340 Fix typo 2013-11-10 23:40:29 -02:00
Eduardo Bart
9d689f0c43 Add missing changes for protocol > 1000 2013-11-10 23:31:59 -02:00
Eduardo Bart
2b34c0ea0a Fix #337
The sound buffer was not being fully preloaded because it's size was incorrect
2013-11-10 20:35:00 -02:00
Eduardo Bart
d32f71c2b9 Partial support for protocol 1020 and minimize lag
I am able to login in global Tibia however there are game protocol errors
* there is a new opcode 167, which I dont know what it is
* there are changes in opcode 147 which handles text messages that I don't know too

Connection writing was optimized, playing "lag" should improve by 10ms,
and improve much more in systems with low fps
2013-11-10 20:10:04 -02:00
Eduardo Bart
3cff331723 Merge github.com:BrunoDCC/otclient 2013-11-10 19:36:06 -02:00
Eduardo Bart
9894f0c0b9 Merge github.com:conde2/otclient 2013-11-10 19:31:24 -02:00
conde2
e60372fdea A little fix! 2013-11-10 03:17:49 -02:00
conde2
d2473fd424 Login support for 10.21 ~ 10.22
Please someone test more it !
Not tested in real Tibia !
2013-11-10 03:13:51 -02:00
BrunoDCC
7d7bd00a63 minor fix :3 2013-11-09 22:48:29 -02:00
BrunoDCC
c81a623c43 Fixes for creatures name
fixes on save houses (Now open most generate some error)
First 10.22 commit (open dat to edit)
Add server id for new versions
2013-11-09 20:04:48 -02:00
Eduardo Bart
8b165b95fd non relevant change 2013-11-05 21:32:27 -02:00
Sam
6f9436dd60 UISprite widget added
Display single sprites
2013-10-30 21:35:29 +01:00
Henrique Santiago
ae95f51346 Merge pull request #367 from shdpl/patch-1
Polish locale update
2013-10-26 09:46:15 -07:00
shdpl
9b84570372 Polish locale update
UTF-8 or UTF-16 pls
2013-10-25 19:27:19 +02:00
Eduardo Bart
38dec168ee Fix boost::asio misuse that would cause lag
io_service::reset was not called before io_service::poll, meaning that new events would be really polled in the next Connection::poll call, this could lead to network lag in computers with low framerate (ie: a user with 10 fps would have 100ms lag just because of that)
2013-10-22 21:42:35 -02:00
Henrique Santiago
84b722f8c7 Merge pull request #365 from conde2/master
Fix - Writeable / Readable items Issues: #362 and #364
2013-10-21 17:29:13 -07:00
Joao Pasqualini Costa
b3717ceb93 Fix - Writeable / Readable items 2013-10-21 22:25:12 -02:00
Henrique Santiago
3340e06da6 Merge pull request #363 from conde2/master
Fix protocol error #362
2013-10-20 17:24:19 -07:00
Joao Pasqualini Costa
084ee45650 A little mistake 2013-10-20 20:33:44 -02:00
Joao Pasqualini Costa
7290ec3334 Some work on container pagination
Bind lua functions for containers
Update onAddItem now it have the pagination slot
Note: getSize as i see is equal to getItemsCount, but not sure thats why
I've added !
2013-10-20 20:21:20 -02:00
Joao Pasqualini Costa
971d90a596 Fix protocol error #362
TODO:
Handle the helpers in LUA, it could be added into Battle Window i think,
if someone know where should this be comment here !
2013-10-20 18:31:20 -02:00
Sam
8230bf2af4 Merge branch 'master' of https://github.com/edubart/otclient 2013-10-20 17:15:30 +02:00
Sam
241b54262a Updated lua consts 2013-10-20 17:15:24 +02:00
Ahmed Samy
63ce68b449 mapio: make sure we read the house tile flag 2013-10-12 11:38:30 +02:00
Ahmed Samy
f47ea05abc Merge https://github.com/BrunoDCC/otclient
# By BrunoDCC
# Via BrunoDCC
* https://github.com/BrunoDCC/otclient:
  Save houses and spawns fix
2013-10-11 11:38:03 +02:00
BrunoDCC
de3160c90d Save houses and spawns fix 2013-10-11 02:35:31 -03:00
Sam
67bd00556d Re-added "Don't stretch/shrink" option
Closes #345
2013-10-09 18:17:50 +02:00
Sam
778559c7b9 Protocol 10.10 fixed containers 2013-10-09 17:58:58 +02:00
Sam
3aac0ac0ec Reverting mapio.cpp change
Should not have been commited.
2013-10-07 21:38:55 +02:00
Sam
4adfafc67e Fix for Client 8.5 2013-10-07 21:32:13 +02:00
Sam
1b27a095a9 Hotkeys for Fluid Containers
Hotkeys now save subType for fluid containers instead of always trying
to use item with subType 0.
2013-10-05 18:08:49 +02:00
Sam
e6977b1b43 Improvement / Fix hotkeys
- Hotkeys now check items in inventory
- Fixed USEONETARGET hotkey bug
2013-10-05 02:31:44 +02:00
Sam
5843b78e87 Support for client version 7.6
Tell me if there are parts which I should change.

- Not everything is tested yet, feel free to report bugs in 7.6
- the data send in between login opcode and account data might not be
correct, but I could not find any documents showing the right protocol
yet
- Hotkeys working
2013-10-04 04:09:54 +02:00
Ahmed Samy
7cf645e715 Woops, fix operator less than 2013-09-02 19:12:12 +00:00
Ahmed Samy
537508021e Make g_map.findItemsById return a map instead of a vector 2013-09-02 17:53:32 +00:00
Ahmed Samy
e741a62ce9 g_map.findItemsByid 2013-08-31 01:18:17 +00:00
Ahmed Samy
56d6ef6642 Introduce 4 new functions:
- g_map.beginGhostMode(float opacity)
- g_map.endGhostMode()
- UIMap::movePixels(int x, int y)
- MapView::move(int x, int y)
2013-08-30 01:54:36 +00:00
Ahmed Samy
3db8f54aa9 Introduce 2 new functions to g_things
- g_things.findItemTypesByName
- g_things.findItemTypesByString
2013-08-29 17:53:21 +00:00
Ahmed Samy
9234030c1b Zone colors: add more tile flags to be checked for 2013-08-27 15:49:48 +00:00
Ahmed Samy
520baa28ea Move "getNPC" that was introduced in commit
00729bbc2e
from Spawn to CreatureType

I didn't have a closer look at how his code was structured, what he
basically did
is that he set all creatures in a spawn as NPC's even if it's a monster
which is
so erroneous.

Highlights:
- Add branch prediction macros
- Minor code style fixes & some others

Hopefully the branch prediction thing will speed up OTB since it's
awfully slow.
2013-08-27 03:40:18 +02:00
Ben Dol
bbdeac2e33 Merge pull request #353 from KnopersPL/master
Fix for protocol 850 errors, thanks @KnopersPL
2013-08-25 03:54:39 -07:00
KnopersPL
1f914351bb Update protocolcodes.cpp
Fix for server messages on 850
2013-08-25 12:24:18 +02:00
KnopersPL
eb68504dc1 Update protocolgameparse.cpp
Fix the slow animation move effect, some opcodes error and logging in as GM
2013-08-25 12:20:54 +02:00
Ahmed Samy
32647f11bf Introduce 2 new functions to Map
- g_map.colorizeThing
- g_map.removeThingColor
2013-08-24 00:09:56 +02:00
Ahmed Samy
644d4daeea More cleanups 2013-08-23 02:12:36 +02:00
Ahmed Samy
b27352c321 Merge branch 'master' of https://github.com/BrunoDCC/otclient
* 'master' of https://github.com/BrunoDCC/otclient:
  Add full npc read (it not cause bug)
  Fixed a npcs save Fixed a spawns and npcs load
2013-08-23 02:03:40 +02:00
BrunoDCC
a6be9ae525 Add full npc read (it not cause bug) 2013-08-22 21:00:48 -03:00
Ahmed Samy
5df3ec8cf2 More cleanups
Also, bind map descriptions auxiliar functions
2013-08-23 01:55:15 +02:00
BrunoDCC
00729bbc2e Fixed a npcs save
Fixed a spawns and npcs load
2013-08-22 20:54:09 -03:00
Ahmed Samy
3b2d8a2b5e Fix build 2013-08-22 23:20:44 +02:00
Ahmed Samy
d5298c1011 Cleanups 2013-08-22 21:15:17 +02:00
Allan Ference
aaad8ab8a0 Fix spawn loading and saving, thanks to @BrunoDCC 2013-08-20 12:14:52 +02:00
Allan Ference
efbd9ab693 Attempt to fix spawn loader 2013-08-19 18:57:25 +02:00
Allan Ference
092209c0bb Attempt to fix houses and spawns save 2013-08-19 10:38:22 +02:00
Allan Ference
05f7f6a4cf Introduce Item::getName() 2013-08-18 18:29:20 +02:00
Ben Dol
2c36ca7215 Merge pull request #350 from conde2/master
Fix Keyboard unbind, WASD walk, Mount Frames, Fix #349, #344, #304, #253 Thanks @conde2!
2013-08-17 19:23:06 -07:00
Joao Pasqualini Costa
926eb9f01e Fix #253
Message Mode 12
2013-08-17 22:57:38 -03:00
Joao Pasqualini Costa
96bb28e806 Fix Mount frame issue
This will fix mount for all protocols
2013-08-17 22:19:24 -03:00
Joao Pasqualini Costa
e15995e1eb Fix walk with WASD
This should be an alone bind, so any other control will not affect the
keys.
2013-08-17 21:56:37 -03:00
Joao Pasqualini Costa
719debfeae Fix keyboard unbind
Now this will unbind the right widget
2013-08-17 21:54:16 -03:00
Allan Ference
e7137c2535 Rework NPCs loader with g_resources (Untested) 2013-08-17 23:36:44 +02:00
Allan Ference
5eabf6f518 Optional and configurable zone colors 2013-08-17 23:06:58 +02:00
Allan Ference
5849136526 Introduce g_things.findItemTypeByName 2013-08-17 15:09:10 +02:00
Allan Ference
0451fd6b58 OTBM saver: don't escape version check for waypoints 2013-08-15 11:20:22 +02:00
Allan Ference
9cf878335a Minor changes
Mainly code style
2013-08-14 18:58:53 +02:00
BeniS
529b646316 Forgot to provide the widget for walk key bind, thanks conde2! 2013-08-13 10:22:34 +12:00
Allan Ference
1ea2b380ca Disregard progressbar 2013-08-09 17:35:56 +02:00
Allan Ference
914fe249ab Merge branch 'master' of https://github.com/BrunoDCC/otclient
* 'master' of https://github.com/BrunoDCC/otclient:
  Fix OTBM save Remove if(version > 1) { (it causes bug)
2013-08-09 14:54:09 +02:00
BrunoDCC
4d498ed0d8 Fix OTBM save
Remove if(version > 1) { (it causes bug)
2013-08-07 20:04:21 -03:00
Allan Ference
9aa667da26 MapEditor: more changes for ease of use 2013-08-04 14:21:12 +02:00
Allan Ference
e6db43ac3a MapEditor specific: Use colors for tile states 2013-08-04 14:19:10 +02:00
Eduardo Bart
b5911cf1de Use client version instead of entergame 2013-08-03 19:26:38 -03:00
BeniS
5fbb71157d Added enable/disable chat mode (allows for alphabetical key controls), battle window anchoring fixed (thank you River) & more:
* Added better walk key binding (for other uses, like the sample given WASD).
* Made string.ends part of the string meta table rather than parameter based.
2013-07-29 10:42:18 +12:00
Allan Ference
0ff36a1a0a Merge https://github.com/marksamman/otclient
* https://github.com/marksamman/otclient:
  Fix login with 9.83-10.10
2013-07-28 07:10:44 +02:00
Mark Samman
4a04a18835 Fix login with 9.83-10.10
This only adds protocol compatibility, the features aren't actually
implemented yet
2013-07-28 07:05:46 +02:00
Allan Ference
6fa9631d6a Merge https://github.com/marksamman/otclient
* https://github.com/marksamman/otclient:
  Fix compiling on OS X
2013-07-28 04:42:31 +02:00
Mark Samman
261642190b Fix compiling on OS X 2013-07-28 04:30:52 +02:00
BeniS
95abf2a1d2 Fix a bug with autowalking & add prompt param for tryLogout.
(We don't want to be prompted when pressing Ctrl + Q or  Ctrl + L).
2013-07-07 05:36:56 +12:00
Sam
c4adf2d817 Added whitelist
- Checkboxes to turn ignore/whitelist off
- Allow VIP messages
2013-07-04 18:03:21 +02:00
Sam
c7c259ef80 Game Menu / Attacking fixes
- No more "Attack" / "Follow" for creatures on different floor
- No rule violation for monsters, only players now
- Alt + Click to target works through roofs -> fixes #203
- Fixed battle list issue (check position when trying to attack)
2013-07-03 14:25:18 +02:00
Sam
987c6d6c91 Mount display issue
fixes #275
Not really needed but enables some cool stuff
2013-07-02 23:16:11 +02:00
Sam
c8185474de Implements battlelist sorting
Features:
Sort by age, name, health or distance
Sort ascending or descending
Button to toggle filters and sort options
http://i.imgur.com/cSCBwr4.png

closes 213
2013-07-02 22:43:52 +02:00
Sam
be071c7103 Fix deathwindow issue 2013-07-02 22:40:19 +02:00
Sam
2f9e2c3e33 Sorting VIP list by name
@ #209
What should sorting by type do?
2013-07-01 22:59:51 +02:00
Sam
b81590f297 Fixed Alt + F4 keycombo on Windows 2013-06-29 16:53:15 +02:00
BeniS
e062562888 Added sublime text 2 project file.
* Changed the order of the dofiles params.
2013-05-12 21:16:43 +12:00
BeniS
18d23653c4 Added some control params to dofiles lua method.
* File name contains string.
* Recursive file checking for deep searches.
2013-05-12 17:00:52 +12:00
Henrique Santiago
6c119627bb Merge pull request #330 from conde2/master
Improvement on styles
2013-04-30 13:26:56 -07:00
Joao Pasqualini Costa
d847a78a4d Improvement on styles
Added VerticalList
2013-04-30 17:21:04 -03:00
Eduardo Bart
0dccc870b5 Merge pull request #324 from TiagoTdA/master
Update/Refresh modules on unload
2013-04-13 18:11:47 -07:00
Tiago T. de Araujo
e4c7ca604b Fix update on unload module 2013-04-12 21:27:10 -03:00
Sam
d427560b98 Countwindow Fix [YAH]
Fixed typing amount in count window. Should be hidden again.
2013-04-12 19:15:28 +02:00
Sam
cc12db0d1f Reverted getTopMultiUseThing() change 2013-04-09 23:54:10 +02:00
Henrique Santiago
1ce6df99ac Merge pull request #318 from conde2/master
Fix #316
2013-04-09 14:21:15 -07:00
Joao Pasqualini Costa
57bb6ff974 Fix #315 by @pacmanis
Credits go to @pacmanis
2013-04-09 18:02:43 -03:00
Joao Pasqualini Costa
9bae1b9e25 Fix #316
It still have a bug when using string like this:

" teste {{}} teste"
2013-04-09 17:54:57 -03:00
Sam
1415de222c Merge pull request #313 from sn3ejk/sn3ejk
Fix extended opcodes for forgottenserver.
2013-04-05 18:50:02 -07:00
Kamil W
34ceb3c95e Fix extended opcodes for forgottenserver. 2013-04-05 22:03:17 +02:00
Eduardo Bart
b43a196eac Minor fixes and add auto resize for images 2013-03-15 21:59:22 -03:00
Eduardo Bart
a3a65d40ce Merge pull request #303 from conde2/master
Properly Fix #301
2013-03-15 08:35:04 -07:00
Joao Pasqualini Costa
6ef3508362 Properly Fix #301 2013-03-14 20:57:02 -03:00
Eduardo Bart
a71e07f063 Restore walk optimization 2013-03-14 00:16:57 -03:00
Eduardo Bart
4bdd1e79fd Fix compile error for win32 2013-03-13 21:18:17 -03:00
Eduardo Bart
e9e4dcd71b Improve walk when lagging 2013-03-13 20:55:20 -03:00
Eduardo Bart
0891e2b30a Add C++ traceback to errors 2013-03-12 01:36:36 -03:00
Eduardo Bart
24664714bd Fix rare but serious bug in Connection
* Implement output pooling for writing data in connection,
this should fix rare cases where sending would fail
2013-03-12 00:18:47 -03:00
Eduardo Bart
da51dd467e Minor fix 2013-03-11 15:00:31 -03:00
205 changed files with 6842 additions and 3527 deletions

71
.gitignore vendored
View File

@@ -1,4 +1,3 @@
/modules/.project
build*
CMakeCache.txt
CMakeFiles
@@ -39,3 +38,73 @@ tags
Thumbs.db
.directory
src/framework/graphics/dx/
modules/.project/modules.sublime-workspace
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 2.6)
project(otclient)
set(VERSION "0.6.2")
set(VERSION "0.6.5")
option(FRAMEWORK_SOUND "Use SOUND " ON)
option(FRAMEWORK_GRAPHICS "Use GRAPHICS " ON)

View File

@@ -8,6 +8,18 @@ that each functionality is a separated module, giving the possibility to users m
anything easily. Users can also create new mods and extend game interface for their own purposes.
Otclient is written in C++2011, the upcoming C++ standard and heavily scripted in lua.
### Where do I download?
Here is the latest v0.6.5 release compiled for both i686(32 bit) and x86_64(64 bit) machines, with OpenGL renderer.
This release is compatible with protocols ranges from 7.60 up to 10.31.
* [For Windows](https://www.dropbox.com/sh/se1okacemoqzjve/XFqFoSKLCg/otclient-win-0.6.5.zip)
* [For Linux](https://www.dropbox.com/sh/se1okacemoqzjve/xKJL7j6vEo/otclient-linux-0.6.5.tar.gz)
**NOTE:** You will need to download spr/dat files on your own and place them in `data/things/VER/` (i.e: `data/things/1031/Tibia.spr`)
Older releases can be downloaded from [here](https://www.dropbox.com/sh/se1okacemoqzjve/-oWK4YFm03)
### Features
Beyond of it's flexibility with scripts, otclient comes with tons of other features that make possible
@@ -18,17 +30,18 @@ to port to mobile platforms. Otclient is also flexible enough to
create tibia tools like map editors just using scripts, because it wasn't designed to be just a
client, instead otclient was designed to be a combination of a framework and tibia APIs.
### Download
Stable versions of otclient master branch compiled for Windows and Linux is available
in the [downloads section](https://github.com/edubart/otclient/downloads) of the project's github page.
### Compiling
A package with all required libraries for compiling OTClient on Windows can be found here:
* [For MSVC 2013](https://www.dropbox.com/sh/se1okacemoqzjve/dI4ODbq7OT/otclient-msvc13-libs.zip)
* [For MingW32](https://www.dropbox.com/sh/se1okacemoqzjve/UAkRCiGXXR/otclient-libs_mingw32-dwarf2.zip)
In short, if you need to compile OTClient, follow these tutorials:
* [Compiling on Windows](https://github.com/edubart/otclient/wiki/Compiling-on-Windows)
* [Compiling on Linux](https://github.com/edubart/otclient/wiki/Compiling-on-Linux)
### Need help?
Try to ask questions in [otland](http://otland.net/f494/), now we have a board for the project there,
@@ -50,4 +63,3 @@ Talk directly with us at #otclient irc.freenode.net or send an email directly to
Otclient is made available under the MIT License, thus this means that you are free
to do whatever you want, commercial, non-commercial, closed or open.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

View File

@@ -1,4 +1,6 @@
-- special thanks for Shaday, who made these translations
--Dominique120 edits: I made some statements to sound more formal and appropriate as well as correcting a few words that were not translated. I also added a few notes for future translators to keep in mind.
locale = {
name = "es",
@@ -9,18 +11,18 @@ locale = {
["1a) Offensive Name"] = "1a) Nombre ofensivo",
["1b) Invalid Name Format"] = "1b) Formato invalido para nombre",
["1c) Unsuitable Name"] = "1c) Nombre no adecuado",
["1d) Name Inciting Rule Violation"] = "1d) Nombre que incita una violacion al reglamento",
["1d) Name Inciting Rule Violation"] = "1d) Nombre que incita una violaci<EFBFBD>n al reglamento",
["2a) Offensive Statement"] = "2a) Comentario ofensivo",
["2b) Spamming"] = "2b) Spamming",
["2c) Illegal Advertising"] = "2c) Publicidad ilicita",
["2d) Off-Topic Public Statement"] = "2d) Publicacion fuera de lugar",
["2e) Non-English Public Statement"] = "2e) Publicacion fuera del ingles",
["2f) Inciting Rule Violation"] = "2f) Incitar a una violacion al reglamento",
["2c) Illegal Advertising"] = "2c) Publicidad il<EFBFBD>cita",
["2d) Off-Topic Public Statement"] = "2d) Publicaci<EFBFBD>n fuera de lugar",
["2e) Non-English Public Statement"] = "2e) Publicaci<EFBFBD>n fuera del ingles",
["2f) Inciting Rule Violation"] = "2f) Incitar a una violaci<EFBFBD>n al reglamento",
["3a) Bug Abuse"] = "3a) Abuso de error",
["3b) Game Weakness Abuse"] = "3b) Abuso de debilidad del juego",
["3c) Using Unofficial Software to Play"] = "3c) Usando software ilegal para jugar",
["3d) Hacking"] = "3d) Hackeo",
["3e) Multi-Clienting"] = "3e) Multiple Cliente",
["3e) Multi-Clienting"] = "3e) Uso de m<>ltiples clientes",
["3f) Account Trading or Sharing"] = "3f) Intercambio de cuenta",
["4a) Threatening Gamemaster"] = "4a) Amenzar a un Gamemaster",
["4b) Pretending to Have Influence on Rule Enforcement"] = "4b) Pretender tener influencia en una parte del reglamento",
@@ -28,7 +30,7 @@ locale = {
["Accept"] = "Aceptar",
["Account name"] = "Nombre de cuenta",
["Account Status:"] = "Estado de cuenta:",
["Action:"] = "Accion:",
["Action:"] = "Acci<EFBFBD>n:",
["Add"] = "A<EFBFBD>adir",
["Add new VIP"] = "A<EFBFBD>adir nuevo VIP",
["Addon 1"] = "Addon 1",
@@ -36,46 +38,46 @@ locale = {
["Addon 3"] = "Addon 1",
["Add to VIP list"] = "A<EFBFBD>adir a lista VIP",
["Adjust volume"] = "Ajustar volumen",
["Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nSimply click on Ok to resume your journeys!"] = "<EFBFBD>Ay! Aventurero valiente, que ha conocido a un triste destino. \nPero no se desespere, porque los dioses le llevar<61> de vuelta \na este mundo a cambio de un peque<75>o sacrificio \n\nSimply haga clic en Aceptar para continuar con sus viajes!",
["Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nSimply click on Ok to resume your journeys!"] = "<EFBFBD>Ay! Aventurero valiente, has conocido un triste destino. \nPero no se desespere, porque los dioses te llevar<61>n de vuelta \na este mundo a cambio de un peque<75>o sacrificio \n\nSimplemente haga clic en Aceptar para continuar con sus viajes!",
["All"] = "Todo",
["All modules and scripts were reloaded."] = "Todos los m<>dulos y scripts se vuelven a cargar.",
["Allow auto chase override"] = "Permitur auto persecuci<63>n override",
["Also known as dash in tibia community, recommended\nfor playing characters with high speed"] = "Conocido por la comunidad de tibia como dash.\nRecomenada para players de alto nivel.",
["Also known as dash in tibia community, recommended\nfor playing characters with high speed"] = "Conocido por la comunidad de tibia como dash.\nRecomenada para jugadores de alto nivel.",
["Ambient light: %s%%"] = "Ambiente de luz: %s%%",
["Amount:"] = "Cantidad:",
["Amount"] = "Cantidad",
["Anonymous"] = "An<EFBFBD>nimo",
["Are you sure you want to logout?"] = "<EFBFBD>Estas seguro de que quieres salir?",
["Are you sure you want to logout?"] = "<EFBFBD>Estas seguro de que deseas salir?",
["Attack"] = "Atacar",
["Author"] = "Autor",
["Autoload"] = "Auto carga",
["Autoload priority"] = "Auto carga prioritaria",
["Auto login"] = "Auto iniciar",
["Auto login selected character on next charlist load"] = "Auto cargar car<61>cter seleccionado en la carga charlist siguiente",
["Axe Fighting"] = "Combate con acha",
["Auto login"] = "Auto ingresar",
["Auto login selected character on next charlist load"] = "Ingresar la siguiente vez que aparece el charlist con el personaje seleccionado",
["Axe Fighting"] = "Combate con hacha",
["Balance:"] = "Saldo:",
["Banishment"] = "Banishment",
["Banishment + Final Warning"] = "Banishment + Final Warning",
["Battle"] = "Batalla",
["Browse"] = "Navegar",
["Bug report sent."] = "Reporte de error enviado.",
["Button Assign"] = "Boton asignado",
["Button Assign"] = "Bot<EFBFBD>n asignado",
["Buy"] = "Compra",
["Buy Now"] = "Compra ahora",
["Buy Offers"] = "Comprar oferta",
["Buy with backpack"] = "Comprar con backpack",
["Buy with backpack"] = "Comprar con mochila",
["Cancel"] = "Cancelar",
["Cannot login while already in game."] = "No se puede iniciar sesi<73>n, mientras que estes en el juego.",
["Cannot login while already in game."] = "No se puede iniciar sesi<73>n, mientras que est<EFBFBD>s en el juego.",
["Cap"] = "Cap",
["Capacity"] = "Capacidad",
["Center"] = "Centrar",
["Channels"] = "Canales",
["Character List"] = "Lista de car<61>cter",
["Classic control"] = "Control Clasico",
["Classic control"] = "Controles Cl<EFBFBD>sicos",
["Clear current message window"] = "Limpiar mensaje actual en ventana",
["Clear Messages"] = "Limpiar mensaje",
["Clear object"] = "Limpiar objeto",
["Client needs update."] = "El cliente necesita actualizacion.",
["Client needs update."] = "El cliente necesita una actualizaci<EFBFBD>n.",
["Close"] = "Cerrar",
["Close this channel"] = "Cerrar este canal",
["Club Fighting"] = "Combate con mazo",
@@ -99,28 +101,28 @@ locale = {
["Delete mark"] = "Borrar Marca",
["Description:"] = "Descripci<EFBFBD>n:",
["Description"] = "Descripci<EFBFBD>n",
["Destructive Behaviour"] = "Comportamiento agresivo",
["Destructive Behaviour"] = "Comportamiento destructivo",
["Detail"] = "Detalle",
["Details"] = "Detalles",
["Disable Shared Experience"] = "Desactivar experiencia compartida",
["Dismount"] = "Desmontar",
["Display connection speed to the server (milliseconds)"] = "Mostrar velocidad de conexion en el servidor (millisegundos)",
["Display connection speed to the server (milliseconds)"] = "Mostrar velocidad de conexi<EFBFBD>n en el servidor (millisegundos)",
["Distance Fighting"] = "Combate a distancia",
["Don\'t stretch/shrink Game Window"] = "No estirar ni reducir el tama<6D>o de ventana",
["Edit hotkey text:"] = "Editar texto de hotkey:",
["Edit List"] = "Editar lista",
["Edit Text"] = "Editar texto",
["Enable music"] = "Habilitar musica",
["Enable music"] = "Habilitar m<EFBFBD>sica",
["Enable Shared Experience"] = "Habilitar experiencia compartida",
["Enable smart walking"] = "Habilitar caminado inteligente",
["Enable vertical synchronization"] = "Habilitar sincronizaci<63>n vertical",
["Enable walk booster"] = "Habilitar caminado turbo",
["Enter Game"] = "Entrar al juego",
["Enter one name per line."] = "Introducir un nombre por linea.",
["Enter with your account again to update your client."] = "",
["Enter with your account again to update your client."] = "Ingrese con su cuenta nuevamente para actualizar el cliente.",
["Error"] = "Error",
["Error"] = "Error",
["Excessive Unjustified Player Killing"] = "Asesinato excesivo injustivicado de players",
["Excessive Unjustified Player Killing"] = "Asesinato excesivo injustificado de jugadores",
["Exclude from private chat"] = "Ejecutar desde un canal privado",
["Exit"] = "Salir",
["Experience"] = "Experiencia",
@@ -132,71 +134,71 @@ locale = {
["Follow"] = "Seguir",
["Force Exit"] = "Forzar salida",
["For Your Information"] = "Para tu informaci<63>n",
["Free Account"] = "Account gratis",
["Free Account"] = "Cuenta gratis",
["Fullscreen"] = "Pantalla completa",
["Game"] = "Juego",
["Game framerate limit: %s"] = "Imagenes por segundo en juego: %s",
["Game framerate limit: %s"] = "Limite de cuadros por segundo en el juego: %s",
["Graphics"] = "Gr<EFBFBD>ficos",
["Graphics card driver not detected"] = "Controlador de tarjeta gr<67>fica de video no detectado",
["Graphics Engine:"] = "Motor Grafico:",
["Graphics Engine:"] = "Motor Gr<EFBFBD>fico:",
["Head"] = "Cabeza",
["Healing"] = "Curacion",
["Health Info"] = "HP Info",
["Health Information"] = "HP Informaci<63>n",
["Hide monsters"] = "Ocultar monsters",
["Hide non-skull players"] = "Ocultar no-skull players",
["Healing"] = "Curaci<EFBFBD>n",
["Health Info"] = "HP Info",--This can be better
["Health Information"] = "HP Informaci<63>n",--This can be better
["Hide monsters"] = "Ocultar monstruos",
["Hide non-skull players"] = "Ocultar jugadores sin skull",
["Hide Npcs"] = "Ocultar NPCs",
["Hide Offline"] = "Ocultar fuera de linea",
["Hide party members"] = "Ocultar miembros de party",
["Hide party members"] = "Ocultar miembros del party",
["Hide players"] = "Ocultar players",
["Hide spells for higher exp. levels"] = "Ocultar spells para niveles mas altos que tu experiencia.",
["Hide spells for other vocations"] = "Ocultar spells que sean para otra vocaci<63>n",
["Hide spells for higher exp. levels"] = "Ocultar hechizos para niveles mas altos que tu experiencia.",
["Hide spells for other vocations"] = "Ocultar hechizos que sean para otra vocaci<63>n",
["Hit Points"] = "Puntos de vida",
["Hold left mouse button to navigate\nScroll mouse middle button to zoom\nRight mouse button to create map marks"] = "Mantenga pulsado el bot<6F>n derecho del rat<61>n para navegar\nScroll bot<6F>n central del rat<61>n para ampliar\nbot<EFBFBD>n derecho del mouse para crear marcas del mapa",
["Hold left mouse button to navigate\nScroll mouse middle button to zoom\nRight mouse button to create map marks"] = "Mantenga presionado el bot<6F>n derecho del rat<61>n para navegar\nDezplaze la rueda central del rat<61>n para ampliar\nbot<EFBFBD>n derecho del mouse para crear marcas del mapa",
["Hotkeys"] = "Hotkeys",
["If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."] = "Si se cierra el programa, tu personaje puede permanecer en el juego.\nHaga clic en 'Salir' para asegurarse de que personaje deja el juego correctamente.\nClick en 'Salir' si desea salir del programa sin tener que salir de tu personaje.",
["If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."] = "Si se cierra el programa, tu personaje puede permanecer en el juego.\nHaga clic en 'Salir' para asegurarse de que personaje deja el juego correctamente.\nHaga click en 'Salir' si desea salir del programa sin tener que salir de tu personaje.",
["Ignore"] = "Ignorar",
["Ignore capacity"] = "Ignorar Capacidad",
["Ignored players:"] = "Players ignorados:",
["Ignore equipped"] = "Ignorar lo equipodo",
["Ignored players:"] = "Jugadores ignorados:",
["Ignore equipped"] = "Ignorar lo equipado",
["Ignore List"] = "Ignorar lista",
["Ignore players"] = "Ignorar players",
["Ignore players"] = "Ignorar jugadores",
["Ignore Private Messages"] = "Ignorar mensajes privados",
["Ignore Yelling"] = "Ignorar gritos",
["Interface framerate limit: %s"] = "Interface de imagenes por segundo: %s",
["Interface framerate limit: %s"] = "Interface de cuadros por segundo: %s",
["Inventory"] = "Inventario",
["Invite to Party"] = "Ivitar a party",
["Invite to Party"] = "Ivitar al party",
["Invite to private chat"] = "Invitar a canal privado",
["IP Address Banishment"] = "Banishment - Direccion IP",
["IP Address Banishment"] = "Banishment - Direcci<EFBFBD>n IP",
["Item Offers"] = "Ofertas de objetos",
["It is empty."] = "Est<EFBFBD> vasio.",
["Join %s\'s Party"] = "Unir %s\'s party",
["Leave Party"] = "Dejar party",
["It is empty."] = "Est<EFBFBD> vaci<EFBFBD>.",
["Join %s\'s Party"] = "Unirse al party de %s ",
["Leave Party"] = "Dejar el party",
["Level"] = "Nivel",
["Lifetime Premium Account"] = "Tiempo de Premium Account",
["Lifetime Premium Account"] = "Tiempo de Premium Account infinito",
["Limits FPS to 60"] = "Limites FPS a 60",
["List of items that you're able to buy"] = "Lista de objetos que puedes de comprar",
["List of items that you're able to sell"] = "Lista de objetos que puedes de vender",
["Load"] = "Cargar",
["Logging out..."] = "Cerrando sesion...",
["Logging out..."] = "Cerrando sesi<EFBFBD>n...",
["Login"] = "Ingresar",
["Login Error"] = "Error de ingreso",
["Login Error"] = "Error de ingreso",
["Logout"] = "Salir",
["Look"] = "Mirar",
["Magic Level"] = "Nivel m<>gico",
["Make sure that your client uses\nthe correct game protocol version"] = "Aseg<EFBFBD>rese de que el cliente utiliza\nes el correcto de versi<EFBFBD>n del protocolo",
["Make sure that your client uses\nthe correct game protocol version"] = "Aseg<EFBFBD>rese de que el cliente este utilizando\nes el versi<73>n del protocolo adecuado",
["Mana"] = "Mana",
["Manage hotkeys:"] = "Administrador de hotkeys:",
["Market"] = "Mercado",
["Market Offers"] = "Ofertas en mercado",
["Message of the day"] = "Mensaje del dia",
["Message of the day"] = "Mensaje del d<EFBFBD>a",
["Message to "] = "Mensaje a",
["Message to %s"] = "Mensaje a %s",
["Minimap"] = "Minimapa",
["Module Manager"] = "Administrador de modulos",
["Module name"] = "Nombre de modulo",
["Mount"] = "Montura",
["Module Manager"] = "Administrador de m<EFBFBD>dulos",
["Module name"] = "Nombre del modulo",
["Mount"] = "Montar", --Unique name?
["Move Stackable Item"] = "Mover objeto apilable",
["Move up"] = "Mover arriba",
["My Offers"] = "Mis ofertas",
@@ -205,7 +207,7 @@ locale = {
["Name Report + Banishment"] = "Name Report + Banishment",
["Name Report + Banishment + Final Warning"] = "Name Report + Banishment + Final Warning",
["No"] = "No",
["No graphics card detected, everything will be drawn using the CPU,\nthus the performance will be really bad.\nPlease update your graphics driver to have a better performance."] = "No graphics card detected, everything will be drawn using the CPU,\npor lo tanto el rendimiento va a ser muy malo.\nPor favor, actualice su controlador de gr<67>ficos para tener un mejor rendimiento.",
["No graphics card detected, everything will be drawn using the CPU,\nthus the performance will be really bad.\nPlease update your graphics driver to have a better performance."] = "No se ha detectado una tarjeta gr<67>fica y todo sera procesado por tu procesador,\npor lo tanto el rendimiento va a ser muy malo.\nPor favor, actualice su controlador de gr<67>ficos para tener un rendimiento optimo.",
["No item selected."] = "No hay elemento seleccionado.",
["No Mount"] = "No montura",
["No Outfit"] = "No outfit",
@@ -220,30 +222,30 @@ locale = {
["on %s.\n"] = "en %s.\n",
["Open"] = "Abierto",
["Open a private message channel:"] = "Abrir mensaje en canal privado:",
["Open charlist automatically when starting client"] = "Abrir lista de players automaticamente al iniciar el cliente",
["Open charlist automatically when starting client"] = "Abrir lista de jugadores autom<EFBFBD>ticamente al iniciar el cliente",
["Open in new window"] = "Abrir en nueva ventana",
["Open new channel"] = "Abrir nuevo canal",
["Options"] = "Opciones",
["Overview"] = "Descripcion",
["Overview"] = "Descripci<EFBFBD>n",
["Pass Leadership to %s"] = "Pasar liderazgo a %s",
["Password"] = "Contrase<EFBFBD>a",
["Piece Price:"] = "Precio por pieza:",
["Please enter a character name:"] = "Por favor ingresar nombre del player:",
["Please enter a character name:"] = "Por favor ingresar nombre del jugador:",
["Please, press the key you wish to add onto your hotkeys manager"] = "Por favor, presiona la tecla que desees para que sea registrada en\nel administrador de hotkeys",
["Please Select"] = "Por favor selecciona",
["Please use this dialog to only report bugs. Do not report rule violations here!"] = "Por favor usa estos dialogos solo para reportar errores.\n<EFBFBD>No reportar violaciones al reglamento aqu<71>!",
["Please wait"] = "Por favor espera",
["Please Select"] = "Por favor seleccione",
["Please use this dialog to only report bugs. Do not report rule violations here!"] = "Por favor usa este di<EFBFBD>logo solo para reportar errores.\n<EFBFBD>No reportar violaciones del reglamento aqu<71>!",
["Please wait"] = "Por favor espere",
["Port"] = "Puerto",
["Position:"] = "Posici<EFBFBD>n:",
["Position: %i %i %i"] = "Posici<EFBFBD>n: %i %i %i",
["Premium Account (%s) days left"] = "Premium Accoun restan dias (%s)",
["Premium Account (%s) days left"] = "Tienes (%s) d<>as de Premium Account restantes",
["Price:"] = "Precio:",
["Primary"] = "Primario",
["Protocol"] = "Protocolo",
["Quest Log"] = "Quest Log",
["Quest Log"] = "Quest Log", --Unique name
["Randomize"] = "Combinar",
["Randomize characters outfit"] = "Combinar oufit del jugador",
["Reason:"] = "Reason:",
["Randomize characters outfit"] = "Combinar vestimenta del jugador",
["Reason:"] = "Raz<EFBFBD>n:",
["Refresh"] = "Refrescar",
["Refresh Offers"] = "Refrescar ofertas",
["Regeneration Time"] = "Tiempo de regeneraci<63>n",
@@ -254,13 +256,13 @@ locale = {
["Remove"] = "Remover",
["Remove %s"] = "Remover %s",
["Report Bug"] = "Reportar error",
["Reserved for more functionality later."] = "eservado para una funcion futura.",
["Reserved for more functionality later."] = "Reservado para una funci<EFBFBD>n futura.",
["Reset Market"] = "Reiniciar mercado",
["Revoke %s\'s Invitation"] = "Anular %s\'s invitaci<63>n",
["Rotate"] = "Rotar",
["Rule Violation"] = "Violacion de regla",
["Save"] = "Salvar",
["Save Messages"] = "Salvar mensaje",
["Rule Violation"] = "Violaci<EFBFBD>n del reglamento",
["Save"] = "Guardar",
["Save Messages"] = "Guardar mensaje",
["Search:"] = "Buscar:",
["Search all items"] = "Buscar todos los objetos",
["Secondary"] = "Secundario",
@@ -268,24 +270,24 @@ locale = {
["Select Outfit"] = "Seleccionar outfit",
["Select your language"] = "Selectionar tu lenguaje",
["Sell"] = "Vender",
["Sell Now"] = "Vender ya",
["Sell Now"] = "Vender ahora",
["Sell Offers"] = "Ofertas de venta",
["Send"] = "Enviar",
["Send automatically"] = "Enviar automaticamente",
["Send automatically"] = "Enviar autom<EFBFBD>ticamente",
["Send Message"] = "Enviar mensaje",
["Server"] = "Server",
["Server Log"] = "Server Log",
["Set Outfit"] = "Fijar outfit",
["Server"] = "Server", --Unique name
["Server Log"] = "Server Log", --Unique name
["Set Outfit"] = "Escoger vestimenta",
["Shielding"] = "Escudo",
["Show all items"] = "Mostrar todos los objetos",
["Show connection ping"] = "Mostrar ping de conexi<78>n",
["Show Depot Only"] = "Mostrar solo el Depot",
["Show event messages in console"] = "Mostrar mensajes de evento en consola",
["Show frame rate"] = "Mostrar velocidad por cuadro",
["Show frame rate"] = "Mostrar informaci<EFBFBD>n de cuadros por secundo",
["Show info messages in console"] = "Mostrar mensajes de informaci<63>n en consola",
["Show left panel"] = "Mostrar panel izquierdo",
["Show levels in console"] = "Mostrar niveles en consola",
["Show Offline"] = "Mostrar offLine",
["Show Offline"] = "Mostrar Desconectados",
["Show private messages in console"] = "Mostrar mensajes privados en consola",
["Show private messages on screen"] = "Mostrar mensajes privados en pantalla",
["Show Server Messages"] = "Mostrar mensajes del servidor",
@@ -295,26 +297,26 @@ locale = {
["Show your depot items only"] = "Mostrar solo tus objetos en depot",
["Skills"] = "Habilidades",
["Soul"] = "Soul",
["Soul Points"] = "Puntos de Soul",
["Soul Points"] = "Puntos de Soul", --I'm leaving these as is because its a unique name, if you want to change it it can be "Alma" or "Esp<73>ritu"
["Special"] = "Especial",
["Speed"] = "Velocidad",
["Spell Cooldowns"] = "Spells Cooldowns",
["Spell List"] = "Lista de spells",
["Stamina"] = "Stamina",
["Spell Cooldowns"] = "Spells Cooldowns", --Could be "Tiempo de recarga para los hechizos".
["Spell List"] = "Lista de hechizos",
["Stamina"] = "Resistencia",
["Statement:"] = "Comentario:",
["Statement Report"] = "Statement Report",
["Statistics"] = "Estadisticas",
["Statement Report"] = "Statement Report", --Could be "reporte del comentario"
["Statistics"] = "Estad<EFBFBD>sticas",
["Stop Attack"] = "Detener ataque",
["Stop Follow"] = "Detener persecucion",
["Stop Follow"] = "Detener persecuci<EFBFBD>n",
["Support"] = "Soporte",
["%s: (use object)"] = "%s: (usar objeto)",
["%s: (use object on target)"] = "%s: (usar objeto en un objetivo)",
["%s: (use object on yourself)"] = "%s: (usar objeto en mi mismo)",
["%s: (use object with crosshair)"] = "%s: (usar objeto en aire)",
["%s: (use object with crosshair)"] = "%s: (usar objeto con punto de mira)",
["Sword Fighting"] = "Combate de espada",
["Terminal"] = "Terminal",
["There is no way."] = "No hay ninguna manera.",
["Title"] = "T<EFBFBD>tulo",
["Title"] = "Titulo",
["Total Price:"] = "Total total:",
["Trade"] = "Intercambio",
["Trade with ..."] = "Intercambiar con ...",
@@ -322,51 +324,51 @@ locale = {
["Unable to load dat file, please place a valid dat in '%s'"] = "No se puede cargar el archivo dat, por favor coloque un dat v<>lido en '%s'",
["Unable to load spr file, please place a valid spr in '%s'"] = "No se puede cargar el archivo spr, por favor coloque un spr v<>lido en '%s'",
["Unable to logout."] = "No se puede cerrar sesi<73>n-",
["Unignore"] = "Desactivar ignorado",
["Unignore"] = "Dejar de ignorar",
["Unload"] = "No cargado",
["Update needed"] = "Es necesario actualizar",
["Use"] = "Uso",
["Use on target"] = "Usar en un objetivo",
["Use on yourself"] = "Usar en mi mismo",
["Use with ..."] = "Usar en ...",
["Version"] = "Version",
["Version"] = "Versi<EFBFBD>n",
["VIP List"] = "Lista Vip",
["Voc."] = "Voc.",
["Vocation"] = "Vocaci<EFBFBD>n",
["Waiting List"] = "Lista de espera",
["Website"] = "Sitio WEB",
["Website"] = "Sitio Web",
["Weight:"] = "Peso:",
["Will detect when to use diagonal step based on the\nkeys you are pressing"] = "Detectar cuando se camina en diagonal usando las flechas",
["Will detect when to use diagonal step based on the\nkeys you are pressing"] = "Detectara cuando se camina en diagonal usando las flechas",
["With crosshair"] = "Con punto de mira",
["Yes"] = "Si",
["You are bleeding"] = "Tu estas sangrado",
["You are burning"] = "Tu estas quemado",
["You are bleeding"] = "Te estas desangrando",
["You are burning"] = "Te estas quemando",
["You are cursed"] = "Tu estas maldecido",
["You are dazzled"] = "Tu estas deslumbrado",
["You are dead."] = "Tu estas muerto.",
["You are dead"] = "Tu estas muerto",
["You are drowning"] = "Tu estas ahotado",
["You are drunk"] = "Tu estas borracho",
["You are electrified"] = "Tu estas electrificado",
["You are freezing"] = "Tu estas congelado",
["You are hasted"] = "Tu estas rapido",
["You are drowning"] = "Te estas ahogando",
["You are drunk"] = "Tu estas ebrio",
["You are electrified"] = "Tu estas electrocutado",
["You are freezing"] = "Te estas congelando",
["You are hasted"] = "Tu estas r<EFBFBD>pido", --I dont know what is the best way to translate this so I'm leaving it as I found it.
["You are hungry"] = "Tu estas hambriento",
["You are paralysed"] = "Tu estas paralizado",
["You are poisoned"] = "Tu estas envenedado",
["You are protected by a magic shield"] = "Tu estas protegido por un campo magico",
["You are protected by a magic shield"] = "Tu estas protegido por un escudo m<EFBFBD>gico",
["You are strengthened"] = "Tu estas reforzado",
["You are within a protection zone"] = "Tu estas dentro de una zona segura",
["You can enter new text."] = "Tu puedes introducir un texto nuevo.",
["You are within a protection zone"] = "Tu estas dentro de una zona de protecci<63>n",
["You can enter new text."] = "Tu puedes ingresar un texto nuevo.",
["You have %s percent"] = "Tu tienes %s por ciento",
["You have %s percent to go"] = "Tu tienes %s por ciento para ir",
["You may not logout during a fight"] = "No puedes salir durante una pelea",
["You may not logout or enter a protection zone"] = "No puedes salir o entrar en una zona de protecci<63>n",
["You must enter a comment."] = "Debes introducir un comentario.",
["You must enter a valid server address and port."] = "Debes introducir una direcci<63>n v<>lida de servidor y el puerto.",
["You must enter a comment."] = "Debes ingresar un comentario.",
["You must enter a valid server address and port."] = "Debes ingresar una direcci<63>n v<>lida de servidor y el puerto.",
["You must select a character to login!"] = "<EFBFBD>Debes seleccionar un personaje para ingresar!",
["Your Capacity:"] = "Tu capacidad:",
["You read the following, written by \n%s\n"] = "Uno lee lo siguiente, escrito por \n%s\n",
["You read the following, written on \n%s.\n"] = "Uno lee lo siguiente, escrito en \n%s\n",
["You read the following, written by \n%s\n"] = "Lees lo siguiente, escrito por \n%s\n",
["You read the following, written on \n%s.\n"] = "Lees lo siguiente, escrito en \n%s\n",
["Your Money:"] = "Tu dinero:",
["Change language"] = "Cambiar idioma",
["Don't stretch or shrink Game Window"] = "No estirar o encoger Ventana de Juego"

View File

@@ -1,36 +1,31 @@
-- by Don Daniello
-- 09 June 2012
-- http://DonDaniello.com
-- http://otland.net/members/Don+Daniello
locale = {
name = "pl",
languageName = "Polski",
translation = {
["1a) Offensive Name"] = false,
["1b) Invalid Name Format"] = false,
["1c) Unsuitable Name"] = false,
["1d) Name Inciting Rule Violation"] = false,
["2a) Offensive Statement"] = false,
["2b) Spamming"] = false,
["2c) Illegal Advertising"] = false,
["2d) Off-Topic Public Statement"] = false,
["2e) Non-English Public Statement"] = false,
["2f) Inciting Rule Violation"] = false,
["3a) Bug Abuse"] = false,
["3b) Game Weakness Abuse"] = false,
["3c) Using Unofficial Software to Play"] = false,
["3d) Hacking"] = false,
["3e) Multi-Clienting"] = false,
["3f) Account Trading or Sharing"] = false,
["4a) Threatening Gamemaster"] = false,
["4b) Pretending to Have Influence on Rule Enforcement"] = false,
["4c) False Report to Gamemaster"] = false,
["Accept"] = false,
["1a) Offensive Name"] = "1a) Obrazliwe Imie",
["1b) Invalid Name Format"] = "1b) Niepoprawny Format Imienia",
["1c) Unsuitable Name"] = "1c) Nieodpowiednie Imie",
["1d) Name Inciting Rule Violation"] = "1d) Imie Nawolujace Do Lamania Regulaminu",
["2a) Offensive Statement"] = "2a) Obrazliwa wypowiedz",
["2b) Spamming"] = "2b) Spamowanie",
["2c) Illegal Advertising"] = "2c) Nielegalne Reklamy",
["2d) Off-Topic Public Statement"] = "2d) Publiczne Wypowiadanie Sie Nie Na Temat",
["2e) Non-English Public Statement"] = "2e) Publiczne wypowiadanie Sie W Jezyku Innym Niz Angielski",
["2f) Inciting Rule Violation"] = "2f) Nawolywanie Do Lamania Regulaminu ",
["3a) Bug Abuse"] = "3a) Wykorzystywanie Bledow",
["3b) Game Weakness Abuse"] = "3b) Wykorzystanie Slabosci Gry",
["3c) Using Unofficial Software to Play"] = "3c) Gra Przy Uzyciu Nieoficjalnego Oprogramowania",
["3d) Hacking"] = "3d) Wlamywanie Sie",
["3e) Multi-Clienting"] = "3e) Uzycie Multi-Klienta",
["3f) Account Trading or Sharing"] = "3f) Handel Lub Udostepnianie Kont",
["4a) Threatening Gamemaster"] = "4a) Grozby Pod Adresem Mistrza Gry",
["4b) Pretending to Have Influence on Rule Enforcement"] = "4b) Udawanie Wplywu na Ustanawianie Regul Gry",
["4c) False Report to Gamemaster"] = "4c) Wyslanie Falszywego Raportu Mistrzowi Gry",
["Accept"] = "Akceptuj",
["Account name"] = "Numer konta",
["Account Status:"] = false,
["Action:"] = false,
["Account Status:"] = "Status Konta:",
["Action:"] = "Akcja:",
["Add"] = "Dodaj",
["Add new VIP"] = "Nowy VIP",
["Addon 1"] = "Addon 1",
@@ -39,15 +34,15 @@ locale = {
["Add to VIP list"] = "Dodaj do VIPow",
["Adjust volume"] = "Zmien glosnosc",
["Alas! Brave adventurer, you have met a sad fate.\nBut do not despair, for the gods will bring you back\ninto this world in exchange for a small sacrifice\n\nSimply click on Ok to resume your journeys!"] = false,
["All"] = false,
["All"] = "Wszystkie",
["All modules and scripts were reloaded."] = "Wszystkie moduly ",
["Allow auto chase override"] = false,
["Also known as dash in tibia community, recommended\nfor playing characters with high speed"] = false,
["Ambient light: %s%%"] = false,
["Ambient light: %s%%"] = "Swiatlo tla: %s%%",
["Amount:"] = "Ilosc:",
["Amount"] = false,
["Anonymous"] = false,
["Are you sure you want to logout?"] = false,
["Amount"] = "Ilosc",
["Anonymous"] = "Anonimowy",
["Are you sure you want to logout?"] = "Czy jestes pewien, ze sie chcesz wylogowac?",
["Attack"] = "Atak",
["Author"] = "Autor",
["Autoload"] = "Autoladowanie",
@@ -59,17 +54,17 @@ locale = {
["Banishment"] = false,
["Banishment + Final Warning"] = false,
["Battle"] = "Bitwa",
["Browse"] = false,
["Bug report sent."] = false,
["Browse"] = "Przegladaj",
["Bug report sent."] = "Raport o bledzie zostal wyslany.",
["Button Assign"] = "Przypisanie Klawisza",
["Buy"] = "Kup",
["Buy Now"] = false,
["Buy Offers"] = false,
["Buy Now"] = "Kup Teraz",
["Buy Offers"] = "Oferty Kupna",
["Buy with backpack"] = "Kupuj z plecakami",
["Cancel"] = "Anuluj",
["Cannot login while already in game."] = false,
["Cannot login while already in game."] = "Nie mozna zalogowac gdy juz w grze",
["Cap"] = false,
["Capacity"] = "Capacidad",
["Capacity"] = "Ladownosc",
["Center"] = false,
["Channels"] = "Kanaly",
["Character List"] = "Lista postaci",
@@ -82,65 +77,64 @@ locale = {
["Close this channel"] = "Zamknij kanal",
["Club Fighting"] = "Walka obuchem",
["Combat Controls"] = "Kontrola walki",
["Comment:"] = false,
["Comment:"] = "Komentarz:",
["Connecting to game server..."] = "Laczenie z serwerem gry...",
["Connecting to login server..."] = "Laczenie z serwerem logowania...",
["Console"] = false,
["Console"] = "Konsola",
["Cooldowns"] = false,
["Copy message"] = false,
["Copy name"] = false,
["Copy Name"] = "Kopiuj Nick",
["Create Map Mark"] = false,
["Create mark"] = false,
["Create New Offer"] = false,
["Create Offer"] = false,
["Copy message"] = "Kopiuj wiadomosc",
["Copy name"] = "Kopiuj imie",
["Copy Name"] = "Kopiuj Imie",
["Create Map Mark"] = "Utworz Znacznik na Mapie",
["Create mark"] = "Utworz znacznik",
["Create New Offer"] = "Utworz Nowa Oferte",
["Create Offer"] = "Utworz Oferte",
["Current hotkeys:"] = "Aktualny hotkey:",
["Current hotkey to add: %s"] = "Aktualny hotkey do dodania: %s",
["Current Offers"] = false,
["Current Offers"] = "Obecne Oferty",
["Default"] = "Domyslny",
["Delete mark"] = false,
["Description:"] = false,
["Delete mark"] = "Usun znacznik",
["Description:"] = "Opis:",
["Description"] = "Opis",
["Destructive Behaviour"] = false,
["Destructive Behaviour"] = "Destrukcyjne Zachowanie",
["Detail"] = "Szczegoly",
["Details"] = false,
["Details"] = "Szczegoly",
["Disable Shared Experience"] = "Wylacz Dzielenie Doswiadczenia",
["Dismount"] = false,
["Display connection speed to the server (milliseconds)"] = false,
["Display connection speed to the server (milliseconds)"] = "Wyswietl ping do serwera (ms)",
["Distance Fighting"] = "Walka na odleglosc",
["Don't stretch/shrink Game Window"] = false,
["Don't stretch/shrink Game Window"] = "Nie rozszerzaj/zwezaj Okna Gry",
["Edit hotkey text:"] = "Edytuj tresc hotkeya:",
["Edit List"] = false,
["Edit List"] = "Lista Edycji",
["Edit Text"] = "Edytuj tekst",
["Enable music"] = false,
["Enable music"] = "Odtwarzaj muzyke",
["Enable Shared Experience"] = "Wlacz dzielenie doswiadczenia",
["Enable smart walking"] = false,
["Enable vertical synchronization"] = "Wlacz synchronizacje pionowa",
["Enable walk booster"] = false,
["Enter Game"] = "Wejdz do gry",
["Enter one name per line."] = false,
["Enter with your account again to update your client."] = false,
["Enter one name per line."] = "Wprowadz jedno imie na linie",
["Enter with your account again to update your client."] = "Zaloguj sie ponownie by zaktualizowac klienta",
["Error"] = "Blad",
["Error"] = "Blad",
["Excessive Unjustified Player Killing"] = false,
["Excessive Unjustified Player Killing"] = "Nadmierne Nieusprawiedliwione Zabijanie Graczy",
["Exclude from private chat"] = "Wyrzuc w prywatnej konwersacji",
["Exit"] = false,
["Experience"] = "Doswiadczenie",
["Filter list to match your level"] = false,
["Filter list to match your vocation"] = false,
["Find:"] = false,
["Filter list to match your level"] = "Wyswietl tylko odpowiednie dla mojego poziomu",
["Filter list to match your vocation"] = "Wyswietl tylko odpowiednie dla mojej klasy",
["Find:"] = "Szukaj:",
["Fishing"] = "Wedkarstwo",
["Fist Fighting"] = "Walka wrecz",
["Follow"] = "Podazaj",
["Force Exit"] = false,
["Force Exit"] = "Wymus Zamkniecie",
["For Your Information"] = false,
["Free Account"] = false,
["Free Account"] = "Darmowe Konto",
["Fullscreen"] = "Pelen ekran",
["Game"] = false,
["Game framerate limit: %s"] = false,
["Game"] = "Gra",
["Game framerate limit: %s"] = "Limit FPS: %s",
["Graphics"] = "Grafika",
["Graphics card driver not detected"] = false,
["Graphics Engine:"] = false,
["Graphics card driver not detected"] = "Nie wykryto karty graficznej",
["Graphics Engine:"] = "Silnik graficzny:",
["Head"] = "Glowa",
["Healing"] = false,
["Health Info"] = false,
@@ -148,43 +142,43 @@ locale = {
["Hide monsters"] = "Ukryj potwory",
["Hide non-skull players"] = "Ukryj graczy bez skulla",
["Hide Npcs"] = "Ukryj NPCe",
["Hide Offline"] = false,
["Hide party members"] = "Ukryj czlonkow zabawy",
["Hide Offline"] = "Ukryj Niedostepnych",
["Hide party members"] = "Ukryj czlonkow druzyny",
["Hide players"] = "Ukryj graczy",
["Hide spells for higher exp. levels"] = false,
["Hide spells for other vocations"] = false,
["Hide spells for higher exp. levels"] = "Ukryj zaklecia wyzszych poziomow postaci",
["Hide spells for other vocations"] = "Ukryj zaklecia innych klas",
["Hit Points"] = "Punkty uderzen",
["Hold left mouse button to navigate\nScroll mouse middle button to zoom\nRight mouse button to create map marks"] = false,
["Hotkeys"] = "Hotkeye",
["If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."] = false,
["Ignore"] = false,
["Ignore"] = "Ignoruj",
["Ignore capacity"] = "Ignoruj pojemnosc",
["Ignored players:"] = false,
["Ignored players:"] = "Ignorowani gracze:",
["Ignore equipped"] = "Ignoruj ekwipunek",
["Ignore List"] = false,
["Ignore players"] = false,
["Ignore Private Messages"] = false,
["Ignore Yelling"] = false,
["Interface framerate limit: %s"] = false,
["Inventory"] = "Inwentarz",
["Invite to Party"] = "Zapros do zabawy",
["Ignore List"] = "Lista Ignorowanych",
["Ignore players"] = "Ignoruj graczy",
["Ignore Private Messages"] = "Ignoruj Prywatne Wiadomosci",
["Ignore Yelling"] = "Ignoruj Krzyki",
["Interface framerate limit: %s"] = "Limit FPS interfejsu: %s",
["Inventory"] = "Ekwipunek",
["Invite to Party"] = "Zapros do druzyny",
["Invite to private chat"] = "Zapros do prywatnej konwersacji",
["IP Address Banishment"] = false,
["Item Offers"] = false,
["It is empty."] = false,
["Join %s's Party"] = false,
["Leave Party"] = "Opusc zabawe",
["IP Address Banishment"] = "Blokada adresu IP",
["Item Offers"] = "Oferty Przedmiotow",
["It is empty."] = "To jest puste.",
["Join %s's Party"] = "Dolacz do druzyny gracza %s",
["Leave Party"] = "Opusc druzyne",
["Level"] = "Poziom",
["Lifetime Premium Account"] = false,
["Lifetime Premium Account"] = "Konto Premium na Stale",
["Limits FPS to 60"] = "Ogranicz FPS do 60",
["List of items that you're able to buy"] = "Lista przedmiotow, ktore mozesz kupic",
["List of items that you're able to sell"] = "Lista przedmiotow, ktore mozesz sprzedac",
["Load"] = "Wczytaj",
["Logging out..."] = false,
["Logging out..."] = "Wylogowuje...",
["Login"] = "Zaloguj",
["Login Error"] = "Blad Logowania",
["Login Error"] = "Blad Logowania",
["Logout"] = false,
["Logout"] = "Wyloguj",
["Look"] = "Spojrz",
["Magic Level"] = "Poziom Magiczny",
["Make sure that your client uses\nthe correct game protocol version"] = "Upewnij sie, ze twoj klient\nuzywa wlasciwego protokolu gry.",
@@ -193,7 +187,7 @@ locale = {
["Market"] = false,
["Market Offers"] = false,
["Message of the day"] = "Wiadomosc dnia",
["Message to "] = false,
["Message to "] = "Wiadomosc do ",
["Message to %s"] = "Wiadomosc do %s",
["Minimap"] = "Minimapa",
["Module Manager"] = "Menedzer modulow",
@@ -201,32 +195,32 @@ locale = {
["Mount"] = false,
["Move Stackable Item"] = "Przenies przedmiot",
["Move up"] = "Przenies wyzej",
["My Offers"] = false,
["My Offers"] = "Moje Oferty",
["Name:"] = "Nazwa:",
["Name Report"] = false,
["Name Report + Banishment"] = false,
["Name Report + Banishment + Final Warning"] = false,
["No"] = false,
["No"] = "Nie",
["No graphics card detected, everything will be drawn using the CPU,\nthus the performance will be really bad.\nPlease update your graphics driver to have a better performance."] = false,
["No item selected."] = false,
["No Mount"] = false,
["No Outfit"] = false,
["No item selected."] = "Nie wybrano przedmiotu.",
["No Mount"] = "Brak wierzchowca",
["No Outfit"] = "Brak stroju",
["No statement has been selected."] = false,
["Notation"] = false,
["NPC Trade"] = "Handel NPC",
["Offer History"] = false,
["Offers"] = false,
["Offer Type:"] = false,
["Offline Training"] = false,
["Offer History"] = "Historia Ofert",
["Offers"] = "Oferty",
["Offer Type:"] = "Typ Oferty:",
["Offline Training"] = "Trening Offline",
["Ok"] = "Ok",
["on %s.\n"] = false,
["Open"] = "Otworz",
["Open a private message channel:"] = "Otworz prywatny kanal:",
["Open charlist automatically when starting client"] = false,
["Open charlist automatically when starting client"] = "Automatycznie otworz liste postaci przy starcie gry",
["Open in new window"] = "Otworz w nowym oknie",
["Open new channel"] = "Otworz nowy kanal",
["Options"] = "Opcje",
["Overview"] = false,
["Overview"] = "Podsumowanie",
["Pass Leadership to %s"] = "Przekaz przywodztwo %s",
["Password"] = "Haslo",
["Piece Price:"] = false,
@@ -236,79 +230,79 @@ locale = {
["Please use this dialog to only report bugs. Do not report rule violations here!"] = false,
["Please wait"] = "Prosze czekac",
["Port"] = "Port",
["Position:"] = false,
["Position: %i %i %i"] = false,
["Premium Account (%s) days left"] = false,
["Position:"] = "Pozycja:",
["Position: %i %i %i"] = "Pozycja: %i %i %i",
["Premium Account (%s) days left"] = "Konto Premium (%s) dni",
["Price:"] = "Cena:",
["Primary"] = "Podstawowy",
["Protocol"] = false,
["Quest Log"] = false,
["Protocol"] = "Protokol",
["Quest Log"] = "Dziennik Misji",
["Randomize"] = false,
["Randomize characters outfit"] = false,
["Reason:"] = false,
["Reason:"] = "Powod:",
["Refresh"] = "Odswiez",
["Refresh Offers"] = false,
["Regeneration Time"] = false,
["Reject"] = false,
["Refresh Offers"] = "Odswiez Oferty",
["Regeneration Time"] = "Czas Regeneracji",
["Reject"] = "Odrzuc",
["Reload All"] = "Przeladuj Wszystko",
["Remember account and password when starts client"] = false,
["Remember account and password when starts client"] = "Zapamietaj identyfikator konta oraz haslo",
["Remember password"] = "Zapamietaj haslo",
["Remove"] = "Usun",
["Remove %s"] = "Usun %s",
["Report Bug"] = false,
["Reserved for more functionality later."] = false,
["Report Bug"] = "Zglos Blad",
["Reserved for more functionality later."] = "Zarezerowane dla przyszlych funkcjonalnosci.",
["Reset Market"] = false,
["Revoke %s's Invitation"] = false,
["Revoke %s's Invitation"] = "Odmow na zaproszenie gracza %s",
["Rotate"] = "Obroc",
["Rule Violation"] = false,
["Save"] = false,
["Save Messages"] = false,
["Rule Violation"] = "Zlamanie Regul",
["Save"] = "Zapisz",
["Save Messages"] = "Zapisz Wiadomosci",
["Search:"] = "Szukaj:",
["Search all items"] = false,
["Search all items"] = "Znajdz wszystkie przedmioty",
["Secondary"] = "Drugorzedny",
["Select object"] = "Wybierz obiekt",
["Select Outfit"] = "Wybierz outfit",
["Select your language"] = false,
["Select your language"] = "Wybierz jezyk",
["Sell"] = "Sprzedaj",
["Sell Now"] = false,
["Sell Offers"] = false,
["Send"] = false,
["Sell Now"] = "Sprzedaj Teraz",
["Sell Offers"] = "Oferty Sprzedazy",
["Send"] = "Wyslij",
["Send automatically"] = "Wyslij automatycznie",
["Send Message"] = false,
["Send Message"] = "Wyslij Wiadomosc",
["Server"] = "Serwer",
["Server Log"] = "Log Serwera",
["Set Outfit"] = "Ustaw outfit",
["Shielding"] = "Obrona tarcza",
["Show all items"] = "Pokaz wszystkie przedmioty",
["Show connection ping"] = false,
["Show connection ping"] = "Wyswietl ping",
["Show Depot Only"] = false,
["Show event messages in console"] = "Pokaz wydarzenia w konsoli",
["Show frame rate"] = "Pokaz ilosc FPS",
["Show frame rate"] = "Pokaz FPS",
["Show info messages in console"] = "Pokaz informacje w konsoli",
["Show left panel"] = false,
["Show left panel"] = "Pokaz lewy panel",
["Show levels in console"] = "Pokaz poziomy w konsoli",
["Show Offline"] = false,
["Show Offline"] = "Pokaz Niedostepnych",
["Show private messages in console"] = "Pokaz prywatne wiadomosci w konsoli",
["Show private messages on screen"] = false,
["Show Server Messages"] = false,
["Show private messages on screen"] = "Pokaz prywatne wiadomosci na ekranie",
["Show Server Messages"] = "Pokaz Wiadomosci Serwera",
["Show status messages in console"] = "Pokaz status w konsoli",
["Show Text"] = false,
["Show Text"] = "Pokaz Tekst",
["Show timestamps in console"] = "Pokaz znaczniki czasu w konsoli",
["Show your depot items only"] = false,
["Skills"] = "Umiejetnosci",
["Soul"] = false,
["Soul"] = "Dusze",
["Soul Points"] = "Punkty Duszy",
["Special"] = false,
["Speed"] = false,
["Speed"] = "Predkosc",
["Spell Cooldowns"] = false,
["Spell List"] = false,
["Spell List"] = "Lista Zaklec",
["Stamina"] = "Wytrzymalosc",
["Statement:"] = false,
["Statement Report"] = false,
["Statistics"] = false,
["Stop Attack"] = "Zatrzymaj atak",
["Stop Follow"] = "Zatrzymaj podazanie",
["Support"] = false,
["Statistics"] = "Statystki",
["Stop Attack"] = "Anuluj atak",
["Stop Follow"] = "Przestan podazac",
["Support"] = "Wsparcie",
["%s: (use object)"] = "%s: (uzyj obiekt)",
["%s: (use object on target)"] = "%s: (uzyj obiektu na celu)",
["%s: (use object on yourself)"] = "%s: (uzyj obiektu na sobie)",
@@ -316,15 +310,15 @@ locale = {
["Sword Fighting"] = "Atak mieczem",
["Terminal"] = "Terminal",
["There is no way."] = "Nie ma drogi.",
["Title"] = false,
["Total Price:"] = false,
["Title"] = "Tytul",
["Total Price"] = "Cena sumaryczna",
["Trade"] = "Handel",
["Trade with ..."] = "Handluj z ...",
["Trying to reconnect in %s seconds."] = false,
["Unable to load dat file, please place a valid dat in '%s'"] = false,
["Unable to load spr file, please place a valid spr in '%s'"] = false,
["Trying to reconnect in %s seconds."] = "Ponowna proba laczenia za %s sekund.",
["Unable to load dat file, please place a valid dat in '%s'"] = "Nie mozna zaladowac pliku .dat z '%s'",
["Unable to load spr file, please place a valid spr in '%s'"] = "Nie mozna zaladowac pliku .spr z '%s'",
["Unable to logout."] = "Nie mozna sie wylogowac.",
["Unignore"] = false,
["Unignore"] = "Anuluj Ignorowanie",
["Unload"] = "Wylacz",
["Update needed"] = false,
["Use"] = "Uzyj",
@@ -334,42 +328,87 @@ locale = {
["Version"] = "Wersja",
["VIP List"] = "Lista VIP",
["Voc."] = false,
["Vocation"] = false,
["Waiting List"] = false,
["Vocation"] = "Klasa",
["Waiting List"] = "Lista Oczekujacych",
["Website"] = "Strona:",
["Weight:"] = "Waga:",
["Will detect when to use diagonal step based on the\nkeys you are pressing"] = false,
["With crosshair"] = "Z celownikiem",
["Yes"] = false,
["You are bleeding"] = false,
["Yes"] = "Tak",
["You are bleeding"] = "Krwawisz",
["You are burning"] = "Palisz sie",
["You are cursed"] = "Jestes przeklety",
["You are dazzled"] = "Jestes oslepiony",
["You are dead."] = "Zginales marnie.",
["You are dead"] = false,
["You are dead"] = "Jestes martwy",
["You are drowning"] = "Topisz sie",
["You are drunk"] = false,
["You are drunk"] = "Caly swiat wiruje",
["You are electrified"] = "Jestes porazony pradem",
["You are freezing"] = "Marzniesz",
["You are hasted"] = "Zapieprzasz",
["You are hungry"] = false,
["You are hungry"] = "Burczy ci w brzuchu",
["You are paralysed"] = "Jestes sparalizowany",
["You are poisoned"] = "Jestes zatruty",
["You are protected by a magic shield"] = "Jestes chroniony magiczna tarcza",
["You are strengthened"] = "Jestes wzmocniony",
["You are within a protection zone"] = "Jestes w strefie ochronnej",
["You can enter new text."] = false,
["You can enter new text."] = "Mozesz wprowadzic nowy tekst.",
["You have %s percent"] = "Masz %s procent",
["You have %s percent to go"] = "Brakuje Ci %s procent",
["You may not logout during a fight"] = "Nie mozesz sie wylogowac w trakcie walki",
["You may not logout or enter a protection zone"] = "Nie mozesz sie wylogowac ani wejsc do strefy ochronnej",
["You must enter a comment."] = false,
["You must enter a valid server address and port."] = false,
["You must enter a comment."] = "Prosze wprowadzic komentarz",
["You must enter a valid server address and port."] = "Prosze wprowadzic poprawny adres i port.",
["You must select a character to login!"] = "Musisz wybrac postac aby sie zalogowac!",
["Your Capacity:"] = false,
["Your Capacity:"] = "Twoja Ladownosc:",
["You read the following, written by \n%s\n"] = false,
["You read the following, written on \n%s.\n"] = false,
["Your Money:"] = false,
["Enable dash walking"] = false,
["Will boost your walk on high speed characters"] = "Przyspieszy poruszanie sie postaci o wysokiej predkosci",
["Display creature names"] = "Wyswietlaj nazwy potworow",
["Display creature health bars"] = "Wyswietlaj paski zycia potworow",
["Display text messages"] = "Wyswietlaj wiadomosci tekstowe",
["Change language"] = "Zmien jezyk",
["Enable lights"] = "Odblokuj oswietlenie",
["Enable audio"] = "Odblokuj dzwiek",
["Enable music sound"] = "Odblokuj muzyke",
["Music volume: %d"] = "Glosnosc muzyki: %d",
["Audio"] = "Dzwiek",
["Server List"] = "Lista serwerow",
["Server list"] = "Lista serwerow",
["Client Version"] = "Wersja klienta",
["Add new server"] = "Dodaj nowy serwer",
["Select"] = "Wybierz",
["New Server"] = "Nowy serwer",
["Host"] = false,
["Reset All"] = "Ustaw domyslne",
["Disable chat mode, allow to walk using ASDW"] = "Zablokuj tryb rozmow, zezwol na poruszanie sie za pomoca klawiszy ADSW",
["Name"] = "Imie",
["Price"] = "Cena",
["Your Money"] = "Twoje fundusze",
["Weight"] = "Waga",
["Your Capacity"] = "Twoj udzwig",
["Search"] = "Szukaj",
["Sell All"] = "Sprzedaj wszystko",
["Statement"] = false,
["Reason"] = "Powod",
["Action"] = "Akcja",
["Comment"] = "Komentarz",
["Balance"] = false,
["Offer Type"] = "Typ oferty",
["Piece Price"] = false,
["Find"] = "Szukaj",
["Formula"] = "Formula",
["Group"] = "Groupa",
["Type"] = "Typ",
["Cooldown"] = false,
["Premium"] = false,
["Any"] = "Dowolny",
["Sorcerer"] = "Czarodziej",
["Druid"] = false,
["Paladin"] = "Paladyn",
["Knight"] = "Rycerz"
}
}

View File

@@ -1,7 +1,6 @@
CheckBox < UICheckBox
size: 16 16
text-align: left
text: aa
text-offset: 16 0
color: #aaaaaa
image-color: #ffffffff

View File

@@ -11,3 +11,9 @@ HorizontalList < UIScrollArea
border-width: 1
border-color: #1d222b
background-color: #222833
VerticalList < UIScrollArea
layout: verticalBox
border-width: 1
border-color: #1d222b
background-color: #222833

View File

@@ -0,0 +1,10 @@
{
"folders":
[
{
"path": "..",
"folder_exclude_patterns": [".*", "*.*~"],
"file_exclude_patterns": [".*", "*.*~"]
}
]
}

View File

@@ -1,5 +1,5 @@
local musicFilename = "/sounds/startup"
local musicChannel = nil
local musicChannel = g_sounds.getChannel(1)
function setMusic(filename)
musicFilename = filename
@@ -14,7 +14,7 @@ function reloadScripts()
g_textures.clearCache()
g_modules.reloadModules()
local script = '/' .. g_app.getCompactName() .. 'rc'
local script = '/' .. g_app.getCompactName() .. 'rc.lua'
if g_resources.fileExists(script) then
dofile(script)
end
@@ -57,14 +57,11 @@ function init()
onExit = exit })
g_window.setMinimumSize({ width = 600, height = 480 })
musicChannel = g_sounds.getChannel(1)
g_sounds.preload(musicFilename)
-- initialize in fullscreen mode on mobile devices
if g_app.getOs() == "android" then
g_window.maximize()
--g_window.setFullscreen(true)
if g_window.getPlatformType() == "X11-EGL" then
g_window.setFullscreen(true)
else
-- window size
local size = { width = 800, height = 600 }
@@ -107,11 +104,6 @@ function terminate()
g_settings.set('window-size', g_window.getUnmaximizedSize())
g_settings.set('window-pos', g_window.getUnmaximizedPos())
g_settings.set('window-maximized', g_window.isMaximized())
local protocolVersion = g_game.getProtocolVersion()
if protocolVersion ~= 0 then
g_settings.set('protocol-version', protocolVersion)
end
end
function exit()

View File

@@ -19,4 +19,4 @@ Module
- client_terminal
- client_modulemanager
- client_serverlist
//- client_stats
- client_stats

View File

@@ -6,7 +6,7 @@ local enterGame
local motdWindow
local motdButton
local enterGameButton
local protocolBox
local clientBox
local protocolLogin
local motdEnabled = true
@@ -73,11 +73,6 @@ local function onCharacterList(protocol, characters, account, otui)
end
end
local function onChangeProtocol(combobox, option)
local clients = g_game.getSupportedClients(option)
protocolBox:setTooltip("Supports Client" .. (#clients > 1 and "s" or "") .. ": " .. table.tostring(clients))
end
local function onUpdateNeeded(protocol, signature)
loadBox:destroy()
loadBox = nil
@@ -109,7 +104,8 @@ function EnterGame.init()
local host = g_settings.get('host')
local port = g_settings.get('port')
local autologin = g_settings.getBoolean('autologin')
local protocolVersion = g_settings.getInteger('protocol-version')
local clientVersion = g_settings.getInteger('client-version')
if clientVersion == 0 then clientVersion = 860 end
if port == nil or port == 0 then port = 7171 end
@@ -120,11 +116,11 @@ function EnterGame.init()
enterGame:getChildById('serverPortTextEdit'):setText(port)
enterGame:getChildById('autoLoginBox'):setChecked(autologin)
protocolBox = enterGame:getChildById('protocolComboBox')
protocolBox.onOptionChange = onChangeProtocol
if protocolVersion then
protocolBox:setCurrentOption(protocolVersion)
clientBox = enterGame:getChildById('clientComboBox')
for _, proto in pairs(g_game.getSupportedClients()) do
clientBox:addOption(proto)
end
clientBox:setCurrentOption(clientVersion)
enterGame:hide()
@@ -154,7 +150,7 @@ function EnterGame.terminate()
enterGame = nil
enterGameButton:destroy()
enterGameButton = nil
protocolBox = nil
clientBox = nil
if motdWindow then
motdWindow:destroy()
motdWindow = nil
@@ -218,8 +214,7 @@ function EnterGame.doLogin()
G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
G.host = enterGame:getChildById('serverHostTextEdit'):getText()
G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
local protocolVersion = tonumber(protocolBox:getText())
local clientVersions = g_game.getSupportedClients(protocolVersion)
local clientVersion = tonumber(clientBox:getText())
EnterGame.hide()
if g_game.isOnline() then
@@ -230,6 +225,7 @@ function EnterGame.doLogin()
g_settings.set('host', G.host)
g_settings.set('port', G.port)
g_settings.set('client-version', clientVersion)
protocolLogin = ProtocolLogin.create()
protocolLogin.onLoginError = onError
@@ -245,10 +241,8 @@ function EnterGame.doLogin()
end })
g_game.chooseRsa(G.host)
g_game.setProtocolVersion(protocolVersion)
if #clientVersions > 0 then
g_game.setClientVersion(clientVersions[#clientVersions])
end
g_game.setClientVersion(clientVersion)
g_game.setProtocolVersion(g_game.getProtocolVersionForClient(clientVersion))
if modules.game_things.isLoaded() then
protocolLogin:login(G.host, G.port, G.account, G.password)
@@ -269,14 +263,14 @@ end
function EnterGame.setDefaultServer(host, port, protocol)
local hostTextEdit = enterGame:getChildById('serverHostTextEdit')
local portTextEdit = enterGame:getChildById('serverPortTextEdit')
local protocolLabel = enterGame:getChildById('protocolLabel')
local clientLabel = enterGame:getChildById('clientLabel')
local accountTextEdit = enterGame:getChildById('accountNameTextEdit')
local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit')
if hostTextEdit:getText() ~= host then
hostTextEdit:setText(host)
portTextEdit:setText(port)
protocolBox:setCurrentOption(protocol)
clientBox:setCurrentOption(protocol)
accountTextEdit:setText('')
passwordTextEdit:setText('')
end
@@ -292,9 +286,9 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
portTextEdit:setVisible(false)
portTextEdit:setHeight(0)
protocolBox:setCurrentOption(protocol)
protocolBox:setVisible(false)
protocolBox:setHeight(0)
clientBox:setCurrentOption(protocol)
clientBox:setVisible(false)
clientBox:setHeight(0)
local serverLabel = enterGame:getChildById('serverLabel')
serverLabel:setVisible(false)
@@ -302,9 +296,9 @@ function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeig
local portLabel = enterGame:getChildById('portLabel')
portLabel:setVisible(false)
portLabel:setHeight(0)
local protocolLabel = enterGame:getChildById('protocolLabel')
protocolLabel:setVisible(false)
protocolLabel:setHeight(0)
local clientLabel = enterGame:getChildById('clientLabel')
clientLabel:setVisible(false)
clientLabel:setHeight(0)
local serverListButton = enterGame:getChildById('serverListButton')
serverListButton:setVisible(false)

View File

@@ -68,7 +68,7 @@ EnterGameWindow
TextEdit
id: serverHostTextEdit
!tooltip: tr('Make sure that your client uses\nthe correct game protocol version')
!tooltip: tr('Make sure that your client uses\nthe correct game client version')
anchors.left: parent.left
anchors.right: serverListButton.left
anchors.top: serverLabel.bottom
@@ -76,8 +76,8 @@ EnterGameWindow
margin-right: 4
MenuLabel
id: protocolLabel
!text: tr('Protocol')
id: clientLabel
!text: tr('Client Version')
anchors.left: parent.left
anchors.top: serverHostTextEdit.bottom
text-auto-resize: true
@@ -85,17 +85,13 @@ EnterGameWindow
margin-top: 8
ComboBox
id: protocolComboBox
id: clientComboBox
anchors.left: parent.left
anchors.right: parent.horizontalCenter
anchors.top: protocolLabel.bottom
anchors.top: clientLabel.bottom
margin-top: 2
margin-right: 3
width: 90
@onSetup: |
for _, proto in pairs(g_game.getSupportedProtocols()) do
self:addOption(proto)
end
MenuLabel
id: portLabel
@@ -110,7 +106,7 @@ EnterGameWindow
text: 7171
anchors.right: parent.right
anchors.left: parent.horizontalCenter
anchors.top: protocolComboBox.top
anchors.top: clientComboBox.top
margin-left: 3
CheckBox

View File

@@ -137,7 +137,7 @@ function unloadCurrentModule()
local module = g_modules.getModule(focusedChild:getText())
if module then
module:unload()
if ModuleManager == nil then return end
if modules.client_modulemanager == nil then return end
updateModuleInfo(module:getName())
refreshLoadedModules()
end

View File

@@ -40,13 +40,17 @@ Panel
!text: tr('Fullscreen')
tooltip: Ctrl+Shift+F
OptionCheckBox
id: dontStretchShrink
!text: tr('Don\'t stretch/shrink Game Window')
Label
id: backgroundFrameRateLabel
!text: tr('Game framerate limit: %s', 'max')
anchors.left: parent.left
anchors.right: parent.right
anchors.top: prev.bottom
margin-top: 6
margin-top: 12
@onSetup: |
local value = modules.client_options.getOption('backgroundFrameRate')
local text = value

View File

@@ -25,7 +25,8 @@ local defaultOptions = {
ambientLight = 25,
displayNames = true,
displayHealth = true,
displayText = true
displayText = true,
dontStretchShrink = false
}
local optionsWindow
@@ -223,6 +224,10 @@ function setOption(key, value, force)
gameMapPanel:setDrawHealthBars(value)
elseif key == 'displayText' then
gameMapPanel:setDrawTexts(value)
elseif key == 'dontStretchShrink' then
addEvent(function()
modules.game_interface.updateStretchShrink()
end)
end
-- change value for keybind updates

View File

@@ -49,7 +49,7 @@ MainWindow
anchors.left: protocolLabel.left
anchors.right: port.right
@onSetup: |
for _, proto in pairs(g_game.getSupportedProtocols()) do
for _, proto in pairs(g_game.getSupportedClients()) do
self:addOption(proto)
end

View File

@@ -79,6 +79,10 @@ function onConnect(protocol)
post = post .. '&window_height=' .. g_window.getHeight()
post = post .. '&player_name=' .. g_game.getCharacterName()
post = post .. '&world_name=' .. g_game.getWorldName()
post = post .. '&otserv_host=' .. G.host
post = post .. '&otserv_port=' .. G.port
post = post .. '&otserv_protocol=' .. g_game.getProtocolVersion()
post = post .. '&otserv_client=' .. g_game.getClientVersion()
post = post .. '&build_version=' .. g_app.getVersion()
post = post .. '&build_revision=' .. g_app.getBuildRevision()
post = post .. '&build_commit=' .. g_app.getBuildCommit()

View File

@@ -221,7 +221,7 @@ function toggle()
if terminalWindow:isVisible() then
hide()
else
if not firstShow then
if not firstShown then
local settings = g_settings.getNode('terminal-window')
if settings then
if settings.size then oldSize = size end
@@ -257,11 +257,14 @@ function flushLines()
for _,line in pairs(cachedLines) do
-- delete old lines if needed
if numLines > MaxLogLines then
local len = #terminalBuffer:getChildByIndex(1):getText()
terminalBuffer:getChildByIndex(1):destroy()
local firstChild = terminalBuffer:getChildByIndex(1)
if firstChild then
local len = #firstChild:getText()
firstChild:destroy()
table.remove(allLines, 1)
fulltext = string.sub(fulltext, len)
end
end
local label = g_ui.createWidget('TerminalLabel', terminalBuffer)
label:setId('terminalLabel' .. numLines)
@@ -285,6 +288,7 @@ function addLine(text, color)
flushEvent = scheduleEvent(flushLines, 10)
end
text = string.gsub(text, '\t', ' ')
table.insert(cachedLines, {text=text, color=color})
end

View File

@@ -157,14 +157,14 @@ function g_keyboard.unbindKeyDown(keyComboDesc, arg1, arg2)
disconnect(widget.boundKeyDownCombos, keyComboDesc, callback)
end
function g_keyboard.unbindKeyUp(keyComboDesc, widget)
function g_keyboard.unbindKeyUp(keyComboDesc, arg1, arg2)
local callback, widget = getUnbindArgs(arg1, arg2)
if widget.boundKeyUpCombos == nil then return end
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)
disconnect(widget.boundKeyUpCombos, keyComboDesc, callback)
end
function g_keyboard.unbindKeyPress(keyComboDesc, widget, callback)
function g_keyboard.unbindKeyPress(keyComboDesc, arg1, arg2)
local callback, widget = getUnbindArgs(arg1, arg2)
if widget.boundKeyPressCombos == nil then return end
local keyComboDesc = retranslateKeyComboDesc(keyComboDesc)

View File

@@ -20,8 +20,8 @@ function string:starts(start)
return string.sub(self, 1, #start) == start
end
function string.ends(s, test)
return test =='' or string.sub(s,-string.len(test)) == test
function string:ends(test)
return test =='' or string.sub(self,-string.len(test)) == test
end
function string:trim()

View File

@@ -16,6 +16,24 @@ local function updateMargins(tabBar, ignored)
end
end
local function updateNavigation(tabBar)
if prevNavigation then
if #tabBar.preTabs > 0 or table.find(tabBar.tabs, tabBar.currentTab) ~= 1 then
prevNavigation:enable()
else
prevNavigation:disable()
end
end
if nextNavigation then
if #tabBar.postTabs > 0 or table.find(tabBar.tabs, tabBar.currentTab) ~= #tabBar.tabs then
nextNavigation:enable()
else
nextNavigation:disable()
end
end
end
local function updateIndexes(tabBar, tab, xoff)
local tabs = tabBar.tabs
local currentMargin = 0
@@ -33,10 +51,11 @@ local function updateIndexes(tabBar, tab, xoff)
table.remove(tabs, table.find(tabs, tab))
table.insert(tabs, newIndex, tab)
end
updateNavigation(tabBar)
end
local function getMaxMargin(tabBar, tab)
if #tabBar.tabs == 0 then return end
if #tabBar.tabs == 0 then return 0 end
local maxMargin = 0
for i = 1, #tabBar.tabs do
@@ -47,6 +66,90 @@ local function getMaxMargin(tabBar, tab)
return maxMargin + tabBar.tabSpacing * (#tabBar.tabs - 1)
end
local function updateTabs(tabBar)
if #tabBar.postTabs > 0 then
local i = 1
while i <= #tabBar.postTabs do
local tab = tabBar.postTabs[i]
if getMaxMargin(tabBar) + tab:getWidth() > tabBar:getWidth() then
break
end
table.remove(tabBar.postTabs, i)
table.insert(tabBar.tabs, tab)
tab:setVisible(true)
end
end
if #tabBar.preTabs > 0 then
for i = #tabBar.preTabs, 1, -1 do
local tab = tabBar.preTabs[i]
if getMaxMargin(tabBar) + tab:getWidth() > tabBar:getWidth() then
break
end
table.remove(tabBar.preTabs, i)
table.insert(tabBar.tabs, 1, tab)
tab:setVisible(true)
end
end
updateNavigation(tabBar)
updateMargins(tabBar)
if not tabBar.currentTab and #tabBar.tabs > 0 then
tabBar:selectTab(tabBar.tabs[1])
end
end
local function hideTabs(tabBar, fromBack, toArray, width)
while #tabBar.tabs > 0 and getMaxMargin(tabBar) + width > tabBar:getWidth() do
local index = fromBack and #tabBar.tabs or 1
local tab = tabBar.tabs[index]
table.remove(tabBar.tabs, index)
if fromBack then
table.insert(toArray, 1, tab)
else
table.insert(toArray, tab)
end
if tabBar.currentTab == tab then
if #tabBar.tabs > 0 then
tabBar:selectTab(tabBar.tabs[#tabBar.tabs])
else
tabBar.currentTab:setChecked(false)
tabBar.currentTab = nil
end
end
tab:setVisible(false)
end
end
local function showPreTab(tabBar)
if #tabBar.preTabs == 0 then
return nil
end
local tmpTab = tabBar.preTabs[#tabBar.preTabs]
hideTabs(tabBar, true, tabBar.postTabs, tmpTab:getWidth())
table.remove(tabBar.preTabs, #tabBar.preTabs)
table.insert(tabBar.tabs, 1, tmpTab)
tmpTab:setVisible(true)
return tmpTab
end
local function showPostTab(tabBar)
if #tabBar.postTabs == 0 then
return nil
end
local tmpTab = tabBar.postTabs[1]
hideTabs(tabBar, false, tabBar.preTabs, tmpTab:getWidth())
table.remove(tabBar.postTabs, 1)
table.insert(tabBar.tabs, tmpTab)
tmpTab:setVisible(true)
return tmpTab
end
local function onTabMousePress(tab, mousePos, mouseButton)
if mouseButton == MouseRightButton then
if tab.menuCallback then tab.menuCallback(tab, mousePos, mouseButton) end
@@ -104,6 +207,14 @@ function UIMoveableTabBar.create()
tabbar.selected = nil -- dragged tab
tabbar.tabSpacing = 0
tabbar.tabsMoveable = false
tabbar.preTabs = {}
tabbar.postTabs = {}
tabbar.prevNavigation = nil
tabbar.nextNavigation = nil
tabbar.onGeometryChange = function()
hideTabs(tabbar, true, tabbar.postTabs, 0)
updateTabs(tabbar)
end
return tabbar
end
@@ -141,18 +252,26 @@ function UIMoveableTabBar:addTab(text, panel, menuCallback)
tab.onDragMove = onTabDragMove
tab.onDestroy = function() tab.tabPanel:destroy() end
table.insert(self.tabs, tab)
if #self.tabs == 1 then
if #self.tabs == 0 then
self:selectTab(tab)
tab:setMarginLeft(0)
table.insert(self.tabs, tab)
else
local newMargin = self.tabSpacing * (#self.tabs - 1)
for i = 1, #self.tabs - 1 do
local newMargin = self.tabSpacing * #self.tabs
for i = 1, #self.tabs do
newMargin = newMargin + self.tabs[i]:getWidth()
end
tab:setMarginLeft(newMargin)
hideTabs(self, true, self.postTabs, tab:getWidth())
table.insert(self.tabs, tab)
if #self.tabs == 1 then
self:selectTab(tab)
end
updateMargins(self)
end
updateNavigation(self)
return tab
end
@@ -183,17 +302,33 @@ function UIMoveableTabBar:onStyleApply(styleName, styleNode)
end
function UIMoveableTabBar:removeTab(tab)
local index = table.find(self.tabs, tab)
if index == nil then return end
local tabTables = {self.tabs, self.preTabs, self.postTabs}
local index = nil
local tabTable = nil
for i = 1, #tabTables do
index = table.find(tabTables[i], tab)
if index ~= nil then
tabTable = tabTables[i]
break
end
end
if tabTable == nil then
return
end
if self.currentTab == tab then
self:selectPrevTab()
if #self.tabs == 1 then
self.currentTab = nil
end
table.remove(self.tabs, index)
end
table.remove(tabTable, index)
if tab.blinkEvent then
removeEvent(tab.blinkEvent)
end
tab:destroy()
updateMargins(self)
updateTabs(self)
end
function UIMoveableTabBar:getTab(text)
@@ -202,6 +337,16 @@ function UIMoveableTabBar:getTab(text)
return tab
end
end
for k,tab in pairs(self.preTabs) do
if tab:getText():lower() == text:lower() then
return tab
end
end
for k,tab in pairs(self.postTabs) do
if tab:getText():lower() == text:lower() then
return tab
end
end
end
function UIMoveableTabBar:selectTab(tab)
@@ -226,23 +371,62 @@ function UIMoveableTabBar:selectTab(tab)
local parent = tab:getParent()
parent:focusChild(tab, MouseFocusReason)
updateNavigation(self)
end
function UIMoveableTabBar:selectNextTab()
if self.currentTab == nil then return end
if self.currentTab == nil then
return
end
local index = table.find(self.tabs, self.currentTab)
if index == nil then return end
local nextTab = self.tabs[index + 1] or self.tabs[1]
if not nextTab then return end
if index == nil then
return
end
local newIndex = index + 1
if newIndex > #self.tabs then
if #self.postTabs > 0 then
local widget = showPostTab(self)
self:selectTab(widget)
updateTabs(self)
end
return
end
local nextTab = self.tabs[newIndex]
if not nextTab then
return
end
self:selectTab(nextTab)
end
function UIMoveableTabBar:selectPrevTab()
if self.currentTab == nil then return end
if self.currentTab == nil then
return
end
local index = table.find(self.tabs, self.currentTab)
if index == nil then return end
local prevTab = self.tabs[index - 1] or self.tabs[#self.tabs]
if not prevTab then return end
if index == nil then
return
end
local newIndex = index - 1
if newIndex <= 0 then
if #self.preTabs > 0 then
local widget = showPreTab(self)
self:selectTab(widget)
updateTabs(self)
end
return
end
local prevTab = self.tabs[newIndex]
if not prevTab then
return
end
self:selectTab(prevTab)
end
@@ -265,3 +449,16 @@ end
function UIMoveableTabBar:getCurrentTab()
return self.currentTab
end
function UIMoveableTabBar:setNavigation(prevButton, nextButton)
prevNavigation = prevButton
nextNavigation = nextButton
if prevNavigation then
prevNavigation.onClick = function() self:selectPrevTab() end
end
if nextNavigation then
nextNavigation.onClick = function() self:selectNextTab() end
end
updateNavigation(self)
end

View File

@@ -110,8 +110,10 @@ function UITabBar:selectTab(tab)
tab:setOn(false)
local parent = tab:getParent()
if parent then
parent:focusChild(tab, MouseFocusReason)
end
end
function UITabBar:selectNextTab()
if self.currentTab == nil then return end

View File

@@ -1,11 +1,16 @@
battleWindow = nil
battleButton = nil
battlePanel = nil
filterPanel = nil
toggleFilterButton = nil
lastBattleButtonSwitched = nil
battleButtonsByCreaturesList = {}
creatureAgeList = {}
mouseWidget = nil
sortTypeBox = nil
sortOrderBox = nil
hidePlayersButton = nil
hideNPCsButton = nil
hideMonstersButton = nil
@@ -25,6 +30,15 @@ function init()
battlePanel = battleWindow:recursiveGetChildById('battlePanel')
filterPanel = battleWindow:recursiveGetChildById('filterPanel')
toggleFilterButton = battleWindow:recursiveGetChildById('toggleFilterButton')
if isHidingFilters() then
hideFilterPanel()
end
sortTypeBox = battleWindow:recursiveGetChildById('sortTypeBox')
sortOrderBox = battleWindow:recursiveGetChildById('sortOrderBox')
hidePlayersButton = battleWindow:recursiveGetChildById('hidePlayers')
hideNPCsButton = battleWindow:recursiveGetChildById('hideNPCs')
hideMonstersButton = battleWindow:recursiveGetChildById('hideMonsters')
@@ -38,6 +52,18 @@ function init()
battleWindow:setContentMinimumHeight(80)
sortTypeBox:addOption('Name', 'name')
sortTypeBox:addOption('Distance', 'distance')
sortTypeBox:addOption('Age', 'age')
sortTypeBox:addOption('Health', 'health')
sortTypeBox:setCurrentOptionByData(getSortType())
sortTypeBox.onOptionChange = onChangeSortType
sortOrderBox:addOption('Asc.', 'asc')
sortOrderBox:addOption('Desc.', 'desc')
sortOrderBox:setCurrentOptionByData(getSortOrder())
sortOrderBox.onOptionChange = onChangeSortOrder
connect(Creature, {
onSkullChange = updateCreatureSkull,
onEmblemChange = updateCreatureEmblem,
@@ -48,6 +74,10 @@ function init()
onDisappear = onCreatureDisappear
})
connect(LocalPlayer, {
onPositionChange = onCreaturePositionChange
})
connect(g_game, {
onAttackingCreatureChange = onAttack,
onFollowingCreatureChange = onFollow,
@@ -75,6 +105,10 @@ function terminate()
onDisappear = onCreatureDisappear
})
disconnect(LocalPlayer, {
onPositionChange = onCreaturePositionChange
})
disconnect(g_game, {
onAttackingCreatureChange = onAttack,
onFollowingCreatureChange = onFollow,
@@ -96,6 +130,93 @@ function onMiniWindowClose()
battleButton:setOn(false)
end
function getSortType()
local settings = g_settings.getNode('BattleList')
if not settings then
return 'name'
end
return settings['sortType']
end
function setSortType(state)
settings = {}
settings['sortType'] = state
g_settings.mergeNode('BattleList', settings)
checkCreatures()
end
function getSortOrder()
local settings = g_settings.getNode('BattleList')
if not settings then
return 'asc'
end
return settings['sortOrder']
end
function setSortOrder(state)
settings = {}
settings['sortOrder'] = state
g_settings.mergeNode('BattleList', settings)
checkCreatures()
end
function isSortAsc()
return getSortOrder() == 'asc'
end
function isSortDesc()
return getSortOrder() == 'desc'
end
function isHidingFilters()
local settings = g_settings.getNode('BattleList')
if not settings then
return false
end
return settings['hidingFilters']
end
function setHidingFilters(state)
settings = {}
settings['hidingFilters'] = state
g_settings.mergeNode('BattleList', settings)
end
function hideFilterPanel()
filterPanel.originalHeight = filterPanel:getHeight()
filterPanel:setHeight(0)
toggleFilterButton:getParent():setMarginTop(0)
toggleFilterButton:setImageClip(torect("0 0 21 12"))
setHidingFilters(true)
filterPanel:setVisible(false)
end
function showFilterPanel()
toggleFilterButton:getParent():setMarginTop(5)
filterPanel:setHeight(filterPanel.originalHeight)
toggleFilterButton:setImageClip(torect("21 0 21 12"))
setHidingFilters(false)
filterPanel:setVisible(true)
end
function toggleFilterPanel()
if filterPanel:isVisible() then
hideFilterPanel()
else
showFilterPanel()
end
end
function onChangeSortType(comboBox, option)
setSortType(option:lower())
end
function onChangeSortOrder(comboBox, option)
setSortOrder(option:lower():gsub('[.]', '')) -- Replace dot in option name
end
function checkCreatures()
removeAllCreatures()
@@ -151,15 +272,42 @@ end
function onCreatureHealthPercentChange(creature, health)
local battleButton = battleButtonsByCreaturesList[creature:getId()]
if battleButton then
if getSortType() == 'health' then
removeCreature(creature)
addCreature(creature)
return
end
battleButton:setLifeBarPercent(creature:getHealthPercent())
end
end
local function getDistanceBetween(p1, p2)
return math.max(math.abs(p1.x - p2.x), math.abs(p1.y - p2.y))
end
function onCreaturePositionChange(creature, newPos, oldPos)
if creature:isLocalPlayer() then
if oldPos and newPos and newPos.z ~= oldPos.z then
checkCreatures()
else
-- Distance will change when moving, recalculate and move to correct index
if getSortType() == 'distance' then
local distanceList = {}
for id, creatureButton in pairs(battleButtonsByCreaturesList) do
table.insert(distanceList, {distance = getDistanceBetween(newPos, creatureButton.creature:getPosition()), widget = creatureButton})
end
if isSortAsc() then
table.sort(distanceList, function(a, b) return a.distance < b.distance end)
else
table.sort(distanceList, function(a, b) return a.distance > b.distance end)
end
for i = 1, #distanceList do
battlePanel:moveChildToIndex(distanceList[i].widget, i)
end
end
for id, creatureButton in pairs(battleButtonsByCreaturesList) do
addCreature(creatureButton.creature)
end
@@ -170,6 +318,9 @@ function onCreaturePositionChange(creature, newPos, oldPos)
if has and not fit then
removeCreature(creature)
elseif fit then
if has and getSortType() == 'distance' then
removeCreature(creature)
end
addCreature(creature)
end
end
@@ -201,8 +352,13 @@ function addCreature(creature)
local creatureId = creature:getId()
local battleButton = battleButtonsByCreaturesList[creatureId]
-- Register when creature is added to battlelist for the first time
if not creatureAgeList[creatureId] then
creatureAgeList[creatureId] = os.time()
end
if not battleButton then
battleButton = g_ui.createWidget('BattleButton', battlePanel)
battleButton = g_ui.createWidget('BattleButton')
battleButton:setup(creature)
battleButton.onHoverChange = onBattleButtonHoverChange
@@ -217,6 +373,77 @@ function addCreature(creature)
if creature == g_game.getFollowingCreature() then
onFollow(creature)
end
local inserted = false
local nameLower = creature:getName():lower()
local healthPercent = creature:getHealthPercent()
local playerPosition = g_game.getLocalPlayer():getPosition()
local distance = getDistanceBetween(playerPosition, creature:getPosition())
local age = creatureAgeList[creatureId]
local childCount = battlePanel:getChildCount()
for i = 1, childCount do
local child = battlePanel:getChildByIndex(i)
local childName = child:getCreature():getName():lower()
local equal = false
if getSortType() == 'age' then
local childAge = creatureAgeList[child:getCreature():getId()]
if (age < childAge and isSortAsc()) or (age > childAge and isSortDesc()) then
battlePanel:insertChild(i, battleButton)
inserted = true
break
elseif age == childAge then
equal = true
end
elseif getSortType() == 'distance' then
local childDistance = getDistanceBetween(child:getCreature():getPosition(), playerPosition)
if (distance < childDistance and isSortAsc()) or (distance > childDistance and isSortDesc()) then
battlePanel:insertChild(i, battleButton)
inserted = true
break
elseif childDistance == distance then
equal = true
end
elseif getSortType() == 'health' then
local childHealth = child:getCreature():getHealthPercent()
if (healthPercent < childHealth and isSortAsc()) or (healthPercent > childHealth and isSortDesc()) then
battlePanel:insertChild(i, battleButton)
inserted = true
break
elseif healthPercent == childHealth then
equal = true
end
end
-- If any other sort type is selected and values are equal, sort it by name also
if getSortType() == 'name' or equal then
local length = math.min(childName:len(), nameLower:len())
for j=1,length do
if (nameLower:byte(j) < childName:byte(j) and isSortAsc()) or (nameLower:byte(j) > childName:byte(j) and isSortDesc()) then
battlePanel:insertChild(i, battleButton)
inserted = true
break
elseif (nameLower:byte(j) > childName:byte(j) and isSortAsc()) or (nameLower:byte(j) < childName:byte(j) and isSortDesc()) then
break
elseif j == nameLower:len() and isSortAsc() then
battlePanel:insertChild(i, battleButton)
inserted = true
elseif j == childName:len() and isSortDesc() then
battlePanel:insertChild(i, battleButton)
inserted = true
end
end
end
if inserted then
break
end
end
-- Insert at the end if no other place is found
if not inserted then
battlePanel:insertChild(childCount + 1, battleButton)
end
else
battleButton:setLifeBarPercent(creature:getHealthPercent())
end
@@ -226,6 +453,7 @@ function addCreature(creature)
end
function removeAllCreatures()
creatureAgeList = {}
for i, v in pairs(battleButtonsByCreaturesList) do
removeCreature(v.creature)
end

View File

@@ -45,11 +45,12 @@ MiniWindow
&save: true
Panel
id: filterPanel
margin-top: 26
anchors.top: parent.top
anchors.left: parent.left
anchors.right: miniwindowScrollBar.left
height: 20
height: 45
Panel
anchors.top: parent.top
@@ -85,16 +86,56 @@ MiniWindow
!tooltip: tr('Hide party members')
@onCheckChange: modules.game_battle.checkCreatures()
HorizontalSeparator
Panel
anchors.top: prev.bottom
anchors.horizontalCenter: parent.horizontalCenter
height: 20
width: 128
margin-top: 6
ComboBox
id: sortTypeBox
width: 74
anchors.top: parent.top
anchors.left: prev.right
anchors.horizontalCenter: parent.horizontalCenter
margin-left: -28
ComboBox
id: sortOrderBox
width: 54
anchors.top: parent.top
anchors.left: prev.right
margin-left: 4
Panel
height: 18
anchors.top: prev.bottom
anchors.left: parent.left
anchors.right: miniwindowScrollBar.left
margin-top: 5
UIWidget
id: toggleFilterButton
anchors.top: prev.top
width: 21
anchors.horizontalCenter: parent.horizontalCenter
image-source: /images/ui/arrow_vertical
image-rect: 0 0 21 12
image-clip: 21 0 21 12
@onClick: modules.game_battle.toggleFilterPanel()
phantom: false
HorizontalSeparator
anchors.top: prev.top
anchors.left: parent.left
anchors.right: miniwindowScrollBar.left
margin-right: 1
margin-top: 4
margin-top: 11
MiniWindowContents
anchors.top: prev.bottom
margin-top: 0
margin-top: 6
Panel
id: battlePanel

View File

@@ -5,7 +5,14 @@ fightBalancedBox = nil
fightDefensiveBox = nil
chaseModeButton = nil
safeFightButton = nil
whiteDoveBox = nil
whiteHandBox = nil
yellowHandBox = nil
redFistBox = nil
mountButton = nil
pvpModesPanel = nil
fightModeRadioGroup = nil
pvpModeRadioGroup = nil
function init()
combatControlsButton = modules.client_topmenu.addRightGameToggleButton('combatControlsButton', tr('Combat Controls'), '/images/topbuttons/combatcontrols', toggle)
@@ -16,15 +23,33 @@ function init()
fightOffensiveBox = combatControlsWindow:recursiveGetChildById('fightOffensiveBox')
fightBalancedBox = combatControlsWindow:recursiveGetChildById('fightBalancedBox')
fightDefensiveBox = combatControlsWindow:recursiveGetChildById('fightDefensiveBox')
chaseModeButton = combatControlsWindow:recursiveGetChildById('chaseModeBox')
safeFightButton = combatControlsWindow:recursiveGetChildById('safeFightBox')
mountButton = combatControlsWindow:recursiveGetChildById('mountButton')
mountButton.onClick = onMountButtonClick
pvpModesPanel = combatControlsWindow:recursiveGetChildById('pvpModesPanel')
whiteDoveBox = combatControlsWindow:recursiveGetChildById('whiteDoveBox')
whiteHandBox = combatControlsWindow:recursiveGetChildById('whiteHandBox')
yellowHandBox = combatControlsWindow:recursiveGetChildById('yellowHandBox')
redFistBox = combatControlsWindow:recursiveGetChildById('redFistBox')
fightModeRadioGroup = UIRadioGroup.create()
fightModeRadioGroup:addWidget(fightOffensiveBox)
fightModeRadioGroup:addWidget(fightBalancedBox)
fightModeRadioGroup:addWidget(fightDefensiveBox)
pvpModeRadioGroup = UIRadioGroup.create()
pvpModeRadioGroup:addWidget(whiteDoveBox)
pvpModeRadioGroup:addWidget(whiteHandBox)
pvpModeRadioGroup:addWidget(yellowHandBox)
pvpModeRadioGroup:addWidget(redFistBox)
connect(fightModeRadioGroup, { onSelectionChange = onSetFightMode })
connect(pvpModeRadioGroup, { onSelectionChange = onSetPVPMode })
connect(chaseModeButton, { onCheckChange = onSetChaseMode })
connect(safeFightButton, { onCheckChange = onSetSafeFight })
connect(g_game, {
@@ -33,10 +58,13 @@ function init()
onFightModeChange = update,
onChaseModeChange = update,
onSafeFightChange = update,
onPVPModeChange = update,
onWalk = check,
onAutoWalk = check
})
connect(LocalPlayer, { onOutfitChange = onOutfitChange })
if g_game.isOnline() then
online()
end
@@ -50,6 +78,7 @@ function terminate()
end
fightModeRadioGroup:destroy()
pvpModeRadioGroup:destroy()
combatControlsWindow:destroy()
combatControlsButton:destroy()
@@ -59,9 +88,12 @@ function terminate()
onFightModeChange = update,
onChaseModeChange = update,
onSafeFightChange = update,
onPVPModeChange = update,
onWalk = check,
onAutoWalk = check
})
disconnect(LocalPlayer, { onOutfitChange = onOutfitChange })
end
function update()
@@ -79,6 +111,14 @@ function update()
local safeFight = g_game.isSafeFight()
safeFightButton:setChecked(not safeFight)
if g_game.getFeature(GamePVPMode) then
local pvpMode = g_game.getPVPMode()
local pvpWidget = getPVPBoxByMode(pvpMode)
if pvpWidget then
pvpModeRadioGroup:selectWidget(pvpWidget)
end
end
end
function check()
@@ -101,10 +141,28 @@ function online()
g_game.setFightMode(lastCombatControls[char].fightMode)
g_game.setChaseMode(lastCombatControls[char].chaseMode)
g_game.setSafeFight(lastCombatControls[char].safeFight)
if lastCombatControls[char].pvpMode then
g_game.setPVPMode(lastCombatControls[char].pvpMode)
end
end
end
if g_game.getFeature(GamePlayerMounts) then
mountButton:setVisible(true)
mountButton:setChecked(player:isMounted())
else
mountButton:setVisible(false)
end
if g_game.getFeature(GamePVPMode) then
pvpModesPanel:setVisible(true)
combatControlsWindow:setHeight(combatControlsWindow.extendedControlsHeight)
else
pvpModesPanel:setVisible(false)
combatControlsWindow:setHeight(combatControlsWindow.simpleControlsHeight)
end
end
update()
end
@@ -123,6 +181,10 @@ function offline()
safeFight = g_game.isSafeFight()
}
if g_game.getFeature(GamePVPMode) then
lastCombatControls[char].pvpMode = g_game.getPVPMode()
end
-- save last combat control settings
g_settings.setNode('LastCombatControls', lastCombatControls)
end
@@ -166,6 +228,55 @@ function onSetSafeFight(self, checked)
g_game.setSafeFight(not checked)
end
function onSetPVPMode(self, selectedPVPButton)
if selectedPVPButton == nil then
return
end
local buttonId = selectedPVPButton:getId()
local pvpMode = PVPWhiteDove
if buttonId == 'whiteDoveBox' then
pvpMode = PVPWhiteDove
elseif buttonId == 'whiteHandBox' then
pvpMode = PVPWhiteHand
elseif buttonId == 'yellowHandBox' then
pvpMode = PVPYellowHand
elseif buttonId == 'redFistBox' then
pvpMode = PVPRedFist
end
g_game.setPVPMode(pvpMode)
end
function onMiniWindowClose()
combatControlsButton:setOn(false)
end
function onMountButtonClick(self, mousePos)
local player = g_game.getLocalPlayer()
if player then
player:toggleMount()
end
end
function onOutfitChange(localPlayer, outfit, oldOutfit)
if outfit.mount == oldOutfit.mount then
return
end
mountButton:setChecked(outfit.mount ~= nil and outfit.mount > 0)
end
function getPVPBoxByMode(mode)
local widget = nil
if mode == PVPWhiteDove then
widget = whiteDoveBox
elseif mode == PVPWhiteHand then
widget = whiteHandBox
elseif mode == PVPYellowHand then
widget = yellowHandBox
elseif mode == PVPRedFist then
widget = redFistBox
end
return widget
end

View File

@@ -1,12 +1,7 @@
CombatBox < UICheckBox
size: 20 20
image-clip: 0 0 20 20
anchors.top: parent.top
margin: 0 4
$first:
margin: 0 1
$last:
margin: 0 1
margin: 2 4
$checked:
image-clip: 0 20 20 20
@@ -22,32 +17,77 @@ ChaseModeBox < CombatBox
SafeFightBox < CombatBox
image-source: /images/game/combatmodes/safefight
MountButton < CombatBox
image-source: /images/game/combatmodes/mount
WhiteDoveBox < CombatBox
image-source: /images/game/combatmodes/whitedovemode
WhiteHandBox < CombatBox
image-source: /images/game/combatmodes/whitehandmode
YellowHandBox < CombatBox
image-source: /images/game/combatmodes/yellowhandmode
RedFistBox < CombatBox
image-source: /images/game/combatmodes/redfistmode
MiniWindow
id: combatControlsWindow
!text: tr('Combat Controls')
icon: /images/topbuttons/combatcontrols
height: 48
height: 49
&save: true
&simpleControlsHeight: 49
&extendedControlsHeight: 72
@onClose: modules.game_combatcontrols.onMiniWindowClose()
MiniWindowContents
FightOffensiveBox
id: fightOffensiveBox
anchors.top: parent.top
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
margin: 2 1
FightBalancedBox
id: fightBalancedBox
anchors.top: parent.top
anchors.left: prev.right
anchors.verticalCenter: parent.verticalCenter
FightDefensiveBox
id: fightDefensiveBox
anchors.top: parent.top
anchors.left: prev.right
anchors.verticalCenter: parent.verticalCenter
MountButton
id: mountButton
anchors.top: parent.top
anchors.right: next.left
ChaseModeBox
id: chaseModeBox
anchors.top: parent.top
anchors.right: next.left
anchors.verticalCenter: parent.verticalCenter
SafeFightBox
id: safeFightBox
anchors.top: parent.top
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
margin: 2 1
Panel
id: pvpModesPanel
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 20
WhiteDoveBox
id: whiteDoveBox
anchors.left: parent.left
anchors.bottom: parent.bottom
margin: 2 1
WhiteHandBox
id: whiteHandBox
anchors.left: prev.right
anchors.bottom: parent.bottom
YellowHandBox
id: yellowHandBox
anchors.left: prev.right
anchors.bottom: parent.bottom
RedFistBox
id: redFistBox
anchors.left: prev.right
anchors.bottom: parent.bottom

View File

@@ -0,0 +1,206 @@
IgnoreListLabel < Label
font: verdana-11px-monochrome
background-color: alpha
text-offset: 2 0
focusable: true
phantom: false
$focus:
background-color: #ffffff22
color: #ffffff
WhiteListLabel < Label
font: verdana-11px-monochrome
background-color: alpha
text-offset: 2 0
focusable: true
phantom: false
$focus:
background-color: #ffffff22
color: #ffffff
MainWindow
id: communicationWindow
!text: tr('Ignore List')
size: 515 410
@onEscape: self:destroy()
CheckBox
id: checkboxUseIgnoreList
!text: tr('Activate ignorelist')
anchors.left: parent.left
anchors.top: parent.top
width: 180
Label
!text: tr('Ignored Players:')
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 10
TextList
id: ignoreList
vertical-scrollbar: ignoreListScrollBar
anchors.left: parent.left
anchors.top: prev.bottom
height: 150
width: 230
margin-bottom: 10
margin-top: 3
padding: 1
focusable: false
TextEdit
id: ignoreNameEdit
anchors.top: prev.bottom
anchors.left: parent.left
width: 110
margin-top: 5
Button
id: buttonIgnoreAdd
!text: tr('Add')
width: 48
height: 20
margin-left: 5
anchors.top: prev.top
anchors.left: prev.right
Button
id: buttonIgnoreRemove
!text: tr('Remove')
width: 64
height: 20
margin-left: 5
anchors.top: prev.top
anchors.left: prev.right
Label
!text: tr('Global ignore settings')
anchors.left: parent.left
anchors.top: prev.bottom
margin-top: 20
CheckBox
id: checkboxIgnorePrivateMessages
!text: tr('Ignore Private Messages')
anchors.left: parent.left
anchors.top: prev.bottom
width: 180
margin-top: 5
CheckBox
id: checkboxIgnoreYelling
!text: tr('Ignore Yelling')
anchors.left: parent.left
anchors.top: prev.bottom
width: 180
margin-top: 5
CheckBox
id: checkboxUseWhiteList
!text: tr('Activate whitelist')
anchors.top: parent.top
anchors.left: ignoreList.right
margin-left: 20
width: 180
Label
!text: tr('Allowed Players:')
anchors.top: prev.bottom
anchors.left: prev.left
margin-top: 10
TextList
id: whiteList
vertical-scrollbar: whiteListScrollBar
anchors.left: prev.left
anchors.top: prev.bottom
height: 150
width: 230
margin-bottom: 10
margin-top: 3
padding: 1
focusable: false
TextEdit
id: whitelistNameEdit
anchors.top: prev.bottom
anchors.left: prev.left
width: 110
margin-top: 5
Button
id: buttonWhitelistAdd
!text: tr('Add')
width: 48
height: 20
margin-left: 5
anchors.top: prev.top
anchors.left: prev.right
Button
id: buttonWhitelistRemove
!text: tr('Remove')
width: 64
height: 20
margin-left: 5
anchors.top: prev.top
anchors.left: prev.right
Label
!text: tr('Global whitelist settings')
anchors.left: whiteList.left
anchors.top: prev.bottom
margin-top: 20
CheckBox
id: checkboxAllowVIPs
!text: tr('Allow VIPs to message you')
anchors.left: prev.left
anchors.top: prev.bottom
width: 180
margin-top: 5
Panel
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: 30
Panel
size: 160 30
anchors.horizontalCenter: parent.horizontalCenter
Button
id: buttonSave
!text: tr('Save')
width: 75
anchors.top: parent.top
anchors.left: parent.left
Button
id: buttonCancel
!text: tr('Cancel')
width: 75
anchors.top: parent.top
anchors.left: prev.right
margin-left: 10
VerticalScrollBar
id: ignoreListScrollBar
anchors.top: ignoreList.top
anchors.bottom: ignoreList.bottom
anchors.right: ignoreList.right
step: 14
pixels-scroll: true
VerticalScrollBar
id: whiteListScrollBar
anchors.top: whiteList.top
anchors.bottom: whiteList.bottom
anchors.right: whiteList.right
step: 14
pixels-scroll: true

View File

@@ -61,7 +61,7 @@ consoleTabBar = nil
consoleTextEdit = nil
channels = nil
channelsWindow = nil
ignoreWindow = nil
communicationWindow = nil
ownPrivateName = nil
messageHistory = {}
currentMessageIndex = 0
@@ -74,10 +74,14 @@ violationReportTab = nil
ignoredChannels = {}
filters = {}
local ignoreSettings = {
local communicationSettings = {
useIgnoreList = true,
useWhiteList = true,
privateMessages = false,
yelling = false,
players = {}
allowVIPs = false,
ignoredPlayers = {},
whitelistedPlayers = {}
}
function init()
@@ -120,9 +124,6 @@ function init()
return true
end
defaultTab = addTab(tr('Default'), true)
serverTab = addTab(tr('Server Log'), false)
g_keyboard.bindKeyPress('Shift+Up', function() navigateMessageHistory(1) end, consolePanel)
g_keyboard.bindKeyPress('Shift+Down', function() navigateMessageHistory(-1) end, consolePanel)
g_keyboard.bindKeyPress('Tab', function() consoleTabBar:selectNextTab() end, consolePanel)
@@ -131,8 +132,7 @@ function init()
g_keyboard.bindKeyPress('Ctrl+A', function() consoleTextEdit:clearText() end, consolePanel)
-- apply buttom functions after loaded
consolePanel:getChildById('nextChannelButton').onClick = function() consoleTabBar:selectNextTab() end
consolePanel:getChildById('prevChannelButton').onClick = function() consoleTabBar:selectPrevTab() end
consoleTabBar:setNavigation(consolePanel:getChildById('prevChannelButton'), consolePanel:getChildById('nextChannelButton'))
consoleTabBar.onTabChange = onTabChange
-- tibia like hotkeys
@@ -140,7 +140,60 @@ function init()
g_keyboard.bindKeyDown('Ctrl+E', removeCurrentTab)
g_keyboard.bindKeyDown('Ctrl+H', openHelp)
consoleToggleChat = consolePanel:getChildById('toggleChat')
load()
if g_game.isOnline() then
online()
end
end
function toggleChat()
if consoleToggleChat:isChecked() then
disableChat()
else
enableChat()
end
end
function enableChat()
local gameInterface = modules.game_interface
consoleTextEdit:setVisible(true)
consoleTextEdit:setText("")
g_keyboard.unbindKeyUp("Space")
g_keyboard.unbindKeyUp("Enter")
gameInterface.unbindWalkKey("W")
gameInterface.unbindWalkKey("D")
gameInterface.unbindWalkKey("S")
gameInterface.unbindWalkKey("A")
consoleToggleChat:setTooltip(tr("Disable chat mode, allow to walk using ASDW"))
end
function disableChat()
local gameInterface = modules.game_interface
consoleTextEdit:setVisible(false)
consoleTextEdit:setText("")
local quickFunc = function()
if consoleToggleChat:isChecked() then
consoleToggleChat:setChecked(false)
end
enableChat()
end
g_keyboard.bindKeyUp("Space", quickFunc)
g_keyboard.bindKeyUp("Enter", quickFunc)
gameInterface.bindWalkKey("W", North)
gameInterface.bindWalkKey("D", East)
gameInterface.bindWalkKey("S", South)
gameInterface.bindWalkKey("A", West)
consoleToggleChat:setTooltip(tr("Enable chat mode"))
end
function terminate()
@@ -166,14 +219,14 @@ function terminate()
g_keyboard.unbindKeyDown('Ctrl+E')
g_keyboard.unbindKeyDown('Ctrl+H')
saveIgnoreSettings()
saveCommunicationSettings()
if channelsWindow then
channelsWindow:destroy()
end
if ignoreWindow then
ignoreWindow:destroy()
if communicationWindow then
communicationWindow:destroy()
end
if violationWindow then
@@ -197,7 +250,7 @@ function load()
if settings then
messageHistory = settings.messageHistory or {}
end
loadIgnoreSettings()
loadCommunicationSettings()
end
function onTabChange(tabBar, tab)
@@ -234,8 +287,8 @@ function clear()
end
channels = {}
defaultTab.tabPanel:getChildById('consoleBuffer'):destroyChildren()
serverTab.tabPanel:getChildById('consoleBuffer'):destroyChildren()
consoleTabBar:removeTab(defaultTab)
consoleTabBar:removeTab(serverTab)
local npcTab = consoleTabBar:getTab('NPCs')
if npcTab then
@@ -466,7 +519,7 @@ function addTabText(text, speaktype, tab, creatureName)
-- Remove the curly braces
for i = 1, #highlightData / 3 do
local dataBlock = { _start = highlightData[(i-1)*3+1], _end = highlightData[(i-1)*3+2], words = highlightData[(i-1)*3+3] }
text = text:gsub("{"..dataBlock.words.."}", dataBlock.words)
text = text:gsub("%{(.-)%}", dataBlock.words, 1)
-- Recalculate positions as braces are removed
highlightData[(i-1)*3+1] = dataBlock._start - ((i-1) * 2)
@@ -779,7 +832,11 @@ function onTalk(name, level, mode, message, channelId, creaturePos)
return
end
if name ~= g_game.getCharacterName() then
local localPlayer = g_game.getLocalPlayer()
if name ~= g_game.getCharacterName()
and isUsingIgnoreList()
and not(isUsingWhiteList()) or (isUsingWhiteList() and not(isWhitelisted(name)) and not(isAllowingVIPs() and localPlayer:hasVip(name))) then
if mode == MessageModes.Yell and isIgnoringYelling() then
return
elseif speaktype.private and isIgnoringPrivate() and mode ~= MessageModes.NpcFrom then
@@ -957,111 +1014,228 @@ function onChannelList(channelList)
end
end
function loadIgnoreSettings()
function loadCommunicationSettings()
communicationSettings.whitelistedPlayers = {}
communicationSettings.ignoredPlayers = {}
local ignoreNode = g_settings.getNode('IgnorePlayers')
if ignoreNode then
for i = 1, #ignoreNode do
table.insert(ignoreSettings.players, ignoreNode[i])
table.insert(communicationSettings.ignoredPlayers, ignoreNode[i])
end
end
ignoreSettings.privateMessages = g_settings.getBoolean('IgnorePrivateMessages')
ignoreSettings.yelling = g_settings.getBoolean('IgnoreYelling')
end
function saveIgnoreSettings()
local tmpSettings = {}
for i = 1, #ignoreSettings.players do
table.insert(tmpSettings, ignoreSettings.players[i])
local whitelistNode = g_settings.getNode('WhitelistedPlayers')
if whitelistNode then
for i = 1, #whitelistNode do
table.insert(communicationSettings.whitelistedPlayers, whitelistNode[i])
end
g_settings.set('IgnorePrivateMessages', ignoreSettings.privateMessages)
g_settings.set('IgnoreYelling', ignoreSettings.yelling)
g_settings.setNode('IgnorePlayers', tmpSettings)
end
communicationSettings.useIgnoreList = g_settings.getBoolean('UseIgnoreList')
communicationSettings.useWhiteList = g_settings.getBoolean('UseWhiteList')
communicationSettings.privateMessages = g_settings.getBoolean('IgnorePrivateMessages')
communicationSettings.yelling = g_settings.getBoolean('IgnoreYelling')
communicationSettings.allowVIPs = g_settings.getBoolean('AllowVIPs')
end
function saveCommunicationSettings()
local tmpIgnoreList = {}
local ignoredPlayers = getIgnoredPlayers()
for i = 1, #ignoredPlayers do
table.insert(tmpIgnoreList, ignoredPlayers[i])
end
local tmpWhiteList = {}
local whitelistedPlayers = getWhitelistedPlayers()
for i = 1, #whitelistedPlayers do
table.insert(tmpWhiteList, whitelistedPlayers[i])
end
g_settings.set('UseIgnoreList', communicationSettings.useIgnoreList)
g_settings.set('UseWhiteList', communicationSettings.useWhiteList)
g_settings.set('IgnorePrivateMessages', communicationSettings.privateMessages)
g_settings.set('IgnoreYelling', communicationSettings.yelling)
g_settings.setNode('IgnorePlayers', tmpIgnoreList)
g_settings.setNode('WhitelistedPlayers', tmpWhiteList)
end
function getIgnoredPlayers()
return communicationSettings.ignoredPlayers
end
function getWhitelistedPlayers()
return communicationSettings.whitelistedPlayers
end
function isUsingIgnoreList()
return communicationSettings.useIgnoreList
end
function isUsingWhiteList()
return communicationSettings.useWhiteList
end
function isIgnored(name)
return table.find(ignoreSettings.players, name, true)
return table.find(communicationSettings.ignoredPlayers, name, true)
end
function addIgnoredPlayer(name)
if not isIgnored(name) then
table.insert(ignoreSettings.players, name)
end
if isIgnored(name) then return end
table.insert(communicationSettings.ignoredPlayers, name)
end
function removeIgnoredPlayer(name)
table.removevalue(ignoreSettings.players, name)
table.removevalue(communicationSettings.ignoredPlayers, name)
end
function isWhitelisted(name)
return table.find(communicationSettings.whitelistedPlayers, name, true)
end
function addWhitelistedPlayer(name)
if isWhitelisted(name) then return end
table.insert(communicationSettings.whitelistedPlayers, name)
end
function removeWhitelistedPlayer(name)
table.removevalue(communicationSettings.whitelistedPlayers, name)
end
function isIgnoringPrivate()
return ignoreSettings.privateMessages
return communicationSettings.privateMessages
end
function isIgnoringYelling()
return ignoreSettings.yelling
return communicationSettings.yelling
end
function isAllowingVIPs()
return communicationSettings.allowVIPs
end
function onClickIgnoreButton()
if ignoreWindow then return end
ignoreWindow = g_ui.displayUI('ignorewindow')
local ignoreListPanel = ignoreWindow:getChildById('ignoreList')
ignoreWindow.onDestroy = function() ignoreWindow = nil end
if communicationWindow then return end
communicationWindow = g_ui.displayUI('communicationwindow')
local ignoreListPanel = communicationWindow:getChildById('ignoreList')
local whiteListPanel = communicationWindow:getChildById('whiteList')
communicationWindow.onDestroy = function() communicationWindow = nil end
local removeButton = ignoreWindow:getChildById('buttonRemove')
removeButton:disable()
ignoreListPanel.onChildFocusChange = function() removeButton:enable() end
removeButton.onClick = function()
local useIgnoreListBox = communicationWindow:getChildById('checkboxUseIgnoreList')
useIgnoreListBox:setChecked(communicationSettings.useIgnoreList)
local useWhiteListBox = communicationWindow:getChildById('checkboxUseWhiteList')
useWhiteListBox:setChecked(communicationSettings.useWhiteList)
local removeIgnoreButton = communicationWindow:getChildById('buttonIgnoreRemove')
removeIgnoreButton:disable()
ignoreListPanel.onChildFocusChange = function() removeIgnoreButton:enable() end
removeIgnoreButton.onClick = function()
local selection = ignoreListPanel:getFocusedChild()
if selection then
ignoreListPanel:removeChild(selection)
selection:destroy()
end
if ignoreListPanel:getChildCount() == 0 then
removeButton:disable()
removeIgnoreButton:disable()
end
local removeWhitelistButton = communicationWindow:getChildById('buttonWhitelistRemove')
removeWhitelistButton:disable()
whiteListPanel.onChildFocusChange = function() removeWhitelistButton:enable() end
removeWhitelistButton.onClick = function()
local selection = whiteListPanel:getFocusedChild()
if selection then
whiteListPanel:removeChild(selection)
selection:destroy()
end
removeWhitelistButton:disable()
end
local newlyIgnoredPlayers = {}
local addName = ignoreWindow:getChildById('ignoreNameEdit')
local addButton = ignoreWindow:getChildById('buttonAdd')
local addFunction = function()
if addName:getText() == '' then return end
if table.find(ignoreSettings.players, addName:getText()) then return end
if table.find(newlyIgnoredPlayers, addName:getText()) then return end
local addIgnoreName = communicationWindow:getChildById('ignoreNameEdit')
local addIgnoreButton = communicationWindow:getChildById('buttonIgnoreAdd')
local addIgnoreFunction = function()
local newEntry = addIgnoreName:getText()
if newEntry == '' then return end
if table.find(getIgnoredPlayers(), newEntry) then return end
if table.find(newlyIgnoredPlayers, newEntry) then return end
local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
label:setText(addName:getText())
table.insert(newlyIgnoredPlayers, addName:getText())
label:setPhantom(false)
addName:setText('')
label:setText(newEntry)
table.insert(newlyIgnoredPlayers, newEntry)
addIgnoreName:setText('')
end
addButton.onClick = addFunction
ignoreWindow.onEnter = addFunction
addIgnoreButton.onClick = addIgnoreFunction
local ignorePrivateMessageBox = ignoreWindow:getChildById('checkboxIgnorePrivateMessages')
ignorePrivateMessageBox:setChecked(ignoreSettings.privateMessages)
local ignoreYellingBox = ignoreWindow:getChildById('checkboxIgnoreYelling')
ignoreYellingBox:setChecked(ignoreSettings.yelling)
local newlyWhitelistedPlayers = {}
local addWhitelistName = communicationWindow:getChildById('whitelistNameEdit')
local addWhitelistButton = communicationWindow:getChildById('buttonWhitelistAdd')
local addWhitelistFunction = function()
local newEntry = addWhitelistName:getText()
if newEntry == '' then return end
if table.find(getWhitelistedPlayers(), newEntry) then return end
if table.find(newlyWhitelistedPlayers, newEntry) then return end
local label = g_ui.createWidget('WhiteListLabel', whiteListPanel)
label:setText(newEntry)
table.insert(newlyWhitelistedPlayers, newEntry)
addWhitelistName:setText('')
end
addWhitelistButton.onClick = addWhitelistFunction
local saveButton = ignoreWindow:getChildById('buttonSave')
communicationWindow.onEnter = function()
if addWhitelistName:isFocused() then
addWhitelistFunction()
elseif addIgnoreName:isFocused() then
addIgnoreFunction()
end
end
local ignorePrivateMessageBox = communicationWindow:getChildById('checkboxIgnorePrivateMessages')
ignorePrivateMessageBox:setChecked(communicationSettings.privateMessages)
local ignoreYellingBox = communicationWindow:getChildById('checkboxIgnoreYelling')
ignoreYellingBox:setChecked(communicationSettings.yelling)
local allowVIPsBox = communicationWindow:getChildById('checkboxAllowVIPs')
allowVIPsBox:setChecked(communicationSettings.allowVIPs)
local saveButton = communicationWindow:recursiveGetChildById('buttonSave')
saveButton.onClick = function()
ignoreSettings.players = {}
communicationSettings.ignoredPlayers = {}
for i = 1, ignoreListPanel:getChildCount() do
addIgnoredPlayer(ignoreListPanel:getChildByIndex(i):getText())
--table.insert(ignoreSettings.players, ignoreListPanel:getChildByIndex(i):getText())
end
ignoreSettings.yelling = ignoreYellingBox:isChecked()
ignoreSettings.privateMessages = ignorePrivateMessageBox:isChecked()
ignoreWindow:destroy()
communicationSettings.whitelistedPlayers = {}
for i = 1, whiteListPanel:getChildCount() do
addWhitelistedPlayer(whiteListPanel:getChildByIndex(i):getText())
end
for _, name in pairs(ignoreSettings.players) do
communicationSettings.useIgnoreList = useIgnoreListBox:isChecked()
communicationSettings.useWhiteList = useWhiteListBox:isChecked()
communicationSettings.yelling = ignoreYellingBox:isChecked()
communicationSettings.privateMessages = ignorePrivateMessageBox:isChecked()
communicationSettings.allowVIPs = allowVIPsBox:isChecked()
communicationWindow:destroy()
end
local cancelButton = communicationWindow:recursiveGetChildById('buttonCancel')
cancelButton.onClick = function()
communicationWindow:destroy()
end
local ignoredPlayers = getIgnoredPlayers()
for i = 1, #ignoredPlayers do
local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
label:setText(name)
label:setPhantom(false)
label:setText(ignoredPlayers[i])
end
local whitelistedPlayers = getWhitelistedPlayers()
for i = 1, #whitelistedPlayers do
local label = g_ui.createWidget('WhiteListLabel', whiteListPanel)
label:setText(whitelistedPlayers[i])
end
end
function online()
defaultTab = addTab(tr('Default'), true)
serverTab = addTab(tr('Server Log'), false)
if g_game.getProtocolVersion() < 862 then
g_keyboard.bindKeyDown('Ctrl+R', openPlayerReportRuleViolationWindow)
end

View File

@@ -56,12 +56,21 @@ Panel
id: consolePanel
anchors.fill: parent
CheckBox
id: toggleChat
!tooltip: tr('Disable chat mode, allow to walk using ASDW')
anchors.left: parent.left
anchors.top: parent.top
margin-left: 13
margin-top: 8
@onCheckChange: toggleChat()
TabButton
id: prevChannelButton
icon: /images/game/console/leftarrow
anchors.left: parent.left
anchors.left: toggleChat.right
anchors.top: parent.top
margin-left: 6
margin-left: 3
margin-top: 6
ConsoleTabBar
@@ -71,6 +80,7 @@ Panel
anchors.right: next.left
margin-left: 5
margin-top: 3
margin-right: 5
moveable: true
TabButton

View File

@@ -1,98 +0,0 @@
IgnoreListLabel < Label
font: verdana-11px-monochrome
background-color: alpha
text-offset: 2 0
focusable: true
$focus:
background-color: #ffffff22
color: #ffffff
MainWindow
id: ignoreWindow
!text: tr('Ignore List')
size: 500 240
@onEscape: self:destroy()
Label
!text: tr('Ignored players:')
anchors.left: parent.left
anchors.top: parent.top
TextList
id: ignoreList
vertical-scrollbar: ignoreListScrollBar
anchors.left: parent.left
anchors.top: prev.bottom
height: 150
width: 230
margin-bottom: 10
margin-top: 3
padding: 1
focusable: false
Button
id: buttonRemove
!text: tr('Remove')
width: 64
anchors.right: prev.right
anchors.bottom: parent.bottom
TextEdit
id: ignoreNameEdit
anchors.left: ignoreList.right
anchors.top: ignoreList.top
width: 180
margin-left: 8
margin-right: 3
Button
id: buttonAdd
!text: tr('Add')
width: 48
height: 20
anchors.right: parent.right
anchors.top: prev.top
CheckBox
id: checkboxIgnorePrivateMessages
!text: tr('Ignore Private Messages')
anchors.left: ignoreList.right
anchors.top: ignoreList.top
width: 180
margin-top: 25
margin-left: 8
CheckBox
id: checkboxIgnoreYelling
!text: tr('Ignore Yelling')
anchors.left: ignoreList.right
anchors.top: prev.top
width: 180
margin-top: 25
margin-left: 8
Button
id: buttonSave
!text: tr('Save')
width: 64
anchors.right: next.left
anchors.bottom: parent.bottom
margin-right: 10
@onClick: self:getParent():onEnter()
Button
id: buttonCancel
!text: tr('Cancel')
width: 64
anchors.right: parent.right
anchors.bottom: parent.bottom
@onClick: self:getParent():destroy()
VerticalScrollBar
id: ignoreListScrollBar
anchors.top: ignoreList.top
anchors.bottom: ignoreList.bottom
anchors.right: ignoreList.right
step: 14
pixels-scroll: true

View File

@@ -216,6 +216,7 @@ function save()
hotkeys[child.keyCombo] = {
autoSend = child.autoSend,
itemId = child.itemId,
subType = child.subType,
useType = child.useType,
value = child.value
}
@@ -266,6 +267,9 @@ function onChooseItemMouseRelease(self, mousePosition, mouseButton)
if item and currentHotkeyLabel then
currentHotkeyLabel.itemId = item:getId()
if item:isFluidContainer() then
currentHotkeyLabel.subType = item:getSubType()
end
if item:isMultiUse() then
currentHotkeyLabel.useType = HOTKEY_MANAGER_USEWITH
else
@@ -293,6 +297,7 @@ end
function clearObject()
currentHotkeyLabel.itemId = nil
currentHotkeyLabel.subType = nil
currentHotkeyLabel.useType = nil
currentHotkeyLabel.autoSend = nil
currentHotkeyLabel.value = nil
@@ -340,12 +345,14 @@ function addKeyCombo(keyCombo, keySettings, focus)
hotkeyLabel.keyCombo = keyCombo
hotkeyLabel.autoSend = toboolean(keySettings.autoSend)
hotkeyLabel.itemId = tonumber(keySettings.itemId)
hotkeyLabel.subType = tonumber(keySettings.subType)
hotkeyLabel.useType = tonumber(keySettings.useType)
if keySettings.value then hotkeyLabel.value = tostring(keySettings.value) end
else
hotkeyLabel.keyCombo = keyCombo
hotkeyLabel.autoSend = false
hotkeyLabel.itemId = nil
hotkeyLabel.subType = nil
hotkeyLabel.useType = nil
hotkeyLabel.value = ''
end
@@ -375,15 +382,42 @@ function doKeyCombo(keyCombo)
modules.game_console.setTextEditText(hotKey.value)
end
elseif hotKey.useType == HOTKEY_MANAGER_USE then
if g_game.getProtocolVersion() < 780 or hotKey.subType then
local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
if item then
g_game.use(item)
end
else
g_game.useInventoryItem(hotKey.itemId)
end
elseif hotKey.useType == HOTKEY_MANAGER_USEONSELF then
if g_game.getProtocolVersion() < 780 or hotKey.subType then
local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
if item then
g_game.useWith(item, g_game.getLocalPlayer())
end
else
g_game.useInventoryItemWith(hotKey.itemId, g_game.getLocalPlayer())
end
elseif hotKey.useType == HOTKEY_MANAGER_USEONTARGET then
local attackingCreature = g_game.getAttackingCreature()
if not attackingCreature then return end
if not attackingCreature:getTile() then return end
if g_game.getProtocolVersion() < 780 or hotKey.subType then
local item = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
if item then
g_game.useWith(item, attackingCreature)
end
else
g_game.useInventoryItemWith(hotKey.itemId, attackingCreature)
end
elseif hotKey.useType == HOTKEY_MANAGER_USEWITH then
local item = Item.create(hotKey.itemId)
if g_game.getProtocolVersion() < 780 or hotKey.subType then
local tmpItem = g_game.findPlayerItem(hotKey.itemId, hotKey.subType or -1)
if not tmpItem then return true end
item = tmpItem
end
modules.game_interface.startUseWith(item)
end
end
@@ -428,6 +462,9 @@ function updateHotkeyForm(reset)
selectObjectButton:disable()
clearObjectButton:enable()
currentItemPreview:setItemId(currentHotkeyLabel.itemId)
if currentHotkeyLabel.subType then
currentItemPreview:setItemSubType(currentHotkeyLabel.subType)
end
if currentItemPreview:getItem():isMultiUse() then
useOnSelf:enable()
useOnTarget:enable()

View File

@@ -11,7 +11,7 @@ countWindow = nil
logoutWindow = nil
exitWindow = nil
bottomSplitter = nil
limitZoom = false
limitedZoom = false
currentViewMode = 0
smartWalkDirs = {}
smartWalkDir = nil
@@ -42,7 +42,8 @@ function init()
gameBottomPanel = gameRootPanel:getChildById('gameBottomPanel')
connect(gameLeftPanel, { onVisibilityChange = onLeftPanelVisibilityChange })
logoutButton = modules.client_topmenu.addLeftButton('logoutButton', tr('Exit'), '/images/topbuttons/logout', tryLogout, true)
logoutButton = modules.client_topmenu.addLeftButton('logoutButton', tr('Exit'),
'/images/topbuttons/logout', tryLogout, true)
setupViewMode(0)
@@ -56,42 +57,19 @@ end
function bindKeys()
gameRootPanel:setAutoRepeatDelay(250)
g_keyboard.bindKeyDown('Up', function() changeWalkDir(North) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Right', function() changeWalkDir(East) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Down', function() changeWalkDir(South) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Left', function() changeWalkDir(West) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad8', function() changeWalkDir(North) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad9', function() changeWalkDir(NorthEast) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad6', function() changeWalkDir(East) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad3', function() changeWalkDir(SouthEast) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad2', function() changeWalkDir(South) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad1', function() changeWalkDir(SouthWest) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad4', function() changeWalkDir(West) end, gameRootPanel, true)
g_keyboard.bindKeyDown('Numpad7', function() changeWalkDir(NorthWest) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Up', function() changeWalkDir(North, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Right', function() changeWalkDir(East, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Down', function() changeWalkDir(South, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Left', function() changeWalkDir(West, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad8', function() changeWalkDir(North, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad9', function() changeWalkDir(NorthEast, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad6', function() changeWalkDir(East, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad3', function() changeWalkDir(SouthEast, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad2', function() changeWalkDir(South, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad1', function() changeWalkDir(SouthWest, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad4', function() changeWalkDir(West, true) end, gameRootPanel, true)
g_keyboard.bindKeyUp('Numpad7', function() changeWalkDir(NorthWest, true) end, gameRootPanel, true)
g_keyboard.bindKeyPress('Up', function() smartWalk(North) end, gameRootPanel)
g_keyboard.bindKeyPress('Right', function() smartWalk(East) end, gameRootPanel)
g_keyboard.bindKeyPress('Down', function() smartWalk(South) end, gameRootPanel)
g_keyboard.bindKeyPress('Left', function() smartWalk(West) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad8', function() smartWalk(North) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad9', function() smartWalk(NorthEast) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad6', function() smartWalk(East) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad3', function() smartWalk(SouthEast) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad2', function() smartWalk(South) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad1', function() smartWalk(SouthWest) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad4', function() smartWalk(West) end, gameRootPanel)
g_keyboard.bindKeyPress('Numpad7', function() smartWalk(NorthWest) end, gameRootPanel)
bindWalkKey('Up', North)
bindWalkKey('Right', East)
bindWalkKey('Down', South)
bindWalkKey('Left', West)
bindWalkKey('Numpad8', North)
bindWalkKey('Numpad9', NorthEast)
bindWalkKey('Numpad6', East)
bindWalkKey('Numpad3', SouthEast)
bindWalkKey('Numpad2', South)
bindWalkKey('Numpad1', SouthWest)
bindWalkKey('Numpad4', West)
bindWalkKey('Numpad7', NorthWest)
g_keyboard.bindKeyPress('Ctrl+Up', function() g_game.turn(North) changeWalkDir(North) end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+Right', function() g_game.turn(East) changeWalkDir(East) end, gameRootPanel)
@@ -104,12 +82,24 @@ function bindKeys()
g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+=', function() gameMapPanel:zoomIn() end, gameRootPanel)
g_keyboard.bindKeyPress('Ctrl+-', function() gameMapPanel:zoomOut() end, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+Q', logout, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+L', logout, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+Q', function() tryLogout(false) end, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+L', function() tryLogout(false) end, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+W', function() g_map.cleanTexts() modules.game_textmessage.clearMessages() end, gameRootPanel)
g_keyboard.bindKeyDown('Ctrl+.', nextViewMode, gameRootPanel)
end
function bindWalkKey(key, dir)
g_keyboard.bindKeyDown(key, function() changeWalkDir(dir) end, gameRootPanel, true)
g_keyboard.bindKeyUp(key, function() changeWalkDir(dir, true) end, gameRootPanel, true)
g_keyboard.bindKeyPress(key, function() smartWalk(dir) end, gameRootPanel)
end
function unbindWalkKey(key)
g_keyboard.unbindKeyDown(key, gameRootPanel)
g_keyboard.unbindKeyUp(key, gameRootPanel)
g_keyboard.unbindKeyPress(key, gameRootPanel)
end
function terminate()
save()
hide()
@@ -137,16 +127,6 @@ function onGameStart()
else
g_game.disableFeature(GameForceFirstAutoWalkStep)
end
addEvent(function()
if not limitZoom or g_game.isGM() then
gameMapPanel:setMaxZoomOut(513)
gameMapPanel:setLimitVisibleRange(false)
else
gameMapPanel:setMaxZoomOut(11)
gameMapPanel:setLimitVisibleRange(true)
end
end)
end
function onGameEnd()
@@ -163,6 +143,16 @@ function show()
setupViewMode(0)
updateStretchShrink()
logoutButton:setTooltip(tr('Logout'))
addEvent(function()
if not limitedZoom or g_game.isGM() then
gameMapPanel:setMaxZoomOut(513)
gameMapPanel:setLimitVisibleRange(false)
else
gameMapPanel:setMaxZoomOut(11)
gameMapPanel:setLimitVisibleRange(true)
end
end)
end
function hide()
@@ -215,8 +205,8 @@ function tryExit()
return true
end
local exitFunc = function() logout() forceExit() end
local logoutFunc = function() logout() exitWindow:destroy() exitWindow = nil end
local exitFunc = function() g_game.safeLogout() forceExit() end
local logoutFunc = function() g_game.safeLogout() exitWindow:destroy() exitWindow = nil end
local cancelFunc = function() exitWindow:destroy() exitWindow = nil end
exitWindow = displayGeneralBox(tr('Exit'), tr("If you shut down the program, your character might stay in the game.\nClick on 'Logout' to ensure that you character leaves the game properly.\nClick on 'Exit' if you want to exit the program without logging out your character."),
@@ -228,14 +218,10 @@ function tryExit()
return true
end
function logout()
if g_game.isOnline() then
g_game.safeLogout()
return true
function tryLogout(prompt)
if type(prompt) ~= "boolean" then
prompt = true
end
end
function tryLogout()
if not g_game.isOnline() then
exit()
return
@@ -245,13 +231,42 @@ function tryLogout()
return
end
local yesCallback = function() logout() logoutWindow:destroy() logoutWindow=nil end
local noCallback = function() logoutWindow:destroy() logoutWindow=nil end
local msg, yesCallback
if not g_game.isConnectionOk() then
msg = 'Your connection is failing, if you logout now your character will be still online, do you want to force logout?'
logoutWindow = displayGeneralBox(tr('Logout'), tr('Are you sure you want to logout?'), {
yesCallback = function()
g_game.forceLogout()
if logoutWindow then
logoutWindow:destroy()
logoutWindow=nil
end
end
else
msg = 'Are you sure you want to logout?'
yesCallback = function()
g_game.safeLogout()
if logoutWindow then
logoutWindow:destroy()
logoutWindow=nil
end
end
end
local noCallback = function()
logoutWindow:destroy()
logoutWindow=nil
end
if prompt then
logoutWindow = displayGeneralBox(tr('Logout'), tr(msg), {
{ text=tr('Yes'), callback=yesCallback },
{ text=tr('No'), callback=noCallback },
anchor=AnchorHorizontalCenter}, yesCallback, noCallback)
else
yesCallback()
end
end
function stopSmartWalk()
@@ -463,7 +478,9 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
end
else
local localPosition = localPlayer:getPosition()
if not classic then shortcut = '(Alt)' else shortcut = nil end
if creatureThing:getPosition().z == localPosition.z then
if g_game.getAttackingCreature() ~= creatureThing then
menu:addOption(tr('Attack'), function() g_game.attack(creatureThing) end, shortcut)
else
@@ -475,6 +492,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
else
menu:addOption(tr('Stop Follow'), function() g_game.cancelFollow() end)
end
end
if creatureThing:isPlayer() then
menu:addSeparator()
@@ -519,7 +537,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
end
end
if modules.game_ruleviolation.hasWindowAccess() then
if modules.game_ruleviolation.hasWindowAccess() and creatureThing:isPlayer() then
menu:addSeparator()
menu:addOption(tr('Rule Violation'), function() modules.game_ruleviolation.show(creatureThing:getName()) end)
end
@@ -531,7 +549,7 @@ function createThingMenu(menuPosition, lookThing, useThing, creatureThing)
menu:display(menuPosition)
end
function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing)
function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, attackCreature)
local keyboardModifiers = g_keyboard.getModifiers()
if not modules.client_options.getOption('classicControl') then
@@ -557,7 +575,10 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
return true
end
return true
elseif creatureThing and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
elseif attackCreature and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
g_game.attack(attackCreature)
return true
elseif creatureThing and creatureThing:getPosition().z == autoWalkPos.z and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
g_game.attack(creatureThing)
return true
end
@@ -566,7 +587,10 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
else
if useThing and keyboardModifiers == KeyboardNoModifier and mouseButton == MouseRightButton and not g_mouse.isPressed(MouseLeftButton) then
local player = g_game.getLocalPlayer()
if creatureThing and creatureThing ~= player then
if attackCreature and attackCreature ~= player then
g_game.attack(attackCreature)
return true
elseif creatureThing and creatureThing ~= player and creatureThing:getPosition().z == autoWalkPos.z then
g_game.attack(creatureThing)
return true
elseif useThing:isContainer() then
@@ -594,7 +618,10 @@ function processMouseAction(menuPosition, mouseButton, autoWalkPos, lookThing, u
elseif useThing and keyboardModifiers == KeyboardCtrlModifier and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
createThingMenu(menuPosition, lookThing, useThing, creatureThing)
return true
elseif creatureThing and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
elseif attackCreature and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
g_game.attack(attackCreature)
return true
elseif creatureThing and creatureThing:getPosition().z == autoWalkPos.z and g_keyboard.isAltPressed() and (mouseButton == MouseLeftButton or mouseButton == MouseRightButton) then
g_game.attack(creatureThing)
return true
end
@@ -655,6 +682,8 @@ function moveStackableItem(item, toPos)
end
g_keyboard.bindKeyPress("Up", function() check() spinbox:up() end, spinbox)
g_keyboard.bindKeyPress("Down", function() check() spinbox:down() end, spinbox)
g_keyboard.bindKeyPress("Right", function() check() spinbox:up() end, spinbox)
g_keyboard.bindKeyPress("Left", function() check() spinbox:down() end, spinbox)
g_keyboard.bindKeyPress("PageUp", function() check() spinbox:setValue(spinbox:getValue()+10) end, spinbox)
g_keyboard.bindKeyPress("PageDown", function() check() spinbox:setValue(spinbox:getValue()-10) end, spinbox)
@@ -746,8 +775,7 @@ function setupViewMode(mode)
gameMapPanel:setZoom(11)
gameMapPanel:setVisibleDimension({ width = 15, height = 11 })
elseif mode == 2 then
local limit = limitZoom and not g_game.isGM()
gameMapPanel:setKeepAspectRatio(false)
local limit = limitedZoom and not g_game.isGM()
gameMapPanel:setLimitVisibleRange(limit)
gameMapPanel:setZoom(11)
gameMapPanel:setVisibleDimension({ width = 15, height = 11 })
@@ -772,5 +800,5 @@ function setupViewMode(mode)
end
function limitZoom()
limitZoom = true
limitedZoom = true
end

View File

@@ -6,11 +6,11 @@ CountWindow < MainWindow
SpinBox
id: spinBox
anchors.left: parent.left
anchors.top: parent.bottom
width: 0
height: 0
anchors.top: parent.top
width: 1
height: 1
phantom: true
padding-bottom: -40
margin-top: 2
focusable: true
Item

View File

@@ -78,6 +78,7 @@ function UIGameMap:onMouseRelease(mousePosition, mouseButton)
local useThing
local creatureThing
local multiUseThing
local attackCreature
local tile = self:getTile(mousePosition)
if tile then
@@ -86,7 +87,12 @@ function UIGameMap:onMouseRelease(mousePosition, mouseButton)
creatureThing = tile:getTopCreature()
end
local ret = modules.game_interface.processMouseAction(mousePosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing)
local autoWalkTile = g_map.getTile(autoWalkPos)
if autoWalkTile then
attackCreature = autoWalkTile:getTopCreature()
end
local ret = modules.game_interface.processMouseAction(mousePosition, mouseButton, autoWalkPos, lookThing, useThing, creatureThing, attackCreature)
if ret then
self.allowNextRelease = false
end

View File

@@ -86,7 +86,7 @@ function UIItem:onMouseRelease(mousePosition, mouseButton)
g_game.look(item)
self.cancelNextRelease = true
return true
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, item) then
elseif modules.game_interface.processMouseAction(mousePosition, mouseButton, nil, item, item, nil, nil) then
return true
end
return false

View File

@@ -1,71 +1,117 @@
modalDialog = nil
function init()
g_ui.importStyle('modaldialog')
connect(g_game, { onModalDialog = onModalDialog,
onGameEnd = destroy })
onGameEnd = destroyDialog })
local dialog = rootWidget:recursiveGetChildById('modalDialog')
if dialog then
modalDialog = dialog
end
end
function terminate()
disconnect(g_game, { onModalDialog = onModalDialog,
onGameEnd = destroy })
destroy()
onGameEnd = destroyDialog })
end
function destroy()
function destroyDialog()
if modalDialog then
modalDialog:destroy()
modalDialog = nil
end
end
function onModalDialog(id, title, message, enterId, enterText, escapeId, escapeText, choices)
if modalDialog then return end
function onModalDialog(id, title, message, buttons, enterButton, escapeButton, choices, priority)
-- priority parameter is unused, not sure what its use is.
if modalDialog then
return
end
modalDialog = g_ui.createWidget('ModalDialog', rootWidget)
local enterButton = modalDialog:getChildById('enterButton')
local escapeButton = modalDialog:getChildById('escapeButton')
local messageLabel = modalDialog:getChildById('messageLabel')
local choiceList = modalDialog:getChildById('choiceList')
local choiceScrollbar = modalDialog:getChildById('choiceScrollBar')
local buttonList = modalDialog:getChildById('buttonList')
modalDialog:setText(title)
messageLabel:setText(message)
enterButton:setText(enterText)
escapeButton:setText(escapeText)
local focusLabel = nil
for k, v in pairs(choices) do
local choiceId = v[1]
local choiceName = v[2]
local horizontalPadding = modalDialog:getPaddingLeft() + modalDialog:getPaddingRight()
modalDialog:setWidth(math.min(modalDialog.maximumWidth, math.max(messageLabel:getWidth(), modalDialog.minimumWidth)))
messageLabel:setWidth(math.min(modalDialog.maximumWidth, math.max(messageLabel:getWidth(), modalDialog.minimumWidth)) - horizontalPadding)
local labelHeight = nil
for i = 1, #choices do
local choiceId = choices[i][1]
local choiceName = choices[i][2]
local label = g_ui.createWidget('ChoiceListLabel', choiceList)
label.choiceId = choiceId
label:setText(choiceName)
label:setPhantom(false)
if not labelHeight then
labelHeight = label:getHeight()
end
end
choiceList:focusNextChild()
if not focusLabel then
focusLabel = label
for i = 1, #buttons do
local buttonId = buttons[i][1]
local buttonText = buttons[i][2]
local button = g_ui.createWidget('ModalButton', buttonList)
button:setText(buttonText)
button.onClick = function(self)
local focusedChoice = choiceList:getFocusedChild()
local choice = 0xFF
if focusedChoice then
choice = focusedChoice.choiceId
end
g_game.answerModalDialog(id, buttonId, choice)
destroyDialog()
end
end
choiceList:focusChild(focusLabel)
local additionalHeight = 0
if #choices > 0 then
choiceList:setVisible(true)
choiceScrollbar:setVisible(true)
additionalHeight = math.min(modalDialog.maximumChoices, math.max(modalDialog.minimumChoices, #choices)) * labelHeight
additionalHeight = additionalHeight + choiceList:getPaddingTop() + choiceList:getPaddingBottom()
end
modalDialog:setHeight(modalDialog:getHeight() + additionalHeight)
addEvent(function()
modalDialog:setHeight(modalDialog:getHeight() + messageLabel:getHeight() - 14)
end)
local enterFunc = function()
g_game.answerModalDialog(id, enterId, choiceList:getFocusedChild().choiceId)
destroy()
local focusedChoice = choiceList:getFocusedChild()
local choice = 0xFF
if focusedChoice then
choice = focusedChoice.choiceId
end
g_game.answerModalDialog(id, enterButton, choice)
destroyDialog()
end
local escapeFunc = function()
g_game.answerModalDialog(id, escapeId, choiceList:getFocusedChild().choiceId)
destroy()
local focusedChoice = choiceList:getFocusedChild()
local choice = 0xFF
if focusedChoice then
choice = focusedChoice.choiceId
end
g_game.answerModalDialog(id, escapeButton, choice)
destroyDialog()
end
choiceList.onDoubleClick = enterFunc
enterButton.onClick = enterFunc
modalDialog.onEnter = enterFunc
escapeButton.onClick = escapeFunc
modalDialog.onEscape = escapeFunc
return
end

View File

@@ -8,52 +8,61 @@ ChoiceListLabel < Label
background-color: #ffffff22
color: #ffffff
ModalDialog < MainWindow
id: modalDialog
!text: tr('Title')
size: 280 230
@onEscape: self:destroy()
Label
id: messageLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text-align: left
text: Message
height: 60
TextList
ChoiceList < TextList
id: choiceList
vertical-scrollbar: choiceScrollBar
anchors.fill: parent
anchors.top: prev.bottom
anchors.bottom: next.top
margin-bottom: 10
margin-top: 10
padding: 1
margin-top: 4
margin-bottom: 10
focusable: false
visible: false
Button
id: enterButton
!text: tr('Ok')
anchors.top: next.top
anchors.right: next.left
margin-right: 8
width: 60
Button
id: escapeButton
!text: tr('Cancel')
anchors.bottom: parent.bottom
anchors.right: parent.right
margin-top: 10
width: 60
VerticalScrollBar
ChoiceScrollBar < VerticalScrollBar
id: choiceScrollBar
anchors.top: choiceList.top
anchors.bottom: choiceList.bottom
anchors.right: choiceList.right
step: 14
pixels-scroll: true
visible: false
ModalButton < Button
width: 60
margin: 2
ModalDialog < MainWindow
id: modalDialog
size: 280 97
&minimumWidth: 200
&maximumWidth: 500
&minimumChoices: 4
&maximumChoices: 10
Label
id: messageLabel
anchors.top: parent.top
anchors.left: parent.left
text-align: left
text-auto-resize: true
text-wrap: true
ChoiceList
HorizontalSeparator
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: next.top
Panel
id: buttonList
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 24
layout: horizontalBox
align-right: true
ChoiceScrollBar

View File

@@ -151,8 +151,7 @@ function onTradeTypeChange(radioTabs, selected, deselected)
ignoreCapacity:setVisible(currentTradeType == BUY)
ignoreEquipped:setVisible(currentTradeType == SELL)
showAllItems:setVisible(currentTradeType == SELL)
sellAllButton:setVisible(false)
--sellAllButton:setVisible(currentTradeType == SELL)
sellAllButton:setVisible(currentTradeType == SELL)
refreshTradeItems()
refreshPlayerGoods()

View File

@@ -252,7 +252,6 @@ MainWindow
margin-right: 10
visible: false
@onClick: modules.game_npctrade.sellAll()
visible: false
Button
id: tradeButton

View File

@@ -94,7 +94,7 @@ function create(creatureOutfit, outfitList, creatureMount, mountList)
addon.widget.onCheckChange = function(self) onAddonCheckChange(self, addon.value) end
end
if outfit.addons > 0 then
if outfit.addons and outfit.addons > 0 then
for _, i in pairs(ADDON_SETS[outfit.addons]) do
addons[i].widget:setChecked(true)
end

View File

@@ -17,6 +17,7 @@ end
function reset()
if deathWindow then
deathWindow:destroy()
deathWindow = nil
end
end
@@ -47,7 +48,7 @@ function openWindow()
deathWindow = nil
end
local cancelFunc = function()
modules.game_interface.logout()
g_game.safeLogout()
cancelButton:getParent():destroy()
deathWindow = nil
end

View File

@@ -43,7 +43,7 @@ function init()
reasonsTextList = ruleViolationWindow:getChildById('reasonList')
actionsTextList = ruleViolationWindow:getChildById('actionList')
g_keyboard.bindKeyDown('Ctrl+Y', show)
g_keyboard.bindKeyDown('Ctrl+Y', function() show() end)
if g_game.isOnline() then
loadReasons()
@@ -63,6 +63,7 @@ end
function loadReasons()
reasonsTextList:destroyChildren()
actionsTextList:destroyChildren()
local actions = g_game.getGMActions()
for reason, actionFlags in pairs(actions) do
@@ -100,7 +101,7 @@ function onSelectReason(reasonLabel, focused)
if reasonLabel.actionFlags and focused then
actionsTextList:destroyChildren()
for actionBaseFlag = 0, #rvactions do
actionFlagString = rvactions[actionBaseFlag]
local actionFlagString = rvactions[actionBaseFlag]
if bit32.band(reasonLabel.actionFlags, math.pow(2, actionBaseFlag)) > 0 then
local label = g_ui.createWidget('RVListLabel', actionsTextList)
label:setText(actionFlagString)
@@ -111,9 +112,21 @@ function onSelectReason(reasonLabel, focused)
end
function report()
local reasonLabel = reasonsTextList:getFocusedChild()
if not reasonLabel then
displayErrorBox(tr("Error"), tr("You must select a reason."))
return
end
local actionLabel = actionsTextList:getFocusedChild()
if not actionLabel then
displayErrorBox(tr("Error"), tr("You must select an action."))
return
end
local target = ruleViolationWindow:getChildById('nameText'):getText()
local reason = reasonsTextList:getFocusedChild().reasonId
local action = actionsTextList:getFocusedChild().actionId
local reason = reasonLabel.reasonId
local action = actionLabel.actionId
local comment = ruleViolationWindow:getChildById('commentText'):getText()
local statement = ruleViolationWindow:getChildById('statementText'):getText()
local statementId = 0 -- TODO: message unique id ?

View File

@@ -28,6 +28,8 @@ MessageTypes = {
[MessageModes.Blue] = MessageSettings.consoleBlue,
[MessageModes.PrivateFrom] = MessageSettings.consoleBlue,
[MessageModes.GamemasterBroadcast] = MessageSettings.consoleRed,
[MessageModes.DamageDealed] = MessageSettings.status,
[MessageModes.DamageReceived] = MessageSettings.status,
[MessageModes.Heal] = MessageSettings.status,

View File

@@ -23,7 +23,7 @@ function destroyWindows()
windows = {}
end
function onGameEditText(id, itemId, maxLength, text, writter, time)
function onGameEditText(id, itemId, maxLength, text, writer, time)
local textWindow = g_ui.createWidget('TextWindow', rootWidget)
local writeable = #text < maxLength and maxLength > 0
@@ -42,8 +42,8 @@ function onGameEditText(id, itemId, maxLength, text, writter, time)
textEdit:setCursorVisible(writeable)
local desc = ''
if #writter > 0 then
desc = tr('You read the following, written by \n%s\n', writter)
if #writer > 0 then
desc = tr('You read the following, written by \n%s\n', writer)
if #time > 0 then
desc = desc .. tr('on %s.\n', time)
end

View File

@@ -18,7 +18,7 @@ function isLoaded()
end
function load()
local version = g_game.getProtocolVersion()
local version = g_game.getClientVersion()
local datPath, sprPath
if filename then

View File

@@ -88,6 +88,23 @@ function isHiddingOffline()
return settings['hideOffline']
end
function getSortedBy()
local settings = g_settings.getNode('VipList')
if not settings then
return 'status'
end
return settings['sortedBy']
end
function sortBy(state)
settings = {}
settings['sortedBy'] = state
g_settings.mergeNode('VipList', settings)
refresh()
end
function onAddVip(id, name, state)
local vipList = vipWindow:getChildById('contentsPanel')
@@ -118,13 +135,13 @@ function onAddVip(id, name, state)
for i=1,childrenCount do
local child = vipList:getChildByIndex(i)
if state == VipState.Online and child.vipState ~= VipState.Online then
if state == VipState.Online and child.vipState ~= VipState.Online and getSortedBy() == 'status' then
vipList:insertChild(i, label)
return
end
if (state ~= VipState.Online and child.vipState ~= VipState.Online)
or (state == VipState.Online and child.vipState == VipState.Online) then
if ((state ~= VipState.Online and child.vipState ~= VipState.Online)
or (state == VipState.Online and child.vipState == VipState.Online)) or getSortedBy() == 'name' then
local childText = child:getText():lower()
local length = math.min(childText:len(), nameLower:len())
@@ -168,6 +185,14 @@ function onVipListMousePress(widget, mousePos, mouseButton)
menu:addOption(tr('Show Offline'), function() hideOffline(false) end)
end
if not(getSortedBy() == 'name') then
menu:addOption(tr('Sort by name'), function() sortBy('name') end)
end
if not(getSortedBy() == 'status') then
menu:addOption(tr('Sort by status'), function() sortBy('status') end)
end
menu:display(mousePos)
return true
@@ -196,6 +221,15 @@ function onVipListLabelMousePress(widget, mousePos, mouseButton)
else
menu:addOption(tr('Show Offline'), function() hideOffline(false) end)
end
if not(getSortedBy() == 'name') then
menu:addOption(tr('Sort by name'), function() sortBy('name') end)
end
if not(getSortedBy() == 'status') then
menu:addOption(tr('Sort by status'), function() sortBy('status') end)
end
menu:display(mousePos)
return true

View File

@@ -22,11 +22,14 @@ ShieldBlueNoSharedExpBlink = 7
ShieldYellowNoSharedExpBlink = 8
ShieldBlueNoSharedExp = 9
ShieldYellowNoSharedExp = 10
ShieldGray = 11
EmblemNone = 0
EmblemGreen = 1
EmblemRed = 2
EmblemBlue = 3
EmblemMember = 4
EmblemOther = 5
North = 0
East = 1
@@ -44,6 +47,11 @@ FightDefensive = 3
DontChase = 0
ChaseOpponent = 1
PVPWhiteDove = 0
PVPWhiteHand = 1
PVPYellowHand = 2
PVPRedFist = 3
GameProtocolChecksum = 1
GameAccountNames = 2
GameChallengeOnLogin = 3
@@ -81,6 +89,17 @@ GameNewSpeedLaw = 36
GameForceFirstAutoWalkStep = 37
GameMinimapRemove = 38
GameDoubleShopSellAmount = 39
GameContainerPagination = 40
GameThingMarks = 41
GameLooktypeU16 = 42
GamePlayerStamina = 43
GamePlayerAddons = 44
GameMessageStatements = 45
GameMesssageLevel = 46
GameNewFluids = 47
GamePlayerStateU16 = 48
GameNewOutfitProtocol = 49
GamePVPMode = 50
TextColors = {
red = '#f55e5e', --'#c83200'
@@ -162,7 +181,7 @@ CIPSOFT_RSA = "1321277432058722840622950990822933849527763264961655079678763618"
"88792221429527047321331896351555606801473202394175817"
-- set to the latest Tibia.pic signature to make otclient compatible with official tibia
PIC_SIGNATURE = 0x50a6469d
PIC_SIGNATURE = 0x52131b61
OsTypes = {
Linux = 1,

View File

@@ -52,23 +52,25 @@ function getShieldImagePathAndBlink(shieldId)
if shieldId == ShieldWhiteYellow then
path, blink = '/images/game/shields/shield_yellow_white', false
elseif shieldId == ShieldWhiteBlue then
path, blink = '/images/game/shields//shield_blue_white', false
path, blink = '/images/game/shields/shield_blue_white', false
elseif shieldId == ShieldBlue then
path, blink = '/images/game/shields//shield_blue', false
path, blink = '/images/game/shields/shield_blue', false
elseif shieldId == ShieldYellow then
path, blink = '/images/game/shields//shield_yellow', false
path, blink = '/images/game/shields/shield_yellow', false
elseif shieldId == ShieldBlueSharedExp then
path, blink = '/images/game/shields//shield_blue_shared', false
path, blink = '/images/game/shields/shield_blue_shared', false
elseif shieldId == ShieldYellowSharedExp then
path, blink = '/images/game/shields//shield_yellow_shared', false
path, blink = '/images/game/shields/shield_yellow_shared', false
elseif shieldId == ShieldBlueNoSharedExpBlink then
path, blink = '/images/game/shields//shield_blue_not_shared', true
path, blink = '/images/game/shields/shield_blue_not_shared', true
elseif shieldId == ShieldYellowNoSharedExpBlink then
path, blink = '/images/game/shields//shield_yellow_not_shared', true
path, blink = '/images/game/shields/shield_yellow_not_shared', true
elseif shieldId == ShieldBlueNoSharedExp then
path, blink = '/images/game/shields//shield_blue_not_shared', false
path, blink = '/images/game/shields/shield_blue_not_shared', false
elseif shieldId == ShieldYellowNoSharedExp then
path, blink = '/images/game/shields//shield_yellow_not_shared', false
path, blink = '/images/game/shields/shield_yellow_not_shared', false
elseif shieldId == ShieldGray then
path, blink = '/images/game/shields/shield_gray', false
end
return path, blink
end
@@ -81,6 +83,10 @@ function getEmblemImagePath(emblemId)
path = '/images/game/emblems/emblem_red'
elseif emblemId == EmblemBlue then
path = '/images/game/emblems/emblem_blue'
elseif emblemId == EmblemMember then
path = '/images/game/emblems/emblem_member'
elseif emblemId == EmblemOther then
path = '/images/game/emblems/emblem_other'
end
return path
end

View File

@@ -4,9 +4,23 @@ function g_game.getRsa()
return currentRsa
end
function g_game.findPlayerItem(itemId, subType)
local localPlayer = g_game.getLocalPlayer()
if localPlayer then
for slot = InventorySlotFirst, InventorySlotLast do
local item = localPlayer:getInventoryItem(slot)
if item and item:getId() == itemId and (subType == -1 or item:getSubType() == subType) then
return item
end
end
end
return g_game.findItemInContainers(itemId, subType)
end
function g_game.chooseRsa(host)
if currentRsa ~= CIPSOFT_RSA and currentRsa ~= OTSERV_RSA then return end
if string.ends(host, '.tibia.com') or string.ends(host, '.cipsoft.com') then
if host:ends('.tibia.com') or host:ends('.cipsoft.com') then
g_game.setRsa(CIPSOFT_RSA)
if g_app.getOs() == 'windows' then
@@ -32,22 +46,31 @@ function g_game.isOfficialTibia()
return currentRsa == CIPSOFT_RSA
end
function g_game.getSupportedProtocols()
function g_game.getSupportedClients()
return {
810, 811, 840, 842, 850, 853, 854,
860, 861, 862, 870, 910, 940, 944,
953, 954, 960, 961, 963, 970, 971,
973, 974
760, 770, 772, 780, 781, 782, 790,
792, 800, 810, 811, 840, 842, 850,
853, 854, 860, 861, 862, 870, 910,
940, 944, 953, 954, 960, 961, 963,
970, 980, 981, 982, 983, 984, 985,
986, 1001, 1002, 1010, 1020, 1021,
1022, 1031
}
end
function g_game.getSupportedClients(protocol)
function g_game.getProtocolVersionForClient(client)
clients = {
[971] = {980},
[973] = {981},
[974] = {982}
[980] = 971,
[981] = 973,
[982] = 974,
[983] = 975,
[984] = 976,
[985] = 977,
[986] = 978,
[1001] = 979,
[1002] = 980,
}
return clients[protocol] or {protocol}
return clients[client] or client
end
g_game.setRsa(OTSERV_RSA)

View File

@@ -27,9 +27,9 @@ end
function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOs())
msg:addU16(g_game.getProtocolVersion())
if g_game.getProtocolVersion() >= 971 then
@@ -46,9 +46,9 @@ function ProtocolLogin:sendLoginPacket()
local offset = msg:getMessageSize()
if g_game.getProtocolVersion() >= 770 then
-- first RSA byte must be 0
msg:addU8(0)
-- xtea key
self:generateXteaKey()
local xteaKey = self:getXteaKey()
@@ -56,6 +56,7 @@ function ProtocolLogin:sendLoginPacket()
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4])
end
if g_game.getFeature(GameAccountNames) then
msg:addString(self.accountName)
@@ -73,14 +74,18 @@ function ProtocolLogin:sendLoginPacket()
local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0)
msg:addPaddingBytes(paddingBytes, 0)
if g_game.getProtocolVersion() >= 770 then
msg:encryptRsa()
end
if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum()
end
self:send(msg)
if g_game.getProtocolVersion() >= 770 then
self:enableXteaEncryption()
end
self:recv()
end
@@ -125,6 +130,33 @@ end
function ProtocolLogin:parseCharacterList(msg)
local characters = {}
if g_game.getProtocolVersion() > 1010 then
local worlds = {}
local worldsCount = msg:getU8()
for i=1, worldsCount do
local world = {}
local worldId = msg:getU8()
world.worldName = msg:getString()
world.worldIp = msg:getString()
world.worldPort = msg:getU16()
msg:getU8() -- unknow byte?
worlds[worldId] = world
end
local charactersCount = msg:getU8()
for i=1, charactersCount do
local character = {}
local worldId = msg:getU8()
character.name = msg:getString()
character.worldName = worlds[worldId].worldName
character.worldIp = worlds[worldId].worldIp
character.worldPort = worlds[worldId].worldPort
characters[i] = character
end
else
local charactersCount = msg:getU8()
for i=1,charactersCount do
local character = {}
@@ -139,6 +171,7 @@ function ProtocolLogin:parseCharacterList(msg)
characters[i] = character
end
end
local account = {}
account.premDays = msg:getU16()

View File

@@ -273,11 +273,20 @@ function UIMinimap:createFlagWindow(pos)
flagRadioGroup:selectWidget(flagRadioGroup:getFirstWidget())
okButton.onClick = function()
local successFunc = function()
self:addFlag(pos, flagRadioGroup:getSelectedWidget().icon, description:getText())
self:destroyFlagWindow()
end
cancelButton.onClick = function() self:destroyFlagWindow() end
local cancelFunc = function()
self:destroyFlagWindow()
end
okButton.onClick = successFunc
cancelButton.onClick = cancelFunc
self.flagWindow.onEnter = successFunc
self.flagWindow.onEscape = cancelFunc
self.flagWindow.onDestroy = function() flagRadioGroup:destroy() end
end

View File

@@ -2,3 +2,4 @@
-- you can place any custom user code here
print 'Startup done :]'

View File

@@ -100,6 +100,8 @@ set(client_SOURCES ${client_SOURCES}
${CMAKE_CURRENT_LIST_DIR}/uiprogressrect.h
${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.cpp
${CMAKE_CURRENT_LIST_DIR}/uimapanchorlayout.h
${CMAKE_CURRENT_LIST_DIR}/uisprite.cpp
${CMAKE_CURRENT_LIST_DIR}/uisprite.h
# util
${CMAKE_CURRENT_LIST_DIR}/position.h

View File

@@ -206,6 +206,13 @@ namespace Otc
ChaseOpponent = 1
};
enum PVPModes {
WhiteDove = 0,
WhiteHand = 1,
YellowHand = 2,
RedFist = 3
};
enum PlayerSkulls {
SkullNone = 0,
SkullYellow,
@@ -227,14 +234,17 @@ namespace Otc
ShieldBlueNoSharedExpBlink, // 7 party member sexp inactive guilty
ShieldYellowNoSharedExpBlink, // 8 // party leader sexp inactive guilty
ShieldBlueNoSharedExp, // 9 party member sexp inactive innocent
ShieldYellowNoSharedExp // 10 party leader sexp inactive innocent
ShieldYellowNoSharedExp, // 10 party leader sexp inactive innocent
ShieldGray // 11 member of another party
};
enum PlayerEmblems {
EmblemNone = 0,
EmblemGreen,
EmblemRed,
EmblemBlue
EmblemBlue,
EmblemMember,
EmblemOther
};
enum PlayerStates {
@@ -353,7 +363,19 @@ namespace Otc
GameForceFirstAutoWalkStep = 37,
GameMinimapRemove = 38,
GameDoubleShopSellAmount = 39,
// 51-100 reserved to be defined in lua
GameContainerPagination = 40,
GameThingMarks = 41,
GameLooktypeU16 = 42,
GamePlayerStamina = 43,
GamePlayerAddons = 44,
GameMessageStatements = 45,
GameMessageLevel = 46,
GameNewFluids = 47,
GamePlayerStateU16 = 48,
GameNewOutfitProtocol = 49,
GamePVPMode = 50,
GameWritableDate = 51,
LastGameFeature = 101
};

View File

@@ -23,7 +23,7 @@
#include "container.h"
#include "item.h"
Container::Container(int id, int capacity, const std::string& name, const ItemPtr& containerItem, bool hasParent)
Container::Container(int id, int capacity, const std::string& name, const ItemPtr& containerItem, bool hasParent, bool isUnlocked, bool hasPages, int containerSize, int firstIndex)
{
m_id = id;
m_capacity = capacity;
@@ -31,6 +31,10 @@ Container::Container(int id, int capacity, const std::string& name, const ItemPt
m_containerItem = containerItem;
m_hasParent = hasParent;
m_closed = false;
m_unlocked = isUnlocked;
m_hasPages = hasPages;
m_size = containerSize;
m_firstIndex = firstIndex;
}
ItemPtr Container::getItem(int slot)
@@ -51,12 +55,20 @@ void Container::onClose()
callLuaField("onClose");
}
void Container::onAddItem(const ItemPtr& item)
void Container::onAddItem(const ItemPtr& item, int slot)
{
m_items.push_front(item);
updateItemsPositions();
callLuaField("onAddItem", 0, item);
callLuaField("onAddItem", slot, item);
}
ItemPtr Container::findItemById(uint itemId, int subType)
{
for(const ItemPtr item : m_items)
if(item->getId() == itemId && (subType == -1 || item->getSubType() == subType))
return item;
return nullptr;
}
void Container::onAddItems(const std::vector<ItemPtr>& items)

View File

@@ -32,7 +32,7 @@
class Container : public LuaObject
{
protected:
Container(int id, int capacity, const std::string& name, const ItemPtr& containerItem, bool hasParent);
Container(int id, int capacity, const std::string& name, const ItemPtr& containerItem, bool hasParent, bool isUnlocked, bool hasPages, int containerSize, int firstIndex);
public:
ItemPtr getItem(int slot);
@@ -45,11 +45,16 @@ public:
std::string getName() { return m_name; }
bool hasParent() { return m_hasParent; }
bool isClosed() { return m_closed; }
bool isUnlocked() { return m_unlocked; }
bool hasPages() { return m_hasPages; }
int getSize() { return m_size; }
int getFirstIndex() { return m_firstIndex; }
ItemPtr findItemById(uint itemId, int subType);
protected:
void onOpen(const ContainerPtr& previousContainer);
void onClose();
void onAddItem(const ItemPtr& item);
void onAddItem(const ItemPtr& item, int slot);
void onAddItems(const std::vector<ItemPtr>& items);
void onUpdateItem(int slot, const ItemPtr& item);
void onRemoveItem(int slot);
@@ -65,6 +70,10 @@ private:
std::string m_name;
bool m_hasParent;
bool m_closed;
bool m_unlocked;
bool m_hasPages;
int m_size;
int m_firstIndex;
std::deque<ItemPtr> m_items;
};

View File

@@ -70,13 +70,13 @@ void Creature::draw(const Point& dest, float scaleFactor, bool animate, LightVie
if(m_showTimedSquare && animate) {
g_painter->setColor(m_timedSquareColor);
g_painter->drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 2)*scaleFactor, Size(28, 28)*scaleFactor), std::max((int)(2*scaleFactor), 1));
g_painter->drawBoundingRect(Rect(dest + (animationOffset - getDisplacement() + 2)*scaleFactor, Size(28, 28)*scaleFactor), std::max<int>((int)(2*scaleFactor), 1));
g_painter->setColor(Color::white);
}
if(m_showStaticSquare && animate) {
g_painter->setColor(m_staticSquareColor);
g_painter->drawBoundingRect(Rect(dest + (animationOffset - getDisplacement())*scaleFactor, Size(Otc::TILE_PIXELS, Otc::TILE_PIXELS)*scaleFactor), std::max((int)(2*scaleFactor), 1));
g_painter->drawBoundingRect(Rect(dest + (animationOffset - getDisplacement())*scaleFactor, Size(Otc::TILE_PIXELS, Otc::TILE_PIXELS)*scaleFactor), std::max<int>((int)(2*scaleFactor), 1));
g_painter->setColor(Color::white);
}
@@ -128,7 +128,7 @@ void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWal
dest -= datType->getDisplacement() * scaleFactor;
datType->draw(dest, scaleFactor, 0, xPattern, 0, 0, animationPhase, lightView);
dest += getDisplacement() * scaleFactor;
zPattern = 1;
zPattern = std::min<int>(1, getNumPatternZ() - 1);
}
PointF jumpOffset = m_jumpOffset * scaleFactor;
@@ -171,7 +171,7 @@ void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWal
// when creature is an effect we cant render the first and last animation phase,
// instead we should loop in the phases between
if(m_outfit.getCategory() == ThingCategoryEffect) {
animationPhases = std::max(1, animationPhases-2);
animationPhases = std::max<int>(1, animationPhases-2);
animateTicks = Otc::INVISIBLE_TICKS_PER_FRAME;
}
@@ -183,7 +183,7 @@ void Creature::internalDrawOutfit(Point dest, float scaleFactor, bool animateWal
}
if(m_outfit.getCategory() == ThingCategoryEffect)
animationPhase = std::min(animationPhase+1, animationPhases);
animationPhase = std::min<int>(animationPhase+1, animationPhases);
type->draw(dest - (getDisplacement() * scaleFactor), scaleFactor, 0, 0, 0, 0, animationPhase, lightView);
}
@@ -372,7 +372,7 @@ void Creature::updateJump()
int nextT, i = 1;
do {
nextT = stdext::round((-b + std::sqrt(std::max(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
nextT = stdext::round((-b + std::sqrt(std::max<int>(b*b + 4*a*(roundHeight+diff*i), 0.0)) * diff) / (2*a));
++i;
if(nextT < halfJumpDuration)
@@ -458,6 +458,11 @@ void Creature::updateWalkAnimation(int totalPixelsWalked)
int footAnimPhases = getAnimationPhases() - 1;
int footDelay = getStepDuration(true) / 3;
// Since mount is a different outfit we need to get the mount animation phases
if(m_outfit.getMount() != 0) {
ThingType *type = g_things.rawGetThingType(m_outfit.getMount(), m_outfit.getCategory());
footAnimPhases = type->getAnimationPhases() - 1;
}
if(footAnimPhases == 0)
m_walkAnimationPhase = 0;
else if(m_footStepDrawn && m_footTimer.ticksElapsed() >= footDelay && totalPixelsWalked < 32) {
@@ -475,7 +480,7 @@ void Creature::updateWalkAnimation(int totalPixelsWalked)
if(!self->m_walking || self->m_walkTimer.ticksElapsed() >= self->getStepDuration(true))
self->m_walkAnimationPhase = 0;
self->m_walkFinishAnimEvent = nullptr;
}, std::min(footDelay, 200));
}, std::min<int>(footDelay, 200));
}
}
@@ -548,10 +553,10 @@ void Creature::nextWalkUpdate()
void Creature::updateWalk()
{
float walkTicksPerPixel = getStepDuration(true) / 32;
int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
int totalPixelsWalked = std::min<int>(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
// needed for paralyze effect
m_walkedPixels = std::max(m_walkedPixels, totalPixelsWalked);
m_walkedPixels = std::max<int>(m_walkedPixels, totalPixelsWalked);
// update walk animation and offsets
updateWalkAnimation(totalPixelsWalked);
@@ -626,7 +631,7 @@ void Creature::setDirection(Otc::Direction direction)
void Creature::setOutfit(const Outfit& outfit)
{
Outfit oldOutfit = outfit;
Outfit oldOutfit = m_outfit;
if(outfit.getCategory() != ThingCategoryCreature) {
if(!g_things.isValidDatId(outfit.getAuxId(), outfit.getCategory()))
return;
@@ -815,7 +820,7 @@ int Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir)
if(g_game.getFeature(Otc::GameNewSpeedLaw) && hasSpeedFormula()) {
int formulatedSpeed = 1;
if(speed > -m_speedFormula[Otc::SpeedFormulaB]) {
formulatedSpeed = std::max(1, (int)floor((m_speedFormula[Otc::SpeedFormulaA] * log((speed / 2)
formulatedSpeed = std::max<int>(1, (int)floor((m_speedFormula[Otc::SpeedFormulaA] * log((speed / 2)
+ m_speedFormula[Otc::SpeedFormulaB]) + m_speedFormula[Otc::SpeedFormulaC]) + 0.5));
}
interval = std::floor(interval / (double)formulatedSpeed);
@@ -830,7 +835,7 @@ int Creature::getStepDuration(bool ignoreDiagonal, Otc::Direction dir)
if(g_game.getProtocolVersion() <= 810)
factor = 2;
interval = std::max(interval, g_game.getServerBeat());
interval = std::max<int>(interval, g_game.getServerBeat());
if(!ignoreDiagonal && (m_lastStepDirection == Otc::NorthWest || m_lastStepDirection == Otc::NorthEast ||
m_lastStepDirection == Otc::SouthWest || m_lastStepDirection == Otc::SouthEast))
@@ -882,7 +887,7 @@ int Creature::getExactSize(int layer, int xPattern, int yPattern, int zPattern,
continue;
for(layer = 0; layer < getLayers(); ++layer)
exactSize = std::max(exactSize, Thing::getExactSize(layer, xPattern, yPattern, zPattern, animationPhase));
exactSize = std::max<int>(exactSize, Thing::getExactSize(layer, xPattern, yPattern, zPattern, animationPhase));
}
return exactSize;

View File

@@ -50,9 +50,9 @@ void CreatureManager::terminate()
void Spawn::load(TiXmlElement* node)
{
Position centerPos;
centerPos.x = node->readType<uint16>("centerx");
centerPos.y = node->readType<uint16>("centery");
centerPos.z = node->readType<uint8>("centerz");
centerPos.x = node->readType<int>("centerx");
centerPos.y = node->readType<int>("centery");
centerPos.z = node->readType<int>("centerz");
setCenterPos(centerPos);
setRadius(node->readType<int32>("radius"));
@@ -65,6 +65,7 @@ void Spawn::load(TiXmlElement* node)
std::string cName = cNode->Attribute("name");
stdext::tolower(cName);
stdext::trim(cName);
stdext::ucwords(cName);
if (!(cType = g_creatures.getCreatureByName(cName)))
continue;
@@ -76,17 +77,18 @@ void Spawn::load(TiXmlElement* node)
dir = (Otc::Direction)dir_;
cType->setDirection(dir);
centerPos.x += cNode->readType<int>("x");
centerPos.y += cNode->readType<int>("y");
centerPos.z = cNode->readType<int>("z");
addCreature(centerPos, cType);
Position placePos;
placePos.x = centerPos.x + cNode->readType<int>("x");
placePos.y = centerPos.y + cNode->readType<int>("y");
placePos.z = cNode->readType<int>("z");
cType->setRace(cNode->ValueStr() == "npc" ? CreatureRaceNpc : CreatureRaceMonster);
addCreature(placePos, cType);
}
}
void Spawn::save(TiXmlElement*& node)
void Spawn::save(TiXmlElement* node)
{
node = new TiXmlElement("spawn");
const Position& c = getCenterPos();
node->SetAttribute("centerx", c.x);
node->SetAttribute("centery", c.y);
@@ -97,10 +99,9 @@ void Spawn::save(TiXmlElement*& node)
TiXmlElement* creatureNode = nullptr;
for(const auto& pair : m_creatures) {
if(!(creatureNode = new TiXmlElement("monster")))
stdext::throw_exception("oom?");
const CreatureTypePtr& creature = pair.second;
if(!(creatureNode = new TiXmlElement(creature->getRace() == CreatureRaceNpc ? "npc" : "monster")))
stdext::throw_exception("Spawn::save: Ran out of memory while allocating XML element! Terminating now.");
creatureNode->SetAttribute("name", creature->getName());
creatureNode->SetAttribute("spawntime", creature->getSpawnTime());
@@ -109,8 +110,8 @@ void Spawn::save(TiXmlElement*& node)
const Position& placePos = pair.first;
assert(placePos.isValid());
creatureNode->SetAttribute("x", placePos.x);
creatureNode->SetAttribute("y", placePos.y);
creatureNode->SetAttribute("x", placePos.x - c.x);
creatureNode->SetAttribute("y", placePos.y - c.y);
creatureNode->SetAttribute("z", placePos.z);
node->LinkEndChild(creatureNode);
@@ -121,11 +122,14 @@ void Spawn::addCreature(const Position& placePos, const CreatureTypePtr& cType)
{
const Position& centerPos = getCenterPos();
int m_radius = getRadius();
if(!isInZone(placePos, centerPos, m_radius))
stdext::throw_exception(stdext::format("cannot place creature at %s %s %d (increment radius)",
if(!isInZone(placePos, centerPos, m_radius)) {
g_logger.warning(stdext::format("cannot place creature at %s (spawn's center position: %s, spawn radius: %d) (increment radius)",
stdext::to_string(placePos), stdext::to_string(centerPos),
m_radius
));
return;
}
g_map.addThing(cType->cast(), placePos, 4);
m_creatures.insert(std::make_pair(placePos, cType));
}
@@ -145,6 +149,8 @@ CreaturePtr CreatureType::cast()
CreaturePtr ret(new Creature);
std::string cName = getName();
stdext::tolower(cName);
stdext::trim(cName);
stdext::ucwords(cName);
ret->setName(cName);
@@ -199,20 +205,12 @@ void CreatureManager::loadNpcs(const std::string& folder)
if(!stdext::ends_with(tmp, "/"))
tmp += "/";
// FIXME: filesystem is not supported anymore, rework the following code with g_resources
/*
boost::filesystem::path npcPath(boost::filesystem::current_path().generic_string() + tmp);
if(!boost::filesystem::exists(npcPath))
if(!g_resources.directoryExists(tmp))
stdext::throw_exception(stdext::format("NPCs folder '%s' was not found.", folder));
for(boost::filesystem::directory_iterator it(npcPath), end; it != end; ++it) {
std::string f = it->path().filename().string();
if(boost::filesystem::is_directory(it->status()))
continue;
loadCreatureBuffer(g_resources.readFileContents(tmp + f));
}
*/
const auto& fileList = g_resources.listDirectoryFiles(tmp);
for(const std::string& file : fileList)
loadCreatureBuffer(g_resources.readFileContents(tmp + file));
}
void CreatureManager::loadSpawns(const std::string& fileName)
@@ -227,6 +225,7 @@ void CreatureManager::loadSpawns(const std::string& fileName)
return;
}
try {
TiXmlDocument doc;
doc.Parse(g_resources.readFileContents(fileName).c_str());
if(doc.Error())
@@ -246,10 +245,14 @@ void CreatureManager::loadSpawns(const std::string& fileName)
}
doc.Clear();
m_spawnLoaded = true;
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to load '%s': %s", fileName, e.what()));
}
}
void CreatureManager::saveSpawns(const std::string& fileName)
{
try {
TiXmlDocument doc;
doc.SetTabSize(2);
@@ -260,13 +263,16 @@ void CreatureManager::saveSpawns(const std::string& fileName)
doc.LinkEndChild(root);
for(auto pair : m_spawns) {
TiXmlElement* elem;
TiXmlElement* elem = new TiXmlElement("spawn");
pair.second->save(elem);
root->LinkEndChild(elem);
}
if(!doc.SaveFile(fileName))
if(!doc.SaveFile("data"+fileName))
stdext::throw_exception(stdext::format("failed to save spawns XML %s: %s", fileName, doc.ErrorDesc()));
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
}
}
void CreatureManager::loadCreatureBuffer(const std::string& buffer)
@@ -283,6 +289,7 @@ void CreatureManager::loadCreatureBuffer(const std::string& buffer)
std::string cName = root->Attribute("name");
stdext::tolower(cName);
stdext::trim(cName);
stdext::ucwords(cName);
CreatureTypePtr newType(new CreatureType(cName));
for(TiXmlElement* attrib = root->FirstChildElement(); attrib; attrib = attrib->NextSiblingElement()) {
@@ -329,6 +336,7 @@ const CreatureTypePtr& CreatureManager::getCreatureByName(std::string name)
{
stdext::tolower(name);
stdext::trim(name);
stdext::ucwords(name);
auto it = std::find_if(m_creatures.begin(), m_creatures.end(),
[=] (const CreatureTypePtr& m) -> bool { return m->getName() == name; });
if(it != m_creatures.end())
@@ -360,11 +368,27 @@ SpawnPtr CreatureManager::getSpawn(const Position& centerPos)
return nullptr;
}
SpawnPtr CreatureManager::getSpawnForPlacePos(const Position& pos)
{
for (const auto& pair : m_spawns) {
const Position& centerPos = pair.first;
const SpawnPtr& spawn = pair.second;
if (isInZone(pos, centerPos, spawn->getRadius()))
return spawn;
}
return nullptr;
}
SpawnPtr CreatureManager::addSpawn(const Position& centerPos, int radius)
{
auto iter = m_spawns.find(centerPos);
if(iter != m_spawns.end())
if(iter != m_spawns.end()) {
if(iter->second->getRadius() != radius)
iter->second->setRadius(radius);
return iter->second;
}
SpawnPtr ret(new Spawn);
@@ -375,3 +399,5 @@ SpawnPtr CreatureManager::addSpawn(const Position& centerPos, int radius)
return ret;
}
/* vim: set ts=4 sw=4 et: */

View File

@@ -33,14 +33,20 @@ enum CreatureAttr : uint8
CreatureAttrName = 1,
CreatureAttrOutfit = 2,
CreatureAttrSpawnTime = 3,
CreatureAttrDir = 4
CreatureAttrDir = 4,
CreatureAttrRace = 5
};
enum CreatureRace : uint8
{
CreatureRaceNpc = 0,
CreatureRaceMonster = 1
};
enum SpawnAttr : uint8
{
SpawnAttrRadius = 0,
SpawnAttrCenter = 1,
SpawnAttrPos = 2
};
class Spawn : public LuaObject
@@ -53,7 +59,7 @@ public:
int32 getRadius() { return m_attribs.get<int32>(SpawnAttrRadius); }
void setCenterPos(const Position& pos) { m_attribs.set(SpawnAttrCenter, pos); }
Position getCenterPos() { return m_attribs.get<Position>(SpawnAttrPos); }
Position getCenterPos() { return m_attribs.get<Position>(SpawnAttrCenter); }
void addCreature(const Position& placePos, const CreatureTypePtr& cType);
void removeCreature(const Position& pos);
@@ -61,7 +67,7 @@ public:
protected:
void load(TiXmlElement* node);
void save(TiXmlElement*& node);
void save(TiXmlElement* node);
private:
stdext::dynamic_storage<uint8> m_attribs;
@@ -87,6 +93,9 @@ public:
void setDirection(Otc::Direction dir) { m_attribs.set(CreatureAttrDir, dir); }
Otc::Direction getDirection() { return m_attribs.get<Otc::Direction>(CreatureAttrDir); }
void setRace(CreatureRace race) { m_attribs.set(CreatureAttrRace, race); }
CreatureRace getRace() { return m_attribs.get<CreatureRace>(CreatureAttrRace); }
CreaturePtr cast();
private:
@@ -112,6 +121,7 @@ public:
const CreatureTypePtr& getCreatureByLook(int look);
SpawnPtr getSpawn(const Position& centerPos);
SpawnPtr getSpawnForPlacePos(const Position& pos);
SpawnPtr addSpawn(const Position& centerPos, int radius);
bool isLoaded() { return m_loaded; }

View File

@@ -77,8 +77,10 @@ typedef stdext::shared_object_ptr<Spawn> SpawnPtr;
typedef std::vector<ThingPtr> ThingList;
typedef std::vector<ThingTypePtr> ThingTypeList;
typedef std::vector<ItemTypePtr> ItemTypeList;
typedef std::vector<HousePtr> HouseList;
typedef std::vector<TownPtr> TownList;
typedef std::list<HousePtr> HouseList;
typedef std::list<TownPtr> TownList;
typedef std::list<ItemPtr> ItemList;
typedef std::vector<ItemPtr> ItemVector;
typedef std::unordered_map<Position, TilePtr, PositionHasher> TileMap;
// net
@@ -96,9 +98,11 @@ class UIMinimap;
class UIProgressRect;
class UIMapAnchorLayout;
class UIPositionAnchor;
class UISprite;
typedef stdext::shared_object_ptr<UIItem> UIItemPtr;
typedef stdext::shared_object_ptr<UICreature> UICreaturePtr;
typedef stdext::shared_object_ptr<UISprite> UISpritePtr;
typedef stdext::shared_object_ptr<UIMap> UIMapPtr;
typedef stdext::shared_object_ptr<UIMinimap> UIMinimapPtr;
typedef stdext::shared_object_ptr<UIProgressRect> UIProgressRectPtr;

View File

@@ -31,7 +31,7 @@ void Effect::draw(const Point& dest, float scaleFactor, bool animate, LightView
int animationPhase = 0;
if(animate)
animationPhase = std::min((int)(m_animationTimer.ticksElapsed() / m_phaseDuration), getAnimationPhases() - 1);
animationPhase = std::min<int>((int)(m_animationTimer.ticksElapsed() / m_phaseDuration), getAnimationPhases() - 1);
rawGetThingType()->draw(dest, scaleFactor, 0, 0, 0, 0, animationPhase, lightView);
}

View File

@@ -51,6 +51,7 @@ Game::Game()
m_canReportBugs = false;
m_fightMode = Otc::FightBalanced;
m_chaseMode = Otc::DontChase;
m_pvpMode = Otc::WhiteDove;
m_safeFight = true;
}
@@ -76,6 +77,7 @@ void Game::resetGameStates()
m_canReportBugs = false;
m_fightMode = Otc::FightBalanced;
m_chaseMode = Otc::DontChase;
m_pvpMode = Otc::WhiteDove;
m_safeFight = true;
m_followingCreature = nullptr;
m_attackingCreature = nullptr;
@@ -99,6 +101,11 @@ void Game::resetGameStates()
m_walkEvent = nullptr;
}
if(m_checkConnectionEvent) {
m_checkConnectionEvent->cancel();
m_checkConnectionEvent = nullptr;
}
m_containers.clear();
m_vips.clear();
m_gmActions.clear();
@@ -171,7 +178,7 @@ void Game::processGameStart()
m_online = true;
// synchronize fight modes with the server
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight);
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
// NOTE: the entire map description and local player information is not known yet (bot call is allowed here)
enableBotCall();
@@ -183,6 +190,16 @@ void Game::processGameStart()
g_game.ping();
}, m_pingDelay);
}
m_checkConnectionEvent = g_dispatcher.cycleEvent([this] {
if(!g_game.isConnectionOk() && !m_connectionFailWarned) {
g_lua.callGlobalField("g_game", "onConnectionFailing", true);
m_connectionFailWarned = true;
} else if(g_game.isConnectionOk() && m_connectionFailWarned) {
g_lua.callGlobalField("g_game", "onConnectionFailing", false);
m_connectionFailWarned = false;
}
}, 1000);
}
void Game::processGameEnd()
@@ -190,6 +207,11 @@ void Game::processGameEnd()
m_online = false;
g_lua.callGlobalField("g_game", "onGameEnd");
if(m_connectionFailWarned) {
g_lua.callGlobalField("g_game", "onConnectionFailing", false);
m_connectionFailWarned = false;
}
// reset game state
resetGameStates();
@@ -214,6 +236,24 @@ void Game::processGMActions(const std::vector<uint8>& actions)
g_lua.callGlobalField("g_game", "onGMActions", actions);
}
void Game::processPlayerHelpers(int helpers)
{
g_lua.callGlobalField("g_game", "onPlayerHelpersUpdate", helpers);
}
void Game::processPlayerModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeMode, Otc::PVPModes pvpMode)
{
m_fightMode = fightMode;
m_chaseMode = chaseMode;
m_safeFight = safeMode;
m_pvpMode = pvpMode;
g_lua.callGlobalField("g_game", "onFightModeChange", fightMode);
g_lua.callGlobalField("g_game", "onChaseModeChange", chaseMode);
g_lua.callGlobalField("g_game", "onSafeFightChange", safeMode);
g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode);
}
void Game::processPing()
{
g_lua.callGlobalField("g_game", "onPing");
@@ -248,10 +288,10 @@ void Game::processTalk(const std::string& name, int level, Otc::MessageMode mode
g_lua.callGlobalField("g_game", "onTalk", name, level, mode, text, channelId, pos);
}
void Game::processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items)
void Game::processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items, bool isUnlocked, bool hasPages, int containerSize, int firstIndex)
{
ContainerPtr previousContainer = getContainer(containerId);
ContainerPtr container = ContainerPtr(new Container(containerId, capacity, name, containerItem, hasParent));
ContainerPtr container = ContainerPtr(new Container(containerId, capacity, name, containerItem, hasParent, isUnlocked, hasPages, containerSize, firstIndex));
m_containers[containerId] = container;
container->onAddItems(items);
@@ -276,7 +316,7 @@ void Game::processCloseContainer(int containerId)
container->onClose();
}
void Game::processContainerAddItem(int containerId, const ItemPtr& item)
void Game::processContainerAddItem(int containerId, const ItemPtr& item, int slot)
{
ContainerPtr container = getContainer(containerId);
if(!container) {
@@ -284,7 +324,7 @@ void Game::processContainerAddItem(int containerId, const ItemPtr& item)
return;
}
container->onAddItem(item);
container->onAddItem(item, slot);
}
void Game::processContainerUpdateItem(int containerId, int slot, const ItemPtr& item)
@@ -450,9 +490,9 @@ void Game::processCloseTrade()
g_lua.callGlobalField("g_game", "onCloseTrade");
}
void Game::processEditText(uint id, int itemId, int maxLength, const std::string& text, const std::string& writter, const std::string& date)
void Game::processEditText(uint id, int itemId, int maxLength, const std::string& text, const std::string& writer, const std::string& date)
{
g_lua.callGlobalField("g_game", "onEditText", id, itemId, maxLength, text, writter, date);
g_lua.callGlobalField("g_game", "onEditText", id, itemId, maxLength, text, writer, date);
}
void Game::processEditList(uint id, int doorId, const std::string& text)
@@ -470,9 +510,9 @@ void Game::processQuestLine(int questId, const std::vector<std::tuple<std::strin
g_lua.callGlobalField("g_game", "onQuestLine", questId, questMissions);
}
void Game::processModalDialog(uint32 id, std::string title, std::string message, int enterId, std::string enterText, int escapeId, std::string escapeText, std::vector<std::tuple<int, std::string> > choiceList)
void Game::processModalDialog(uint32 id, std::string title, std::string message, std::vector<std::tuple<int, std::string> > buttonList, int enterButton, int escapeButton, std::vector<std::tuple<int, std::string> > choiceList, bool priority)
{
g_lua.callGlobalField("g_game", "onModalDialog", id, title, message, enterId, enterText, escapeId, escapeText, choiceList);
g_lua.callGlobalField("g_game", "onModalDialog", id, title, message, buttonList, enterButton, escapeButton, choiceList, priority);
}
void Game::processAttackCancel(uint seq)
@@ -554,7 +594,7 @@ bool Game::walk(Otc::Direction direction)
if(m_lastWalkDir != direction) {
// must add a new walk event
float ticks = m_localPlayer->getStepTicksLeft();
if(ticks < 0) { ticks = 0; }
if(ticks <= 0) { ticks = 1; }
if(m_walkEvent) {
m_walkEvent->cancel();
@@ -697,7 +737,9 @@ void Game::autoWalk(std::vector<Otc::Direction> dirs)
auto it = dirs.begin();
Otc::Direction direction = *it;
if(m_localPlayer->canWalk(direction)) {
if(!m_localPlayer->canWalk(direction))
return;
TilePtr toTile = g_map.getTile(m_localPlayer->getPosition().translatedToDirection(direction));
if(toTile && toTile->isWalkable() && !m_localPlayer->isServerWalking()) {
m_localPlayer->preWalk(direction);
@@ -707,7 +749,6 @@ void Game::autoWalk(std::vector<Otc::Direction> dirs)
dirs.erase(it);
}
}
}
g_lua.callGlobalField("g_game", "onAutoWalk", dirs);
@@ -863,7 +904,7 @@ void Game::useWith(const ItemPtr& item, const ThingPtr& toThing)
if(!pos.isValid()) // virtual item
pos = Position(0xFFFF, 0, 0); // means that is a item in inventory
if(toThing->isCreature())
if(toThing->isCreature() && g_game.getProtocolVersion() >= 780)
m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackPos(), toThing->getId());
else
m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackPos(), toThing->getPosition(), toThing->getId(), toThing->getStackPos());
@@ -882,6 +923,20 @@ void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing)
m_protocolGame->sendUseItemWith(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toThing->getStackPos());
}
ItemPtr Game::findItemInContainers(uint itemId, int subType)
{
for(auto& it : m_containers) {
const ContainerPtr& container = it.second;
if(container) {
ItemPtr item = container->findItemById(itemId, subType);
if(item != nullptr)
return item;
}
}
return nullptr;
}
int Game::open(const ItemPtr& item, const ContainerPtr& previousContainer)
{
if(!canPerformGameAction() || !item)
@@ -1144,7 +1199,7 @@ void Game::setChaseMode(Otc::ChaseModes chaseMode)
if(m_chaseMode == chaseMode)
return;
m_chaseMode = chaseMode;
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight);
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
g_lua.callGlobalField("g_game", "onChaseModeChange", chaseMode);
}
@@ -1155,7 +1210,7 @@ void Game::setFightMode(Otc::FightModes fightMode)
if(m_fightMode == fightMode)
return;
m_fightMode = fightMode;
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight);
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
g_lua.callGlobalField("g_game", "onFightModeChange", fightMode);
}
@@ -1166,10 +1221,23 @@ void Game::setSafeFight(bool on)
if(m_safeFight == on)
return;
m_safeFight = on;
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight);
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
g_lua.callGlobalField("g_game", "onSafeFightChange", on);
}
void Game::setPVPMode(Otc::PVPModes pvpMode)
{
if(!canPerformGameAction())
return;
if(!getFeature(Otc::GamePVPMode))
return;
if(m_pvpMode == pvpMode)
return;
m_pvpMode = pvpMode;
m_protocolGame->sendChangeFightModes(m_fightMode, m_chaseMode, m_safeFight, m_pvpMode);
g_lua.callGlobalField("g_game", "onPVPModeChange", pvpMode);
}
void Game::inspectNpcTrade(const ItemPtr& item)
{
if(!canPerformGameAction() || !item)
@@ -1305,7 +1373,6 @@ void Game::mount(bool mount)
{
if(!canPerformGameAction())
return;
m_protocolGame->sendMountStatus(mount);
}
@@ -1320,7 +1387,6 @@ void Game::answerModalDialog(int dialog, int button, int choice)
{
if(!canPerformGameAction())
return;
m_protocolGame->sendAnswerModalDialog(dialog, button, choice);
}
@@ -1379,24 +1445,41 @@ void Game::setProtocolVersion(int version)
if(isOnline())
stdext::throw_exception("Unable to change protocol version while online");
if(version != 0 && (version < 810 || version > 974))
if(version != 0 && (version < 760 || version > 1031))
stdext::throw_exception(stdext::format("Protocol version %d not supported", version));
m_features.reset();
enableFeature(Otc::GameFormatCreatureName);
if(version >= 770)
{
enableFeature(Otc::GameLooktypeU16);
enableFeature(Otc::GameMessageStatements);
enableFeature(Otc::GameWritableDate); // might not be since 770
}
if(version >= 780)
{
enableFeature(Otc::GamePlayerAddons);
enableFeature(Otc::GamePlayerStamina);
enableFeature(Otc::GameNewFluids);
enableFeature(Otc::GameMessageLevel);
enableFeature(Otc::GamePlayerStateU16);
enableFeature(Otc::GameNewOutfitProtocol);
}
if(version >= 840) {
enableFeature(Otc::GameProtocolChecksum);
enableFeature(Otc::GameChallengeOnLogin);
enableFeature(Otc::GameAccountNames);
enableFeature(Otc::GameDoubleFreeCapacity);
}
if(version <= 854) {
if(version >= 780 && version <= 854) { // 780 might not be accurate
enableFeature(Otc::GameChargeableItems);
}
if(version >= 854) {
enableFeature(Otc::GameDoubleFreeCapacity);
enableFeature(Otc::GameCreatureEmblems);
}
@@ -1443,6 +1526,18 @@ void Game::setProtocolVersion(int version)
enableFeature(Otc::GameNewSpeedLaw);
}
if(version >= 976) {
enableFeature(Otc::GameContainerPagination);
}
if(version >= 979) {
enableFeature(Otc::GameThingMarks);
}
if(version >= 1000) {
enableFeature(Otc::GamePVPMode);
}
m_protocolVersion = version;
Proto::buildMessageModesMap(version);
@@ -1458,7 +1553,7 @@ void Game::setClientVersion(int version)
if(isOnline())
stdext::throw_exception("Unable to change client version while online");
if(version != 0 && (version < 810 || version > 982))
if(version != 0 && (version < 760 || version > 1031))
stdext::throw_exception(stdext::format("Client version %d not supported", version));
m_clientVersion = version;

View File

@@ -73,14 +73,17 @@ protected:
void processAttackCancel(uint seq);
void processWalkCancel(Otc::Direction direction);
void processPlayerHelpers(int helpers);
void processPlayerModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeMode, Otc::PVPModes pvpMode);
// message related
void processTextMessage(Otc::MessageMode mode, const std::string& text);
void processTalk(const std::string& name, int level, Otc::MessageMode mode, const std::string& text, int channelId, const Position& pos);
// container related
void processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items);
void processOpenContainer(int containerId, const ItemPtr& containerItem, const std::string& name, int capacity, bool hasParent, const std::vector<ItemPtr>& items, bool isUnlocked, bool hasPages, int containerSize, int firstIndex);
void processCloseContainer(int containerId);
void processContainerAddItem(int containerId, const ItemPtr& item);
void processContainerAddItem(int containerId, const ItemPtr& item, int slot);
void processContainerUpdateItem(int containerId, int slot, const ItemPtr& item);
void processContainerRemoveItem(int containerId, int slot);
@@ -121,7 +124,7 @@ protected:
void processCloseTrade();
// edit text/list
void processEditText(uint id, int itemId, int maxLength, const std::string& text, const std::string& writter, const std::string& date);
void processEditText(uint id, int itemId, int maxLength, const std::string& text, const std::string& writer, const std::string& date);
void processEditList(uint id, int doorId, const std::string& text);
// questlog
@@ -129,7 +132,7 @@ protected:
void processQuestLine(int questId, const std::vector<std::tuple<std::string, std::string> >& questMissions);
// modal dialogs >= 970
void processModalDialog(uint32 id, std::string title, std::string message, int enterId, std::string enterText, int escapeId, std::string escapeText, std::vector<std::tuple<int, std::string> > choiceList);
void processModalDialog(uint32 id, std::string title, std::string message, std::vector<std::tuple<int, std::string> > buttonList, int enterButton, int escapeButton, std::vector<std::tuple<int, std::string> > choiceList, bool priority);
friend class ProtocolGame;
friend class Map;
@@ -158,6 +161,7 @@ public:
void useWith(const ItemPtr& fromThing, const ThingPtr& toThing);
void useInventoryItem(int itemId);
void useInventoryItemWith(int itemId, const ThingPtr& toThing);
ItemPtr findItemInContainers(uint itemId, int subType);
// container related
int open(const ItemPtr& item, const ContainerPtr& previousContainer);
@@ -207,9 +211,11 @@ public:
void setChaseMode(Otc::ChaseModes chaseMode);
void setFightMode(Otc::FightModes fightMode);
void setSafeFight(bool on);
void setPVPMode(Otc::PVPModes pvpMode);
Otc::ChaseModes getChaseMode() { return m_chaseMode; }
Otc::FightModes getFightMode() { return m_fightMode; }
bool isSafeFight() { return m_safeFight; }
Otc::PVPModes getPVPMode() { return m_pvpMode; }
// npc trade related
void inspectNpcTrade(const ItemPtr& item);
@@ -281,8 +287,9 @@ public:
bool isDead() { return m_dead; }
bool isAttacking() { return !!m_attackingCreature; }
bool isFollowing() { return !!m_followingCreature; }
bool isConnectionOk() { return m_protocolGame && m_protocolGame->getElapsedTicksSinceLastRead() < 5000; }
int getPing() { return m_ping >= 0 ? std::max(m_ping, m_pingTimer.elapsed_millis()) : -1; }
int getPing() { return m_ping >= 0 ? std::max<int>(m_ping, m_pingTimer.elapsed_millis()) : -1; }
ContainerPtr getContainer(int index) { return m_containers[index]; }
std::map<int, ContainerPtr> getContainers() { return m_containers; }
std::map<int, Vip> getVips() { return m_vips; }
@@ -331,6 +338,7 @@ private:
int m_pingDelay;
Otc::FightModes m_fightMode;
Otc::ChaseModes m_chaseMode;
Otc::PVPModes m_pvpMode;
Otc::Direction m_lastWalkDir;
bool m_safeFight;
bool m_canReportBugs;
@@ -340,6 +348,8 @@ private:
std::bitset<Otc::LastGameFeature> m_features;
ScheduledEventPtr m_pingEvent;
ScheduledEventPtr m_walkEvent;
ScheduledEventPtr m_checkConnectionEvent;
bool m_connectionFailWarned;
int m_protocolVersion;
int m_clientVersion;
std::string m_clientSignature;

View File

@@ -28,12 +28,10 @@ HouseManager g_houses;
House::House()
{
m_nullTile = TilePtr(new Tile(Position()));
}
House::House(uint32 hId, const std::string &name, const Position &pos)
{
m_nullTile = TilePtr(new Tile(Position()));
setId(hId);
setName(name);
if(pos.isValid())
@@ -42,16 +40,32 @@ House::House(uint32 hId, const std::string &name, const Position &pos)
void House::setTile(const TilePtr& tile)
{
tile->setFlags(TILESTATE_HOUSE);
tile->setFlag(TILESTATE_HOUSE);
tile->setHouseId(getId());
m_tiles.insert(std::make_pair(tile->getPosition(), tile));
}
const TilePtr& House::getTile(const Position& position)
TilePtr House::getTile(const Position& position)
{
TileMap::const_iterator iter = m_tiles.find(position);
if(iter != m_tiles.end())
return iter->second;
return m_nullTile;
return nullptr;
}
void House::addDoor(const ItemPtr& door)
{
if (!door) return;
door->setDoorId(m_lastDoorId);
m_doors[m_lastDoorId++] = door;
}
void House::removeDoorById(uint32 doorId)
{
if(doorId >= m_lastDoorId)
stdext::throw_exception(stdext::format("Failed to remove door of id %d (would overflow), max id: %d",
doorId, m_lastDoorId));
m_doors[doorId] = nullptr;
}
void House::load(const TiXmlElement *elem)
@@ -67,16 +81,14 @@ void House::load(const TiXmlElement *elem)
m_isGuildHall = elem->readType<bool>("guildhall");
Position entryPos;
entryPos.x = elem->readType<uint16>("entryx");
entryPos.y = elem->readType<uint16>("entryy");
entryPos.z = elem->readType<uint8>("entryz");
entryPos.x = elem->readType<int>("entryx");
entryPos.y = elem->readType<int>("entryy");
entryPos.z = elem->readType<int>("entryz");
setEntry(entryPos);
}
void House::save(TiXmlElement*& elem)
void House::save(TiXmlElement* elem)
{
elem = new TiXmlElement("house");
elem->SetAttribute("name", getName());
elem->SetAttribute("houseid", getId());
@@ -93,7 +105,6 @@ void House::save(TiXmlElement*& elem)
HouseManager::HouseManager()
{
m_nullHouse = HousePtr(new House);
}
void HouseManager::addHouse(const HousePtr& house)
@@ -109,15 +120,22 @@ void HouseManager::removeHouse(uint32 houseId)
m_houses.erase(it);
}
const HousePtr& HouseManager::getHouse(uint32 houseId)
HousePtr HouseManager::getHouse(uint32 houseId)
{
auto it = findHouse(houseId);
return it != m_houses.end() ? *it : nullptr;
}
HousePtr HouseManager::getHouseByName(std::string name)
{
auto it = std::find_if(m_houses.begin(), m_houses.end(),
[=] (const HousePtr& house) -> bool { return house->getId() == houseId; });
return it != m_houses.end() ? *it : m_nullHouse;
[=] (const HousePtr& house) -> bool { return house->getName() == name; });
return it != m_houses.end() ? *it : nullptr;
}
void HouseManager::load(const std::string& fileName)
{
try {
TiXmlDocument doc;
doc.Parse(g_resources.readFileContents(fileName).c_str());
if(doc.Error())
@@ -138,10 +156,14 @@ void HouseManager::load(const std::string& fileName)
house->load(elem);
}
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to load '%s': %s", fileName, e.what()));
}
}
void HouseManager::save(const std::string& fileName)
{
try {
TiXmlDocument doc;
doc.SetTabSize(2);
@@ -152,13 +174,25 @@ void HouseManager::save(const std::string& fileName)
doc.LinkEndChild(root);
for(auto house : m_houses) {
TiXmlElement *elem;
TiXmlElement *elem = new TiXmlElement("house");
house->save(elem);
root->LinkEndChild(elem);
}
if(!doc.SaveFile(fileName))
if(!doc.SaveFile("data"+fileName))
stdext::throw_exception(stdext::format("failed to save houses XML %s: %s", fileName, doc.ErrorDesc()));
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
}
}
HouseList HouseManager::filterHouses(uint32 townId)
{
HouseList ret;
for(const HousePtr& house : m_houses)
if(house->getTownId() == townId)
ret.push_back(house);
return ret;
}
HouseList::iterator HouseManager::findHouse(uint32 houseId)
@@ -166,3 +200,5 @@ HouseList::iterator HouseManager::findHouse(uint32 houseId)
return std::find_if(m_houses.begin(), m_houses.end(),
[=] (const HousePtr& house) -> bool { return house->getId() == houseId; });
}
/* vim: set ts=4 sw=4 et: */

View File

@@ -43,10 +43,10 @@ class House : public LuaObject
public:
House();
House(uint32 hId, const std::string& name = "", const Position& pos=Position());
~House() { m_tiles.clear(); m_nullTile = nullptr; }
~House() { m_tiles.clear(); }
void setTile(const TilePtr& tile);
const TilePtr& getTile(const Position& pos);
TilePtr getTile(const Position& pos);
void setName(const std::string& name) { m_attribs.set(HouseAttrName, name); }
std::string getName() { return m_attribs.get<std::string>(HouseAttrName); }
@@ -66,14 +66,19 @@ public:
void setEntry(const Position& p) { m_attribs.set(HouseAttrEntry, p); }
Position getEntry() { return m_attribs.get<Position>(HouseAttrEntry); }
void addDoor(const ItemPtr& door);
void removeDoor(const ItemPtr& door) { removeDoorById(door->getDoorId()); }
void removeDoorById(uint32 doorId);
protected:
void load(const TiXmlElement* elem);
void save(TiXmlElement*& elem);
void save(TiXmlElement* elem);
private:
stdext::packed_storage<uint8> m_attribs;
TileMap m_tiles;
TilePtr m_nullTile;
ItemVector m_doors;
uint32 m_lastDoorId;
stdext::boolean<false> m_isGuildHall;
friend class HouseManager;
@@ -85,16 +90,18 @@ public:
void addHouse(const HousePtr& house);
void removeHouse(uint32 houseId);
HouseList getHouseList() { return m_houses; }
const HousePtr& getHouse(uint32 houseId);
void clear() { m_houses.clear(); m_nullHouse = nullptr; }
HousePtr getHouse(uint32 houseId);
HousePtr getHouseByName(std::string name);
void load(const std::string& fileName);
void save(const std::string& fileName);
void clear() { m_houses.clear(); }
HouseList getHouseList() { return m_houses; }
HouseList filterHouses(uint32 townId);
private:
HouseList m_houses;
HousePtr m_nullHouse;
protected:
HouseList::iterator findHouse(uint32 houseId);

View File

@@ -40,7 +40,11 @@
Item::Item() :
m_clientId(0),
m_serverId(0),
m_countOrSubType(1)
m_countOrSubType(1),
m_color(Color::alpha),
m_async(true),
m_phase(0),
m_lastPhase(0)
{
}
@@ -58,6 +62,11 @@ ItemPtr Item::createFromOtb(int id)
return item;
}
std::string Item::getName()
{
return g_things.findItemTypeByClientId(m_clientId)->getName();
}
void Item::draw(const Point& dest, float scaleFactor, bool animate, LightView *lightView)
{
if(m_clientId == 0)
@@ -70,7 +79,15 @@ void Item::draw(const Point& dest, float scaleFactor, bool animate, LightView *l
int xPattern = 0, yPattern = 0, zPattern = 0;
calculatePatterns(xPattern, yPattern, zPattern);
if(m_color != Color::alpha)
g_painter->setColor(m_color);
rawGetThingType()->draw(dest, scaleFactor, 0, xPattern, yPattern, zPattern, animationPhase, lightView);
/// Sanity check
/// This is just to ensure that we don't overwrite some color and
/// screw up the whole rendering.
if(m_color != Color::alpha)
g_painter->resetColor();
}
void Item::setId(uint32 id)
@@ -187,6 +204,11 @@ void Item::serializeItem(const OutputBinaryTreePtr& out)
out->addU16(getDepotId());
}
if(isHouseDoor()) {
out->addU8(ATTR_HOUSEDOORID);
out->addU8(getDoorId());
}
uint16 aid = m_attribs.get<uint16>(ATTR_ACTION_ID);
uint16 uid = m_attribs.get<uint16>(ATTR_UNIQUE_ID);
if(aid) {
@@ -199,11 +221,20 @@ void Item::serializeItem(const OutputBinaryTreePtr& out)
out->addU16(uid);
}
out->endNode();
if(!m_containerItems.empty()) {
for(auto c : m_containerItems)
c->serializeItem(out);
std::string text = getText();
if(g_things.getItemType(m_serverId)->isWritable() && !text.empty()) {
out->addU8(ATTR_TEXT);
out->addString(text);
}
std::string desc = getDescription();
if(!desc.empty()) {
out->addU8(ATTR_DESC);
out->addString(desc);
}
out->endNode();
for(auto i : m_containerItems)
i->serializeItem(out);
}
int Item::getSubType()
@@ -271,6 +302,7 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern)
}
} else if(isSplash() || isFluidContainer()) {
int color = Otc::FluidTransparent;
if(g_game.getFeature(Otc::GameNewFluids)) {
switch(m_countOrSubType) {
case Otc::FluidNone:
color = Otc::FluidTransparent;
@@ -330,6 +362,8 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern)
color = Otc::FluidTransparent;
break;
}
} else
color = m_countOrSubType;
xPattern = (color % 4) % getNumPatternX();
yPattern = (color / 4) % getNumPatternY();
@@ -343,9 +377,17 @@ void Item::calculatePatterns(int& xPattern, int& yPattern, int& zPattern)
int Item::calculateAnimationPhase(bool animate)
{
if(getAnimationPhases() > 1) {
if(animate)
if(animate) {
if(m_async)
return (g_clock.millis() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME;
else
else {
if(g_clock.millis() - m_lastPhase >= Otc::ITEM_TICKS_PER_FRAME) {
m_phase = (m_phase + 1) % getAnimationPhases();
m_lastPhase = g_clock.millis();
}
return m_phase;
}
} else
return getAnimationPhases()-1;
}
return 0;
@@ -367,3 +409,4 @@ ThingType* Item::rawGetThingType()
{
return g_things.rawGetThingType(m_clientId, ThingCategoryItem);
}
/* vim: set ts=4 sw=4 et :*/

View File

@@ -24,6 +24,7 @@
#define ITEM_H
#include <framework/global.h>
#include "thing.h"
#include "effect.h"
#include "itemtype.h"
@@ -88,6 +89,7 @@ public:
void setCountOrSubType(int value) { m_countOrSubType = value; }
void setCount(int count) { m_countOrSubType = count; }
void setSubType(int subType) { m_countOrSubType = subType; }
void setColor(const Color& c) { m_color = c; }
int getCountOrSubType() { return m_countOrSubType; }
int getSubType();
@@ -95,10 +97,9 @@ public:
uint32 getId() { return m_clientId; }
uint16 getClientId() { return m_clientId; }
uint16 getServerId() { return m_serverId; }
std::string getName();
bool isValid();
ItemPtr clone();
void unserializeItem(const BinaryTreePtr& in);
void serializeItem(const OutputBinaryTreePtr& out);
@@ -108,9 +109,22 @@ public:
void setDoorId(uint8 doorId) { m_attribs.set(ATTR_HOUSEDOORID, doorId); }
uint8 getDoorId() { return m_attribs.get<uint8>(ATTR_HOUSEDOORID); }
uint16 getUniqueId() { return m_attribs.get<uint16>(ATTR_ACTION_ID); }
uint16 getActionId() { return m_attribs.get<uint16>(ATTR_UNIQUE_ID); }
void setActionId(uint16 actionId) { m_attribs.set(ATTR_ACTION_ID, actionId); }
void setUniqueId(uint16 uniqueId) { m_attribs.set(ATTR_UNIQUE_ID, uniqueId); }
std::string getText() { return m_attribs.get<std::string>(ATTR_TEXT); }
std::string getDescription() { return m_attribs.get<std::string>(ATTR_DESC); }
void setDescription(std::string desc) { m_attribs.set(ATTR_DESC, desc); }
void setText(std::string txt) { m_attribs.set(ATTR_TEXT, txt); }
Position getTeleportDestination() { return m_attribs.get<Position>(ATTR_TELE_DEST); }
void setTeleportDestination(const Position& pos) { m_attribs.set(ATTR_TELE_DEST, pos); }
void setAsync(bool enable) { m_async = enable; }
bool isHouseDoor() { return m_attribs.has(ATTR_HOUSEDOORID); }
bool isDepot() { return m_attribs.has(ATTR_DEPOT_ID); }
bool isContainer() { return m_attribs.has(ATTR_CONTAINER_ITEMS); }
bool isDoor() { return m_attribs.has(ATTR_HOUSEDOORID); }
@@ -118,10 +132,15 @@ public:
bool isMoveable();
bool isGround();
ItemPtr clone();
ItemPtr asItem() { return static_self_cast<Item>(); }
bool isItem() { return true; }
ItemVector getContainerItems() { return m_containerItems; }
ItemPtr getContainerItem(int slot) { return m_containerItems[slot]; }
void addContainerItemIndexed(const ItemPtr& i, int slot) { m_containerItems[slot] = i; }
void addContainerItem(const ItemPtr& i) { m_containerItems.push_back(i); }
void removeContainerItem(int slot) { m_containerItems[slot] = nullptr; }
void clearContainerItems() { m_containerItems.clear(); }
void calculatePatterns(int& xPattern, int& yPattern, int& zPattern);
@@ -136,7 +155,12 @@ private:
uint16 m_serverId;
uint8 m_countOrSubType;
stdext::packed_storage<uint8> m_attribs;
std::vector<ItemPtr> m_containerItems;
ItemVector m_containerItems;
Color m_color;
bool m_async;
uint8 m_phase;
ticks_t m_lastPhase;
};
#pragma pack(pop)

View File

@@ -23,6 +23,7 @@
#include "thingtypemanager.h"
#include "thingtype.h"
#include "game.h"
#include <framework/core/filestream.h>
#include <framework/core/binarytree.h>
@@ -50,28 +51,40 @@ void ItemType::unserialize(const BinaryTreePtr& node)
switch(attr) {
case ItemTypeAttrServerId: {
uint16 serverId = node->getU16();
if(g_game.getProtocolVersion() < 960) {
if(serverId > 20000 && serverId < 20100) {
serverId -= 20000;
} else if(lastId > 99 && lastId != serverId - 1) {
while(lastId != serverId - 1) {
ItemTypePtr tmp(new ItemType);
tmp->setServerId(lastId++);
g_things.addItemType(tmp);
}
}
} else {
if(serverId > 30000 && serverId < 30100) {
serverId -= 30000;
} else if(lastId > 99 && lastId != serverId - 1) {
while(lastId != serverId - 1) {
ItemTypePtr tmp(new ItemType);
tmp->setServerId(lastId++);
g_things.addItemType(tmp);
}
}
}
setServerId(serverId);
lastId = serverId;
break;
}
case ItemTypeAttrClientId: {
case ItemTypeAttrClientId:
setClientId(node->getU16());
break;
}
case ItemTypeAttrName: {
case ItemTypeAttrName:
setName(node->getString(len));
break;
}
case ItemTypeAttrWritable:
m_attribs.set(ItemTypeAttrWritable, true);
break;
default:
node->skip(len); // skip attribute
break;

View File

@@ -148,6 +148,7 @@ public:
std::string getDesc() { return m_attribs.get<std::string>(ItemTypeAttrDesc); }
bool isNull() { return m_null; }
bool isWritable() { return m_attribs.get<bool>(ItemTypeAttrWritable); }
private:
ItemCategory m_category;

View File

@@ -26,7 +26,6 @@
#include <framework/graphics/framebuffermanager.h>
#include <framework/graphics/painter.h>
#include <framework/graphics/image.h>
#include <framework/graphics/ogl/textureogl.h>
enum {
MAX_LIGHT_INTENSITY = 8,
@@ -51,7 +50,7 @@ TexturePtr LightView::generateLightBubble(float centerFactor)
for(int x = 0; x < bubbleDiameter; x++) {
for(int y = 0; y < bubbleDiameter; y++) {
float radius = std::sqrt((bubbleRadius - x)*(bubbleRadius - x) + (bubbleRadius - y)*(bubbleRadius - y));
float intensity = std::max(std::min((bubbleRadius-radius)/(float)(bubbleRadius-centerRadius), 1.0f), 0.0f);
float intensity = stdext::clamp<float>((bubbleRadius - radius) / (float)(bubbleRadius - centerRadius), 0.0f, 1.0f);
// light intensity varies inversely with the square of the distance
intensity = intensity * intensity;
@@ -62,7 +61,7 @@ TexturePtr LightView::generateLightBubble(float centerFactor)
}
}
TexturePtr tex = TexturePtr(new TextureOGL(lightImage, true));
TexturePtr tex = TexturePtr(new Texture(lightImage, true));
tex->setSmooth(true);
return tex;
}

View File

@@ -58,7 +58,7 @@ LocalPlayer::LocalPlayer()
void LocalPlayer::lockWalk(int millis)
{
m_walkLockExpiration = std::max(m_walkLockExpiration, (ticks_t) g_clock.millis() + millis);
m_walkLockExpiration = std::max<int>(m_walkLockExpiration, (ticks_t) g_clock.millis() + millis);
}
bool LocalPlayer::canWalk(Otc::Direction direction)
@@ -72,7 +72,7 @@ bool LocalPlayer::canWalk(Otc::Direction direction)
return false;
// last walk is not done yet
if(m_walkTimer.ticksElapsed() < getStepDuration())
if((m_walkTimer.ticksElapsed() < getStepDuration()) && !isAutoWalking())
return false;
// prewalk has a timeout, because for some reason that I don't know yet the server sometimes doesn't answer the prewalk
@@ -83,7 +83,7 @@ bool LocalPlayer::canWalk(Otc::Direction direction)
return false;
// cannot walk while already walking
if(m_walking && !prewalkTimeouted)
if((m_walking && !isAutoWalking()) && (!prewalkTimeouted || m_secondPreWalk))
return false;
return true;
@@ -95,6 +95,7 @@ void LocalPlayer::walk(const Position& oldPos, const Position& newPos)
if(m_preWalking) {
// switch to normal walking
m_preWalking = false;
m_secondPreWalk = false;
m_lastPrewalkDone = true;
// if is to the last prewalk destination, updates the walk preserving the animation
if(newPos == m_lastPrewalkDestination) {
@@ -118,7 +119,8 @@ void LocalPlayer::preWalk(Otc::Direction direction)
Position newPos = m_position.translatedToDirection(direction);
// avoid reanimating prewalks
if(m_preWalking && m_lastPrewalkDestination == newPos) {
if(m_preWalking) {
m_secondPreWalk = true;
return;
}
@@ -261,7 +263,7 @@ void LocalPlayer::updateWalk()
{
int stepDuration = getStepDuration();
float walkTicksPerPixel = getStepDuration(true) / 32.0f;
int totalPixelsWalked = std::min(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
int totalPixelsWalked = std::min<int>(m_walkTimer.ticksElapsed() / walkTicksPerPixel, 32.0f);
// update walk animation and offsets
updateWalkAnimation(totalPixelsWalked);
@@ -277,6 +279,7 @@ void LocalPlayer::terminateWalk()
{
Creature::terminateWalk();
m_preWalking = false;
m_secondPreWalk = false;
m_idleTimer.restart();
auto self = asLocalPlayer();

View File

@@ -126,6 +126,7 @@ private:
ticks_t m_walkLockExpiration;
stdext::boolean<false> m_preWalking;
stdext::boolean<true> m_lastPrewalkDone;
stdext::boolean<false> m_secondPreWalk;
stdext::boolean<false> m_serverWalking;
stdext::boolean<false> m_knownCompletePath;

View File

@@ -47,6 +47,7 @@
#include "uiminimap.h"
#include "uimapanchorlayout.h"
#include "uiprogressrect.h"
#include "uisprite.h"
#include "outfit.h"
#include <framework/luaengine/luainterface.h>
@@ -66,20 +67,26 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_things", "getItemType", &ThingTypeManager::getItemType, &g_things);
g_lua.bindSingletonFunction("g_things", "getThingTypes", &ThingTypeManager::getThingTypes, &g_things);
g_lua.bindSingletonFunction("g_things", "findItemTypeByClientId", &ThingTypeManager::findItemTypeByClientId, &g_things);
g_lua.bindSingletonFunction("g_things", "findThingTypeByAttr", &ThingTypeManager::findThingTypeByAttr, &g_things);
g_lua.bindSingletonFunction("g_things", "findItemTypeByName", &ThingTypeManager::findItemTypeByName, &g_things);
g_lua.bindSingletonFunction("g_things", "findItemTypesByName", &ThingTypeManager::findItemTypesByName, &g_things);
g_lua.bindSingletonFunction("g_things", "findItemTypesByString", &ThingTypeManager::findItemTypesByString, &g_things);
g_lua.bindSingletonFunction("g_things", "findItemTypeByCategory", &ThingTypeManager::findItemTypeByCategory, &g_things);
g_lua.bindSingletonFunction("g_things", "findThingTypeByAttr", &ThingTypeManager::findThingTypeByAttr, &g_things);
g_lua.registerSingletonClass("g_houses");
g_lua.bindSingletonFunction("g_houses", "clear", &HouseManager::clear, &g_houses);
g_lua.bindSingletonFunction("g_houses", "load", &HouseManager::load, &g_houses);
g_lua.bindSingletonFunction("g_houses", "save", &HouseManager::save, &g_houses);
g_lua.bindSingletonFunction("g_houses", "getHouse", &HouseManager::getHouse, &g_houses);
g_lua.bindSingletonFunction("g_houses", "getHouseByName", &HouseManager::getHouseByName, &g_houses);
g_lua.bindSingletonFunction("g_houses", "addHouse", &HouseManager::addHouse, &g_houses);
g_lua.bindSingletonFunction("g_houses", "removeHouse", &HouseManager::removeHouse, &g_houses);
g_lua.bindSingletonFunction("g_houses", "getHouseList", &HouseManager::getHouseList, &g_houses);
g_lua.bindSingletonFunction("g_houses", "filterHouses", &HouseManager::filterHouses, &g_houses);
g_lua.registerSingletonClass("g_towns");
g_lua.bindSingletonFunction("g_towns", "getTown", &TownManager::getTown, &g_towns);
g_lua.bindSingletonFunction("g_towns", "getTownByName",&TownManager::getTownByName,&g_towns);
g_lua.bindSingletonFunction("g_towns", "addTown", &TownManager::addTown, &g_towns);
g_lua.bindSingletonFunction("g_towns", "removeTown", &TownManager::removeTown, &g_towns);
g_lua.bindSingletonFunction("g_towns", "getTowns", &TownManager::getTowns, &g_towns);
@@ -99,6 +106,8 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_map", "getThing", &Map::getThing, &g_map);
g_lua.bindSingletonFunction("g_map", "removeThingByPos", &Map::removeThingByPos, &g_map);
g_lua.bindSingletonFunction("g_map", "removeThing", &Map::removeThing, &g_map);
g_lua.bindSingletonFunction("g_map", "colorizeThing", &Map::colorizeThing, &g_map);
g_lua.bindSingletonFunction("g_map", "removeThingColor", &Map::removeThingColor, &g_map);
g_lua.bindSingletonFunction("g_map", "clean", &Map::clean, &g_map);
g_lua.bindSingletonFunction("g_map", "cleanTile", &Map::cleanTile, &g_map);
g_lua.bindSingletonFunction("g_map", "cleanTexts", &Map::cleanTexts, &g_map);
@@ -118,7 +127,27 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_map", "getSpawnFile", &Map::getSpawnFile, &g_map);
g_lua.bindSingletonFunction("g_map", "setSpawnFile", &Map::setSpawnFile, &g_map);
g_lua.bindSingletonFunction("g_map", "createTile", &Map::createTile, &g_map);
g_lua.bindSingletonFunction("g_map", "setWidth", &Map::setWidth, &g_map);
g_lua.bindSingletonFunction("g_map", "setHeight", &Map::setHeight, &g_map);
g_lua.bindSingletonFunction("g_map", "getSize", &Map::getSize, &g_map);
g_lua.bindSingletonFunction("g_map", "setDescription", &Map::setDescription, &g_map);
g_lua.bindSingletonFunction("g_map", "getDescriptions", &Map::getDescriptions, &g_map);
g_lua.bindSingletonFunction("g_map", "clearDescriptions", &Map::clearDescriptions, &g_map);
g_lua.bindSingletonFunction("g_map", "setShowZone", &Map::setShowZone, &g_map);
g_lua.bindSingletonFunction("g_map", "setShowZones", &Map::setShowZones, &g_map);
g_lua.bindSingletonFunction("g_map", "setZoneColor", &Map::setZoneColor, &g_map);
g_lua.bindSingletonFunction("g_map", "setZoneOpacity", &Map::setZoneOpacity, &g_map);
g_lua.bindSingletonFunction("g_map", "getZoneOpacity", &Map::getZoneOpacity, &g_map);
g_lua.bindSingletonFunction("g_map", "getZoneColor", &Map::getZoneColor, &g_map);
g_lua.bindSingletonFunction("g_map", "showZones", &Map::showZones, &g_map);
g_lua.bindSingletonFunction("g_map", "showZone", &Map::showZone, &g_map);
g_lua.bindSingletonFunction("g_map", "setForceShowAnimations", &Map::setForceShowAnimations, &g_map);
g_lua.bindSingletonFunction("g_map", "isForcingAnimations", &Map::isForcingAnimations, &g_map);
g_lua.bindSingletonFunction("g_map", "isShowingAnimations", &Map::isShowingAnimations, &g_map);
g_lua.bindSingletonFunction("g_map", "setShowAnimations", &Map::setShowAnimations, &g_map);
g_lua.bindSingletonFunction("g_map", "beginGhostMode", &Map::beginGhostMode, &g_map);
g_lua.bindSingletonFunction("g_map", "endGhostMode", &Map::endGhostMode, &g_map);
g_lua.bindSingletonFunction("g_map", "findItemsById", &Map::findItemsById, &g_map);
g_lua.registerSingletonClass("g_minimap");
g_lua.bindSingletonFunction("g_minimap", "clean", &Minimap::clean, &g_minimap);
@@ -132,6 +161,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_creatures", "getCreatureByName", &CreatureManager::getCreatureByName, &g_creatures);
g_lua.bindSingletonFunction("g_creatures", "getCreatureByLook", &CreatureManager::getCreatureByLook, &g_creatures);
g_lua.bindSingletonFunction("g_creatures", "getSpawn", &CreatureManager::getSpawn, &g_creatures);
g_lua.bindSingletonFunction("g_creatures", "getSpawnForPlacePos", &CreatureManager::getSpawnForPlacePos, &g_creatures);
g_lua.bindSingletonFunction("g_creatures", "addSpawn", &CreatureManager::addSpawn, &g_creatures);
g_lua.bindSingletonFunction("g_creatures", "loadMonsters", &CreatureManager::loadMonsters, &g_creatures);
g_lua.bindSingletonFunction("g_creatures", "loadNpcs", &CreatureManager::loadNpcs, &g_creatures);
@@ -162,6 +192,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "useWith", &Game::useWith, &g_game);
g_lua.bindSingletonFunction("g_game", "useInventoryItem", &Game::useInventoryItem, &g_game);
g_lua.bindSingletonFunction("g_game", "useInventoryItemWith", &Game::useInventoryItemWith, &g_game);
g_lua.bindSingletonFunction("g_game", "findItemInContainers", &Game::findItemInContainers, &g_game);
g_lua.bindSingletonFunction("g_game", "open", &Game::open, &g_game);
g_lua.bindSingletonFunction("g_game", "openParent", &Game::openParent, &g_game);
g_lua.bindSingletonFunction("g_game", "close", &Game::close, &g_game);
@@ -194,9 +225,11 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "removeVip", &Game::removeVip, &g_game);
g_lua.bindSingletonFunction("g_game", "setChaseMode", &Game::setChaseMode, &g_game);
g_lua.bindSingletonFunction("g_game", "setFightMode", &Game::setFightMode, &g_game);
g_lua.bindSingletonFunction("g_game", "setPVPMode", &Game::setPVPMode, &g_game);
g_lua.bindSingletonFunction("g_game", "setSafeFight", &Game::setSafeFight, &g_game);
g_lua.bindSingletonFunction("g_game", "getChaseMode", &Game::getChaseMode, &g_game);
g_lua.bindSingletonFunction("g_game", "getFightMode", &Game::getFightMode, &g_game);
g_lua.bindSingletonFunction("g_game", "getPVPMode", &Game::getPVPMode, &g_game);
g_lua.bindSingletonFunction("g_game", "isSafeFight", &Game::isSafeFight, &g_game);
g_lua.bindSingletonFunction("g_game", "inspectNpcTrade", &Game::inspectNpcTrade, &g_game);
g_lua.bindSingletonFunction("g_game", "buyItem", &Game::buyItem, &g_game);
@@ -230,6 +263,7 @@ void Client::registerLuaFunctions()
g_lua.bindSingletonFunction("g_game", "isDead", &Game::isDead, &g_game);
g_lua.bindSingletonFunction("g_game", "isAttacking", &Game::isAttacking, &g_game);
g_lua.bindSingletonFunction("g_game", "isFollowing", &Game::isFollowing, &g_game);
g_lua.bindSingletonFunction("g_game", "isConnectionOk", &Game::isConnectionOk, &g_game);
g_lua.bindSingletonFunction("g_game", "getPing", &Game::getPing, &g_game);
g_lua.bindSingletonFunction("g_game", "getContainer", &Game::getContainer, &g_game);
g_lua.bindSingletonFunction("g_game", "getContainers", &Game::getContainers, &g_game);
@@ -293,6 +327,10 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<Container>("getContainerItem", &Container::getContainerItem);
g_lua.bindClassMemberFunction<Container>("hasParent", &Container::hasParent);
g_lua.bindClassMemberFunction<Container>("isClosed", &Container::isClosed);
g_lua.bindClassMemberFunction<Container>("isUnlocked", &Container::isUnlocked);
g_lua.bindClassMemberFunction<Container>("hasPages", &Container::hasPages);
g_lua.bindClassMemberFunction<Container>("getSize", &Container::getSize);
g_lua.bindClassMemberFunction<Container>("getFirstIndex", &Container::getFirstIndex);
g_lua.registerClass<Thing>();
g_lua.bindClassMemberFunction<Thing>("setId", &Thing::setId);
@@ -335,6 +373,7 @@ void Client::registerLuaFunctions()
g_lua.registerClass<House>();
g_lua.bindClassStaticFunction<House>("create", []{ return HousePtr(new House); });
g_lua.bindClassMemberFunction<House>("setId", &House::setId);
g_lua.bindClassMemberFunction<House>("getId", &House::getId);
g_lua.bindClassMemberFunction<House>("setName", &House::setName);
g_lua.bindClassMemberFunction<House>("getName", &House::getName);
g_lua.bindClassMemberFunction<House>("setTownId", &House::setTownId);
@@ -343,6 +382,9 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<House>("getTile", &House::getTile);
g_lua.bindClassMemberFunction<House>("setEntry", &House::setEntry);
g_lua.bindClassMemberFunction<House>("getEntry", &House::getEntry);
g_lua.bindClassMemberFunction<House>("addDoor", &House::addDoor);
g_lua.bindClassMemberFunction<House>("removeDoor", &House::removeDoor);
g_lua.bindClassMemberFunction<House>("removeDoorById", &House::removeDoorById);
g_lua.bindClassMemberFunction<House>("setSize", &House::setSize);
g_lua.bindClassMemberFunction<House>("getSize", &House::getSize);
g_lua.bindClassMemberFunction<House>("setRent", &House::setRent);
@@ -409,6 +451,7 @@ void Client::registerLuaFunctions()
g_lua.registerClass<ItemType>();
g_lua.bindClassMemberFunction<ItemType>("getServerId", &ItemType::getServerId);
g_lua.bindClassMemberFunction<ItemType>("getClientId", &ItemType::getClientId);
g_lua.bindClassMemberFunction<ItemType>("isWritable", &ItemType::isWritable);
g_lua.registerClass<ThingType>();
g_lua.bindClassMemberFunction<ThingType>("getId", &ThingType::getId);
@@ -420,11 +463,32 @@ void Client::registerLuaFunctions()
g_lua.bindClassStaticFunction<Item>("create", &Item::create);
g_lua.bindClassStaticFunction<Item>("createOtb", &Item::createFromOtb);
g_lua.bindClassMemberFunction<Item>("clone", &Item::clone);
g_lua.bindClassMemberFunction<Item>("getContainerItems", &Item::getContainerItems);
g_lua.bindClassMemberFunction<Item>("getContainerItem", &Item::getContainerItem);
g_lua.bindClassMemberFunction<Item>("addContainerItem", &Item::addContainerItem);
g_lua.bindClassMemberFunction<Item>("addContainerItemIndexed", &Item::addContainerItemIndexed);
g_lua.bindClassMemberFunction<Item>("removeContainerItem", &Item::removeContainerItem);
g_lua.bindClassMemberFunction<Item>("clearContainerItems", &Item::clearContainerItems);
g_lua.bindClassMemberFunction<Item>("getContainerItem", &Item::getContainerItem);
g_lua.bindClassMemberFunction<Item>("setCount", &Item::setCount);
g_lua.bindClassMemberFunction<Item>("getCount", &Item::getCount);
g_lua.bindClassMemberFunction<Item>("getSubType", &Item::getSubType);
g_lua.bindClassMemberFunction<Item>("getId", &Item::getId);
g_lua.bindClassMemberFunction<Item>("getServerId", &Item::getServerId);
g_lua.bindClassMemberFunction<Item>("getName", &Item::getName);
g_lua.bindClassMemberFunction<Item>("getDescription", &Item::getDescription);
g_lua.bindClassMemberFunction<Item>("getText", &Item::getText);
g_lua.bindClassMemberFunction<Item>("setDescription", &Item::setDescription);
g_lua.bindClassMemberFunction<Item>("setText", &Item::setText);
g_lua.bindClassMemberFunction<Item>("getUniqueId", &Item::getUniqueId);
g_lua.bindClassMemberFunction<Item>("getActionId", &Item::getActionId);
g_lua.bindClassMemberFunction<Item>("setUniqueId", &Item::setUniqueId);
g_lua.bindClassMemberFunction<Item>("setActionId", &Item::setActionId);
g_lua.bindClassMemberFunction<Item>("getTeleportDestination", &Item::getTeleportDestination);
g_lua.bindClassMemberFunction<Item>("setTeleportDestination", &Item::setTeleportDestination);
g_lua.bindClassMemberFunction<Item>("isStackable", &Item::isStackable);
g_lua.bindClassMemberFunction<Item>("isMarketable", &Item::isMarketable);
g_lua.bindClassMemberFunction<Item>("isFluidContainer", &Item::isFluidContainer);
g_lua.bindClassMemberFunction<Item>("getMarketData", &Item::getMarketData);
g_lua.bindClassMemberFunction<Item>("getClothSlot", &Item::getClothSlot);
@@ -521,6 +585,14 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<Tile>("isClickable", &Tile::isClickable);
g_lua.bindClassMemberFunction<Tile>("isPathable", &Tile::isPathable);
g_lua.bindClassMemberFunction<Tile>("overwriteMinimapColor", &Tile::overwriteMinimapColor);
g_lua.bindClassMemberFunction<Tile>("select", &Tile::select);
g_lua.bindClassMemberFunction<Tile>("unselect", &Tile::unselect);
g_lua.bindClassMemberFunction<Tile>("isSelected", &Tile::isSelected);
g_lua.bindClassMemberFunction<Tile>("remFlag", &Tile::remFlag);
g_lua.bindClassMemberFunction<Tile>("setFlag", &Tile::setFlag);
g_lua.bindClassMemberFunction<Tile>("setFlags", &Tile::setFlags);
g_lua.bindClassMemberFunction<Tile>("getFlags", &Tile::getFlags);
g_lua.bindClassMemberFunction<Tile>("hasFlag", &Tile::hasFlag);
g_lua.registerClass<UIItem, UIWidget>();
g_lua.bindClassStaticFunction<UIItem>("create", []{ return UIItemPtr(new UIItem); });
@@ -538,6 +610,13 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIItem>("isVirtual", &UIItem::isVirtual);
g_lua.bindClassMemberFunction<UIItem>("isItemVisible", &UIItem::isItemVisible);
g_lua.registerClass<UISprite, UIWidget>();
g_lua.bindClassStaticFunction<UISprite>("create", []{ return UISpritePtr(new UISprite); });
g_lua.bindClassMemberFunction<UISprite>("setSpriteId", &UISprite::setSpriteId);
g_lua.bindClassMemberFunction<UISprite>("clearSprite", &UISprite::clearSprite);
g_lua.bindClassMemberFunction<UISprite>("getSpriteId", &UISprite::getSpriteId);
g_lua.bindClassMemberFunction<UISprite>("setSpriteColor", &UISprite::setSpriteColor);
g_lua.registerClass<UICreature, UIWidget>();
g_lua.bindClassStaticFunction<UICreature>("create", []{ return UICreaturePtr(new UICreature); } );
g_lua.bindClassMemberFunction<UICreature>("setCreature", &UICreature::setCreature);
@@ -549,6 +628,7 @@ void Client::registerLuaFunctions()
g_lua.registerClass<UIMap, UIWidget>();
g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); });
g_lua.bindClassMemberFunction<UIMap>("drawSelf", &UIMap::drawSelf);
g_lua.bindClassMemberFunction<UIMap>("movePixels", &UIMap::movePixels);
g_lua.bindClassMemberFunction<UIMap>("setZoom", &UIMap::setZoom);
g_lua.bindClassMemberFunction<UIMap>("zoomIn", &UIMap::zoomIn);
g_lua.bindClassMemberFunction<UIMap>("zoomOut", &UIMap::zoomOut);
@@ -557,6 +637,8 @@ void Client::registerLuaFunctions()
g_lua.bindClassMemberFunction<UIMap>("setMaxZoomIn", &UIMap::setMaxZoomIn);
g_lua.bindClassMemberFunction<UIMap>("setMaxZoomOut", &UIMap::setMaxZoomOut);
g_lua.bindClassMemberFunction<UIMap>("setMultifloor", &UIMap::setMultifloor);
g_lua.bindClassMemberFunction<UIMap>("lockVisibleFloor", &UIMap::lockVisibleFloor);
g_lua.bindClassMemberFunction<UIMap>("unlockVisibleFloor", &UIMap::unlockVisibleFloor);
g_lua.bindClassMemberFunction<UIMap>("setVisibleDimension", &UIMap::setVisibleDimension);
g_lua.bindClassMemberFunction<UIMap>("setViewMode", &UIMap::setViewMode);
g_lua.bindClassMemberFunction<UIMap>("setAutoViewMode", &UIMap::setAutoViewMode);

View File

@@ -25,13 +25,15 @@
int push_luavalue(const Outfit& outfit)
{
g_lua.newTable();
g_lua.createTable(0, 8);
g_lua.pushInteger(outfit.getId());
g_lua.setField("type");
g_lua.pushInteger(outfit.getAuxId());
g_lua.setField("auxType");
if(g_game.getFeature(Otc::GamePlayerAddons)) {
g_lua.pushInteger(outfit.getAddons());
g_lua.setField("addons");
}
g_lua.pushInteger(outfit.getHead());
g_lua.setField("head");
g_lua.pushInteger(outfit.getBody());
@@ -54,8 +56,10 @@ bool luavalue_cast(int index, Outfit& outfit)
outfit.setId(g_lua.popInteger());
g_lua.getField("auxType", index);
outfit.setAuxId(g_lua.popInteger());
if(g_game.getFeature(Otc::GamePlayerAddons)) {
g_lua.getField("addons", index);
outfit.setAddons(g_lua.popInteger());
}
g_lua.getField("head", index);
outfit.setHead(g_lua.popInteger());
g_lua.getField("body", index);
@@ -76,7 +80,7 @@ bool luavalue_cast(int index, Outfit& outfit)
int push_luavalue(const Position& pos)
{
if(pos.isValid()) {
g_lua.newTable();
g_lua.createTable(0, 3);
g_lua.pushInteger(pos.x);
g_lua.setField("x");
g_lua.pushInteger(pos.y);
@@ -104,7 +108,7 @@ bool luavalue_cast(int index, Position& pos)
int push_luavalue(const MarketData& data)
{
g_lua.newTable();
g_lua.createTable(0, 6);
g_lua.pushInteger(data.category);
g_lua.setField("category");
g_lua.pushString(data.name);

View File

@@ -39,6 +39,7 @@ TilePtr Map::m_nulltile;
void Map::init()
{
resetAwareRange();
m_animationFlags |= Animation_Show;
}
void Map::terminate()
@@ -138,7 +139,7 @@ void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos)
int y = 12 - 48 * t / (float)Otc::ANIMATED_TEXT_DURATION;
offset += Point(0, y);
}
offset.y = std::min(offset.y, 12);
offset.y = std::min<int>(offset.y, 12);
animatedText->setOffset(offset);
}
m_animatedTexts.push_back(animatedText);
@@ -179,34 +180,34 @@ bool Map::removeThing(const ThingPtr& thing)
if(!thing)
return false;
notificateTileUpdate(thing->getPosition());
bool ret = false;
if(thing->isMissile()) {
MissilePtr missile = thing->static_self_cast<Missile>();
int z = missile->getPosition().z;
auto it = std::find(m_floorMissiles[z].begin(), m_floorMissiles[z].end(), missile);
if(it != m_floorMissiles[z].end()) {
m_floorMissiles[z].erase(it);
return true;
ret = true;
}
} else if(thing->isAnimatedText()) {
AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>();
auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText);
if(it != m_animatedTexts.end()) {
m_animatedTexts.erase(it);
return true;
ret = true;
}
} else if(thing->isStaticText()) {
StaticTextPtr staticText = thing->static_self_cast<StaticText>();
auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText);
if(it != m_staticTexts.end()) {
m_staticTexts.erase(it);
return true;
ret = true;
}
} else if(const TilePtr& tile = thing->getTile())
return tile->removeThing(thing);
ret = tile->removeThing(thing);
return false;
notificateTileUpdate(thing->getPosition());
return ret;
}
bool Map::removeThingByPos(const Position& pos, int stackPos)
@@ -216,6 +217,42 @@ bool Map::removeThingByPos(const Position& pos, int stackPos)
return false;
}
void Map::colorizeThing(const ThingPtr& thing, const Color& color)
{
if(!thing)
return;
if(thing->isItem())
thing->static_self_cast<Item>()->setColor(color);
else if(thing->isCreature()) {
const TilePtr& tile = thing->getTile();
assert(tile);
const ThingPtr& topThing = tile->getTopThing();
assert(topThing);
topThing->static_self_cast<Item>()->setColor(color);
}
}
void Map::removeThingColor(const ThingPtr& thing)
{
if(!thing)
return;
if(thing->isItem())
thing->static_self_cast<Item>()->setColor(Color::alpha);
else if(thing->isCreature()) {
const TilePtr& tile = thing->getTile();
assert(tile);
const ThingPtr& topThing = tile->getTopThing();
assert(topThing);
topThing->static_self_cast<Item>()->setColor(Color::alpha);
}
}
StaticTextPtr Map::getStaticText(const Position& pos)
{
for(auto staticText : m_staticTexts) {
@@ -305,6 +342,90 @@ void Map::cleanTile(const Position& pos)
}
}
void Map::setShowZone(tileflags_t zone, bool show)
{
if(show)
m_zoneFlags |= (uint32)zone;
else
m_zoneFlags &= ~(uint32)zone;
}
void Map::setShowZones(bool show)
{
if(!show)
m_zoneFlags = 0;
else if(m_zoneFlags == 0)
m_zoneFlags = TILESTATE_HOUSE | TILESTATE_PROTECTIONZONE;
}
void Map::setZoneColor(tileflags_t zone, const Color& color)
{
if((m_zoneFlags & zone) == zone)
m_zoneColors[zone] = color;
}
void Map::setForceShowAnimations(bool force)
{
if(force) {
if(!(m_animationFlags & Animation_Force))
m_animationFlags |= Animation_Force;
} else
m_animationFlags &= ~Animation_Force;
}
bool Map::isForcingAnimations()
{
return (m_animationFlags & Animation_Force) == Animation_Force;
}
bool Map::isShowingAnimations()
{
return (m_animationFlags & Animation_Show) == Animation_Show;
}
void Map::setShowAnimations(bool show)
{
if(show) {
if(!(m_animationFlags & Animation_Show))
m_animationFlags |= Animation_Show;
} else
m_animationFlags &= ~Animation_Show;
}
void Map::beginGhostMode(float opacity)
{
g_painter->setOpacity(opacity);
}
void Map::endGhostMode()
{
g_painter->resetOpacity();
}
std::map<Position, ItemPtr> Map::findItemsById(uint16 clientId, uint32 max)
{
std::map<Position, ItemPtr> ret;
uint32 count = 0;
for(uint8_t z = 0; z <= Otc::MAX_Z; ++z) {
for(const auto& pair : m_tileBlocks[z]) {
const TileBlock& block = pair.second;
for(const TilePtr& tile : block.getTiles()) {
if(unlikely(!tile || tile->isEmpty()))
continue;
for(const ItemPtr& item : tile->getItems()) {
if(item->getId() == clientId) {
ret.insert(std::make_pair(tile->getPosition(), item));
if(++count >= max)
break;
}
}
}
}
}
return ret;
}
void Map::addCreature(const CreaturePtr& creature)
{
m_knownCreatures[creature->getId()] = creature;
@@ -330,7 +451,7 @@ void Map::removeCreatureById(uint32 id)
void Map::removeUnawareThings()
{
// remove creatures from tiles that we are not aware anymore
// remove creatures from tiles that we are not aware of anymore
for(const auto& pair : m_knownCreatures) {
const CreaturePtr& creature = pair.second;
if(!isAwareOfPosition(creature->getPosition()))
@@ -523,7 +644,7 @@ int Map::getFirstAwareFloor()
int Map::getLastAwareFloor()
{
if(m_centralPosition.z > Otc::SEA_FLOOR)
return std::min(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
return std::min<int>(m_centralPosition.z+Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
else
return Otc::SEA_FLOOR;
}
@@ -708,3 +829,5 @@ std::tuple<std::vector<Otc::Direction>, Otc::PathFindResult> Map::findPath(const
return ret;
}
/* vim: set ts=4 sw=4 et: */

View File

@@ -93,6 +93,11 @@ enum {
BLOCK_SIZE = 32
};
enum : uint8 {
Animation_Force,
Animation_Show
};
class TileBlock {
public:
TileBlock() { m_tiles.fill(nullptr); }
@@ -144,13 +149,15 @@ public:
bool loadOtcm(const std::string& fileName);
void saveOtcm(const std::string& fileName);
void loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar = 0);
void saveOtbm(const std::string& fileName, const UIWidgetPtr& pbar = 0);
void loadOtbm(const std::string& fileName);
void saveOtbm(const std::string& fileName);
// otbm attributes (description, size, etc.)
void setHouseFile(const std::string& file) { m_attribs.set(OTBM_ATTR_HOUSE_FILE, file); }
void setSpawnFile(const std::string& file) { m_attribs.set(OTBM_ATTR_SPAWN_FILE, file); }
void setDescription(const std::string& desc) { m_attribs.set(OTBM_ATTR_DESCRIPTION, desc); }
void clearDescriptions() { m_attribs.remove(OTBM_ATTR_DESCRIPTION); }
void setWidth(uint16 w) { m_attribs.set(OTBM_ATTR_WIDTH, w); }
void setHeight(uint16 h) { m_attribs.set(OTBM_ATTR_HEIGHT, h); }
@@ -168,6 +175,8 @@ public:
ThingPtr getThing(const Position& pos, int stackPos);
bool removeThing(const ThingPtr& thing);
bool removeThingByPos(const Position& pos, int stackPos);
void colorizeThing(const ThingPtr& thing, const Color& color);
void removeThingColor(const ThingPtr& thing);
StaticTextPtr getStaticText(const Position& pos);
@@ -179,6 +188,28 @@ public:
const TilePtr& getTile(const Position& pos);
void cleanTile(const Position& pos);
// tile zone related
void setShowZone(tileflags_t zone, bool show);
void setShowZones(bool show);
void setZoneColor(tileflags_t flag, const Color& color);
void setZoneOpacity(float opacity) { m_zoneOpacity = opacity; }
float getZoneOpacity() { return m_zoneOpacity; }
Color getZoneColor(tileflags_t flag) { return m_zoneColors[flag]; }
tileflags_t getZoneFlags() { return (tileflags_t)m_zoneFlags; }
bool showZones() { return m_zoneFlags != 0; }
bool showZone(tileflags_t zone) { return (m_zoneFlags & zone) == zone; }
void setForceShowAnimations(bool force);
bool isForcingAnimations();
bool isShowingAnimations();
void setShowAnimations(bool show);
void beginGhostMode(float opacity);
void endGhostMode();
std::map<Position, ItemPtr> findItemsById(uint16 clientId, uint32 max);
// known creature related
void addCreature(const CreaturePtr& creature);
CreaturePtr getCreatureById(uint32 id);
@@ -223,6 +254,11 @@ private:
std::vector<MapViewPtr> m_mapViews;
std::unordered_map<Position, std::string, PositionHasher> m_waypoints;
uint8 m_animationFlags;
uint32 m_zoneFlags;
std::array<Color, TILESTATE_LAST> m_zoneColors;
float m_zoneOpacity;
Light m_light;
Position m_centralPosition;
Rect m_tilesRect;
@@ -235,3 +271,5 @@ private:
extern Map g_map;
#endif
/* vim: set ts=4 sw=4 et: */

View File

@@ -32,18 +32,23 @@
#include <framework/xml/tinyxml.h>
#include <framework/ui/uiwidget.h>
void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
void Map::loadOtbm(const std::string& fileName)
{
FileStreamPtr fin = g_resources.openFile(fileName);
if(!fin)
stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName));
fin->cache();
try {
if(!g_things.isOtbLoaded())
stdext::throw_exception("OTB isn't loaded yet to load a map.");
if(fin->getU32())
stdext::throw_exception("Unknown file version detected");
FileStreamPtr fin = g_resources.openFile(fileName);
if(!fin)
stdext::throw_exception(stdext::format("Unable to load map '%s'", fileName));
fin->cache();
char identifier[4];
if(fin->read(identifier, 1, 4) < 4)
stdext::throw_exception("Could not read file identifier");
if(memcmp(identifier, "OTBM", 4) != 0 && memcmp(identifier, "\0\0\0\0", 4) != 0)
stdext::throw_exception(stdext::format("Invalid file identifier detected: %s", identifier));
BinaryTreePtr root = fin->getBinaryTree();
if(root->getU8())
@@ -94,7 +99,6 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
for(const BinaryTreePtr& nodeMapData : node->getChildren()) {
uint8 mapDataType = nodeMapData->getU8();
if(mapDataType == OTBM_TILE_AREA) {
Position basePos;
basePos.x = nodeMapData->getU16();
basePos.y = nodeMapData->getU16();
@@ -102,7 +106,7 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
for(const BinaryTreePtr &nodeTile : nodeMapData->getChildren()) {
uint8 type = nodeTile->getU8();
if(type != OTBM_TILE && type != OTBM_HOUSETILE)
if(unlikely(type != OTBM_TILE && type != OTBM_HOUSETILE))
stdext::throw_exception(stdext::format("invalid node tile type %d", (int)type));
HousePtr house = nullptr;
@@ -136,7 +140,6 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
if((_flags & TILESTATE_REFRESH) == TILESTATE_REFRESH)
flags |= TILESTATE_REFRESH;
break;
}
case OTBM_ATTR_ITEM: {
@@ -151,7 +154,7 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
}
for(const BinaryTreePtr& nodeItem : nodeTile->getChildren()) {
if(nodeItem->getU8() != OTBM_ITEM)
if(unlikely(nodeItem->getU8() != OTBM_ITEM))
stdext::throw_exception("invalid item node");
ItemPtr item = Item::createFromOtb(nodeItem->getU16());
@@ -178,9 +181,8 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
if(const TilePtr& tile = getTile(pos)) {
if(house)
tile->setHouseId(house->getId());
tile->setFlags((tileflags_t)flags);
//if(!(++pbarvalue % 8192) && pbar);
tile->setFlag(TILESTATE_HOUSE);
tile->setFlag(flags);
}
}
} else if(mapDataType == OTBM_TOWNS) {
@@ -197,10 +199,8 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
townCoords.y = nodeTown->getU16();
townCoords.z = nodeTown->getU8();
if(!(town = g_towns.getTown(townId))) {
town = TownPtr(new Town(townId, townName, townCoords));
g_towns.addTown(town);
}
if(!(town = g_towns.getTown(townId)))
g_towns.addTown(TownPtr(new Town(townId, townName, townCoords)));
}
} else if(mapDataType == OTBM_WAYPOINTS && headerVersion > 1) {
for(const BinaryTreePtr &nodeWaypoint : nodeMapData->getChildren()) {
@@ -222,10 +222,14 @@ void Map::loadOtbm(const std::string& fileName, const UIWidgetPtr& pbar)
}
fin->close();
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to load '%s': %s", fileName, e.what()));
}
}
void Map::saveOtbm(const std::string& fileName, const UIWidgetPtr&/* pbar*/)
void Map::saveOtbm(const std::string& fileName)
{
try {
FileStreamPtr fin = g_resources.createFile(fileName);
if(!fin)
stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName));
@@ -280,25 +284,14 @@ void Map::saveOtbm(const std::string& fileName, const UIWidgetPtr&/* pbar*/)
root->startNode(OTBM_MAP_DATA);
{
// own description.
for(const auto& desc : getDescriptions()) {
root->addU8(OTBM_ATTR_DESCRIPTION);
root->addString(desc);
}
root->addString(m_attribs.get<std::string>(OTBM_ATTR_DESCRIPTION));
// special one
root->addU8(OTBM_ATTR_DESCRIPTION);
root->addString(stdext::format("Saved with %s v%s", g_app.getName(), g_app.getVersion()));
// spawn file.
root->addU8(OTBM_ATTR_SPAWN_FILE);
root->addString(spawnFile);
// house file.
if(version > 1) {
root->addU8(OTBM_ATTR_HOUSE_FILE);
root->addString(houseFile);
}
int px = -1, py = -1, pz =-1;
bool firstNode = true;
@@ -307,11 +300,11 @@ void Map::saveOtbm(const std::string& fileName, const UIWidgetPtr&/* pbar*/)
for(const auto& it : m_tileBlocks[z]) {
const TileBlock& block = it.second;
for(const TilePtr& tile : block.getTiles()) {
if(!tile || tile->isEmpty())
if(unlikely(!tile || tile->isEmpty()))
continue;
const Position& pos = tile->getPosition();
if(!pos.isValid())
if(unlikely(!pos.isValid()))
continue;
if(pos.x < px || pos.x >= px + 256
@@ -365,21 +358,26 @@ void Map::saveOtbm(const std::string& fileName, const UIWidgetPtr&/* pbar*/)
root->startNode(OTBM_TOWNS);
for(const TownPtr& town : g_towns.getTowns()) {
root->startNode(OTBM_TOWN);
root->addU32(town->getId());
root->addString(town->getName());
Position townPos = town->getPos();
root->addPos(townPos.x, townPos.y, townPos.z);
root->endNode();
}
root->endNode();
if(version > 1) {
root->startNode(OTBM_WAYPOINTS);
for(const auto& it : m_waypoints) {
root->startNode(OTBM_WAYPOINT);
root->addString(it.second);
Position pos = it.first;
root->addPos(pos.x, pos.y, pos.z);
root->endNode();
}
root->endNode();
}
@@ -390,6 +388,9 @@ void Map::saveOtbm(const std::string& fileName, const UIWidgetPtr&/* pbar*/)
fin->flush();
fin->close();
} catch(std::exception& e) {
g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what()));
}
}
bool Map::loadOtcm(const std::string& fileName)
@@ -537,3 +538,5 @@ void Map::saveOtcm(const std::string& fileName)
g_logger.error(stdext::format("failed to save OTCM map: %s", e.what()));
}
}
/* vim: set ts=4 sw=4 et: */

View File

@@ -85,11 +85,22 @@ void MapView::draw(const Rect& rect)
Position cameraPosition = getCameraPosition();
int drawFlags = 0;
// First branch:
// This is unlikely to be false because a lot of us
// don't wanna hear their GPU fan while playing a
// 2D game.
//
// Second & Third branch:
// This is likely to be true since not many people have
// low-end graphics cards.
if(unlikely(g_map.isForcingAnimations()) || (likely(g_map.isShowingAnimations()) && m_viewMode == NEAR_VIEW))
drawFlags = Otc::DrawAnimations;
if(m_viewMode == NEAR_VIEW)
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls |
Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles | Otc::DrawAnimations;
drawFlags |= Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls |
Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles;
else
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems;
drawFlags |= Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems;
if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) {
m_framebuffer->bind();
@@ -159,7 +170,7 @@ void MapView::draw(const Rect& rect)
}
if(m_shaderSwitchDone && m_shader && m_fadeInTime > 0)
fadeOpacity = std::min(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.0f);
fadeOpacity = std::min<float>(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.0f);
Rect srcRect = calcFramebufferSource(rect.size());
Point drawOffset = srcRect.topLeft();
@@ -311,7 +322,7 @@ void MapView::updateVisibleTilesCache(int start)
// loop through / diagonals beginning at top left and going to top right
for(int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
// loop current diagonal tiles
int advance = std::max(diagonal - m_drawDimension.height(), 0);
int advance = std::max<int>(diagonal - m_drawDimension.height(), 0);
for(int iy = diagonal - advance, ix = advance; iy >= 0 && ix < m_drawDimension.width() && !stop; --iy, ++ix) {
// only start really looking tiles in the desired start
if(m_updateTilesPos < start) {
@@ -364,10 +375,10 @@ void MapView::updateVisibleTilesCache(int start)
};
for(int i=0;i<4;++i) {
int sx = std::max(lines[i].left(), area.left());
int ex = std::min(lines[i].right(), area.right());
int sy = std::max(lines[i].top(), area.top());
int ey = std::min(lines[i].bottom(), area.bottom());
int sx = std::max<int>(lines[i].left(), area.left());
int ex = std::min<int>(lines[i].right(), area.right());
int sy = std::max<int>(lines[i].top(), area.top());
int ey = std::min<int>(lines[i].bottom(), area.bottom());
for(int qx=sx;qx<=ex;++qx)
for(int qy=sy;qy<=ey;++qy)
m_spiral[count++] = Point(qx, qy);
@@ -564,12 +575,38 @@ Position MapView::getPosition(const Point& point, const Size& mapSize)
return position;
}
void MapView::move(int x, int y)
{
m_moveOffset.x += x;
m_moveOffset.y += y;
int32_t tmp = m_moveOffset.x / 32;
bool requestTilesUpdate = false;
if(tmp != 0) {
m_customCameraPosition.x += tmp;
m_moveOffset.x %= 32;
requestTilesUpdate = true;
}
tmp = m_moveOffset.y / 32;
if(tmp != 0) {
m_customCameraPosition.y += tmp;
m_moveOffset.y %= 32;
requestTilesUpdate = true;
}
if(requestTilesUpdate)
requestVisibleTilesCacheUpdate();
}
Rect MapView::calcFramebufferSource(const Size& destSize)
{
float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
if(isFollowingCreature())
drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
else if(!m_moveOffset.isNull())
drawOffset += m_moveOffset * scaleFactor;
Size srcSize = destSize;
Size srcVisible = m_visibleDimension * m_tileSize;
@@ -600,7 +637,7 @@ int MapView::calcFirstVisibleFloor()
// limits to underground floors while under sea level
if(cameraPosition.z > Otc::SEA_FLOOR)
firstFloor = std::max(cameraPosition.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::UNDERGROUND_FLOOR);
firstFloor = std::max<int>(cameraPosition.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::UNDERGROUND_FLOOR);
// loop in 3x3 tiles around the camera
for(int ix = -1; ix <= 1 && firstFloor < cameraPosition.z; ++ix) {
@@ -608,21 +645,21 @@ int MapView::calcFirstVisibleFloor()
Position pos = cameraPosition.translated(ix, iy);
// process tiles that we can look through, e.g. windows, doors
if((ix == 0 && iy == 0) || (/*(std::abs(ix) != std::abs(iy)) && */g_map.isLookPossible(pos))) {
if((ix == 0 && iy == 0) || ((std::abs(ix) != std::abs(iy)) && g_map.isLookPossible(pos))) {
Position upperPos = pos;
Position coveredPos = pos;
while(coveredPos.coveredUp() && upperPos.up() && upperPos.z >= firstFloor) {
// check tiles physically above
TilePtr tile = g_map.getTile(upperPos);
if(tile && tile->limitsFloorsView()) {
if(tile && tile->limitsFloorsView(!g_map.isLookPossible(pos))) {
firstFloor = upperPos.z + 1;
break;
}
// check tiles geometrically above
tile = g_map.getTile(coveredPos);
if(tile && tile->limitsFloorsView()) {
if(tile && tile->limitsFloorsView(g_map.isLookPossible(pos))) {
firstFloor = coveredPos.z + 1;
break;
}
@@ -636,7 +673,7 @@ int MapView::calcFirstVisibleFloor()
}
// just ensure the that the floor is in the valid range
z = std::min(std::max(z, 0), (int)Otc::MAX_Z);
z = stdext::clamp<int>(z, 0, (int)Otc::MAX_Z);
return z;
}
@@ -658,10 +695,10 @@ int MapView::calcLastVisibleFloor()
}
if(m_lockedFirstVisibleFloor != -1)
z = std::max(m_lockedFirstVisibleFloor, z);
z = std::max<int>(m_lockedFirstVisibleFloor, z);
// just ensure the that the floor is in the valid range
z = std::min(std::max(z, 0), (int)Otc::MAX_Z);
z = stdext::clamp<int>(z, 0, (int)Otc::MAX_Z);
return z;
}
@@ -703,3 +740,5 @@ void MapView::setDrawLights(bool enable)
m_lightView = nullptr;
m_drawLights = enable;
}
/* vim: set ts=4 sw=4 et: */

View File

@@ -108,6 +108,8 @@ public:
void setDrawLights(bool enable);
bool isDrawingLights() { return m_drawLights; }
void move(int x, int y);
void setAnimated(bool animated) { m_animated = animated; requestVisibleTilesCacheUpdate(); }
bool isAnimating() { return m_animated; }
@@ -139,6 +141,7 @@ private:
Size m_optimizedSize;
Point m_virtualCenterOffset;
Point m_visibleCenterOffset;
Point m_moveOffset;
Position m_customCameraPosition;
stdext::boolean<true> m_mustUpdateVisibleTilesCache;
stdext::boolean<true> m_mustDrawVisibleTilesCache;

View File

@@ -29,7 +29,6 @@
#include <framework/graphics/painter.h>
#include <framework/graphics/image.h>
#include <framework/graphics/framebuffermanager.h>
#include <framework/graphics/ogl/textureogl.h>
#include <framework/core/resourcemanager.h>
#include <framework/core/filestream.h>
#include <zlib.h>
@@ -66,7 +65,7 @@ void MinimapBlock::update()
if(shouldDraw) {
if(!m_texture) {
m_texture = TexturePtr(new TextureOGL(image, true));
m_texture = TexturePtr(new Texture(image, true));
} else {
m_texture->uploadPixels(image, true);
}
@@ -201,7 +200,7 @@ void Minimap::updateTile(const Position& pos, const TilePtr& tile)
minimapTile.flags |= MinimapTileNotWalkable;
if(!tile->isPathable())
minimapTile.flags |= MinimapTileNotPathable;
minimapTile.speed = std::min((int)std::ceil(tile->getGroundSpeed() / 10.0f), 255);
minimapTile.speed = std::min<int>((int)std::ceil(tile->getGroundSpeed() / 10.0f), 255);
}
if(minimapTile != MinimapTile()) {

View File

@@ -189,6 +189,8 @@ public:
bool isInRange(const Position& pos, int minXRange, int maxXRange, int minYRange, int maxYRange) const {
return (pos.x >= x-minXRange && pos.x <= x+maxXRange && pos.y >= y-minYRange && pos.y <= y+maxYRange && pos.z == z);
}
// operator less than for std::map
bool operator<(const Position& other) const { return x < other.x || y < other.y || z < other.z; }
bool up(int n = 1) {
int nz = z-n;

View File

@@ -57,7 +57,7 @@ void buildMessageModesMap(int version) {
messageModesMap[Otc::MessageFailure] = 20;
messageModesMap[Otc::MessageBlue] = 21;
messageModesMap[Otc::MessageRed] = 22;
} else if(version >= 854) {
} else if(version >= 840) {
messageModesMap[Otc::MessageNone] = 0;
messageModesMap[Otc::MessageSay] = 1;
messageModesMap[Otc::MessageWhisper] = 2;
@@ -87,7 +87,7 @@ void buildMessageModesMap(int version) {
messageModesMap[Otc::MessageLook] = 25;
messageModesMap[Otc::MessageFailure] = 26;
messageModesMap[Otc::MessageBlue] = 27;
} else if(version >= 810) {
} else if(version >= 760) {
messageModesMap[Otc::MessageNone] = 0;
messageModesMap[Otc::MessageSay] = 1;
messageModesMap[Otc::MessageWhisper] = 2;

View File

@@ -102,6 +102,8 @@ namespace Proto {
GameServerCreatureSkull = 144,
GameServerCreatureParty = 145,
GameServerCreatureUnpass = 146,
GameServerCreatureMarks = 147,
GameServerPlayerHelpers = 148,
GameServerEditText = 150,
GameServerEditList = 151,
GameServerPlayerDataBasic = 159, // 950
@@ -109,6 +111,7 @@ namespace Proto {
GameServerPlayerSkills = 161,
GameServerPlayerState = 162,
GameServerClearTarget = 163,
GameServerPlayerModes = 167,
GameServerSpellDelay = 164, // 870
GameServerSpellGroupDelay = 165, // 870
GameServerMultiUseDelay = 166, // 870
@@ -142,7 +145,7 @@ namespace Proto {
GameServerMarketLeave = 247, // 944
GameServerMarketDetail = 248, // 944
GameServerMarketBrowse = 249, // 944
GameServerShowModalDialog = 250 // 960
GameServerModalDialog = 250 // 960
};
enum ClientOpcodes : uint8
@@ -247,7 +250,9 @@ namespace Proto {
enum CreatureType {
CreatureTypePlayer = 0,
CreatureTypeMonster,
CreatureTypeNpc
CreatureTypeNpc,
CreatureTypeSummonOwn,
CreatureTypeSummonOther
};
enum CreaturesIdRange {

View File

@@ -83,7 +83,7 @@ public:
void sendCloseRuleViolation(const std::string& reporter);
void sendCancelRuleViolation();
void sendCloseNpcChannel();
void sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight);
void sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight, Otc::PVPModes pvpMode);
void sendAttack(uint creatureId, uint seq);
void sendFollow(uint creatureId, uint seq);
void sendInviteToParty(uint creatureId);
@@ -125,6 +125,7 @@ public:
void addPosition(const OutputMessagePtr& msg, const Position& position);
private:
void parsePlayerHelpers(const InputMessagePtr& msg);
void parseMessage(const InputMessagePtr& msg);
void parsePendingGame(const InputMessagePtr& msg);
void parseEnterGame(const InputMessagePtr& msg);
@@ -178,6 +179,7 @@ private:
void parsePlayerSkills(const InputMessagePtr& msg);
void parsePlayerState(const InputMessagePtr& msg);
void parsePlayerCancelAttack(const InputMessagePtr& msg);
void parsePlayerModes(const InputMessagePtr& msg);
void parseSpellCooldown(const InputMessagePtr& msg);
void parseSpellGroupCooldown(const InputMessagePtr& msg);
void parseMultiUseCooldown(const InputMessagePtr& msg);
@@ -210,9 +212,10 @@ private:
void parseChannelEvent(const InputMessagePtr& msg);
void parseItemInfo(const InputMessagePtr& msg);
void parsePlayerInventory(const InputMessagePtr& msg);
void parseShowModalDialog(const InputMessagePtr& msg);
void parseModalDialog(const InputMessagePtr& msg);
void parseExtendedOpcode(const InputMessagePtr& msg);
void parseChangeMapAwareRange(const InputMessagePtr& msg);
void parseCreaturesMark(const InputMessagePtr& msg);
public:
void setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height);

View File

@@ -219,6 +219,9 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerClearTarget:
parsePlayerCancelAttack(msg);
break;
case Proto::GameServerPlayerModes:
parsePlayerModes(msg);
break;
case Proto::GameServerTalk:
parseTalk(msg);
break;
@@ -313,8 +316,8 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
parsePlayerInfo(msg);
break;
// PROTOCOL>=970
case Proto::GameServerShowModalDialog:
parseShowModalDialog(msg);
case Proto::GameServerModalDialog:
parseModalDialog(msg);
break;
// PROTOCOL>=980
case Proto::GameServerLoginSuccess:
@@ -323,6 +326,13 @@ void ProtocolGame::parseMessage(const InputMessagePtr& msg)
case Proto::GameServerEnterGame:
parseEnterGame(msg);
break;
case Proto::GameServerPlayerHelpers:
parsePlayerHelpers(msg);
break;
// PROTOCOL>=1000
case Proto::GameServerCreatureMarks:
parseCreaturesMark(msg);
break;
// otclient ONLY
case Proto::GameServerExtendedOpcode:
parseExtendedOpcode(msg);
@@ -379,13 +389,27 @@ void ProtocolGame::parseEnterGame(const InputMessagePtr& msg)
}
}
void ProtocolGame::parsePlayerHelpers(const InputMessagePtr& msg)
{
uint id = msg->getU32();
int helpers = msg->getU16();
CreaturePtr creature = g_map.getCreatureById(id);
if(creature)
g_game.processPlayerHelpers(helpers);
else
g_logger.traceError("could not get creature");
}
void ProtocolGame::parseGMActions(const InputMessagePtr& msg)
{
std::vector<uint8> actions;
int numViolationReasons;
if(g_game.getProtocolVersion() >= 854)
if(g_game.getProtocolVersion() >= 840)
numViolationReasons = 23;
else if(g_game.getProtocolVersion() >= 850)
numViolationReasons = 20;
else
numViolationReasons = 32;
@@ -535,7 +559,7 @@ void ProtocolGame::parseTileAddThing(const InputMessagePtr& msg)
Position pos = getPosition(msg);
int stackPos = -1;
if(g_game.getProtocolVersion() >= 854)
if(g_game.getProtocolVersion() >= 841)
stackPos = msg->getU8();
ThingPtr thing = getThing(msg);
@@ -603,13 +627,26 @@ void ProtocolGame::parseOpenContainer(const InputMessagePtr& msg)
std::string name = msg->getString();
int capacity = msg->getU8();
bool hasParent = (msg->getU8() != 0);
bool isUnlocked = false;
bool hasPages = false;
int containerSize = 0;
int firstIndex = 0;
if(g_game.getFeature(Otc::GameContainerPagination)) {
isUnlocked = (msg->getU8() != 0); // drag and drop
hasPages = (msg->getU8() != 0); // pagination
containerSize = msg->getU16(); // container size
firstIndex = msg->getU16(); // first index
}
int itemCount = msg->getU8();
std::vector<ItemPtr> items(itemCount);
for(int i = 0; i < itemCount; i++)
items[i] = getItem(msg);
g_game.processOpenContainer(containerId, containerItem, name, capacity, hasParent, items);
g_game.processOpenContainer(containerId, containerItem, name, capacity, hasParent, items, isUnlocked, hasPages, containerSize, firstIndex);
}
void ProtocolGame::parseCloseContainer(const InputMessagePtr& msg)
@@ -621,14 +658,23 @@ void ProtocolGame::parseCloseContainer(const InputMessagePtr& msg)
void ProtocolGame::parseContainerAddItem(const InputMessagePtr& msg)
{
int containerId = msg->getU8();
int slot = 0;
if(g_game.getFeature(Otc::GameContainerPagination)) {
slot = msg->getU16(); // slot
}
ItemPtr item = getItem(msg);
g_game.processContainerAddItem(containerId, item);
g_game.processContainerAddItem(containerId, item, slot);
}
void ProtocolGame::parseContainerUpdateItem(const InputMessagePtr& msg)
{
int containerId = msg->getU8();
int slot = msg->getU8();
int slot;
if(g_game.getFeature(Otc::GameContainerPagination)) {
slot = msg->getU16();
} else {
slot = msg->getU8();
}
ItemPtr item = getItem(msg);
g_game.processContainerUpdateItem(containerId, slot, item);
}
@@ -636,7 +682,16 @@ void ProtocolGame::parseContainerUpdateItem(const InputMessagePtr& msg)
void ProtocolGame::parseContainerRemoveItem(const InputMessagePtr& msg)
{
int containerId = msg->getU8();
int slot = msg->getU8();
int slot;
if(g_game.getFeature(Otc::GameContainerPagination)) {
slot = msg->getU16();
int itemId = msg->getU16();
if(itemId != 0)
getItem(msg, itemId);
} else {
slot = msg->getU8();
}
g_game.processContainerRemoveItem(containerId, slot);
}
@@ -931,13 +986,24 @@ void ProtocolGame::parseCreatureUnpass(const InputMessagePtr& msg)
void ProtocolGame::parseEditText(const InputMessagePtr& msg)
{
uint id = msg->getU32();
int itemId = msg->getU16();
int itemId;
if(g_game.getProtocolVersion() >= 1010) {
// TODO: processEditText with ItemPtr as parameter
ItemPtr item = getItem(msg);
itemId = item->getId();
} else
itemId = msg->getU16();
int maxLength = msg->getU16();
std::string text = msg->getString();
std::string writter = msg->getString();
std::string date = msg->getString();
g_game.processEditText(id, itemId, maxLength, text, writter, date);
std::string writer = msg->getString();
std::string date = "";
if(g_game.getFeature(Otc::GameWritableDate))
date = msg->getString();
g_game.processEditText(id, itemId, maxLength, text, writer, date);
}
void ProtocolGame::parseEditList(const InputMessagePtr& msg)
@@ -1016,7 +1082,9 @@ void ProtocolGame::parsePlayerStats(const InputMessagePtr& msg)
double magicLevelPercent = msg->getU8();
double soul = msg->getU8();
double stamina = msg->getU16();
double stamina = 0;
if(g_game.getFeature(Otc::GamePlayerStamina))
stamina = msg->getU16();
double baseSpeed = 0;
if(g_game.getFeature(Otc::GameSkillsBase))
@@ -1070,7 +1138,12 @@ void ProtocolGame::parsePlayerSkills(const InputMessagePtr& msg)
void ProtocolGame::parsePlayerState(const InputMessagePtr& msg)
{
int states = msg->getU16();
int states;
if(g_game.getFeature(Otc::GamePlayerStateU16))
states = msg->getU16();
else
states = msg->getU8();
m_localPlayer->setStates(states);
}
@@ -1083,6 +1156,20 @@ void ProtocolGame::parsePlayerCancelAttack(const InputMessagePtr& msg)
g_game.processAttackCancel(seq);
}
void ProtocolGame::parsePlayerModes(const InputMessagePtr& msg)
{
int fightMode = msg->getU8();
int chaseMode = msg->getU8();
bool safeMode = msg->getU8();
int pvpMode = 0;
if(g_game.getFeature(Otc::GamePVPMode))
pvpMode = msg->getU8();
g_game.processPlayerModes((Otc::FightModes)fightMode, (Otc::ChaseModes)chaseMode, safeMode, (Otc::PVPModes)pvpMode);
}
void ProtocolGame::parseSpellCooldown(const InputMessagePtr& msg)
{
int spellId = msg->getU8();
@@ -1108,10 +1195,15 @@ void ProtocolGame::parseMultiUseCooldown(const InputMessagePtr& msg)
void ProtocolGame::parseTalk(const InputMessagePtr& msg)
{
if(g_game.getFeature(Otc::GameMessageStatements))
msg->getU32(); // channel statement guid
std::string name = g_game.formatCreatureName(msg->getString());
int level = msg->getU16();
int level = 0;
if(g_game.getFeature(Otc::GameMessageLevel))
level = msg->getU16();
Otc::MessageMode mode = Proto::translateMessageModeFromServer(msg->getU8());
int channelId = 0;
Position pos;
@@ -1358,8 +1450,9 @@ void ProtocolGame::parseFloorChangeDown(const InputMessagePtr& msg)
void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg)
{
Outfit currentOutfit = getOutfit(msg);
std::vector<std::tuple<int, std::string, int> > outfitList;
if(g_game.getFeature(Otc::GameNewOutfitProtocol)) {
int outfitCount = msg->getU8();
for(int i = 0; i < outfitCount; i++) {
int outfitId = msg->getU16();
@@ -1368,6 +1461,19 @@ void ProtocolGame::parseOpenOutfitWindow(const InputMessagePtr& msg)
outfitList.push_back(std::make_tuple(outfitId, outfitName, outfitAddons));
}
} else {
int outfitStart, outfitEnd;
if(g_game.getFeature(Otc::GameLooktypeU16)) {
outfitStart = msg->getU16();
outfitEnd = msg->getU16();
} else {
outfitStart = msg->getU8();
outfitEnd = msg->getU8();
}
for(int i = outfitStart; i <= outfitEnd; i++)
outfitList.push_back(std::make_tuple(i, "", 0));
}
std::vector<std::tuple<int, std::string> > mountList;
if(g_game.getFeature(Otc::GamePlayerMounts)) {
@@ -1502,18 +1608,18 @@ void ProtocolGame::parsePlayerInventory(const InputMessagePtr& msg)
}
}
void ProtocolGame::parseShowModalDialog(const InputMessagePtr& msg)
void ProtocolGame::parseModalDialog(const InputMessagePtr& msg)
{
uint32 id = msg->getU32();
std::string title = msg->getString();
std::string message = msg->getString();
int sizeButtons = msg->getU8();
std::map<int, std::string > buttonList;
std::vector<std::tuple<int, std::string> > buttonList;
for(int i = 0; i < sizeButtons; ++i) {
std::string value = msg->getString();
int id = msg->getU8();
buttonList[id] = value;
buttonList.push_back(std::make_tuple(id, value));
}
int sizeChoices = msg->getU8();
@@ -1534,21 +1640,9 @@ void ProtocolGame::parseShowModalDialog(const InputMessagePtr& msg)
escapeButton = msg->getU8();
}
msg->getU8(); // popup value (no clue what it is for)
bool priority = msg->getU8() == 0x01;
std::map<int, std::string>::iterator itEnter = buttonList.find(enterButton);
if(itEnter == buttonList.end()) {
g_logger.info(stdext::format("Enter button does not exist for dialog id: %d", id));
return;
}
std::map<int, std::string >::iterator itEscape = buttonList.find(escapeButton);
if(itEscape == buttonList.end()) {
g_logger.info(stdext::format("Escape button does not exist for dialog id: %d", id));
return;
}
g_game.processModalDialog(id, title, message, itEnter->first, itEnter->second, itEscape->first, itEscape->second, choiceList);
g_game.processModalDialog(id, title, message, buttonList, enterButton, escapeButton, choiceList, priority);
}
void ProtocolGame::parseExtendedOpcode(const InputMessagePtr& msg)
@@ -1579,13 +1673,35 @@ void ProtocolGame::parseChangeMapAwareRange(const InputMessagePtr& msg)
g_lua.callGlobalField("g_game", "onMapChangeAwareRange", xrange, yrange);
}
void ProtocolGame::parseCreaturesMark(const InputMessagePtr& msg)
{
int len = msg->getU8();
for(int i=0; i<len; ++i) {
uint32 id = msg->getU32();
bool isPermanent = msg->getU8() != 1;
uint8 markType = msg->getU8();
CreaturePtr creature = g_map.getCreatureById(id);
if(creature) {
if(isPermanent) {
if(markType == 0xff)
creature->hideStaticSquare();
else
creature->showStaticSquare(Color::from8bit(markType));
} else
creature->addTimedSquare(markType);
} else
g_logger.traceError("could not get creature");
}
}
void ProtocolGame::setMapDescription(const InputMessagePtr& msg, int x, int y, int z, int width, int height)
{
int startz, endz, zstep;
if(z > Otc::SEA_FLOOR) {
startz = z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
endz = std::min(z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
endz = std::min<int>(z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::MAX_Z);
zstep = 1;
}
else {
@@ -1643,14 +1759,21 @@ Outfit ProtocolGame::getOutfit(const InputMessagePtr& msg)
{
Outfit outfit;
int lookType = msg->getU16();
int lookType;
if(g_game.getFeature(Otc::GameLooktypeU16))
lookType = msg->getU16();
else
lookType = msg->getU8();
if(lookType != 0) {
outfit.setCategory(ThingCategoryCreature);
int head = msg->getU8();
int body = msg->getU8();
int legs = msg->getU8();
int feet = msg->getU8();
int addons = msg->getU8();
int addons = 0;
if(g_game.getFeature(Otc::GamePlayerAddons))
addons = msg->getU8();
if(!g_things.isValidDatId(lookType, ThingCategoryCreature)) {
g_logger.traceError(stdext::format("invalid outfit looktype %d", lookType));
@@ -1804,10 +1927,24 @@ CreaturePtr ProtocolGame::getCreature(const InputMessagePtr& msg, int type)
// emblem is sent only when the creature is not known
int emblem = -1;
bool unpass = true;
uint8 mark;
if(g_game.getFeature(Otc::GameCreatureEmblems) && !known)
emblem = msg->getU8();
if(g_game.getFeature(Otc::GameThingMarks)) {
msg->getU8(); // creature type for summons
mark = msg->getU8(); // mark
msg->getU16(); // helpers
if(creature) {
if(mark == 0xff)
creature->hideStaticSquare();
else
creature->showStaticSquare(Color::from8bit(mark));
}
}
if(g_game.getProtocolVersion() >= 854)
unpass = msg->getU8();
@@ -1860,6 +1997,10 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
if(item->getId() == 0)
stdext::throw_exception(stdext::format("unable to create item with invalid id %d", id));
if(g_game.getFeature(Otc::GameThingMarks)) {
msg->getU8(); // mark
}
if(item->isStackable() || item->isFluidContainer() || item->isSplash() || item->isChargeable())
item->setCountOrSubType(msg->getU8());
@@ -1867,7 +2008,7 @@ ItemPtr ProtocolGame::getItem(const InputMessagePtr& msg, int id)
if(item->getAnimationPhases() > 1) {
// 0xfe => random phase
// 0xff => async?
msg->getU8(); // phase
item->setAsync(msg->getU8() == 0xff);
}
}

View File

@@ -65,6 +65,8 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addU8(0); // first RSA byte must be 0
if(g_game.getProtocolVersion() >= 770)
{
// xtea key
generateXteaKey();
msg->addU32(m_xteaKey[0]);
@@ -72,6 +74,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addU32(m_xteaKey[2]);
msg->addU32(m_xteaKey[3]);
msg->addU8(0); // is gm set?
}
if(g_game.getFeature(Otc::GameAccountNames))
msg->addString(m_accountName);
@@ -96,6 +99,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
msg->addPaddingBytes(paddingBytes);
// encrypt with RSA
if(g_game.getProtocolVersion() >= 770)
msg->encryptRsa();
if(g_game.getFeature(Otc::GameProtocolChecksum))
@@ -103,6 +107,7 @@ void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRando
send(msg);
if(g_game.getProtocolVersion() >= 770)
enableXteaEncryption();
}
@@ -560,13 +565,17 @@ void ProtocolGame::sendCloseNpcChannel()
send(msg);
}
void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight)
void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight, Otc::PVPModes pvpMode)
{
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientChangeFightModes);
msg->addU8(fightMode);
msg->addU8(chaseMode);
msg->addU8(safeFight ? 0x01: 0x00);
if(g_game.getFeature(Otc::GamePVPMode))
msg->addU8(pvpMode);
send(msg);
}
@@ -688,11 +697,15 @@ void ProtocolGame::sendChangeOutfit(const Outfit& outfit)
{
OutputMessagePtr msg(new OutputMessage);
msg->addU8(Proto::ClientChangeOutfit);
if(g_game.getFeature(Otc::GameLooktypeU16))
msg->addU16(outfit.getId());
else
msg->addU8(outfit.getId());
msg->addU8(outfit.getHead());
msg->addU8(outfit.getBody());
msg->addU8(outfit.getLegs());
msg->addU8(outfit.getFeet());
if(g_game.getFeature(Otc::GamePlayerAddons))
msg->addU8(outfit.getAddons());
if(g_game.getFeature(Otc::GamePlayerMounts))
msg->addU16(outfit.getMount());

View File

@@ -48,11 +48,9 @@ bool SpriteManager::loadSpr(std::string file)
file = g_resources.guessFilePath(file, "spr");
m_spritesFile = g_resources.openFile(file);
// cache file buffer to avoid lags from hard drive
#ifndef MOBILE
m_spritesFile->cache();
#endif
m_signature = m_spritesFile->getU32();
m_spritesCount = g_game.getFeature(Otc::GameSpritesU32) ? m_spritesFile->getU32() : m_spritesFile->getU16();
m_spritesOffset = m_spritesFile->tell();

View File

@@ -28,7 +28,6 @@
#include <framework/graphics/graphics.h>
#include <framework/graphics/texture.h>
#include <framework/graphics/image.h>
#include <framework/graphics/ogl/textureogl.h>
#include <framework/graphics/texturemanager.h>
#include <framework/core/filestream.h>
#include <framework/otml/otml.h>
@@ -68,6 +67,17 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
attr -= 1;
}
if(g_game.getProtocolVersion() >= 1010) {
/* In 10.10 all attributes from 16 and up were
* incremented by 1 to make space for 16 as
* "No Movement Animation" flag.
*/
if(attr == 16)
attr = ThingAttrNoMoveAnimation;
else if(attr > 16)
attr -= 1;
}
switch(attr) {
case ThingAttrDisplacement: {
m_displacement.x = fin->getU16();
@@ -93,6 +103,7 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
m_attribs.set(attr, market);
break;
}
case ThingAttrUsable:
case ThingAttrElevation: {
m_elevation = fin->getU16();
m_attribs.set(attr, m_elevation);
@@ -118,7 +129,7 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
uint8 width = fin->getU8();
uint8 height = fin->getU8();
m_size = Size(width, height);
m_exactSize = (width > 1 || height > 1) ? std::min((int)fin->getU8(), std::max(width * 32, height * 32)) : 32;
m_exactSize = (width > 1 || height > 1) ? std::min<int>((int)fin->getU8(), std::max<int>(width * 32, height * 32)) : 32;
m_layers = fin->getU8();
m_numPatternX = fin->getU8();
m_numPatternY = fin->getU8();
@@ -127,8 +138,8 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS
int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases;
if(totalSprites == 0)
stdext::throw_exception("a thing type has no sprites");
// if(totalSprites == 0)
// stdext::throw_exception("a thing type has no sprites");
if(totalSprites > 4096)
stdext::throw_exception("a thing type has more than 4096 sprites");
@@ -269,10 +280,10 @@ const TexturePtr& ThingType::getTexture(int animationPhase)
for(int y = framePos.y; y < framePos.y + m_size.height() * Otc::TILE_PIXELS; ++y) {
uint8 *p = fullImage->getPixel(x,y);
if(p[3] != 0x00) {
drawRect.setTop (std::min(y, (int)drawRect.top()));
drawRect.setLeft (std::min(x, (int)drawRect.left()));
drawRect.setBottom(std::max(y, (int)drawRect.bottom()));
drawRect.setRight (std::max(x, (int)drawRect.right()));
drawRect.setTop (std::min<int>(y, (int)drawRect.top()));
drawRect.setLeft (std::min<int>(x, (int)drawRect.left()));
drawRect.setBottom(std::max<int>(y, (int)drawRect.bottom()));
drawRect.setRight (std::max<int>(x, (int)drawRect.right()));
}
}
}
@@ -284,7 +295,7 @@ const TexturePtr& ThingType::getTexture(int animationPhase)
}
}
}
animationPhaseTexture = TexturePtr(new TextureOGL(fullImage));
animationPhaseTexture = TexturePtr(new Texture(fullImage, true));
animationPhaseTexture->setSmooth(true);
}
return animationPhaseTexture;
@@ -351,5 +362,5 @@ int ThingType::getExactSize(int layer, int xPattern, int yPattern, int zPattern,
getTexture(animationPhase); // we must calculate it anyway.
int frameIndex = getTextureIndex(layer, xPattern, yPattern, zPattern);
Size size = m_texturesFramesOriginRects[animationPhase][frameIndex].size() - m_texturesFramesOffsets[animationPhase][frameIndex].toSize();
return std::max(size.width(), size.height());
return std::max<int>(size.width(), size.height());
}

View File

@@ -76,11 +76,13 @@ enum ThingAttr : uint8 {
ThingAttrLook = 31,
ThingAttrCloth = 32,
ThingAttrMarket = 33,
ThingAttrUsable = 34,
// additional
ThingAttrOpacity = 100,
ThingAttrNotPreWalkable = 101,
ThingAttrNoMoveAnimation = 253, // real value is 16, but we need to do this for backwards compatibility
ThingAttrChargeable = 254, // deprecated
ThingLastAttr = 255
};

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