Compare commits

..

122 Commits

Author SHA1 Message Date
zaoqi
094097ee71 cheat:Privilege+NoSendDamage 2018-02-08 12:23:28 +08:00
you
87e08b1b3a Add minetest.is_player (#7013)
* Add minetest.is_player

* First use for is_player
2018-02-05 15:17:10 +01:00
paramat
b7ff40eea2 Lua_api.txt: Document 'wielditem' visual in object properties 2018-02-05 05:07:36 +00:00
paramat
520293b4cb Item entity: Prevent motion in ignore nodes 2018-02-05 05:07:29 +00:00
Wuzzy
7b2687ffc6 Add kill chat command (#6992)
Replace minetest.* with core.* in 1 file
2018-02-04 19:21:41 +01:00
lisacvuk
4f5090ff68 Make hud_get return aligment, offset and size. (#7006)
* Make hud_get return aligment and offset.

* Return size aswell.
2018-02-04 10:17:46 +01:00
Dániel Juhász
735fc2a1f2 Remove unused light updating code
Also remove the unit test that tests the removed algorithms.
2018-02-04 03:16:45 +00:00
sfan5
cf0bcebc76 Refine movement anticheat again (#7004)
* Account for walking speed in vertical dir
* Avoid undefined behaviour due to division-by-zero
2018-02-02 23:34:09 +01:00
number Zero
49b65a5593 Fix liquid bottoms not being rendered 2018-01-30 21:13:24 +01:00
Wayward One
cc400581e2 Add Android drivers to the video_driver drop-down menu
Adds the Android video drivers ogles1 and ogles2 to the video_drivers drop-down menu
2018-01-30 21:11:47 +01:00
Loïc Blot
584d00a01c
Add minetest.bulk_set_node call + optimize Environment::set_node call (#6958)
* Add minetest.bulk_set_node call + experimental mod unittest

* Optimize set_node function to prevent triple lookup on contentfeatures

Do only one lookup for old, and try to merge old and new lookup if node is same than previous node

* Add benchmark function + optimize vector population to have real results
2018-01-30 00:30:02 +01:00
Wuzzy
3b4df956b1 Make chat command + privilege help slightly more accurate (#6964)
* Make chat command help slightly more accurate

* Slightly more accurate privilege help

* Simplify command/priv help

* More command/priv help tweaks
2018-01-29 23:39:36 +01:00
sfan5
de2c40c8fc Apply physics overrides correctly during anticheat calculations (#6970) 2018-01-28 10:21:21 +01:00
SmallJoker
7d3295e21f [CSM] HUD Fix not updating server HUDs caused by 4f688d5 2018-01-26 21:27:30 +01:00
red-001
4f688d5616 Fix issues with earlier CSM HUD commit (#6940)
The CSM HUD PR caused some strange behavior including aborts due to parts of it using some slightly hacky code, the event refactor changing how events are processed and a minor oversight.
2018-01-26 16:05:47 +01:00
Tre
3a5959ae6b Main menu: Change tabs to 'Start Game' and 'Join Game' (#6955) 2018-01-25 20:59:24 +01:00
lisacvuk
62c10e3d08 Disable fall damage when "immortal" group set (#6946) 2018-01-23 19:28:21 +01:00
Muhammad Nur Hidayat Yasuyoshi (MNH48.com)
04e5a65c65 Add missing languages from menu (#6953)
The following languages exist in Minetest PO folder were missing from the selection menu:
- dv (Dhivehi)
- ms (Malay)
- sl (Slovenian)
- sv (Swedish)
- sw (Swahili)
2018-01-23 19:27:41 +01:00
Paramat
01bc817fe0 Intersects_protection(): Move from Minetest Game to builtin (#6952)
A useful function that applies 'core.is_protected()' to a 3D lattice of
points evenly spaced throughout a defined volume, with a parameter for
the maximum spacing of points.
2018-01-23 19:04:58 +01:00
red-001
0425c6b8c8 CSM: Remove screenshot API
Reverted from commit 19960e26c672c6337f8c6ffbe27f2c6bca49750c
(* [CSM] add screenshot api lua)
2018-01-23 05:27:38 +00:00
red-001
d6050bee51 [CSM] Don't Load the package library (#6944)
Already removed by a latter step in CSM init so this just saves on pointless work.
2018-01-22 08:34:49 +01:00
Wayward One
684e70477d Change include from "cmake_config.h" to "config.h"
Fixes #6894
2018-01-21 23:25:04 +01:00
red-001
49ff1d2ea8 [CSM] Remove on_connect callback (#6941)
Fixes #6939
2018-01-21 18:27:27 +01:00
red-001
5dab742645 [CSM] Add functions to create particles and particlespawners. (#6072) 2018-01-20 23:31:53 +01:00
Loïc Blot
da80e8af8a
Add minetest issue template (#6936)
* Add minetest issue template

This permits end user to have a basic template permitting to do issue triage and for users to know what we expect.
2018-01-20 23:31:15 +01:00
Loic Blot
99c9e7a986 Game refactor [4/X]: keycache is now owned by InputHandler
* Make InputHandler own the key cache
* Add a helper function InputHandler::cancelPressed to avoid multiple similar calls in game.cpp
* Move RandomInputHandler::step definition into cpp file
2018-01-20 16:38:38 +01:00
Loic Blot
f5a006dce7 Game refactor [3/X]: Move keycache to inputhandler 2018-01-20 16:38:38 +01:00
Loic Blot
64fe79b53b Game refactor [2/X]: Various moves (profilergraph, nodePlacementPrediction, create_formspec_menu)
* Move profilergraph to dedicated files
* Move nodePlacementPrediction to Game class
* Rename create_formspec_menu to GUIFormSpecMenu::create
2018-01-20 16:38:38 +01:00
Loic Blot
362323cdc2 Game/Input refactor [1/X]: make RealInputHandler handle joystick inputs with standard input
Joystick input is a RealInputHandler only usage, make it intelligent and handle the joystick with keyboard direct.
This permits to remove many getters in game which should be owned by RealInputHandler
2018-01-20 16:38:38 +01:00
red-001
9649e47214 [CSM] Add basic HUD manipulation. (#6067)
* [CSM] Add basic HUD manipulation.

Workaround for on_connect not working right now.
2018-01-20 14:09:58 +01:00
paramat
d45e5da8ca Biomes: Add 'get heat', 'get humidity', 'get biome data' APIs
'get biome data' returns biome id, heat and humidity.
Clean up nearby lines in lua_api.txt.
2018-01-16 08:47:07 +00:00
red-001
4c0d4e4105 Load a texturepack from the 'textures' subfolder of a game 2018-01-16 08:45:17 +00:00
paramat
70a90bc83a Lua_api.txt: Various improvements
Improve documentation for formspec 'position' and 'anchor' elements.
Add missing documentation of 'animation' and 'glow' for particle spawners.
2018-01-15 04:42:14 +00:00
paramat
142474196a Mapgen folder: Update and improve copyright information of files 2018-01-15 04:42:08 +00:00
ezhh
5435b07d4e Lua_api.txt: Improve bullet point indentation consistency 2018-01-13 19:37:17 +00:00
Muhammad Rifqi Priyo Susanto
670f8afd18 Registration confirmation dialog: Fix grammar
Fixes commit 792752997c5ae2aaa4f54d0a2e2af2a96d7d1e9f.
2018-01-13 19:30:34 +00:00
ezhh
8349a3db10 Lua_api.txt: Add chat command params info 2018-01-13 14:59:18 +00:00
Muhammad Rifqi Priyo Susanto
792752997c Add confirmation on new player registration (#6849)
* Attempt to add registration confirmation

Using SRP auth mechanism, if server sent AUTH_MECHANISM_FIRST_SRP that means the player isn't exist.
Also tell player about the server and chosen username.
Local game has localhost as IP address of the server.
Add RenderingEngine::draw_menu_scene() to draw GUI and clouds background.
aborted -> connection_aborted

* Rewrite information message text

Client::promptConfirmRegister() -> Client::promptConfirmRegistration()
2018-01-13 12:07:16 +01:00
Lars Hofhansl
fad263dec9 Revert "Add an active object step time budget #6721"
This reverts commit 9c669016d1578a5c62f932c6ccb7a2b4b1e21f0a.
See #6907
2018-01-12 23:47:39 -08:00
Loïc Blot
7e50529867
Add a build step to test non freetype builds (#6908)
* Add a build step to test non freetype builds
2018-01-12 15:36:54 +01:00
Loic Blot
6f2fe8a554
Forget to fix non freetype build in StaticText 2018-01-12 08:36:38 +01:00
paramat
62872dabac Lua_api.txt: Fix, improve and add to Object Properties documentation
Correct 'automatic rotate' to be a number instead of a bool.
2018-01-12 05:51:40 +00:00
paramat
05e9e128b9 Lua_api.txt: Improve and complete ABM documentation
Document 'active object count (wider)'.
2018-01-12 05:45:39 +00:00
paramat
b8fc6a1955 Settingtypes.txt: Correct value of 'max block send distance' 2018-01-12 05:45:39 +00:00
number Zero
b4df0d67dd Fix ambient occlusion and dark lines at mapblock borders 2018-01-12 05:44:11 +00:00
Pedro Gimeno
f77f19a941 Fix off-by-one in log output line length (#6896) 2018-01-09 19:07:14 +01:00
Pedro Gimeno
63f4ee21b0 Fix buffer parameter not working in LuaPerlinNoiseMap::l_getMapSlice() 2018-01-08 20:32:15 +00:00
Sokomine
2992b774fe Lua API docs: Add warning that schematic placing is cached 2018-01-07 11:47:52 +01:00
rubenwardy
0a83c42dfd
Fix naming conventions of noise userdata 2018-01-07 01:06:18 +00:00
Loic Blot
9146c6a50f Don't recalculate statustext initial color everytime & review fixes 2018-01-05 20:59:30 +01:00
Loic Blot
f40f4143df GameUI refactor (part 7/7): Finish to include profiler things to GameUI
Other changes:
* Add GameUI clarification comment
* Move force_fog_off & disable_camera_update flags from GameUI to Game, it's not UI related
* Properly init GameUI::Flags
* Move toggleChat toggleHud & toggleProfiler to GameUI
* Add gameui.cpp to LINT whitelist
2018-01-05 20:59:30 +01:00
Loic Blot
02f82eca0b GameUI refactor (part 6/X): Move Game::guitext_profiler & showStatusTextSimple to GameUI class
Other enhancements:
* Move showStatusTextSimple to GameUI class & rename to showTranslatedStatusText
2018-01-05 20:59:30 +01:00
Loic Blot
326b0faa5e GameUI refactor (part 5/X): Move Game::guitext_chat to GameUI class
Other enhancements:
* Move update_profiler_gui to Game class
* Move updateChat to Game class
2018-01-05 20:59:30 +01:00
Loic Blot
fe510d90c1 GameUI refactor (part 4/X): Move Game::guitext_status, Game::m_statustext, GameRunData::statustext_time to GameUI class
Other enhancements:
* Simplify setStatusText to showStatusText, as it shows the label too (preventing almost every setStatusText to call setStatusTextTime(0)
* Add unittests
2018-01-05 20:59:30 +01:00
Loic Blot
aab3b18e4b GameUI refactor (part 3/X): Move Game::guitext2, Game::guitext_info, Game::infotext to GameUI class
Other enhancements:
* Drop unused GameRunData::time_of_day
* Little GameUI::update code path optimizations
2018-01-05 20:59:30 +01:00
Loic Blot
3a772e7ed6 GameUI refactor (part 2/X): Move Game::guitext to GameUI + enhancements on StaticText
Other enhancements:
* C++ friendlyness for addStaticText() -> move to static StaticText::add()
2018-01-05 20:59:30 +01:00
Loic Blot
0ebaed430a GameUI refactor (part 1/X): GameUI object creation + GameUIFlags move to GameUI
Game class is too huge and has too specialization on various subjects, like UI, formspecs, client, renderer. Start to move UI related things to GameUI object and cleanup them

Other improvements:
* updateChat: more performance on error messages by remove string copies
* Initialize all game class members in definition instead of constructor (with nullptr instead of NULL)
* Drop unused Client::show{GameChat,GameHud,Profiler,GameFog}
* Add GameUI unittests
2018-01-05 20:59:30 +01:00
Paramat
549cfd9db8 Biomes: Add vertical biome blend (#6853)
Add 'vertical blend' parameter to biome registration that defines how
many nodes above the biome's 'y max' limit the blend will extend.
2018-01-05 00:10:55 +01:00
Wayward One
ff2ceed381 Prevent Android from automatically locking display (#6876)
As mentioned in #5759
2018-01-04 21:18:18 +01:00
you
e7b44c3295 Fix Wstringop-overflow warning from util/srp.cpp (#6855)
* Fix Wstringop-overflow warning from util/srp.cpp
2018-01-04 14:25:20 +01:00
red-001
30821ad8de [CSM] Don't load the IO library. (#6087)
* [CSM] Don't load the IO library.

* Rename the function to match the Lua API function name and add a missing `const`

* Add a comment to explain some strange code and fix the other issues pointed out by shadowninja.
2018-01-04 08:21:12 +01:00
paramat
e7396a0c50 Mgv7: Add docs for the new floatland exponent parameter 2018-01-04 04:29:04 +00:00
rubenwardy
2af47e6f5f Fix crash on can_bypass_userlimit returning non-boolean 2018-01-03 14:18:13 +00:00
paramat
2aad3be2cb Mgv7 floatlands: Add exponent parameter
Allows more control over shape of floatland mountain terrain.
Terrain shape is unchanged.
2018-01-03 04:05:55 +00:00
Thomas--S
f3b9d87076 Connected Nodeboxes: Add disconnected boxes
The `disconnected_*` boxes are the opposites of the `connect_*` ones,
i.e. when a node has no suitable neighbours on the respective side, the
according disconnected box is drawn.

* disconnected_top
* disconnected_bottom
* disconnected_front
* disconnected_left
* disconnected_back
* disconnected_right
* disconnected (when there is *no* neighbour)
* disconnected_sides (when there are *no* neighbours to the sides)
2018-01-03 04:05:21 +00:00
Hybrid Dog
345e1041a2 Tool.cpp/.h, lua_api/l_util.cpp: Tidy up code and remove dead code 2018-01-03 04:01:15 +00:00
Hybrid Dog
d7c1f6c92e Tool getDigParams: Fix selecting the best fitting time
Previously, time was compared with result_time before dividing it by
the level difference.
2018-01-03 03:58:11 +00:00
Loïc Blot
0d6b58a772
Add unittests on ActiveObject and BanManager class (#6866)
* Add unittests on ActiveObject and BanManager class

This also permit to fix a bug in ban manager setting bans modified when no modification occurs
2018-01-01 18:48:52 +01:00
Loïc Blot
9f6d84aee3
Update README.md copyright notice too 2018-01-01 17:17:31 +01:00
Loïc Blot
f6a97c5c8a
Happy new year 2018: update LICENSE.txt 2018-01-01 17:13:37 +01:00
Muhammad Rifqi Priyo Susanto
4e652ea9dd Advanced settings: Add range check for float type 2017-12-26 21:55:58 +00:00
rubenwardy
026ad912af Fix rounding error in g/set_node caused by truncation to float 2017-12-26 21:55:08 +00:00
rubenwardy
0bcc2f33eb Add check to pause game on lost window focus 2017-12-26 21:53:45 +00:00
Dániel Juhász
2153965cf9 Line_of_sight: Improve using VoxelLineIterator
This commit rewrites line_of_sight with VoxelLineIterator.
Stepsize is no longer needed, the results will be always accurate.
2017-12-26 21:51:32 +00:00
sofar
ca64f564cd Wireshark plugin: Complete all commands. (#6841)
This still leaves plenty of data undecoded, but just having the packet
types all covered 100% for 0.4.16 will make looking at traces a lot
simpler than seeing half the packets show up as unknown.
2017-12-26 14:06:31 +01:00
number Zero
189daf87a0 Fix dancing text 2017-12-25 15:00:50 +01:00
Rob Blanckaert
787cd15c14 - Clear colors when reading property info.
- Set vertex colors on upright_sprites.
2017-12-25 15:00:25 +01:00
nOOb3167
8037eda42c Fix error if setting menu_last_game is not a valid game 2017-12-22 10:39:51 +00:00
nOOb3167
7354d0f3d8 Fix undefined behaviour on getting pointer to data in empty vector
`&vector[0]` is undefined if vector.empty(), causing build failure on MSVC
2017-12-22 10:39:25 +00:00
Vitaliy
257626ceed Fix wrong scrolling (#6809) 2017-12-21 20:58:06 +01:00
Paramat
d04c41ad80 Vector functions: Fix vector.direction() function, improve documentation (#6801)
vector.direction() now returns a normalised vector with direction p1 to p2.
2017-12-21 20:57:42 +01:00
Jordan Irwin
18b921015a Allow 'default' parameter in 'settings:get_bool' function
Default value is used when the setting key is not found in the config
file. If default value is not set, 'nil' is returned.

#6188
2017-12-17 15:27:37 +00:00
Ezhh
26c7e98e3d Adjust default console height
#6797
2017-12-17 04:38:54 +00:00
Ezhh
649eef9e4f Give subgames the ability to disallow specific mapgens (#6792) 2017-12-16 17:02:08 +01:00
Vitaliy
770eb09adc Fix items turning black (#6780) 2017-12-16 17:01:37 +01:00
Tre
fd71a7c163 Change Normal Map setting to be less ambigous 2017-12-16 04:05:36 +00:00
paramat
f1d2bc0965 Zoom: Add 'disabled by game or mod' message 2017-12-14 20:59:54 +00:00
paramat
8a99c8c94a Minimap messages: Improve 'disabled by server' message 2017-12-14 20:59:54 +00:00
ashtrayoz
abd8a30c05 Add callback to preserve node metadata as item metadata 2017-12-14 20:54:04 +00:00
SmallJoker
6e5109fd46
Chat: Remove prompt history duplicates (#6762) 2017-12-14 19:47:29 +01:00
adrido
551c12391a directiontables: Fix MSVC compiler error (#6785) 2017-12-14 19:46:42 +01:00
Vitaliy
2d9f0d344e Update light decoding table size (#6696)
Fix old undiminish_light bug
2017-12-12 19:29:55 +01:00
SmallJoker
b19241b9bc Builtin: Fix handle_node_drops crash with nil digger 2017-12-12 19:19:04 +01:00
Loïc Blot
308bb69eef
CSM fixes: load mods after flavours & add flavour to block mod loading (#6738)
* CSM fixes: load mods after flavours & add flavour to block mod loading

* Don't permit to load mods twice

* Prepare builtin integrity global algorithm

* Add missing doc & use a nicer byteflag for LOAD_CLIENT_MODS flavour

* flag typo fix

* Invert CSM_FL_LOOKUP_NODES & CSM_FL_LOAD_CLIENT_MODS ids
2017-12-11 17:33:44 +01:00
ashtrayoz
02cc257fe0 Lua_api.txt: Fix a spelling error 2017-12-10 14:30:37 +00:00
ThomasMonroe314
419799b831 F5 debug info: Add colons, use lowercase except for FPS and RTT 2017-12-10 14:24:28 +00:00
SmallJoker
03caa1e319 Damage: Remove damage ignore timer 2017-12-10 14:23:45 +00:00
adrido
d677f292cc Use std::vector instead of dynamic C-Array (#6744) 2017-12-10 09:07:24 +01:00
Paramat
da298a26ff Pointed thing to face pos: Use 'eye height' object property (#6754) 2017-12-09 14:30:26 +01:00
Lars Hofhansl
9c669016d1 Add an active object step time budget #6721
This can be set via the active_object_interval option.
2017-12-06 12:21:54 -08:00
you
e049405fdc Add coloured logs (#4549)
The setting log_colour can be used to en-/disable or autodetect it.
2017-12-06 18:50:39 +01:00
sfan5
4edf087090 Auth handler: Player deletion & Iterator (#6741)
* Add player deletion method to auth handler (fixes #6653)
* Support iterating over the auth database

There was no way to do this previously and a recent commit
broke doing this the "hacky" way by accessing `core.auth_table`.
2017-12-06 17:32:49 +01:00
Luis Cáceres
2b5341c518 Ensure no item stack is being held before crafting (#4779) 2017-12-06 17:32:05 +01:00
Lars Hofhansl
fd9f195fcc Use Irrlicht's mesh cache for animated meshes.
Fixes #6676.
Allow animated meshes to be cached in Irrlicht's builtin mesh cache.
Use Material.EmmissiveColor instead of manipulating the mesh' vertex colors.
2017-12-04 22:29:11 -08:00
paramat
f470cb7270 Zoom: Set zoom FOV per-player using a player object property
Remove player object property 'can zoom'.
Add player object property 'zoom fov'.
Remove clientside setting for 'zoom fov'.
Object property default is 15 degrees in creative mode, zoom disabled
in survival mode.

Needed due to zoom now loading and/or generating distant world
according to zoom FOV.

Update object properties serialisation version to 3.
2017-12-04 02:25:10 +00:00
stujones11
2507d32afe Android: Update build system for ndk-r15x
Add workarounds for ndk-r16.
2017-12-04 02:20:32 +00:00
Lars Hofhansl
2174298ffa Document extended meaning of active_object_send_range_blocks setting. 2017-12-03 17:56:39 -08:00
Lars Hofhansl
5a03b1f5f9 Optionally extend the active object in a players camera direction.
See #6667

By setting active_object_send_range_blocks > active_block_range a server admin
can allow clients to retrieve active objects futher out from the player at
relatively low cost to the server
(only objects in the players' view cone are considered).
2017-12-03 17:52:05 -08:00
raymoo
83b12ed481 Shut down mapgen threads before other shutdown tasks (#6689)
Solves some issues with ModStorage functionality in mapgen threads
that occurred when mapgen threads continued to run after the main
server thread had stopped. Also shuts down mapgen threads before
shutdown callbacks are called.
2017-12-03 10:28:35 +01:00
sfan5
ab947bd582 Update documentation regarding authentication handler and related functions
Properly document it instead of referencing the builtin handler as
authoritative "example" code.
Also adds definition of get_auth_handler() which was missing previously.
2017-12-01 09:30:03 +01:00
sfan5
36838ea22f Make core.auth_table private and structure builtin/auth.lua
If you give modders the ability to do something, they will...
2017-12-01 09:30:03 +01:00
ezhh
6efb453cdb Lua_api.txt: Remove MT version, fix spelling and clean up
Removes references to MT version in intro section.
Update bump_version.sh to no longer manage version information.
2017-12-01 05:43:30 +00:00
SmallJoker
089f594582 CAO/SAO: Nicer velocity-controlled, interpolated rotation property:
'automatic_face_movement_max_rotation_per_sec'.
Rotate towards the smaller angle.
2017-12-01 05:36:49 +00:00
Lars Hofhansl
f4fedfed07 Turn off verbose info message introduced accidentally with ae9b1aa 2017-11-29 22:55:37 -08:00
Bluebird
8b1a844e69 Fix spelling mistakes in client_lua_api.txt 2017-11-29 02:24:40 +00:00
paramat
18b914ac74 Client lua api documentation: .md -> .txt
For consistency, and for much improved access on Github.

On Github:
.md disables being able to link to a line, needed daily when
quickly referring people to particular lines.
Search returns results in .md files, but you cannot then click
the line number to go to that line in the document, making searching
difficult.
Line numbers are not displayed for .md documents.
2017-11-28 23:45:57 +00:00
ezhh
b312ab4455 Fix lua_api.txt indentation issues 2017-11-28 23:45:51 +00:00
Ezhh
c0dd4ea46a Fix documentation formatting for on_death callback 2017-11-28 00:38:35 +00:00
paramat
ea1ae07beb F5 Debug info: More compact, return to 2 lines
All data fits on width 960, most useful data still visible on width 800.
2017-11-27 01:28:47 +00:00
sfan5
813d819d15 Hint at problematic code when logging deprecated calls 2017-11-27 01:28:39 +00:00
paramat
03c11a73d8 Light curve: Add and tune mid boost gaussian
Create a closer match to the light curve of 0.4.16 stable.
Results in darker shadows while maintaining the 'brightness' and light
spread.
2017-11-27 01:28:29 +00:00
Ezhh
76eb3f2b7a Improve documentation for player:set_attribute() 2017-11-25 23:18:50 +00:00
179 changed files with 6183 additions and 4321 deletions

31
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,31 @@
##### Issue type
<!-- Pick one below and delete others -->
- Bug report
- Feature request
- Documentation issue
- Build issue
##### Minetest version
<!--
Paste Minetest version between quotes below
If you are on a devel version, please add git commit hash
You can use `minetest --version` to find it.
-->
```
```
##### OS / Hardware
<!-- General information about your hardware and operating system -->
Operating system:
CPU:
<!-- For graphical issues only -->
GPU model:
OpenGL version:
##### Summary
<!-- Describe your problem here -->
##### Steps to reproduce
<!-- For bug reports or build issues, explain how the problem happened -->

2
.gitignore vendored
View File

@ -86,6 +86,8 @@ locale/
*.gch
cmake-build-debug/
cmake-build-release/
cmake_config.h
cmake_config_githash.h
## Android build files
build/android/src/main/assets

View File

@ -70,6 +70,15 @@ matrix:
sources: &sources
- llvm-toolchain-trusty-5.0
- env: PLATFORM=Unix COMPILER=clang-5.0 FREETYPE=0
compiler: clang
os: linux
addons:
apt:
packages: ['clang-5.0', 'clang++-5.0']
sources: &sources
- llvm-toolchain-trusty-5.0
- env: PLATFORM=Unix COMPILER=clang-5.0 VALGRIND=1
compiler: clang
os: linux

View File

@ -32,7 +32,7 @@ License of Minetest source code
-------------------------------
Minetest
Copyright (C) 2010-2017 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -7,7 +7,7 @@ Minetest
An InfiniMiner/Minecraft inspired game.
Copyright (c) 2010-2017 Perttu Ahola <celeron55@gmail.com>
Copyright (c) 2010-2018 Perttu Ahola <celeron55@gmail.com>
and contributors (see source file comments and the version control log)
In case you downloaded the source code:

View File

@ -6,7 +6,8 @@ OS := $(shell uname)
# GPROF = 1
# build for build platform
APP_PLATFORM = android-9
API = 14
APP_PLATFORM = android-$(API)
ANDR_ROOT = $(shell pwd)
PROJ_ROOT = $(shell realpath $(ANDR_ROOT)/../..)
@ -30,7 +31,7 @@ TARGET_HOST = arm-linux
TARGET_ABI = armeabi-v7a
TARGET_LIBDIR = armeabi-v7a
TARGET_TOOLCHAIN = arm-linux-androideabi-
TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3
TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3 -O3 -D__ANDROID_API__=$(API)
TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON)
TARGET_ARCH = armv7
CROSS_PREFIX = arm-linux-androideabi-
@ -131,7 +132,6 @@ SQLITE3_URL = https://www.sqlite.org/2017/$(SQLITE3_FOLDER).zip
ANDROID_SDK = $(shell grep '^sdk\.dir' local.properties | sed 's/^.*=[[:space:]]*//')
ANDROID_NDK = $(shell grep '^ndk\.dir' local.properties | sed 's/^.*=[[:space:]]*//')
NDK_MODULE_PATH = $(ANDROID_NDK)/toolchains
#use interim target variable to switch leveldb on or off
ifeq ($(HAVE_LEVELDB),1)
@ -217,14 +217,10 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP)
if [ $$REFRESH -ne 0 ] ; then \
echo "changed timestamp for openal detected building..."; \
cd ${OPENAL_DIR}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} APP_ABI=${TARGET_ABI} \
TARGET_ARCH_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
touch ${OPENAL_TIMESTAMP}; \
touch ${OPENAL_TIMESTAMP_INT}; \
else \
@ -248,7 +244,6 @@ ogg_download :
git clone ${OGG_URL_GIT}|| exit 1; \
cd libvorbis-libogg-android ; \
patch -p1 < ${ANDR_ROOT}/patches/libvorbis-libogg-fpu.patch || exit 1; \
sed -i 's-:-?-' jni/Application.mk; \
fi
ogg : $(OGG_LIB)
@ -265,14 +260,10 @@ $(OGG_LIB): $(OGG_TIMESTAMP)
if [ $$REFRESH -ne 0 ] ; then \
echo "changed timestamp for ogg detected building..."; \
cd ${OGG_DIR}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \
APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
touch ${OGG_TIMESTAMP}; \
touch ${OGG_TIMESTAMP_INT}; \
else \
@ -317,7 +308,7 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) $(GMP_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
CC=${CROSS_PREFIX}gcc ./Configure enable-gmp -DL_ENDIAN -I${GMP_DIR} -L${GMP_DIR}/usr/lib android-${TARGET_ARCH};\
@ -368,7 +359,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
@ -420,14 +411,10 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
mkdir -p ${FREETYPE_DIR}; \
echo "changed timestamp for freetype detected building..."; \
cd ${FREETYPE_DIR}/Android/jni; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \
APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Deps.mk || exit 1; \
touch ${FREETYPE_TIMESTAMP}; \
touch ${FREETYPE_TIMESTAMP_INT}; \
else \
@ -478,9 +465,10 @@ $(ICONV_LIB) : $(ICONV_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \
export CC=${CROSS_PREFIX}gcc; \
export CXX=${CROSS_PREFIX}g++; \
export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
@ -513,6 +501,7 @@ irrlicht_download :
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-touchcount.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-back_button.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-texturehack.patch || exit 1; \
patch -p1 < ${ANDR_ROOT}/patches/irrlicht-native_activity.patch || exit 1; \
fi
$(IRRLICHT_TIMESTAMP) : irrlicht_download
@ -538,14 +527,10 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
mkdir -p ${IRRLICHT_DIR}; \
echo "changed timestamp for irrlicht detected building..."; \
cd deps/irrlicht/source/Irrlicht/Android; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build NDEBUG=${NDEBUG} \
NDK_MODULE_PATH=${NDK_MODULE_PATH} \
APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
NDK_APPLICATION_MK=${ANDR_ROOT}/jni/Irrlicht.mk || exit 1; \
touch ${IRRLICHT_TIMESTAMP}; \
touch ${IRRLICHT_TIMESTAMP_INT}; \
else \
@ -593,7 +578,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
@ -653,7 +638,7 @@ $(GMP_LIB): $(GMP_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
--platform=android-9 \
--platform=${APP_PLATFORM} \
--install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
@ -767,15 +752,11 @@ clean_assets :
apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \
$(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ANDR_ROOT)/jni/src/android_version.h \
$(ANDR_ROOT)/jni/src/android_version_githash.h sqlite3_download
+ @${ANDROID_NDK}/ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \
GPROF=${GPROF} APP_ABI=${TARGET_ABI} HAVE_LEVELDB=${HAVE_LEVELDB} \
APP_PLATFORM=${APP_PLATFORM} \
PRIVATE_CC=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}gcc \
PRIVATE_CXX=${NDK_MODULE_PATH}/${TARGET_TOOLCHAIN}${COMPILER_VERSION}/prebuilt/linux-x86_64/bin/${TARGET_TOOLCHAIN}g++ \
TARGET_LIBDIR=${TARGET_LIBDIR} \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ @export TARGET_LIBDIR=${TARGET_LIBDIR}; \
export HAVE_LEVELDB=${HAVE_LEVELDB}; \
export APP_PLATFORM=${APP_PLATFORM}; \
export TARGET_ABI=${TARGET_ABI}; \
${ANDROID_NDK}/ndk-build || exit 1; \
if [ ! -e ${APP_ROOT}/jniLibs ]; then \
ln -s ${ANDR_ROOT}/libs ${APP_ROOT}/jniLibs || exit 1; \
fi; \

View File

@ -16,8 +16,8 @@ android {
defaultConfig {
versionCode 17
versionName "${System.env.VERSION_STR}.${versionCode}"
minSdkVersion 9
targetSdkVersion 9
minSdkVersion 14
targetSdkVersion 14
applicationId "net.minetest.minetest"
manifestPlaceholders = [ package: "net.minetest.minetest", project: project.name ]
}

View File

@ -158,6 +158,7 @@ LOCAL_SRC_FILES := \
jni/src/gui/guiTable.cpp \
jni/src/guiscalingfilter.cpp \
jni/src/gui/guiVolumeChange.cpp \
jni/src/gui/profilergraph.cpp \
jni/src/httpfetch.cpp \
jni/src/hud.cpp \
jni/src/imagefilters.cpp \
@ -270,6 +271,7 @@ LOCAL_SRC_FILES := \
jni/src/settings.cpp \
jni/src/wieldmesh.cpp \
jni/src/client/clientlauncher.cpp \
jni/src/client/hud.cpp \
jni/src/client/inputhandler.cpp \
jni/src/client/renderingengine.cpp \
jni/src/client/tile.cpp \
@ -325,6 +327,7 @@ LOCAL_SRC_FILES += \
jni/src/script/lua_api/l_noise.cpp \
jni/src/script/lua_api/l_object.cpp \
jni/src/script/lua_api/l_particles.cpp \
jni/src/script/lua_api/l_particles_local.cpp\
jni/src/script/lua_api/l_rollback.cpp \
jni/src/script/lua_api/l_server.cpp \
jni/src/script/lua_api/l_settings.cpp \

View File

@ -1,9 +1,9 @@
# NDK_TOOLCHAIN_VERSION := clang3.8
APP_PLATFORM := android-9
APP_MODULES := minetest
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := gnustl_static
NDK_TOOLCHAIN_VERSION := 4.9
APP_DEPRECATED_HEADERS := true
APP_MODULES := minetest
APP_CPPFLAGS += -fexceptions
APP_GNUSTL_FORCE_CPP_FEATURES := rtti

View File

@ -0,0 +1,8 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := gnustl_static
NDK_TOOLCHAIN_VERSION := 4.9
APP_DEPRECATED_HEADERS := true
APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3
APP_CPPFLAGS += -fexceptions

View File

@ -0,0 +1,9 @@
APP_PLATFORM := ${APP_PLATFORM}
APP_ABI := ${TARGET_ABI}
APP_STL := gnustl_static
NDK_TOOLCHAIN_VERSION := 4.9
APP_DEPRECATED_HEADERS := true
APP_MODULES := Irrlicht
APP_CLAFGS += -mfloat-abi=softfp -mfpu=vfpv3 -O3
APP_CPPFLAGS += -fexceptions

View File

@ -0,0 +1,12 @@
--- irrlicht/source/Irrlicht/CEGLManager.cpp.orig 2017-11-15 18:19:58.467279274 +0000
+++ irrlicht/source/Irrlicht/CEGLManager.cpp 2017-11-15 18:19:54.175279087 +0000
@@ -8,6 +8,9 @@
#include "irrString.h"
#include "os.h"
+#if defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_)
+#include <android/native_activity.h>
+#endif
namespace irr
{

View File

@ -10,6 +10,7 @@ public class MtNativeActivity extends NativeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
m_MessagReturnCode = -1;
m_MessageReturnValue = "";

View File

@ -60,7 +60,6 @@ end
core.registered_globalsteps, core.register_globalstep = make_registration()
core.registered_on_shutdown, core.register_on_shutdown = make_registration()
core.registered_on_connect, core.register_on_connect = make_registration()
core.registered_on_receiving_chat_message, core.register_on_receiving_chat_message = make_registration()
core.registered_on_sending_chat_message, core.register_on_sending_chat_message = make_registration()
core.registered_on_death, core.register_on_death = make_registration()

View File

@ -551,7 +551,7 @@ end
--------------------------------------------------------------------------------
if INIT == "mainmenu" then
function core.get_game(index)
local games = game.get_games()
local games = core.get_games()
if index > 0 and index <= #games then
return games[index]
@ -670,6 +670,7 @@ end
-- Returns the exact coordinate of a pointed surface
--------------------------------------------------------------------------------
function core.pointed_thing_to_face_pos(placer, pointed_thing)
local eye_height = placer:get_properties().eye_height
local eye_offset_first = placer:get_eye_offset()
local node_pos = pointed_thing.under
local camera_pos = placer:get_pos()
@ -689,7 +690,7 @@ function core.pointed_thing_to_face_pos(placer, pointed_thing)
end
local fine_pos = {[nc] = node_pos[nc] + offset}
camera_pos.y = camera_pos.y + 1.625 + eye_offset_first.y / 10
camera_pos.y = camera_pos.y + eye_height + eye_offset_first.y / 10
local f = (node_pos[nc] + offset - camera_pos[nc]) / look_dir[nc]
for i = 1, #oc do

View File

@ -63,34 +63,13 @@ function vector.distance(a, b)
end
function vector.direction(pos1, pos2)
local x_raw = pos2.x - pos1.x
local y_raw = pos2.y - pos1.y
local z_raw = pos2.z - pos1.z
local x_abs = math.abs(x_raw)
local y_abs = math.abs(y_raw)
local z_abs = math.abs(z_raw)
if x_abs >= y_abs and
x_abs >= z_abs then
y_raw = y_raw * (1 / x_abs)
z_raw = z_raw * (1 / x_abs)
x_raw = x_raw / x_abs
end
if y_abs >= x_abs and
y_abs >= z_abs then
x_raw = x_raw * (1 / y_abs)
z_raw = z_raw * (1 / y_abs)
y_raw = y_raw / y_abs
end
if z_abs >= y_abs and
z_abs >= x_abs then
x_raw = x_raw * (1 / z_abs)
y_raw = y_raw * (1 / z_abs)
z_raw = z_raw / z_abs
end
return {x=x_raw, y=y_raw, z=z_raw}
return vector.normalize({
x = pos2.x - pos1.x,
y = pos2.y - pos1.y,
z = pos2.z - pos1.z
})
end
function vector.add(a, b)
if type(b) == "table" then
return {x = a.x + b.x,

View File

@ -1,17 +1,17 @@
-- Minetest: builtin/auth.lua
--
-- Authentication handler
-- Builtin authentication handler
--
core.auth_file_path = core.get_worldpath().."/auth.txt"
core.auth_table = {}
local auth_file_path = core.get_worldpath().."/auth.txt"
local auth_table = {}
local function read_auth_file()
local newtable = {}
local file, errmsg = io.open(core.auth_file_path, 'rb')
local file, errmsg = io.open(auth_file_path, 'rb')
if not file then
core.log("info", core.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
core.log("info", auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world")
return
end
for line in file:lines() do
@ -27,14 +27,14 @@ local function read_auth_file()
end
end
io.close(file)
core.auth_table = newtable
auth_table = newtable
core.notify_authentication_modified()
end
local function save_auth_file()
local newtable = {}
-- Check table for validness before attempting to save
for name, stuff in pairs(core.auth_table) do
for name, stuff in pairs(auth_table) do
assert(type(name) == "string")
assert(name ~= "")
assert(type(stuff) == "table")
@ -43,13 +43,13 @@ local function save_auth_file()
assert(stuff.last_login == nil or type(stuff.last_login) == "number")
end
local content = ""
for name, stuff in pairs(core.auth_table) do
for name, stuff in pairs(auth_table) do
local priv_string = core.privs_to_string(stuff.privileges)
local parts = {name, stuff.password, priv_string, stuff.last_login or ""}
content = content .. table.concat(parts, ":") .. "\n"
end
if not core.safe_file_write(core.auth_file_path, content) then
error(core.auth_file_path.." could not be written to")
if not core.safe_file_write(auth_file_path, content) then
error(auth_file_path.." could not be written to")
end
end
@ -63,13 +63,13 @@ core.builtin_auth_handler = {
-- usually empty too)
local new_password_hash = ""
-- If not in authentication table, return nil
if not core.auth_table[name] then
if not auth_table[name] then
return nil
end
-- Figure out what privileges the player should have.
-- Take a copy of the privilege table
local privileges = {}
for priv, _ in pairs(core.auth_table[name].privileges) do
for priv, _ in pairs(auth_table[name].privileges) do
privileges[priv] = true
end
-- If singleplayer, give all privileges except those marked as give_to_singleplayer = false
@ -89,31 +89,41 @@ core.builtin_auth_handler = {
end
-- All done
return {
password = core.auth_table[name].password,
password = auth_table[name].password,
privileges = privileges,
-- Is set to nil if unknown
last_login = core.auth_table[name].last_login,
last_login = auth_table[name].last_login,
}
end,
create_auth = function(name, password)
assert(type(name) == "string")
assert(type(password) == "string")
core.log('info', "Built-in authentication handler adding player '"..name.."'")
core.auth_table[name] = {
auth_table[name] = {
password = password,
privileges = core.string_to_privs(core.settings:get("default_privs")),
last_login = os.time(),
}
save_auth_file()
end,
delete_auth = function(name)
assert(type(name) == "string")
if not auth_table[name] then
return false
end
core.log('info', "Built-in authentication handler deleting player '"..name.."'")
auth_table[name] = nil
save_auth_file()
return true
end,
set_password = function(name, password)
assert(type(name) == "string")
assert(type(password) == "string")
if not core.auth_table[name] then
if not auth_table[name] then
core.builtin_auth_handler.create_auth(name, password)
else
core.log('info', "Built-in authentication handler setting password of player '"..name.."'")
core.auth_table[name].password = password
auth_table[name].password = password
save_auth_file()
end
return true
@ -121,7 +131,7 @@ core.builtin_auth_handler = {
set_privileges = function(name, privileges)
assert(type(name) == "string")
assert(type(privileges) == "table")
if not core.auth_table[name] then
if not auth_table[name] then
core.builtin_auth_handler.create_auth(name,
core.get_password_hash(name,
core.settings:get("default_password")))
@ -129,19 +139,19 @@ core.builtin_auth_handler = {
-- Run grant callbacks
for priv, _ in pairs(privileges) do
if not core.auth_table[name].privileges[priv] then
if not auth_table[name].privileges[priv] then
core.run_priv_callbacks(name, priv, nil, "grant")
end
end
-- Run revoke callbacks
for priv, _ in pairs(core.auth_table[name].privileges) do
for priv, _ in pairs(auth_table[name].privileges) do
if not privileges[priv] then
core.run_priv_callbacks(name, priv, nil, "revoke")
end
end
core.auth_table[name].privileges = privileges
auth_table[name].privileges = privileges
core.notify_authentication_modified(name)
save_auth_file()
end,
@ -151,11 +161,41 @@ core.builtin_auth_handler = {
end,
record_login = function(name)
assert(type(name) == "string")
assert(core.auth_table[name]).last_login = os.time()
assert(auth_table[name]).last_login = os.time()
save_auth_file()
end,
iterate = function()
local names = {}
for k in pairs(auth_table) do
names[k] = true
end
return pairs(names)
end,
}
core.register_on_prejoinplayer(function(name, ip)
if core.registered_auth_handler ~= nil then
return -- Don't do anything if custom auth handler registered
end
if auth_table[name] ~= nil then
return
end
local name_lower = name:lower()
for k in pairs(auth_table) do
if k:lower() == name_lower then
return string.format("\nCannot create new player called '%s'. "..
"Another account called '%s' is already registered. "..
"Please check the spelling if it's your account "..
"or use a different nickname.", name, k)
end
end
end)
--
-- Authentication API
--
function core.register_authentication_handler(handler)
if core.registered_auth_handler then
error("Add-on authentication handler already registered by "..core.registered_auth_handler_modname)
@ -181,28 +221,10 @@ end
core.set_player_password = auth_pass("set_password")
core.set_player_privs = auth_pass("set_privileges")
core.remove_player_auth = auth_pass("delete_auth")
core.auth_reload = auth_pass("reload")
local record_login = auth_pass("record_login")
core.register_on_joinplayer(function(player)
record_login(player:get_player_name())
end)
core.register_on_prejoinplayer(function(name, ip)
local auth = core.auth_table
if auth[name] ~= nil then
return
end
local name_lower = name:lower()
for k in pairs(auth) do
if k:lower() == name_lower then
return string.format("\nCannot create new player called '%s'. "..
"Another account called '%s' is already registered. "..
"Please check the spelling if it's your account "..
"or use a different nickname.", name, k)
end
end
end)

View File

@ -71,7 +71,7 @@ end
--
core.register_chatcommand("me", {
params = "<action>",
description = "Display chat action (e.g., '/me orders a pizza' displays"
description = "Show chat action (e.g., '/me orders a pizza' displays"
.. " '<player name> orders a pizza')",
privs = {shout=true},
func = function(name, param)
@ -82,7 +82,7 @@ core.register_chatcommand("me", {
core.register_chatcommand("admin", {
description = "Show the name of the server owner",
func = function(name)
local admin = minetest.settings:get("name")
local admin = core.settings:get("name")
if admin then
return true, "The administrator of this server is "..admin.."."
else
@ -93,7 +93,7 @@ core.register_chatcommand("admin", {
core.register_chatcommand("privs", {
params = "[<name>]",
description = "Print privileges of player",
description = "Show privileges of yourself or another player",
func = function(caller, param)
param = param:trim()
local name = (param ~= "" and param or caller)
@ -104,7 +104,7 @@ core.register_chatcommand("privs", {
})
local function handle_grant_command(caller, grantname, grantprivstr)
local caller_privs = minetest.get_player_privs(caller)
local caller_privs = core.get_player_privs(caller)
if not (caller_privs.privs or caller_privs.basic_privs) then
return false, "Your privileges are insufficient."
end
@ -149,7 +149,7 @@ end
core.register_chatcommand("grant", {
params = "<name> (<privilege> | all)",
description = "Give privilege to player",
description = "Give privileges to player",
func = function(name, param)
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
if not grantname or not grantprivstr then
@ -172,7 +172,7 @@ core.register_chatcommand("grantme", {
core.register_chatcommand("revoke", {
params = "<name> (<privilege> | all)",
description = "Remove privilege from player",
description = "Remove privileges from player",
privs = {},
func = function(name, param)
if not core.check_player_privs(name, {privs=true}) and
@ -263,7 +263,7 @@ core.register_chatcommand("setpassword", {
core.register_chatcommand("clearpassword", {
params = "<name>",
description = "Set empty password",
description = "Set empty password for a player",
privs = {password=true},
func = function(name, param)
local toname = param
@ -290,7 +290,7 @@ core.register_chatcommand("auth_reload", {
core.register_chatcommand("remove_player", {
params = "<name>",
description = "Remove player data",
description = "Remove a player's data",
privs = {server=true},
func = function(name, param)
local toname = param
@ -315,7 +315,7 @@ core.register_chatcommand("remove_player", {
core.register_chatcommand("teleport", {
params = "<X>,<Y>,<Z> | <to_name> | (<name> <X>,<Y>,<Z>) | (<name> <to_name>)",
description = "Teleport to player or position",
description = "Teleport to position or player",
privs = {teleport=true},
func = function(name, param)
-- Returns (pos, true) if found, otherwise (pos, false)
@ -588,7 +588,7 @@ local function handle_give_command(cmd, giver, receiver, stackstring)
end
core.register_chatcommand("give", {
params = "<name> <ItemString>",
params = "<name> <ItemString> [<count> [<wear>]]",
description = "Give item to player",
privs = {give=true},
func = function(name, param)
@ -601,7 +601,7 @@ core.register_chatcommand("give", {
})
core.register_chatcommand("giveme", {
params = "<ItemString>",
params = "<ItemString> [<count> [<wear>]]",
description = "Give item to yourself",
privs = {give=true},
func = function(name, param)
@ -629,7 +629,7 @@ core.register_chatcommand("spawnentity", {
core.log("error", "Unable to spawn entity, player is nil")
return false, "Unable to spawn entity, player is nil"
end
if not minetest.registered_entities[entityname] then
if not core.registered_entities[entityname] then
return false, "Cannot spawn an unknown entity"
end
if p == "" then
@ -766,15 +766,15 @@ core.register_chatcommand("rollback", {
})
core.register_chatcommand("status", {
description = "Print server status",
description = "Show server status",
func = function(name, param)
return true, core.get_server_status()
end,
})
core.register_chatcommand("time", {
params = "<0..23>:<0..59> | <0..24000>",
description = "Set time of day",
params = "[<0..23>:<0..59> | <0..24000>]",
description = "Show or set time of day",
privs = {},
func = function(name, param)
if param == "" then
@ -813,7 +813,7 @@ core.register_chatcommand("time", {
})
core.register_chatcommand("days", {
description = "Display day count",
description = "Show day count since world creation",
func = function(name, param)
return true, "Current day is " .. core.get_day_count()
end
@ -839,12 +839,17 @@ core.register_chatcommand("shutdown", {
})
core.register_chatcommand("ban", {
params = "<name>",
description = "Ban IP of player",
params = "[<name> | <IP_address>]",
description = "Ban player or show ban list",
privs = {ban=true},
func = function(name, param)
if param == "" then
return true, "Ban list: " .. core.get_ban_list()
local ban_list = core.get_ban_list()
if ban_list == "" then
return true, "The ban list is empty."
else
return true, "Ban list: " .. ban_list
end
end
if not core.get_player_by_name(param) then
return false, "No such player."
@ -860,7 +865,7 @@ core.register_chatcommand("ban", {
core.register_chatcommand("unban", {
params = "<name> | <IP_address>",
description = "Remove IP ban",
description = "Remove player ban",
privs = {ban=true},
func = function(name, param)
if not core.unban_player_or_ip(param) then
@ -938,7 +943,7 @@ core.register_chatcommand("msg", {
core.register_chatcommand("last-login", {
params = "[<name>]",
description = "Get the last login time of a player",
description = "Get the last login time of a player or yourself",
func = function(name, param)
if param == "" then
param = name
@ -961,7 +966,7 @@ core.register_chatcommand("clearinv", {
if param and param ~= "" and param ~= name then
if not core.check_player_privs(name, {server=true}) then
return false, "You don't have permission"
.. " to run this command (missing privilege: server)"
.. " to clear another player's inventory (missing privilege: server)"
end
player = core.get_player_by_name(param)
core.chat_send_player(param, name.." cleared your inventory.")
@ -980,3 +985,34 @@ core.register_chatcommand("clearinv", {
end
end,
})
local function handle_kill_command(killer, victim)
if core.settings:get_bool("enable_damage") == false then
return false, "Players can't be killed, damage has been disabled."
end
local victimref = core.get_player_by_name(victim)
if victimref == nil then
return false, string.format("Player %s is not online.", victim)
elseif victimref:get_hp() <= 0 then
if killer == victim then
return false, "You are already dead."
else
return false, string.format("%s is already dead.", victim)
end
end
if not killer == victim then
core.log("action", string.format("%s killed %s", killer, victim))
end
-- Kill victim
victimref:set_hp(0)
return true, string.format("%s has been killed.", victim)
end
core.register_chatcommand("kill", {
params = "[<name>]",
description = "Kill player or yourself",
privs = {server=true},
func = function(name, param)
return handle_kill_command(name, param == "" and name or param)
end,
})

View File

@ -150,8 +150,22 @@ end
local function drop_attached_node(p)
local n = core.get_node(p)
local drops = core.get_node_drops(n, "")
local def = core.registered_items[n.name]
if def and def.preserve_metadata then
local oldmeta = core.get_meta(p):to_table().fields
-- Copy pos and node because the callback can modify them.
local pos_copy = {x=p.x, y=p.y, z=p.z}
local node_copy = {name=n.name, param1=n.param1, param2=n.param2}
local drop_stacks = {}
for k, v in pairs(drops) do
drop_stacks[k] = ItemStack(v)
end
drops = drop_stacks
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
end
core.remove_node(p)
for _, item in pairs(core.get_node_drops(n, "")) do
for _, item in pairs(drops) do
local pos = {
x = p.x + math.random()/2 - 0.25,
y = p.y + math.random()/2 - 0.25,

View File

@ -516,7 +516,8 @@ function core.handle_node_drops(pos, drops, digger)
end
else
give_item = function(item)
return item
-- itemstring to ItemStack for left:is_empty()
return ItemStack(item)
end
end
@ -578,6 +579,20 @@ function core.node_dig(pos, node, digger)
digger:set_wielded_item(wielded)
end
-- Check to see if metadata should be preserved.
if def and def.preserve_metadata then
local oldmeta = core.get_meta(pos):to_table().fields
-- Copy pos and node because the callback can modify them.
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
local drop_stacks = {}
for k, v in pairs(drops) do
drop_stacks[k] = ItemStack(v)
end
drops = drop_stacks
def.preserve_metadata(pos_copy, node_copy, oldmeta, drops)
end
-- Handle drops
core.handle_node_drops(pos, drops, digger)

View File

@ -147,9 +147,9 @@ core.register_entity(":__builtin:item", {
})
local vel = self.object:getvelocity()
local def = node and core.registered_nodes[node.name]
-- Ignore is nil -> stop until the block loaded
local is_moving = (def and not def.walkable) or
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
-- Avoid entity falling into ignore nodes or unloaded areas
local is_moving = node and node.name ~= "ignore" and
((def and not def.walkable) or vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0)
local is_slippery = false
if def and def.walkable then

View File

@ -5,12 +5,11 @@
--
function core.check_player_privs(name, ...)
local arg_type = type(name)
if (arg_type == "userdata" or arg_type == "table") and
name.get_player_name then -- If it quacks like a Player...
if core.is_player(name) then
name = name:get_player_name()
elseif arg_type ~= "string" then
error("Invalid core.check_player_privs argument type: " .. arg_type, 2)
elseif type(name) ~= "string" then
error("core.check_player_privs expects a player or playername as " ..
"argument.", 2)
end
local requested_privs = {...}
@ -40,14 +39,17 @@ function core.check_player_privs(name, ...)
return true, ""
end
local player_list = {}
function core.send_join_message(player_name)
if not minetest.is_singleplayer() then
core.chat_send_all("*** " .. player_name .. " joined the game.")
end
end
function core.send_leave_message(player_name, timed_out)
local announcement = "*** " .. player_name .. " left the game."
if timed_out then
@ -56,18 +58,21 @@ function core.send_leave_message(player_name, timed_out)
core.chat_send_all(announcement)
end
core.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
player_list[player_name] = player
core.send_join_message(player_name)
end)
core.register_on_leaveplayer(function(player, timed_out)
local player_name = player:get_player_name()
player_list[player_name] = nil
core.send_leave_message(player_name, timed_out)
end)
function core.get_connected_players()
local temp_table = {}
for index, value in pairs(player_list) do
@ -78,12 +83,24 @@ function core.get_connected_players()
return temp_table
end
function core.is_player(player)
-- a table being a player is also supported because it quacks sufficiently
-- like a player if it has the is_player function
local t = type(player)
return (t == "userdata" or t == "table") and
type(player.is_player) == "function" and player:is_player()
end
function minetest.player_exists(name)
return minetest.get_auth_handler().get_auth(name) ~= nil
end
-- Returns two position vectors representing a box of `radius` in each
-- direction centered around the player corresponding to `player_name`
function core.get_player_radius_area(player_name, radius)
local player = core.get_player_by_name(player_name)
if player == nil then
@ -101,10 +118,12 @@ function core.get_player_radius_area(player_name, radius)
return p1, p2
end
function core.hash_node_position(pos)
return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768
end
function core.get_position_from_hash(hash)
local pos = {}
pos.x = (hash%65536) - 32768
@ -115,6 +134,7 @@ function core.get_position_from_hash(hash)
return pos
end
function core.get_item_group(name, group)
if not core.registered_items[name] or not
core.registered_items[name].groups[group] then
@ -123,11 +143,13 @@ function core.get_item_group(name, group)
return core.registered_items[name].groups[group]
end
function core.get_node_group(name, group)
core.log("deprecated", "Deprecated usage of get_node_group, use get_item_group instead")
return core.get_item_group(name, group)
end
function core.setting_get_pos(name)
local value = core.settings:get(name)
if not value then
@ -136,17 +158,64 @@ function core.setting_get_pos(name)
return core.string_to_pos(value)
end
-- To be overriden by protection mods
function core.is_protected(pos, name)
return false
end
function core.record_protection_violation(pos, name)
for _, func in pairs(core.registered_on_protection_violation) do
func(pos, name)
end
end
-- Checks if specified volume intersects a protected volume
function core.intersects_protection(minp, maxp, player_name, interval)
-- 'interval' is the largest allowed interval for the 3D lattice of checks.
-- Compute the optimal float step 'd' for each axis so that all corners and
-- borders are checked. 'd' will be smaller or equal to 'interval'.
-- Subtracting 1e-4 ensures that the max co-ordinate will be reached by the
-- for loop (which might otherwise not be the case due to rounding errors).
-- Default to 4
interval = interval or 4
local d = {}
for _, c in pairs({"x", "y", "z"}) do
if maxp[c] > minp[c] then
d[c] = (maxp[c] - minp[c]) /
math.ceil((maxp[c] - minp[c]) / interval) - 1e-4
elseif maxp[c] == minp[c] then
d[c] = 1 -- Any value larger than 0 to avoid division by zero
else -- maxp[c] < minp[c], print error and treat as protection intersected
minetest.log("error", "maxp < minp in 'minetest.intersects_protection()'")
return true
end
end
for zf = minp.z, maxp.z, d.z do
local z = math.floor(zf + 0.5)
for yf = minp.y, maxp.y, d.y do
local y = math.floor(yf + 0.5)
for xf = minp.x, maxp.x, d.x do
local x = math.floor(xf + 0.5)
if core.is_protected({x = x, y = y, z = z}, player_name) then
return true
end
end
end
end
return false
end
local raillike_ids = {}
local raillike_cur_id = 0
function core.raillike_group(name)
@ -159,7 +228,9 @@ function core.raillike_group(name)
return id
end
-- HTTP callback interface
function core.http_add_fetch(httpenv)
httpenv.fetch = function(req, callback)
local handle = httpenv.fetch_async(req)
@ -178,11 +249,12 @@ function core.http_add_fetch(httpenv)
return httpenv
end
function core.close_formspec(player_name, formname)
return minetest.show_formspec(player_name, formname, "")
end
function core.cancel_shutdown_requests()
core.request_shutdown("", false, -1)
end

View File

@ -34,7 +34,7 @@ core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privil
core.register_privilege("privs", "Can modify privileges")
core.register_privilege("teleport", {
description = "Can use /teleport command",
description = "Can teleport self",
give_to_singleplayer = false,
})
core.register_privilege("bring", {
@ -42,7 +42,7 @@ core.register_privilege("bring", {
give_to_singleplayer = false,
})
core.register_privilege("settime", {
description = "Can use /time",
description = "Can set the time of day using /time",
give_to_singleplayer = false,
})
core.register_privilege("server", {
@ -74,15 +74,15 @@ core.register_privilege("password", {
give_to_admin = true,
})
core.register_privilege("fly", {
description = "Can fly using the free_move mode",
description = "Can use fly mode",
give_to_singleplayer = false,
})
core.register_privilege("fast", {
description = "Can walk fast using the fast_move mode",
description = "Can use fast mode",
give_to_singleplayer = false,
})
core.register_privilege("noclip", {
description = "Can fly through walls",
description = "Can fly through solid nodes using noclip mode",
give_to_singleplayer = false,
})
core.register_privilege("rollback", {

View File

@ -15,11 +15,42 @@
--with this program; if not, write to the Free Software Foundation, Inc.,
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
local worldname = ""
local function create_world_formspec(dialogdata)
local mapgens = core.get_mapgen_names()
local current_seed = core.settings:get("fixed_map_seed") or ""
local current_mg = core.settings:get("mg_name")
local gameid = core.settings:get("menu_last_game")
local game, gameidx = nil , 0
if gameid ~= nil then
game, gameidx = gamemgr.find_by_gameid(gameid)
if gameidx == nil then
gameidx = 0
end
end
local game_by_gameidx = core.get_game(gameidx)
if game_by_gameidx ~= nil then
local gamepath = game_by_gameidx.path
local gameconfig = Settings(gamepath.."/game.conf")
local disallowed_mapgens = (gameconfig:get("disallowed_mapgens") or ""):split()
for key, value in pairs(disallowed_mapgens) do
disallowed_mapgens[key] = value:trim()
end
if disallowed_mapgens then
for i = #mapgens, 1, -1 do
if table.indexof(disallowed_mapgens, mapgens[i]) > 0 then
table.remove(mapgens, i)
end
end
end
end
local mglist = ""
local selindex = 1
@ -33,22 +64,11 @@ local function create_world_formspec(dialogdata)
end
mglist = mglist:sub(1, -2)
local gameid = core.settings:get("menu_last_game")
local game, gameidx = nil , 0
if gameid ~= nil then
game, gameidx = gamemgr.find_by_gameid(gameid)
if gameidx == nil then
gameidx = 0
end
end
current_seed = core.formspec_escape(current_seed)
local retval =
"size[11.5,6.5,true]" ..
"label[2,0;" .. fgettext("World name") .. "]"..
"field[4.5,0.4;6,0.5;te_world_name;;]" ..
"field[4.5,0.4;6,0.5;te_world_name;;" .. minetest.formspec_escape(worldname) .. "]" ..
"label[2,1;" .. fgettext("Seed") .. "]"..
"field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" ..
@ -121,7 +141,11 @@ local function create_world_buttonhandler(this, fields)
return true
end
worldname = fields.te_world_name
if fields["games"] then
local gameindex = core.get_textlist_index("games")
core.settings:set("menu_last_game", gamemgr.games[gameindex].id)
return true
end
@ -135,6 +159,7 @@ end
function create_create_world_dlg(update_worldlistfilter)
worldname = ""
local retval = dialog_create("sp_create_world",
create_world_formspec,
create_world_buttonhandler,

View File

@ -752,6 +752,18 @@ local function handle_change_setting_buttons(this, fields)
core.update_formspec(this:get_formspec())
return true
end
if setting.min and new_value < setting.min then
this.data.error_message = fgettext_ne("The value must be at least $1.", setting.min)
this.data.entered_text = fields["te_setting_value"]
core.update_formspec(this:get_formspec())
return true
end
if setting.max and new_value > setting.max then
this.data.error_message = fgettext_ne("The value must not be larger than $1.", setting.max)
this.data.entered_text = fields["te_setting_value"]
core.update_formspec(this:get_formspec())
return true
end
core.settings:set(setting.name, new_value)
elseif setting.type == "flags" then

View File

@ -310,7 +310,7 @@ end
--------------------------------------------------------------------------------
return {
name = "local",
caption = fgettext("Local Game"),
caption = fgettext("Start Game"),
cbf_formspec = get_formspec,
cbf_button_handler = main_button_handler,
on_change = on_change

View File

@ -344,7 +344,7 @@ end
--------------------------------------------------------------------------------
return {
name = "online",
caption = fgettext("Play Online"),
caption = fgettext("Join Game"),
cbf_formspec = get_formspec,
cbf_button_handler = main_button_handler,
on_change = on_change

View File

@ -235,7 +235,7 @@ local function formspec(tabview, name, tabdata)
.. dump(core.settings:get_bool("enable_bumpmapping")) .. "]" ..
"checkbox[8.25,1;cb_tonemapping;" .. fgettext("Tone Mapping") .. ";"
.. dump(core.settings:get_bool("tone_mapping")) .. "]" ..
"checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Normal Mapping") .. ";"
"checkbox[8.25,1.5;cb_generate_normalmaps;" .. fgettext("Generate Normal Maps") .. ";"
.. dump(core.settings:get_bool("generate_normalmaps")) .. "]" ..
"checkbox[8.25,2;cb_parallax;" .. fgettext("Parallax Occlusion") .. ";"
.. dump(core.settings:get_bool("enable_parallax_occlusion")) .. "]" ..
@ -252,7 +252,7 @@ local function formspec(tabview, name, tabdata)
"label[8.38,1.2;" .. core.colorize("#888888",
fgettext("Tone Mapping")) .. "]" ..
"label[8.38,1.7;" .. core.colorize("#888888",
fgettext("Normal Mapping")) .. "]" ..
fgettext("Generate Normal Maps")) .. "]" ..
"label[8.38,2.2;" .. core.colorize("#888888",
fgettext("Parallax Occlusion")) .. "]" ..
"label[8.38,2.7;" .. core.colorize("#888888",

View File

@ -536,6 +536,9 @@ fps_max (Maximum FPS) int 60
# Maximum FPS when game is paused.
pause_fps_max (FPS in pause menu) int 20
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is open.
pause_on_lost_focus (Pause on lost window focus) bool false
# View distance in nodes.
viewing_range (Viewing range) int 100 20 4000
@ -566,23 +569,31 @@ vsync (V-Sync) bool false
# Field of view in degrees.
fov (Field of view) int 72 30 160
# Field of view while zooming in degrees.
# Requires to be allowed by server-sided mods.
zoom_fov (Field of view for zoom) int 15 7 160
# Adjust the gamma encoding for the light tables. Higher numbers are brighter.
# This setting is for the client only and is ignored by the server.
display_gamma (Gamma) float 1.0 0.5 3.0
# Gradient of light curve at minimum light level.
lighting_alpha (Darkness sharpness) float 0.0 0.0 4.0
lighting_beta (Lightness sharpness) float 0.0 0.0 4.0
# Gradient of light curve at maximum light level.
lighting_beta (Lightness sharpness) float 1.5 0.0 4.0
# Strength of light curve mid-boost.
lighting_boost (Light curve mid boost) float 0.2 0.0 1.0
# Center of light curve mid-boost.
lighting_boost_center (Light curve mid boost center) float 0.5 0.0 1.0
# Spread of light curve mid-boost.
# Standard deviation of the mid-boost gaussian.
lighting_boost_spread (Light curve mid boost spread) float 0.2 0.0 1.0
# Path to texture directory. All textures are first searched from here.
texture_path (Texture path) path
# The rendering back-end for Irrlicht.
video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl
video_driver (Video driver) enum opengl null,software,burningsvideo,direct3d8,direct3d9,opengl,ogles1,ogles2
# Radius of cloud area stated in number of 64 node cloud squares.
# Values larger than 26 will start to produce sharp cutoffs at cloud area corners.
@ -608,7 +619,7 @@ fall_bobbing_amount (Fall bobbing factor) float 0.0
3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,pageflip
# In-game chat console height, between 0.1 (10%) and 1.0 (100%).
console_height (Console height) float 1.0 0.1 1.0
console_height (Console height) float 0.6 0.1 1.0
# In-game chat console background color (R,G,B).
console_color (Console color) string (0,0,0)
@ -990,14 +1001,20 @@ kick_msg_crash (Crash message) string This server has experienced an internal er
ask_reconnect_on_crash (Ask to reconnect after crash) bool false
# From how far clients know about objects, stated in mapblocks (16 nodes).
#
# Setting this larger than active_block_range will also cause the server
# to maintain active objects up to this distance in the direction the
# player is looking. (This can avoid mobs suddenly disappearing from view)
active_object_send_range_blocks (Active object send range) int 3
# How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes).
# In active blocks objects are loaded and ABMs run.
# This is also the minimum range in which active objects (mobs) are maintained.
# This should be configured together with active_object_range.
active_block_range (Active block range) int 3
# From how far blocks are sent to clients, stated in mapblocks (16 nodes).
max_block_send_distance (Max block send distance) int 10
max_block_send_distance (Max block send distance) int 9
# Maximum number of forceloaded mapblocks.
max_forceloaded_blocks (Maximum forceloaded blocks) int 16
@ -1104,12 +1121,13 @@ server_side_occlusion_culling (Server side occlusion culling) bool true
# Restricts the access of certain client-side functions on servers
# Combine these byteflags below to restrict more client-side features:
# LOOKUP_NODES_LIMIT: 1 (limits get_node call client-side to csm_flavour_noderange_limit)
# LOAD_CLIENT_MODS: 1 (disable client mods loading)
# CHAT_MESSAGES: 2 (disable send_chat_message call client-side)
# READ_ITEMDEFS: 4 (disable get_item_def call client-side)
# READ_NODEDEFS: 8 (disable get_node_def call client-side)
# LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to csm_flavour_noderange_limit)
# type: int
csm_flavour_limits (Client side modding flavour limits) int 3
csm_flavour_limits (Client side modding flavour limits) int 18
# If the CSM flavour for node range is enabled, get_node is limited to
# this many nodes from the player.
@ -1181,7 +1199,8 @@ name (Player name) string
# Set the language. Leave empty to use the system language.
# A restart is required after changing this.
language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,ky,lt,nb,nl,pl,pt,pt_BR,ro,ru,sr_Cyrl,tr,uk,zh_CN,zh_TW
language (Language) enum ,be,ca,cs,da,de,dv,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,ky,lt,ms,nb,nl,pl,pt,pt_BR,ro,ru,sl,sr_Cyrl,sv,sw,tr,uk,zh_CN,zh_TW
# Level of logging to be written to debug.txt:
# - <nothing> (no logging)
@ -1193,6 +1212,13 @@ language (Language) enum ,be,ca,cs,da,de,en,eo,es,et,fr,he,hu,id,it,ja,jbo,ko,
# - verbose
debug_log_level (Debug log level) enum action ,none,error,warning,action,info,verbose
# ANSI colored logs: red error log, yellow warning and grey info and verbose logs
# Note that it doesn't work on Windows
# "yes" always enables it,
# "detect" enables it when printing to terminal and
# "no" disables it
log_color (Colored logs) enum detect yes,detect,no
# IPv6 support.
enable_ipv6 (IPv6) bool true
@ -1387,13 +1413,16 @@ mgv7_large_cave_depth (Large cave depth) int -33
# Y of upper limit of lava in large caves.
mgv7_lava_depth (Lava depth) int -256
# Controls the density of floatland mountain terrain.
# Is an offset added to the 'np_mountain' noise value.
# Controls the density of mountain-type floatlands.
# Is a noise offset added to the 'mgv7_np_mountain' noise value.
mgv7_float_mount_density (Floatland mountain density) float 0.6
# Typical maximum height, above and below midpoint, of floatland mountain terrain.
# Typical maximum height, above and below midpoint, of floatland mountains.
mgv7_float_mount_height (Floatland mountain height) float 128.0
# Alters how mountain-type floatlands taper above and below midpoint.
mgv7_float_mount_exponent (Floatland mountain exponent) float 0.75
# Y-level of floatland midpoint and lake surface.
mgv7_floatland_level (Floatland level) int 1280

View File

@ -7,22 +7,32 @@ dofile("preview:example.lua")
core.register_on_shutdown(function()
print("[PREVIEW] shutdown client")
end)
local id = nil
core.register_on_connect(function()
print("[PREVIEW] Player connection completed")
local server_info = core.get_server_info()
print("Server version: " .. server_info.protocol_version)
print("Server ip: " .. server_info.ip)
print("Server address: " .. server_info.address)
print("Server port: " .. server_info.port)
local server_info = core.get_server_info()
print("Server version: " .. server_info.protocol_version)
print("Server ip: " .. server_info.ip)
print("Server address: " .. server_info.address)
print("Server port: " .. server_info.port)
mod_channel = core.mod_channel_join("experimental_preview")
mod_channel = core.mod_channel_join("experimental_preview")
core.after(4, function()
if mod_channel:is_writeable() then
mod_channel:send_all("preview talk to experimental")
end
end)
core.after(4, function()
if mod_channel:is_writeable() then
mod_channel:send_all("preview talk to experimental")
end
end)
core.after(1, function()
id = core.localplayer:hud_add({
hud_elem_type = "text",
name = "example",
number = 0xff0000,
position = {x=0, y=1},
offset = {x=8, y=-8},
text = "You are using the preview mod",
scale = {x=200, y=60},
alignment = {x=1, y=-1},
})
end)
core.register_on_modchannel_message(function(channel, sender, message)
@ -184,3 +194,9 @@ core.register_chatcommand("privs", {
return true, core.privs_to_string(minetest.get_privilege_list())
end,
})
core.register_chatcommand("text", {
func = function(param)
return core.localplayer:hud_change(id, "text", param)
end,
})

View File

@ -6,7 +6,7 @@ Minetest Lua Client Modding API Reference 0.5.0
Introduction
------------
**WARNING: The client API is currently unstable, and may break/change without warning.**
** WARNING: The client API is currently unstable, and may break/change without warning. **
Content and functionality can be added to Minetest 0.4.15-dev+ by using Lua
scripting in run-time loaded mods.
@ -14,7 +14,7 @@ scripting in run-time loaded mods.
A mod is a self-contained bunch of scripts, textures and other related
things that is loaded by and interfaces with Minetest.
Transfering client-sided mods form the server to the client is planned, but not implemented yet.
Transferring client-sided mods from the server to the client is planned, but not implemented yet.
If you see a deficiency in the API, feel free to attempt to add the
functionality in the engine and API. You can send such improvements as
@ -626,7 +626,7 @@ Minetest namespace reference
* `hash`: Full git version (only set if available), eg, "1.2.3-dev-01234567-dirty"
Use this for informational purposes only. The information in the returned
table does not represent the capabilities of the engine, nor is it
reliable or verifyable. Compatible forks will have a different name and
reliable or verifiable. Compatible forks will have a different name and
version entirely. To check for the presence of engine features, test
whether the functions exported by the wanted features exist. For example:
`if minetest.check_for_falling then ... end`.
@ -651,8 +651,6 @@ Call these functions only at load time!
* **Warning**: If the client terminates abnormally (i.e. crashes), the registered
callbacks **will likely not be run**. Data should be saved at
semi-frequent intervals as well as on server shutdown.
* `minetest.register_on_connect(func())`
* Called at the end of client connection (when player is loaded onto map)
* `minetest.register_on_receiving_chat_message(func(message))`
* Called always when a client receive a message
* Return `true` to mark the message as handled, which means that it will not be shown to chat
@ -727,7 +725,7 @@ Call these functions only at load time!
* `minetest.get_node_or_nil(pos)`
* Returns the node at the given position as table in the format
`{name="node_name", param1=0, param2=0}`, returns `nil`
for unloaded areas or flavour limited areas.
for unloaded areas or flavor limited areas.
* `minetest.find_node_near(pos, radius, nodenames, [search_center])`: returns pos or `nil`
* `radius`: using a maximum metric
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
@ -754,7 +752,7 @@ Call these functions only at load time!
### Privileges
* `minetest.get_privilege_list()`
* Returns a list of privileges the currect player has in the format `{priv1=true,...}`
* Returns a list of privileges the current player has in the format `{priv1=true,...}`
* `minetest.string_to_privs(str)`: returns `{priv1=true,...}`
* `minetest.privs_to_string(privs)`: returns `"priv1,priv2,..."`
* Convert between two privilege representations
@ -765,8 +763,6 @@ Call these functions only at load time!
* `minetest.disconnect()`
* Disconnect from the server and exit to main menu.
* Returns `false` if the client is already disconnecting otherwise returns `true`.
* `minetest.take_screenshot()`
* Take a screenshot.
* `minetest.get_server_info()`
* Returns [server info](#server-info).
* `minetest.send_respawn()`
@ -784,8 +780,16 @@ Call these functions only at load time!
* Client joins channel `channel_name`, and creates it, if necessary. You
should listen from incoming messages with `minetest.register_on_modchannel_message`
call to receive incoming messages. Warning, this function is asynchronous.
* You should use a minetest.register_on_connect(function() ... end) to perform
a successful channel join on client startup.
### Particles
* `minetest.add_particle(particle definition)`
* `minetest.add_particlespawner(particlespawner definition)`
* Add a `ParticleSpawner`, an object that spawns an amount of particles over `time` seconds
* Returns an `id`, and -1 if adding didn't succeed
* `minetest.delete_particlespawner(id)`
* Delete `ParticleSpawner` with `id` (return value from `minetest.add_particlespawner`)
### Misc.
* `minetest.parse_json(string[, nullvalue])`: returns something
@ -870,9 +874,9 @@ An interface to use mod channels on client and server
* No more incoming or outgoing messages can be sent to this channel from client mods.
* This invalidate all future object usage
* Ensure your set mod_channel to nil after that to free Lua resources
* `is_writeable()`: returns true if channel is writeable and mod can send over it.
* `is_writeable()`: returns true if channel is writable and mod can send over it.
* `send_all(message)`: Send `message` though the mod channel.
* If mod channel is not writeable or invalid, message will be dropped.
* If mod channel is not writable or invalid, message will be dropped.
* Message size is limited to 65535 characters by protocol.
### Minimap
@ -891,14 +895,14 @@ An interface to manipulate minimap on client UI
* `get_shape()`: Gets the minimap shape. (0 = square, 1 = round)
### Camera
An interface to get or set information about the camera and cameranode.
An interface to get or set information about the camera and camera-node.
Please do not try to access the reference until the camera is initialized, otherwise the reference will be nil.
#### Methods
* `set_camera_mode(mode)`
* Pass `0` for first-person, `1` for third person, and `2` for third person front
* `get_camera_mode()`
* Returns with same syntax as above
* Returns 0, 1, or 2 as described above
* `get_fov()`
* Returns:
@ -925,18 +929,7 @@ Please do not try to access the reference until the camera is initialized, other
* Returns aspect ratio of screen
### LocalPlayer
An interface to retrieve information about the player. The player is
not accessible until the client is fully done loading and therefore
not at module init time.
To get the localplayer handle correctly, use `on_connect()` as follows:
```lua
local localplayer
minetest.register_on_connect(function()
localplayer = minetest.localplayer
end)
```
An interface to retrieve information about the player.
Methods:
@ -1025,6 +1018,17 @@ Methods:
* returns last look vertical angle
* `get_key_pressed()`:
* returns last key typed by the player
* `hud_add(definition)`
* add a HUD element described by HUD def, returns ID number on success and `nil` on failure.
* See [`HUD definition`](#hud-definition-hud_add-hud_get)
* `hud_get(id)`
* returns the [`definition`](#hud-definition-hud_add-hud_get) of the HUD with that ID number or `nil`, if non-existent.
* `hud_remove(id)`
* remove the HUD element of the specified id, returns `true` on success
* `hud_change(id, stat, value)`
* change a value of a previously added HUD element
* element `stat` values: `position`, `name`, `scale`, `text`, `number`, `item`, `dir`
* Returns `true` on success, otherwise returns `nil`
### Settings
An interface to read config files in the format of `minetest.conf`.
@ -1136,7 +1140,7 @@ Can be obtained via `minetest.get_meta(pos)`.
stack_max = number, -- Number of items stackable together
usable = bool, -- Has on_use callback defined
liquids_pointable = bool, -- Whether you can point at liquids with the item
tool_capabilities = <table>, -- If the item is a tool, tool capabiltites of the item
tool_capabilities = <table>, -- If the item is a tool, tool capabilities of the item
groups = table, -- Groups of the item
sound_place = SimpleSoundSpec, -- Sound played when placed
sound_place_failed = SimpleSoundSpec, -- Sound played when placement failed
@ -1163,6 +1167,30 @@ Can be obtained via `minetest.get_meta(pos)`.
}
```
### HUD Definition (`hud_add`, `hud_get`)
```lua
{
hud_elem_type = "image", -- see HUD element types, default "text"
-- ^ type of HUD element, can be either of "image", "text", "statbar", or "inventory"
position = {x=0.5, y=0.5},
-- ^ Left corner position of element, default `{x=0,y=0}`.
name = "<name>", -- default ""
scale = {x=2, y=2}, -- default {x=0,y=0}
text = "<text>", -- default ""
number = 2, -- default 0
item = 3, -- default 0
-- ^ Selected item in inventory. 0 for no item selected.
direction = 0, -- default 0
-- ^ Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
alignment = {x=0, y=0}, -- default {x=0, y=0}
-- ^ See "HUD Element Types"
offset = {x=0, y=0}, -- default {x=0, y=0}
-- ^ See "HUD Element Types"
size = { x=100, y=100 }, -- default {x=0, y=0}
-- ^ Size of element in pixels
}
```
Escape sequences
----------------
Most text can contain escape sequences, that can for example color the text.
@ -1206,3 +1234,134 @@ value must (always) be two hexadecimal digits.
`Color`
-------------
`{a = alpha, r = red, g = green, b = blue}` defines an ARGB8 color.
HUD element types
-----------------
The position field is used for all element types.
To account for differing resolutions, the position coordinates are the percentage
of the screen, ranging in value from `0` to `1`.
The name field is not yet used, but should contain a description of what the
HUD element represents. The direction field is the direction in which something
is drawn.
`0` draws from left to right, `1` draws from right to left, `2` draws from
top to bottom, and `3` draws from bottom to top.
The `alignment` field specifies how the item will be aligned. It ranges from `-1` to `1`,
with `0` being the center, `-1` is moved to the left/up, and `1` is to the right/down.
Fractional values can be used.
The `offset` field specifies a pixel offset from the position. Contrary to position,
the offset is not scaled to screen size. This allows for some precisely-positioned
items in the HUD.
**Note**: `offset` _will_ adapt to screen DPI as well as user defined scaling factor!
Below are the specific uses for fields in each type; fields not listed for that type are ignored.
**Note**: Future revisions to the HUD API may be incompatible; the HUD API is still
in the experimental stages.
### `image`
Displays an image on the HUD.
* `scale`: The scale of the image, with 1 being the original texture size.
Only the X coordinate scale is used (positive values).
Negative values represent that percentage of the screen it
should take; e.g. `x=-100` means 100% (width).
* `text`: The name of the texture that is displayed.
* `alignment`: The alignment of the image.
* `offset`: offset in pixels from position.
### `text`
Displays text on the HUD.
* `scale`: Defines the bounding rectangle of the text.
A value such as `{x=100, y=100}` should work.
* `text`: The text to be displayed in the HUD element.
* `number`: An integer containing the RGB value of the color used to draw the text.
Specify `0xFFFFFF` for white text, `0xFF0000` for red, and so on.
* `alignment`: The alignment of the text.
* `offset`: offset in pixels from position.
### `statbar`
Displays a horizontal bar made up of half-images.
* `text`: The name of the texture that is used.
* `number`: The number of half-textures that are displayed.
If odd, will end with a vertically center-split texture.
* `direction`
* `offset`: offset in pixels from position.
* `size`: If used, will force full-image size to this value (override texture pack image size)
### `inventory`
* `text`: The name of the inventory list to be displayed.
* `number`: Number of items in the inventory to be displayed.
* `item`: Position of item that is selected.
* `direction`
* `offset`: offset in pixels from position.
### `waypoint`
Displays distance to selected world position.
* `name`: The name of the waypoint.
* `text`: Distance suffix. Can be blank.
* `number:` An integer containing the RGB value of the color used to draw the text.
* `world_pos`: World position of the waypoint.
### Particle definition (`add_particle`)
{
pos = {x=0, y=0, z=0},
velocity = {x=0, y=0, z=0},
acceleration = {x=0, y=0, z=0},
-- ^ Spawn particle at pos with velocity and acceleration
expirationtime = 1,
-- ^ Disappears after expirationtime seconds
size = 1,
collisiondetection = false,
-- ^ collisiondetection: if true collides with physical objects
collision_removal = false,
-- ^ collision_removal: if true then particle is removed when it collides,
-- ^ requires collisiondetection = true to have any effect
vertical = false,
-- ^ vertical: if true faces player using y axis only
texture = "image.png",
-- ^ Uses texture (string)
animation = {Tile Animation definition},
-- ^ optional, specifies how to animate the particle texture
glow = 0
-- ^ optional, specify particle self-luminescence in darkness
}
### `ParticleSpawner` definition (`add_particlespawner`)
{
amount = 1,
time = 1,
-- ^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
minpos = {x=0, y=0, z=0},
maxpos = {x=0, y=0, z=0},
minvel = {x=0, y=0, z=0},
maxvel = {x=0, y=0, z=0},
minacc = {x=0, y=0, z=0},
maxacc = {x=0, y=0, z=0},
minexptime = 1,
maxexptime = 1,
minsize = 1,
maxsize = 1,
-- ^ The particle's properties are random values in between the bounds:
-- ^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
-- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
collisiondetection = false,
-- ^ collisiondetection: if true uses collision detection
collision_removal = false,
-- ^ collision_removal: if true then particle is removed when it collides,
-- ^ requires collisiondetection = true to have any effect
vertical = false,
-- ^ vertical: if true faces player using y axis only
texture = "image.png",
-- ^ Uses texture (string)
}

File diff suppressed because it is too large Load Diff

View File

@ -682,6 +682,74 @@ minetest.register_chatcommand("test1", {
end,
})
minetest.register_chatcommand("test_bulk_set_node", {
params = "",
description = "Test 2: bulk set a node",
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then
return
end
local pos_list = {}
local ppos = player:get_pos()
local i = 1
for x=2,10 do
for y=2,10 do
for z=2,10 do
pos_list[i] = {x=ppos.x + x,y = ppos.y + y,z = ppos.z + z}
i = i + 1
end
end
end
minetest.bulk_set_node(pos_list, {name = "default:stone"})
minetest.chat_send_player(name, "Done.");
end,
})
minetest.register_chatcommand("bench_bulk_set_node", {
params = "",
description = "Test 3: bulk set a node (bench)",
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then
return
end
local pos_list = {}
local ppos = player:get_pos()
local i = 1
for x=2,100 do
for y=2,100 do
for z=2,100 do
pos_list[i] = {x=ppos.x + x,y = ppos.y + y,z = ppos.z + z}
i = i + 1
end
end
end
minetest.chat_send_player(name, "Benching bulk set node. Warming up...");
-- warm up with default:stone to prevent having different callbacks
-- due to different node topology
minetest.bulk_set_node(pos_list, {name = "default:stone"})
minetest.chat_send_player(name, "Warming up finished, now benching...");
local start_time = os.clock()
for i=1,#pos_list do
minetest.set_node(pos_list[i], {name = "default:stone"})
end
local middle_time = os.clock()
minetest.bulk_set_node(pos_list, {name = "default:stone"})
local end_time = os.clock()
minetest.chat_send_player(name,
string.format("Bench results: set_node loop[%.2fms], bulk_set_node[%.2fms]",
(middle_time - start_time) * 1000,
(end_time - middle_time) * 1000
)
);
end,
})
minetest.register_on_player_receive_fields(function(player, formname, fields)
experimental.print_to_everything("Inventory fields 1: player="..player:get_player_name()..", fields="..dump(fields))
end)

View File

@ -701,7 +701,7 @@
# In-game chat console height, between 0.1 (10%) and 1.0 (100%).
# type: float min: 0.1 max: 1
# console_height = 1.0
# console_height = 0.6
# In-game chat console background color (R,G,B).
# type: string
@ -994,6 +994,10 @@
# type: int
# max_out_chat_queue_size = 20
# Open the pause menu when the window's focus is lost. Does not pause if a formspec is open.
# type: bool
# pause_on_lost_focus = false
## Advanced
# Timeout for client to remove unused map data from memory.
@ -1346,13 +1350,13 @@
# Restricts the access of certain client-side functions on servers
# Combine these byteflags below to restrict more client-side features:
# LOOKUP_NODES_LIMIT: 1 (limits get_node call client-side to csm_flavour_noderange_limit)
# LOAD_CLIENT_MODS: 1 (disable client mods loading)
# CHAT_MESSAGES: 2 (disable send_chat_message call client-side)
# READ_ITEMDEFS: 4 (disable get_item_def call client-side)
# READ_NODEDEFS: 8 (disable get_node_def call client-side)
# type: int
# type: int
# csm_flavour_limits = 3
# LOOKUP_NODES_LIMIT: 16 (limits get_node call client-side to csm_flavour_noderange_limit)
# csm_flavour_limits = 18
# If the CSM flavour for node range is enabled, get_node is limited to
# this many nodes from the player.

View File

@ -448,6 +448,7 @@ set(common_SRCS
version.cpp
voxel.cpp
voxelalgorithms.cpp
hud.cpp
${common_network_SRCS}
${JTHREAD_SRCS}
${common_SCRIPT_SRCS}

View File

@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic BLOT <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@ -125,11 +126,11 @@ void BanManager::remove(const std::string &ip_or_name)
for (StringMap::iterator it = m_ips.begin(); it != m_ips.end();) {
if ((it->first == ip_or_name) || (it->second == ip_or_name)) {
m_ips.erase(it++);
m_modified = true;
} else {
++it;
}
}
m_modified = true;
}

View File

@ -72,8 +72,7 @@ Camera::Camera(MapDrawControl &draw_control, Client *client):
m_cache_fall_bobbing_amount = g_settings->getFloat("fall_bobbing_amount");
m_cache_view_bobbing_amount = g_settings->getFloat("view_bobbing_amount");
m_cache_fov = g_settings->getFloat("fov");
m_cache_zoom_fov = g_settings->getFloat("zoom_fov");
m_arm_inertia = g_settings->getBool("arm_inertia");
m_arm_inertia = g_settings->getBool("arm_inertia");
m_nametags.clear();
}
@ -453,12 +452,13 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, f32 tool_r
// Get FOV
f32 fov_degrees;
if (player->getPlayerControl().zoom && player->getCanZoom()) {
fov_degrees = m_cache_zoom_fov;
// Disable zoom with zoom FOV = 0
if (player->getPlayerControl().zoom && player->getZoomFOV() > 0.001f) {
fov_degrees = player->getZoomFOV();
} else {
fov_degrees = m_cache_fov;
}
fov_degrees = rangelim(fov_degrees, 1.0, 160.0);
fov_degrees = rangelim(fov_degrees, 1.0f, 160.0f);
// FOV and aspect ratio
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();

View File

@ -225,7 +225,6 @@ private:
f32 m_cache_fall_bobbing_amount;
f32 m_cache_view_bobbing_amount;
f32 m_cache_fov;
f32 m_cache_zoom_fov;
bool m_arm_inertia;
std::list<Nametag *> m_nametags;

View File

@ -18,11 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "chat.h"
#include "debug.h"
#include "config.h"
#include "util/strfnd.h"
#include <algorithm>
#include <cctype>
#include <sstream>
#include "config.h"
#include "debug.h"
#include "util/strfnd.h"
#include "util/string.h"
#include "util/numeric.h"
@ -403,8 +406,14 @@ void ChatPrompt::input(const std::wstring &str)
void ChatPrompt::addToHistory(std::wstring line)
{
if (!line.empty())
if (!line.empty() &&
(m_history.size() == 0 || m_history.back() != line)) {
// Remove all duplicates
m_history.erase(std::remove(m_history.begin(), m_history.end(),
line), m_history.end());
// Push unique line
m_history.push_back(line);
}
if (m_history.size() > m_history_limit)
m_history.erase(m_history.begin());
m_history_index = m_history.size();
@ -699,11 +708,10 @@ ChatBuffer& ChatBackend::getRecentBuffer()
return m_recent_buffer;
}
EnrichedString ChatBackend::getRecentChat()
EnrichedString ChatBackend::getRecentChat() const
{
EnrichedString result;
for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i)
{
for (u32 i = 0; i < m_recent_buffer.getLineCount(); ++i) {
const ChatLine& line = m_recent_buffer.getLine(i);
if (i != 0)
result += L"\n";

View File

@ -265,7 +265,7 @@ public:
// Get the recent messages buffer
ChatBuffer& getRecentBuffer();
// Concatenate all recent messages
EnrichedString getRecentChat();
EnrichedString getRecentChat() const;
// Get the console prompt
ChatPrompt& getPrompt();

View File

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "network/networkpacket.h"
#include "threading/mutex_auto_lock.h"
#include "client/clientevent.h"
#include "client/gameui.h"
#include "client/renderingengine.h"
#include "client/tile.h"
#include "util/auth.h"
@ -74,7 +75,7 @@ Client::Client(
ISoundManager *sound,
MtEventManager *event,
bool ipv6,
GameUIFlags *game_ui_flags
GameUI *game_ui
):
m_tsrc(tsrc),
m_shsrc(shsrc),
@ -96,7 +97,7 @@ Client::Client(
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
m_media_downloader(new ClientMediaDownloader()),
m_state(LC_Created),
m_game_ui_flags(game_ui_flags),
m_game_ui(game_ui),
m_modchannel_mgr(new ModChannelMgr())
{
// Add local player
@ -113,18 +114,38 @@ Client::Client(
m_script->setEnv(&m_env);
}
void Client::loadMods()
void Client::loadBuiltin()
{
// Load builtin
scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath());
// If modding is not enabled, don't load mods, just builtin
if (!m_modding_enabled) {
m_script->loadModFromMemory(BUILTIN_MOD_NAME);
}
void Client::loadMods()
{
// Don't permit to load mods twice
if (m_mods_loaded) {
return;
}
// If modding is not enabled or flavour disable it, don't load mods, just builtin
if (!m_modding_enabled) {
warningstream << "Client side mods are disabled by configuration." << std::endl;
return;
}
if (checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOAD_CLIENT_MODS)) {
warningstream << "Client side mods are disabled by server." << std::endl;
// If mods loading is disabled and builtin integrity is wrong, disconnect user.
if (!checkBuiltinIntegrity()) {
// @TODO disconnect user
}
return;
}
ClientModConfiguration modconf(getClientModsLuaPath());
m_mods = modconf.getMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies
if (!modconf.isConsistent()) {
modconf.printUnsatisfiedModsError();
@ -145,6 +166,18 @@ void Client::loadMods()
}
scanModIntoMemory(mod.name, mod.path);
}
// Load and run "mod" scripts
for (const ModSpec &mod : m_mods)
m_script->loadModFromMemory(mod.name);
m_mods_loaded = true;
}
bool Client::checkBuiltinIntegrity()
{
// @TODO
return true;
}
void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
@ -164,20 +197,6 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo
}
}
void Client::initMods()
{
m_script->loadModFromMemory(BUILTIN_MOD_NAME);
// If modding is not enabled, don't load mods, just builtin
if (!m_modding_enabled) {
return;
}
// Load and run "mod" scripts
for (const ModSpec &mod : m_mods)
m_script->loadModFromMemory(mod.name);
}
const std::string &Client::getBuiltinLuaPath()
{
static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin";
@ -264,14 +283,9 @@ void Client::connect(Address address, bool is_local_server)
void Client::step(float dtime)
{
// Limit a bit
if(dtime > 2.0)
if (dtime > 2.0)
dtime = 2.0;
if(m_ignore_damage_timer > dtime)
m_ignore_damage_timer -= dtime;
else
m_ignore_damage_timer = 0.0;
m_animation_time += dtime;
if(m_animation_time > 60.0)
m_animation_time -= 60.0;
@ -304,6 +318,10 @@ void Client::step(float dtime)
initial_step = false;
}
else if(m_state == LC_Created) {
if (m_is_registration_confirmation_state) {
// Waiting confirmation
return;
}
float &counter = m_connection_reinit_timer;
counter -= dtime;
if(counter <= 0.0) {
@ -394,18 +412,16 @@ void Client::step(float dtime)
ClientEnvEvent envEvent = m_env.getClientEnvEvent();
if (envEvent.type == CEE_PLAYER_DAMAGE) {
if (m_ignore_damage_timer <= 0) {
u8 damage = envEvent.player_damage.amount;
u8 damage = envEvent.player_damage.amount;
if (envEvent.player_damage.send_to_server)
sendDamage(damage);
if (envEvent.player_damage.send_to_server)
sendDamage(damage);
// Add to ClientEvent queue
ClientEvent *event = new ClientEvent();
event->type = CE_PLAYER_DAMAGE;
event->player_damage.amount = damage;
m_client_event_queue.push(event);
}
// Add to ClientEvent queue
ClientEvent *event = new ClientEvent();
event->type = CE_PLAYER_DAMAGE;
event->player_damage.amount = damage;
m_client_event_queue.push(event);
}
}
@ -962,6 +978,18 @@ void Client::sendInit(const std::string &playerName)
Send(&pkt);
}
void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;
m_is_registration_confirmation_state = true;
}
void Client::confirmRegistration()
{
m_is_registration_confirmation_state = false;
startAuth(m_chosen_auth_mech);
}
void Client::startAuth(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;
@ -1165,9 +1193,6 @@ void Client::sendChangePassword(const std::string &oldpassword,
void Client::sendDamage(u8 damage)
{
NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
pkt << damage;
Send(&pkt);
}
void Client::sendRespawn()
@ -1670,7 +1695,6 @@ void Client::afterContentReceived()
if (g_settings->getBool("enable_client_modding")) {
m_script->on_client_ready(m_env.getLocalPlayer());
m_script->on_connect();
}
text = wgettext("Done!");
@ -1760,34 +1784,9 @@ void Client::pushToEventQueue(ClientEvent *event)
m_client_event_queue.push(event);
}
void Client::showGameChat(const bool show)
{
m_game_ui_flags->show_chat = show;
}
void Client::showGameHud(const bool show)
{
m_game_ui_flags->show_hud = show;
}
void Client::showMinimap(const bool show)
{
m_game_ui_flags->show_minimap = show;
}
void Client::showProfiler(const bool show)
{
m_game_ui_flags->show_profiler_graph = show;
}
void Client::showGameFog(const bool show)
{
m_game_ui_flags->force_fog_off = !show;
}
void Client::showGameDebug(const bool show)
{
m_game_ui_flags->show_debug = show;
m_game_ui->showMinimap(show);
}
// IGameDef interface
@ -1836,7 +1835,7 @@ ParticleManager* Client::getParticleManager()
return &m_particle_manager;
}
scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
scene::IAnimatedMesh* Client::getMesh(const std::string &filename, bool cache)
{
StringMap::const_iterator it = m_mesh_data.find(filename);
if (it == m_mesh_data.end()) {
@ -1855,10 +1854,9 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
scene::IAnimatedMesh *mesh = RenderingEngine::get_scene_manager()->getMesh(rfile);
rfile->drop();
// NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
// of uniquely named instances and re-use them
mesh->grab();
RenderingEngine::get_mesh_cache()->removeMesh(mesh);
if (!cache)
RenderingEngine::get_mesh_cache()->removeMesh(mesh);
return mesh;
}

View File

@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h"
#include "inventorymanager.h"
#include "localplayer.h"
#include "hud.h"
#include "client/hud.h"
#include "particles.h"
#include "mapnode.h"
#include "tileanimation.h"
@ -112,7 +112,7 @@ private:
};
class ClientScripting;
struct GameUIFlags;
class GameUI;
class Client : public con::PeerHandler, public InventoryManager, public IGameDef
{
@ -133,14 +133,14 @@ public:
ISoundManager *sound,
MtEventManager *event,
bool ipv6,
GameUIFlags *game_ui_flags
GameUI *game_ui
);
~Client();
DISABLE_CLASS_COPY(Client);
// Load local mods into memory
void loadMods();
void loadBuiltin();
void scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
std::string mod_subpath);
inline void scanModIntoMemory(const std::string &mod_name, const std::string &mod_path)
@ -148,9 +148,6 @@ public:
scanModSubfolder(mod_name, mod_path, "");
}
// Initizle the mods
void initMods();
/*
request all threads managed by client to be stopped
*/
@ -348,6 +345,9 @@ public:
{ return m_proto_ver; }
bool connectedToServer();
void confirmRegistration();
bool m_is_registration_confirmation_state = false;
bool m_simple_singleplayer_mode;
float mediaReceiveProgress();
@ -376,7 +376,7 @@ public:
virtual ParticleManager* getParticleManager();
bool checkLocalPrivilege(const std::string &priv)
{ return checkPrivilege(priv); }
virtual scene::IAnimatedMesh* getMesh(const std::string &filename);
virtual scene::IAnimatedMesh* getMesh(const std::string &filename, bool cache = false);
const std::string* getModFile(const std::string &filename);
virtual std::string getModStoragePath() const;
@ -403,12 +403,7 @@ public:
void pushToEventQueue(ClientEvent *event);
void showGameChat(bool show = true);
void showGameHud(bool show = true);
void showMinimap(bool show = true);
void showProfiler(bool show = true);
void showGameFog(bool show = true);
void showGameDebug(bool show = true);
const Address getServerAddress();
@ -427,12 +422,19 @@ public:
return m_csm_noderange_limit;
}
inline std::unordered_map<u32, u32> &getHUDTranslationMap()
{
return m_hud_server_to_client;
}
bool joinModChannel(const std::string &channel);
bool leaveModChannel(const std::string &channel);
bool sendModChannelMessage(const std::string &channel, const std::string &message);
ModChannel *getModChannel(const std::string &channel);
private:
void loadMods();
bool checkBuiltinIntegrity();
// Virtual methods from con::PeerHandler
void peerAdded(con::Peer *peer);
@ -454,6 +456,7 @@ private:
static AuthMechanism choseAuthMech(const u32 mechs);
void sendInit(const std::string &playerName);
void promptConfirmRegistration(AuthMechanism chosen_auth_mechanism);
void startAuth(AuthMechanism chosen_auth_mechanism);
void sendDeletedBlocks(std::vector<v3s16> &blocks);
void sendGotBlocks(v3s16 block);
@ -469,7 +472,6 @@ private:
float m_connection_reinit_timer = 0.1f;
float m_avg_rtt_timer = 0.0f;
float m_playerpos_send_timer = 0.0f;
float m_ignore_damage_timer = 0.0f; // Used after server moves player
IntervalLimiter m_map_timer_and_unload_interval;
IWritableTextureSource *m_tsrc;
@ -537,6 +539,7 @@ private:
std::queue<ClientEvent *> m_client_event_queue;
bool m_itemdef_received = false;
bool m_nodedef_received = false;
bool m_mods_loaded = false;
ClientMediaDownloader *m_media_downloader;
// time_of_day speed approximation for old protocol
@ -556,6 +559,12 @@ private:
// And relations to objects
std::unordered_map<int, u16> m_sounds_to_objects;
// CSM/client IDs to SSM/server IDs Mapping
// Map server particle spawner IDs to client IDs
std::unordered_map<u32, u32> m_particles_server_to_client;
// Map server hud ids to client hud ids
std::unordered_map<u32, u32> m_hud_server_to_client;
// Privileges
std::unordered_set<std::string> m_privileges;
@ -571,6 +580,8 @@ private:
// own state
LocalClientState m_state;
GameUI *m_game_ui;
// Used for saving server map to disk client-side
MapDatabase *m_localdb = nullptr;
IntervalLimiter m_localdb_save_interval;
@ -581,7 +592,6 @@ private:
std::unordered_map<std::string, ModMetadata *> m_mod_storages;
float m_mod_storage_save_timer = 10.0f;
std::vector<ModSpec> m_mods;
GameUIFlags *m_game_ui_flags;
bool m_shutdown = false;

View File

@ -9,8 +9,10 @@ set(client_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/render/stereo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/renderingengine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
${CMAKE_CURRENT_SOURCE_DIR}/gameui.cpp
${CMAKE_CURRENT_SOURCE_DIR}/inputhandler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/joystick_controller.cpp
${CMAKE_CURRENT_SOURCE_DIR}/hud.cpp
PARENT_SCOPE
)

View File

@ -116,7 +116,7 @@ struct ClientEvent
} delete_particlespawner;
struct
{
u32 id;
u32 server_id;
u8 type;
v2f *pos;
std::string *name;

304
src/client/gameui.cpp Normal file
View File

@ -0,0 +1,304 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gameui.h"
#include <irrlicht_changes/static_text.h>
#include <gettext.h>
#include "gui/mainmenumanager.h"
#include "util/pointedthing.h"
#include "client.h"
#include "clientmap.h"
#include "fontengine.h"
#include "nodedef.h"
#include "profiler.h"
#include "renderingengine.h"
#include "version.h"
inline static const char *yawToDirectionString(int yaw)
{
static const char *direction[4] = {"N +Z", "W -X", "S -Z", "E +X"};
yaw = wrapDegrees_0_360(yaw);
yaw = (yaw + 45) % 360 / 90;
return direction[yaw];
}
GameUI::GameUI()
{
if (guienv && guienv->getSkin())
m_statustext_initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
else
m_statustext_initial_color = video::SColor(255, 0, 0, 0);
}
void GameUI::init()
{
// First line of debug text
m_guitext = gui::StaticText::add(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
// Second line of debug text
m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false,
false, guiroot);
// At the middle of the screen
// Object infos are shown in this
m_guitext_info = gui::StaticText::add(guienv, L"",
core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5)
+ v2s32(100, 200), false, true, guiroot);
// Status text (displays info when showing and hiding GUI stuff, etc.)
m_guitext_status = gui::StaticText::add(guienv, L"<Status>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
m_guitext_status->setVisible(false);
// Chat text
m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0),
//false, false); // Disable word wrap as of now
false, true, guiroot);
// Profiler text (size is updated when text is updated)
m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>",
core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
m_guitext_profiler->setBackgroundColor(video::SColor(120, 0, 0, 0));
m_guitext_profiler->setVisible(false);
m_guitext_profiler->setWordWrap(true);
}
void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_control,
const CameraOrientation &cam, const PointedThing &pointed_old, float dtime)
{
v2u32 screensize = RenderingEngine::get_instance()->getWindowSize();
if (m_flags.show_debug) {
static float drawtime_avg = 0;
drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05;
u16 fps = 1.0 / stats.dtime_jitter.avg;
std::ostringstream os(std::ios_base::binary);
os << std::fixed
<< PROJECT_NAME_C " " << g_version_hash
<< ", FPS: " << fps
<< std::setprecision(0)
<< ", drawtime: " << drawtime_avg << "ms"
<< std::setprecision(1)
<< ", dtime jitter: "
<< (stats.dtime_jitter.max_fraction * 100.0) << "%"
<< std::setprecision(1)
<< ", view range: "
<< (draw_control->range_all ? "All" : itos(draw_control->wanted_range))
<< std::setprecision(3)
<< ", RTT: " << client->getRTT() << "s";
setStaticText(m_guitext, utf8_to_wide(os.str()).c_str());
m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X,
5 + g_fontengine->getTextHeight()));
}
// Finally set the guitext visible depending on the flag
m_guitext->setVisible(m_flags.show_debug);
if (m_flags.show_debug) {
LocalPlayer *player = client->getEnv().getLocalPlayer();
v3f player_position = player->getPosition();
std::ostringstream os(std::ios_base::binary);
os << std::setprecision(1) << std::fixed
<< "pos: (" << (player_position.X / BS)
<< ", " << (player_position.Y / BS)
<< ", " << (player_position.Z / BS)
<< "), yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° "
<< yawToDirectionString(cam.camera_yaw)
<< ", seed: " << ((u64)client->getMapSeed());
if (pointed_old.type == POINTEDTHING_NODE) {
ClientMap &map = client->getEnv().getClientMap();
const INodeDefManager *nodedef = client->getNodeDefManager();
MapNode n = map.getNodeNoEx(pointed_old.node_undersurface);
if (n.getContent() != CONTENT_IGNORE && nodedef->get(n).name != "unknown") {
os << ", pointed: " << nodedef->get(n).name
<< ", param2: " << (u64) n.getParam2();
}
}
setStaticText(m_guitext2, utf8_to_wide(os.str()).c_str());
m_guitext2->setRelativePosition(core::rect<s32>(5,
5 + g_fontengine->getTextHeight(), screensize.X,
5 + g_fontengine->getTextHeight() * 2
));
}
m_guitext2->setVisible(m_flags.show_debug);
setStaticText(m_guitext_info, translate_string(m_infotext).c_str());
m_guitext_info->setVisible(m_flags.show_hud && g_menumgr.menuCount() == 0);
static const float statustext_time_max = 1.5f;
if (!m_statustext.empty()) {
m_statustext_time += dtime;
if (m_statustext_time >= statustext_time_max) {
clearStatusText();
m_statustext_time = 0.0f;
}
}
setStaticText(m_guitext_status, translate_string(m_statustext).c_str());
m_guitext_status->setVisible(!m_statustext.empty());
if (!m_statustext.empty()) {
s32 status_width = m_guitext_status->getTextWidth();
s32 status_height = m_guitext_status->getTextHeight();
s32 status_y = screensize.Y - 150;
s32 status_x = (screensize.X - status_width) / 2;
m_guitext_status->setRelativePosition(core::rect<s32>(status_x ,
status_y - status_height, status_x + status_width, status_y));
// Fade out
video::SColor final_color = m_statustext_initial_color;
final_color.setAlpha(0);
video::SColor fade_color = m_statustext_initial_color.getInterpolated_quadratic(
m_statustext_initial_color, final_color, m_statustext_time / statustext_time_max);
m_guitext_status->setOverrideColor(fade_color);
m_guitext_status->enableOverrideColor(true);
}
}
void GameUI::initFlags()
{
memset(&m_flags, 0, sizeof(GameUI::Flags));
m_flags.show_chat = true;
m_flags.show_hud = true;
m_flags.show_debug = g_settings->getBool("show_debug");
}
void GameUI::showMinimap(bool show)
{
m_flags.show_minimap = show;
}
void GameUI::showTranslatedStatusText(const char *str)
{
const wchar_t *wmsg = wgettext(str);
showStatusText(wmsg);
delete[] wmsg;
}
void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count)
{
setStaticText(m_guitext_chat, chat_text);
// Update gui element size and position
s32 chat_y = 5;
if (m_flags.show_debug)
chat_y += 2 * g_fontengine->getLineHeight();
// first pass to calculate height of text to be set
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
s32 width = std::min(g_fontengine->getTextWidth(chat_text.c_str()) + 10,
window_size.X - 20);
m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width,
chat_y + window_size.Y));
// now use real height of text and adjust rect according to this size
m_guitext_chat->setRelativePosition(core::rect<s32>(10, chat_y, width,
chat_y + m_guitext_chat->getTextHeight()));
// Don't show chat if disabled or empty or profiler is enabled
m_guitext_chat->setVisible(m_flags.show_chat &&
recent_chat_count != 0 && m_profiler_current_page == 0);
}
void GameUI::updateProfiler()
{
if (m_profiler_current_page != 0) {
std::ostringstream os(std::ios_base::binary);
g_profiler->printPage(os, m_profiler_current_page, m_profiler_max_page);
std::wstring text = translate_string(utf8_to_wide(os.str()));
setStaticText(m_guitext_profiler, text.c_str());
s32 w = g_fontengine->getTextWidth(text);
if (w < 400)
w = 400;
u32 text_height = g_fontengine->getTextHeight();
core::position2di upper_left, lower_right;
upper_left.X = 6;
upper_left.Y = (text_height + 5) * 2;
lower_right.X = 12 + w;
lower_right.Y = upper_left.Y + (text_height + 1) * MAX_PROFILER_TEXT_ROWS;
s32 screen_height = RenderingEngine::get_video_driver()->getScreenSize().Height;
if (lower_right.Y > screen_height * 2 / 3)
lower_right.Y = screen_height * 2 / 3;
m_guitext_profiler->setRelativePosition(core::rect<s32>(upper_left, lower_right));
}
m_guitext_profiler->setVisible(m_profiler_current_page != 0);
}
void GameUI::toggleChat()
{
m_flags.show_chat = !m_flags.show_chat;
if (m_flags.show_chat)
showTranslatedStatusText("Chat shown");
else
showTranslatedStatusText("Chat hidden");
}
void GameUI::toggleHud()
{
m_flags.show_hud = !m_flags.show_hud;
if (m_flags.show_hud)
showTranslatedStatusText("HUD shown");
else
showTranslatedStatusText("HUD hidden");
}
void GameUI::toggleProfiler()
{
m_profiler_current_page = (m_profiler_current_page + 1) % (m_profiler_max_page + 1);
// FIXME: This updates the profiler with incomplete values
updateProfiler();
if (m_profiler_current_page != 0) {
wchar_t buf[255];
const wchar_t* str = wgettext("Profiler shown (page %d of %d)");
swprintf(buf, sizeof(buf) / sizeof(wchar_t), str,
m_profiler_current_page, m_profiler_max_page);
delete[] str;
showStatusText(buf);
} else {
showTranslatedStatusText("Profiler hidden");
}
}

110
src/client/gameui.h Normal file
View File

@ -0,0 +1,110 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <IGUIEnvironment.h>
#include "util/enriched_string.h"
#include "util/pointedthing.h"
#include "game.h"
using namespace irr;
class Client;
struct MapDrawControl;
/*
* This object intend to contain the core UI elements
* It includes:
* - status texts
* - debug texts
* - chat texts
* - hud flags
*/
class GameUI
{
// Temporary between coding time to move things here
friend class Game;
// Permit unittests to access members directly
friend class TestGameUI;
public:
GameUI();
~GameUI() = default;
// Flags that can, or may, change during main game loop
struct Flags
{
bool show_chat = true;
bool show_hud = true;
bool show_minimap = true;
bool show_debug = true;
bool show_profiler_graph = true;
};
void init();
void update(const RunStats &stats, Client *client, MapDrawControl *draw_control,
const CameraOrientation &cam, const PointedThing &pointed_old,
float dtime);
void initFlags();
const Flags &getFlags() const { return m_flags; }
void showMinimap(bool show);
inline void setInfoText(const std::wstring &str) { m_infotext = str; }
inline void clearInfoText() { m_infotext.clear(); }
inline void showStatusText(const std::wstring &str)
{
m_statustext = str;
m_statustext_time = 0.0f;
}
void showTranslatedStatusText(const char *str);
inline void clearStatusText() { m_statustext.clear(); }
void setChatText(const EnrichedString &chat_text, u32 recent_chat_count);
void updateProfiler();
void toggleChat();
void toggleHud();
void toggleProfiler();
private:
Flags m_flags;
gui::IGUIStaticText *m_guitext = nullptr; // First line of debug text
gui::IGUIStaticText *m_guitext2 = nullptr; // Second line of debug text
gui::IGUIStaticText *m_guitext_info = nullptr; // At the middle of the screen
std::wstring m_infotext;
gui::IGUIStaticText *m_guitext_status = nullptr;
std::wstring m_statustext;
float m_statustext_time = 0.0f;
video::SColor m_statustext_initial_color;
gui::IGUIStaticText *m_guitext_chat = nullptr; // Chat text
gui::IGUIStaticText *m_guitext_profiler = nullptr; // Profiler text
u8 m_profiler_current_page = 0;
const u8 m_profiler_max_page = 3;
};

775
src/client/hud.cpp Normal file
View File

@ -0,0 +1,775 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "client/hud.h"
#include "settings.h"
#include "util/numeric.h"
#include "log.h"
#include "client.h"
#include "inventory.h"
#include "shader.h"
#include "client/tile.h"
#include "localplayer.h"
#include "camera.h"
#include "porting.h"
#include "fontengine.h"
#include "guiscalingfilter.h"
#include "mesh.h"
#include "wieldmesh.h"
#include "client/renderingengine.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "gui/touchscreengui.h"
#endif
Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory)
{
driver = RenderingEngine::get_video_driver();
this->guienv = guienv;
this->client = client;
this->player = player;
this->inventory = inventory;
m_hud_scaling = g_settings->getFloat("hud_scaling");
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5f);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
for (auto &hbar_color : hbar_colors)
hbar_color = video::SColor(255, 255, 255, 255);
tsrc = client->getTextureSource();
v3f crosshair_color = g_settings->getV3F("crosshair_color");
u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
m_selection_boxes.clear();
m_halo_boxes.clear();
std::string mode_setting = g_settings->get("node_highlighting");
if (mode_setting == "halo") {
m_mode = HIGHLIGHT_HALO;
} else if (mode_setting == "none") {
m_mode = HIGHLIGHT_NONE;
} else {
m_mode = HIGHLIGHT_BOX;
}
m_selection_material.Lighting = false;
if (g_settings->getBool("enable_shaders")) {
IShaderSource *shdrsrc = client->getShaderSource();
u16 shader_id = shdrsrc->getShader(
m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
} else {
m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
if (m_mode == HIGHLIGHT_BOX) {
m_selection_material.Thickness =
rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
} else if (m_mode == HIGHLIGHT_HALO) {
m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
} else {
m_selection_material.MaterialType = video::EMT_SOLID;
}
}
Hud::~Hud()
{
if (m_selection_mesh)
m_selection_mesh->drop();
}
void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
bool selected)
{
if (selected) {
/* draw hihlighting around selected item */
if (use_hotbar_selected_image) {
core::rect<s32> imgrect2 = rect;
imgrect2.UpperLeftCorner.X -= (m_padding*2);
imgrect2.UpperLeftCorner.Y -= (m_padding*2);
imgrect2.LowerRightCorner.X += (m_padding*2);
imgrect2.LowerRightCorner.Y += (m_padding*2);
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, imgrect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
} else {
video::SColor c_outside(255,255,0,0);
//video::SColor c_outside(255,0,0,0);
//video::SColor c_inside(255,192,192,192);
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
// Black base borders
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1 - m_padding),
v2s32(x2 + m_padding, y1)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y2),
v2s32(x2 + m_padding, y2 + m_padding)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + m_padding, y2)
), NULL);
/*// Light inside borders
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1 - padding/2),
v2s32(x2 + padding/2, y1)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y2),
v2s32(x2 + padding/2, y2 + padding/2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + padding/2, y2)
), NULL);
*/
}
}
video::SColor bgcolor2(128, 0, 0, 0);
if (!use_hotbar_image)
driver->draw2DRectangle(bgcolor2, rect, NULL);
drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
}
//NOTE: selectitem = 0 -> no selected; selectitem 1-based
void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
{
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui && inv_offset == 0)
g_touchscreengui->resetHud();
#endif
s32 height = m_hotbar_imagesize + m_padding * 2;
s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
s32 tmp = height;
height = width;
width = tmp;
}
// Position of upper left corner of bar
v2s32 pos = screen_offset;
pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos += upperleftpos;
// Store hotbar_image in member variable, used by drawItem()
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
if (!hotbar_image.empty())
use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
else
use_hotbar_image = false;
}
// Store hotbar_selected_image in member variable, used by drawItem()
if (hotbar_selected_image != player->hotbar_selected_image) {
hotbar_selected_image = player->hotbar_selected_image;
if (!hotbar_selected_image.empty())
use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
else
use_hotbar_selected_image = false;
}
// draw customized item background
if (use_hotbar_image) {
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
}
// Draw items
core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
v2s32 steppos;
switch (direction) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
break;
default:
steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
break;
}
drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
#endif
}
}
void Hud::drawLuaElements(const v3s16 &camera_offset)
{
u32 text_height = g_fontengine->getTextHeight();
irr::gui::IGUIFont* font = g_fontengine->getFont();
for (size_t i = 0; i != player->maxHudId(); i++) {
HudElement *e = player->getHud(i);
if (!e)
continue;
v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
floor(e->pos.Y * (float) m_screensize.Y + 0.5));
switch (e->type) {
case HUD_ELEM_IMAGE: {
video::ITexture *texture = tsrc->getTexture(e->text);
if (!texture)
continue;
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
core::dimension2di imgsize(texture->getOriginalSize());
v2s32 dstsize(imgsize.Width * e->scale.X,
imgsize.Height * e->scale.Y);
if (e->scale.X < 0)
dstsize.X = m_screensize.X * (e->scale.X * -0.01);
if (e->scale.Y < 0)
dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
(e->align.Y - 1.0) * dstsize.Y / 2);
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, colors, true);
break; }
case HUD_ELEM_TEXT: {
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
std::wstring text = unescape_translate(utf8_to_wide(e->text));
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
v2s32 offs(e->offset.X, e->offset.Y);
font->draw(text.c_str(), size + pos + offset + offs, color);
break; }
case HUD_ELEM_STATBAR: {
v2s32 offs(e->offset.X, e->offset.Y);
drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
inv, e->item, e->dir);
break; }
case HUD_ELEM_WAYPOINT: {
v3f p_pos = player->getPosition() / BS;
v3f w_pos = e->world_pos * BS;
float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
scene::ICameraSceneNode* camera =
RenderingEngine::get_scene_manager()->getActiveCamera();
w_pos -= intToFloat(camera_offset, BS);
core::matrix4 trans = camera->getProjectionMatrix();
trans *= camera->getViewMatrix();
f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
trans.multiplyWith1x4Matrix(transformed_pos);
if (transformed_pos[3] < 0)
break;
f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
core::reciprocal(transformed_pos[3]);
pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
std::wstring text = unescape_translate(utf8_to_wide(e->name));
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
os << distance << e->text;
text = unescape_translate(utf8_to_wide(os.str()));
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }
default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
" of hud element ID " << i << " due to unrecognized type" << std::endl;
}
}
}
void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size)
{
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
video::ITexture *stat_texture = tsrc->getTexture(texture);
if (!stat_texture)
return;
core::dimension2di srcd(stat_texture->getOriginalSize());
core::dimension2di dstd;
if (size == v2s32()) {
dstd = srcd;
} else {
float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
dstd.Height = size.Y * size_factor;
dstd.Width = size.X * size_factor;
offset.X *= size_factor;
offset.Y *= size_factor;
}
v2s32 p = pos;
if (corner & HUD_CORNER_LOWER)
p -= dstd.Height;
p += offset;
v2s32 steppos;
core::rect<s32> srchalfrect, dsthalfrect;
switch (drawdir) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-1, 0);
srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(0, 1);
srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(0, -1);
srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
break;
default:
steppos = v2s32(1, 0);
srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
}
steppos.X *= dstd.Width;
steppos.Y *= dstd.Height;
for (s32 i = 0; i < count / 2; i++) {
core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
dstrect += p;
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
p += steppos;
}
if (count % 2 == 1) {
dsthalfrect += p;
draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
}
}
void Hud::drawHotbar(u16 playeritem) {
v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
InventoryList *mainlist = inventory->getList("main");
if (mainlist == NULL) {
//silently ignore this we may not be initialized completely
return;
}
s32 hotbar_itemcount = player->hud_hotbar_itemcount;
s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if ( (float) width / (float) window_size.X <=
g_settings->getFloat("hud_hotbar_max_width")) {
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
}
} else {
pos.X += width/4;
v2s32 secondpos = pos;
pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
mainlist, playeritem + 1, 0);
drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
}
}
//////////////////////////// compatibility code to be removed //////////////
// this is ugly as hell but there's no other way to keep compatibility to
// old servers
if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "heart.png",
player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
}
if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
(player->getBreath() < 11)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "bubble.png",
player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
}
////////////////////////////////////////////////////////////////////////////
}
void Hud::drawCrosshair()
{
if (use_crosshair_image) {
video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
v2u32 size = crosshair->getOriginalSize();
v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
m_displaycenter.Y - (size.Y / 2));
driver->draw2DImage(crosshair, lsize,
core::rect<s32>(0, 0, size.X, size.Y),
0, crosshair_argb, true);
} else {
driver->draw2DLine(m_displaycenter - v2s32(10, 0),
m_displaycenter + v2s32(10, 0), crosshair_argb);
driver->draw2DLine(m_displaycenter - v2s32(0, 10),
m_displaycenter + v2s32(0, 10), crosshair_argb);
}
}
void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
m_selection_pos = pos;
m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
}
void Hud::drawSelectionMesh()
{
if (m_mode == HIGHLIGHT_BOX) {
// Draw 3D selection boxes
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
for (std::vector<aabb3f>::const_iterator
i = m_selection_boxes.begin();
i != m_selection_boxes.end(); ++i) {
aabb3f box = aabb3f(
i->MinEdge + m_selection_pos_with_offset,
i->MaxEdge + m_selection_pos_with_offset);
u32 r = (selectionbox_argb.getRed() *
m_selection_mesh_color.getRed() / 255);
u32 g = (selectionbox_argb.getGreen() *
m_selection_mesh_color.getGreen() / 255);
u32 b = (selectionbox_argb.getBlue() *
m_selection_mesh_color.getBlue() / 255);
driver->draw3DBox(box, video::SColor(255, r, g, b));
}
driver->setMaterial(oldmaterial);
} else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
// Draw selection mesh
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
setMeshColor(m_selection_mesh, m_selection_mesh_color);
video::SColor face_color(0,
MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
face_color);
scene::IMesh* mesh = cloneMesh(m_selection_mesh);
translateMesh(mesh, m_selection_pos_with_offset);
u32 mc = m_selection_mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
driver->drawMeshBuffer(buf);
}
mesh->drop();
driver->setMaterial(oldmaterial);
}
}
void Hud::updateSelectionMesh(const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
if (m_mode != HIGHLIGHT_HALO)
return;
if (m_selection_mesh) {
m_selection_mesh->drop();
m_selection_mesh = NULL;
}
if (m_selection_boxes.empty()) {
// No pointed object
return;
}
// New pointed object, create new mesh.
// Texture UV coordinates for selection boxes
static f32 texture_uv[24] = {
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1
};
// Use single halo box instead of multiple overlapping boxes.
// Temporary solution - problem can be solved with multiple
// rendering targets, or some method to remove inner surfaces.
// Thats because of halo transparency.
aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
m_halo_boxes.clear();
for (const auto &selection_box : m_selection_boxes) {
halo_box.addInternalBox(selection_box);
}
m_halo_boxes.push_back(halo_box);
m_selection_mesh = convertNodeboxesToMesh(
m_halo_boxes, texture_uv, 0.5);
}
void Hud::resizeHotbar() {
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if (m_screensize != window_size) {
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
m_screensize = window_size;
m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
}
}
struct MeshTimeInfo {
u64 time;
scene::IMesh *mesh;
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind)
{
static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations");
if (item.empty()) {
if (rotation_kind < IT_ROT_NONE) {
rotation_time_infos[rotation_kind].mesh = NULL;
}
return;
}
const ItemDefinition &def = item.getDefinition(client->idef());
ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
if (imesh && imesh->mesh) {
scene::IMesh *mesh = imesh->mesh;
driver->clearZBuffer();
s32 delta = 0;
if (rotation_kind < IT_ROT_NONE) {
MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
if (mesh != ti.mesh) {
ti.mesh = mesh;
ti.time = porting::getTimeMs();
} else {
delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
}
}
core::rect<s32> oldViewPort = driver->getViewPort();
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
core::matrix4 ProjMatrix;
ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
driver->setTransform(video::ETS_VIEW, ProjMatrix);
core::matrix4 matrix;
matrix.makeIdentity();
if (enable_animations) {
float timer_f = (float) delta / 5000.0;
matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
}
driver->setTransform(video::ETS_WORLD, matrix);
driver->setViewPort(rect);
video::SColor basecolor =
client->idef()->getItemstackColor(item, client);
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; ++j) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
// we can modify vertices relatively fast,
// because these meshes are not buffered.
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) {
ItemPartColor *p = &imesh->buffer_colors[j];
if (p->override_base)
c = p->color;
}
if (imesh->needs_shading)
colorizeMeshBuffer(buf, &c);
else
setMeshBufferColor(buf, c);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
driver->setMaterial(material);
driver->drawMeshBuffer(buf);
}
driver->setTransform(video::ETS_VIEW, oldViewMat);
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort);
}
if(def.type == ITEM_TOOL && item.wear != 0)
{
// Draw a progressbar
float barheight = rect.getHeight()/16;
float barpad_x = rect.getWidth()/16;
float barpad_y = rect.getHeight()/16;
core::rect<s32> progressrect(
rect.UpperLeftCorner.X + barpad_x,
rect.LowerRightCorner.Y - barpad_y - barheight,
rect.LowerRightCorner.X - barpad_x,
rect.LowerRightCorner.Y - barpad_y);
// Shrink progressrect by amount of tool damage
float wear = item.wear / 65535.0;
int progressmid =
wear * progressrect.UpperLeftCorner.X +
(1-wear) * progressrect.LowerRightCorner.X;
// Compute progressbar color
// wear = 0.0: green
// wear = 0.5: yellow
// wear = 1.0: red
video::SColor color(255,255,255,255);
int wear_i = MYMIN(floor(wear * 600), 511);
wear_i = MYMIN(wear_i + 10, 511);
if(wear_i <= 255)
color.set(255, wear_i, 255, 0);
else
color.set(255, 255, 511-wear_i, 0);
core::rect<s32> progressrect2 = progressrect;
progressrect2.LowerRightCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
color = video::SColor(255,0,0,0);
progressrect2 = progressrect;
progressrect2.UpperLeftCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
}
if(font != NULL && item.count >= 2)
{
// Get the item count as a string
std::string text = itos(item.count);
v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
v2s32 sdim(dim.X,dim.Y);
core::rect<s32> rect2(
/*rect.UpperLeftCorner,
core::dimension2d<u32>(rect.getWidth(), 15)*/
rect.LowerRightCorner - sdim,
sdim
);
video::SColor bgcolor(128,0,0,0);
driver->draw2DRectangle(bgcolor, rect2, clip);
video::SColor color(255,255,255,255);
font->draw(text.c_str(), rect2, color, false, false, clip);
}
}

136
src/client/hud.h Normal file
View File

@ -0,0 +1,136 @@
/*
Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2017 red-001 <red-001@outlook.ie>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef CLIENT_HUD_HEADER
#define CLIENT_HUD_HEADER
#include <vector>
#include <IGUIFont.h>
#include "irr_aabb3d.h"
#include "../hud.h"
class Client;
class ITextureSource;
class Inventory;
class InventoryList;
class LocalPlayer;
struct ItemStack;
class Hud
{
public:
video::IVideoDriver *driver;
scene::ISceneManager *smgr;
gui::IGUIEnvironment *guienv;
Client *client;
LocalPlayer *player;
Inventory *inventory;
ITextureSource *tsrc;
video::SColor crosshair_argb;
video::SColor selectionbox_argb;
bool use_crosshair_image = false;
std::string hotbar_image = "";
bool use_hotbar_image = false;
std::string hotbar_selected_image = "";
bool use_hotbar_selected_image = false;
Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory);
~Hud();
void drawHotbar(u16 playeritem);
void resizeHotbar();
void drawCrosshair();
void drawSelectionMesh();
void updateSelectionMesh(const v3s16 &camera_offset);
std::vector<aabb3f> *getSelectionBoxes() { return &m_selection_boxes; }
void setSelectionPos(const v3f &pos, const v3s16 &camera_offset);
v3f getSelectionPos() const { return m_selection_pos; }
void setSelectionMeshColor(const video::SColor &color)
{
m_selection_mesh_color = color;
}
void setSelectedFaceNormal(const v3f &face_normal)
{
m_selected_face_normal = face_normal;
}
void drawLuaElements(const v3s16 &camera_offset);
private:
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size = v2s32());
void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem,
u16 direction);
void drawItem(const ItemStack &item, const core::rect<s32> &rect, bool selected);
float m_hud_scaling; // cached minetest setting
v3s16 m_camera_offset;
v2u32 m_screensize;
v2s32 m_displaycenter;
s32 m_hotbar_imagesize; // Takes hud_scaling into account, updated by resizeHotbar()
s32 m_padding; // Takes hud_scaling into account, updated by resizeHotbar()
video::SColor hbar_colors[4];
std::vector<aabb3f> m_selection_boxes;
std::vector<aabb3f> m_halo_boxes;
v3f m_selection_pos;
v3f m_selection_pos_with_offset;
scene::IMesh *m_selection_mesh = nullptr;
video::SColor m_selection_mesh_color;
v3f m_selected_face_normal;
video::SMaterial m_selection_material;
enum
{
HIGHLIGHT_BOX,
HIGHLIGHT_HALO,
HIGHLIGHT_NONE
} m_mode;
};
enum ItemRotationKind
{
IT_ROT_SELECTED,
IT_ROT_HOVERED,
IT_ROT_DRAGGED,
IT_ROT_NONE, // Must be last, also serves as number
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind);
#endif

View File

@ -22,6 +22,75 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inputhandler.h"
#include "gui/mainmenumanager.h"
void KeyCache::populate_nonchanging()
{
key[KeyType::ESC] = EscapeKey;
}
void KeyCache::populate()
{
key[KeyType::FORWARD] = getKeySetting("keymap_forward");
key[KeyType::BACKWARD] = getKeySetting("keymap_backward");
key[KeyType::LEFT] = getKeySetting("keymap_left");
key[KeyType::RIGHT] = getKeySetting("keymap_right");
key[KeyType::JUMP] = getKeySetting("keymap_jump");
key[KeyType::SPECIAL1] = getKeySetting("keymap_special1");
key[KeyType::SNEAK] = getKeySetting("keymap_sneak");
key[KeyType::AUTOFORWARD] = getKeySetting("keymap_autoforward");
key[KeyType::DROP] = getKeySetting("keymap_drop");
key[KeyType::INVENTORY] = getKeySetting("keymap_inventory");
key[KeyType::CHAT] = getKeySetting("keymap_chat");
key[KeyType::CMD] = getKeySetting("keymap_cmd");
key[KeyType::CMD_LOCAL] = getKeySetting("keymap_cmd_local");
key[KeyType::CONSOLE] = getKeySetting("keymap_console");
key[KeyType::MINIMAP] = getKeySetting("keymap_minimap");
key[KeyType::FREEMOVE] = getKeySetting("keymap_freemove");
key[KeyType::FASTMOVE] = getKeySetting("keymap_fastmove");
key[KeyType::NOCLIP] = getKeySetting("keymap_noclip");
key[KeyType::HOTBAR_PREV] = getKeySetting("keymap_hotbar_previous");
key[KeyType::HOTBAR_NEXT] = getKeySetting("keymap_hotbar_next");
key[KeyType::MUTE] = getKeySetting("keymap_mute");
key[KeyType::INC_VOLUME] = getKeySetting("keymap_increase_volume");
key[KeyType::DEC_VOLUME] = getKeySetting("keymap_decrease_volume");
key[KeyType::CINEMATIC] = getKeySetting("keymap_cinematic");
key[KeyType::SCREENSHOT] = getKeySetting("keymap_screenshot");
key[KeyType::TOGGLE_HUD] = getKeySetting("keymap_toggle_hud");
key[KeyType::TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat");
key[KeyType::TOGGLE_FORCE_FOG_OFF] = getKeySetting("keymap_toggle_force_fog_off");
key[KeyType::TOGGLE_UPDATE_CAMERA] = getKeySetting("keymap_toggle_update_camera");
key[KeyType::TOGGLE_DEBUG] = getKeySetting("keymap_toggle_debug");
key[KeyType::TOGGLE_PROFILER] = getKeySetting("keymap_toggle_profiler");
key[KeyType::CAMERA_MODE] = getKeySetting("keymap_camera_mode");
key[KeyType::INCREASE_VIEWING_RANGE] =
getKeySetting("keymap_increase_viewing_range_min");
key[KeyType::DECREASE_VIEWING_RANGE] =
getKeySetting("keymap_decrease_viewing_range_min");
key[KeyType::RANGESELECT] = getKeySetting("keymap_rangeselect");
key[KeyType::ZOOM] = getKeySetting("keymap_zoom");
key[KeyType::QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next");
key[KeyType::QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev");
key[KeyType::QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc");
key[KeyType::QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec");
for (int i = 0; i < 23; i++) {
std::string slot_key_name = "keymap_slot" + std::to_string(i + 1);
key[KeyType::SLOT_1 + i] = getKeySetting(slot_key_name.c_str());
}
if (handler) {
// First clear all keys, then re-add the ones we listen for
handler->dontListenForKeys();
for (const KeyPress &k : key) {
handler->listenForKey(k);
}
handler->listenForKey(EscapeKey);
handler->listenForKey(CancelKey);
}
}
bool MyEventReceiver::OnEvent(const SEvent &event)
{
/*
@ -116,3 +185,72 @@ s32 RandomInputHandler::Rand(s32 min, s32 max)
{
return (myrand() % (max - min + 1)) + min;
}
void RandomInputHandler::step(float dtime)
{
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_jump"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_special1"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_forward"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_left"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 20);
mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 30);
leftdown = !leftdown;
if (leftdown)
leftclicked = true;
if (!leftdown)
leftreleased = true;
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 15);
rightdown = !rightdown;
if (rightdown)
rightclicked = true;
if (!rightdown)
rightreleased = true;
}
}
mousepos += mousespeed;
}

View File

@ -29,6 +29,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gui/touchscreengui.h"
#endif
class InputHandler;
/****************************************************************************
Fast key cache for main game loop
****************************************************************************/
/* This is faster than using getKeySetting with the tradeoff that functions
* using it must make sure that it's initialised before using it and there is
* no error handling (for example bounds checking). This is really intended for
* use only in the main running loop of the client (the_game()) where the faster
* (up to 10x faster) key lookup is an asset. Other parts of the codebase
* (e.g. formspecs) should continue using getKeySetting().
*/
struct KeyCache
{
KeyCache()
{
handler = NULL;
populate();
populate_nonchanging();
}
void populate();
// Keys that are not settings dependent
void populate_nonchanging();
KeyPress key[KeyType::INTERNAL_ENUM_COUNT];
InputHandler *handler;
};
class KeyList : private std::list<KeyPress>
{
typedef std::list<KeyPress> super;
@ -179,12 +211,17 @@ private:
class InputHandler
{
public:
InputHandler() = default;
InputHandler()
{
keycache.handler = this;
keycache.populate();
}
virtual ~InputHandler() = default;
virtual bool isKeyDown(const KeyPress &keyCode) = 0;
virtual bool wasKeyDown(const KeyPress &keyCode) = 0;
virtual bool isKeyDown(GameKeyType k) = 0;
virtual bool wasKeyDown(GameKeyType k) = 0;
virtual bool cancelPressed() = 0;
virtual void listenForKey(const KeyPress &keyCode) {}
virtual void dontListenForKeys() {}
@ -212,6 +249,7 @@ public:
virtual void clear() {}
JoystickController joystick;
KeyCache keycache;
};
/*
Separated input handler
@ -224,13 +262,17 @@ public:
{
m_receiver->joystick = &joystick;
}
virtual bool isKeyDown(const KeyPress &keyCode)
virtual bool isKeyDown(GameKeyType k)
{
return m_receiver->IsKeyDown(keyCode);
return m_receiver->IsKeyDown(keycache.key[k]) || joystick.isKeyDown(k);
}
virtual bool wasKeyDown(const KeyPress &keyCode)
virtual bool wasKeyDown(GameKeyType k)
{
return m_receiver->WasKeyDown(keyCode);
return m_receiver->WasKeyDown(keycache.key[k]) || joystick.wasKeyDown(k);
}
virtual bool cancelPressed()
{
return wasKeyDown(KeyType::ESC) || m_receiver->WasKeyDown(CancelKey);
}
virtual void listenForKey(const KeyPress &keyCode)
{
@ -259,18 +301,58 @@ public:
}
}
virtual bool getLeftState() { return m_receiver->left_active; }
virtual bool getRightState() { return m_receiver->right_active; }
virtual bool getLeftState()
{
return m_receiver->left_active || joystick.isKeyDown(KeyType::MOUSE_L);
}
virtual bool getRightState()
{
return m_receiver->right_active || joystick.isKeyDown(KeyType::MOUSE_R);
}
virtual bool getLeftClicked() { return m_receiver->leftclicked; }
virtual bool getRightClicked() { return m_receiver->rightclicked; }
virtual void resetLeftClicked() { m_receiver->leftclicked = false; }
virtual void resetRightClicked() { m_receiver->rightclicked = false; }
virtual bool getLeftClicked()
{
return m_receiver->leftclicked ||
joystick.getWasKeyDown(KeyType::MOUSE_L);
}
virtual bool getRightClicked()
{
return m_receiver->rightclicked ||
joystick.getWasKeyDown(KeyType::MOUSE_R);
}
virtual bool getLeftReleased() { return m_receiver->leftreleased; }
virtual bool getRightReleased() { return m_receiver->rightreleased; }
virtual void resetLeftReleased() { m_receiver->leftreleased = false; }
virtual void resetRightReleased() { m_receiver->rightreleased = false; }
virtual void resetLeftClicked()
{
m_receiver->leftclicked = false;
joystick.clearWasKeyDown(KeyType::MOUSE_L);
}
virtual void resetRightClicked()
{
m_receiver->rightclicked = false;
joystick.clearWasKeyDown(KeyType::MOUSE_R);
}
virtual bool getLeftReleased()
{
return m_receiver->leftreleased ||
joystick.wasKeyReleased(KeyType::MOUSE_L);
}
virtual bool getRightReleased()
{
return m_receiver->rightreleased ||
joystick.wasKeyReleased(KeyType::MOUSE_R);
}
virtual void resetLeftReleased()
{
m_receiver->leftreleased = false;
joystick.clearWasKeyReleased(KeyType::MOUSE_L);
}
virtual void resetRightReleased()
{
m_receiver->rightreleased = false;
joystick.clearWasKeyReleased(KeyType::MOUSE_R);
}
virtual s32 getMouseWheel() { return m_receiver->getMouseWheel(); }
@ -290,8 +372,9 @@ class RandomInputHandler : public InputHandler
public:
RandomInputHandler() = default;
virtual bool isKeyDown(const KeyPress &keyCode) { return keydown[keyCode]; }
virtual bool wasKeyDown(const KeyPress &keyCode) { return false; }
virtual bool isKeyDown(GameKeyType k) { return keydown[keycache.key[k]]; }
virtual bool wasKeyDown(GameKeyType k) { return false; }
virtual bool cancelPressed() { return false; }
virtual v2s32 getMousePos() { return mousepos; }
virtual void setMousePos(s32 x, s32 y) { mousepos = v2s32(x, y); }
@ -310,74 +393,7 @@ public:
virtual s32 getMouseWheel() { return 0; }
virtual void step(float dtime)
{
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_jump"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_special1"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_forward"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 40);
keydown.toggle(getKeySetting("keymap_left"));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 20);
mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 30);
leftdown = !leftdown;
if (leftdown)
leftclicked = true;
if (!leftdown)
leftreleased = true;
}
}
{
static float counter1 = 0;
counter1 -= dtime;
if (counter1 < 0.0) {
counter1 = 0.1 * Rand(1, 15);
rightdown = !rightdown;
if (rightdown)
rightclicked = true;
if (!rightdown)
rightreleased = true;
}
}
mousepos += mousespeed;
}
virtual void step(float dtime);
s32 Rand(s32 min, s32 max);

View File

@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sidebyside.h"
#include <ICameraSceneNode.h>
#include "hud.h"
#include "client/hud.h"
RenderingCoreSideBySide::RenderingCoreSideBySide(
IrrlichtDevice *_device, Client *_client, Hud *_hud, bool _horizontal)

View File

@ -25,8 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "clouds.h"
#include "util/numeric.h"
#include "guiscalingfilter.h"
#include "hud.h"
#include "localplayer.h"
#include "client/hud.h"
#include "camera.h"
#include "minimap.h"
#include "clientmap.h"
@ -410,6 +410,26 @@ void RenderingEngine::_draw_load_screen(const std::wstring &text,
guitext->remove();
}
/*
Draws the menu scene including (optional) cloud background.
*/
void RenderingEngine::_draw_menu_scene(gui::IGUIEnvironment *guienv,
float dtime, bool clouds)
{
bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
if (cloud_menu_background) {
g_menuclouds->step(dtime * 3);
g_menuclouds->render();
get_video_driver()->beginScene(
true, true, video::SColor(255, 140, 186, 250));
g_menucloudsmgr->drawAll();
} else
get_video_driver()->beginScene(true, true, video::SColor(255, 0, 0, 0));
guienv->drawAll();
get_video_driver()->endScene();
}
std::vector<core::vector3d<u32>> RenderingEngine::getSupportedVideoModes()
{
IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);

View File

@ -110,6 +110,12 @@ public:
text, guienv, tsrc, dtime, percent, clouds);
}
inline static void draw_menu_scene(
gui::IGUIEnvironment *guienv, float dtime, bool clouds)
{
s_singleton->_draw_menu_scene(guienv, dtime, clouds);
}
inline static void draw_scene(video::SColor skycolor, bool show_hud,
bool show_minimap, bool draw_wield_tool, bool draw_crosshair)
{
@ -138,6 +144,9 @@ private:
ITextureSource *tsrc, float dtime = 0, int percent = 0,
bool clouds = true);
void _draw_menu_scene(gui::IGUIEnvironment *guienv, float dtime = 0,
bool clouds = true);
void _draw_scene(video::SColor skycolor, bool show_hud, bool show_minimap,
bool draw_wield_tool, bool draw_crosshair);

View File

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "raycast.h"
#include "voxelalgorithms.h"
#include "settings.h"
#include "content_cao.h"
#include <algorithm>
#include "client/renderingengine.h"
@ -207,6 +208,8 @@ void ClientEnvironment::step(float dtime)
//std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
bool player_immortal = lplayer->getCAO() && lplayer->getCAO()->isImmortal();
for (const CollisionInfo &info : player_collisions) {
v3f speed_diff = info.new_speed - info.old_speed;;
// Handle only fall damage
@ -227,7 +230,7 @@ void ClientEnvironment::step(float dtime)
pre_factor = 1.0 + (float)addp/100.0;
}
float speed = pre_factor * speed_diff.getLength();
if (speed > tolerance) {
if (speed > tolerance && !player_immortal) {
f32 damage_f = (speed - tolerance) / BS * post_factor;
u8 damage = (u8)MYMIN(damage_f + 0.5, 255);
if (damage != 0) {

View File

@ -199,7 +199,7 @@ void RemoteClient::GetNextBlocks (
const s16 full_d_max = std::min(adjustDist(m_max_send_distance, camera_fov), wanted_range);
const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, camera_fov), wanted_range);
const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;
infostream << "Fov from client " << camera_fov << " full_d_max " << full_d_max << std::endl;
//infostream << "Fov from client " << camera_fov << " full_d_max " << full_d_max << std::endl;
s16 d_max = full_d_max;
s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, camera_fov), wanted_range);

View File

@ -360,6 +360,11 @@ v3f GenericCAO::getPosition()
return pos_translator.vect_show;
}
const bool GenericCAO::isImmortal()
{
return itemgroup_get(getGroups(), "immortal");
}
scene::ISceneNode* GenericCAO::getSceneNode()
{
if (m_meshnode) {
@ -563,7 +568,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
}
else if(m_prop.visual == "mesh") {
infostream<<"GenericCAO::addToScene(): mesh"<<std::endl;
scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh);
scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh, true);
if(mesh)
{
m_animated_meshnode = RenderingEngine::get_scene_manager()->
@ -575,13 +580,17 @@ void GenericCAO::addToScene(ITextureSource *tsrc)
m_prop.visual_size.Y,
m_prop.visual_size.X));
u8 li = m_last_light;
// set vertex colors to ensure alpha is set
setMeshColor(m_animated_meshnode->getMesh(), video::SColor(255,li,li,li));
setAnimatedMeshColor(m_animated_meshnode, video::SColor(255,li,li,li));
bool backface_culling = m_prop.backface_culling;
if (m_is_player)
backface_culling = false;
m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, false);
m_animated_meshnode->setMaterialFlag(video::EMF_LIGHTING, true);
m_animated_meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
m_animated_meshnode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
m_animated_meshnode->setMaterialFlag(video::EMF_FOG_ENABLE, true);
@ -669,7 +678,7 @@ void GenericCAO::updateLightNoCheck(u8 light_at_pos)
if (m_meshnode) {
setMeshColor(m_meshnode->getMesh(), color);
} else if (m_animated_meshnode) {
setMeshColor(m_animated_meshnode->getMesh(), color);
setAnimatedMeshColor(m_animated_meshnode, color);
} else if (m_wield_meshnode) {
m_wield_meshnode->setColor(color);
} else if (m_spritenode) {
@ -904,19 +913,19 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
}
if (!getParent() && m_prop.automatic_face_movement_dir &&
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
{
float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
+ m_prop.automatic_face_movement_dir_offset;
float max_rotation_delta =
dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
float delta = wrapDegrees_0_360(target_yaw - m_yaw);
if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
(fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
m_yaw = wrapDegrees_0_360(m_yaw);
} else {
m_yaw = optimal_yaw;
m_yaw = target_yaw;
}
updateNodePos();
}
@ -1025,7 +1034,7 @@ void GenericCAO::updateTextures(std::string mod)
// Set material flags and texture
video::SMaterial& material = m_animated_meshnode->getMaterial(i);
material.TextureLayer[0].Texture = texture;
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_LIGHTING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
// don't filter low-res textures, makes them look blurry
@ -1130,10 +1139,12 @@ void GenericCAO::updateTextures(std::string mod)
buf->getMaterial().AmbientColor = m_prop.colors[1];
buf->getMaterial().DiffuseColor = m_prop.colors[1];
buf->getMaterial().SpecularColor = m_prop.colors[1];
setMeshColor(mesh, m_prop.colors[1]);
} else if (!m_prop.colors.empty()) {
buf->getMaterial().AmbientColor = m_prop.colors[0];
buf->getMaterial().DiffuseColor = m_prop.colors[0];
buf->getMaterial().SpecularColor = m_prop.colors[0];
setMeshColor(mesh, m_prop.colors[0]);
}
buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, use_trilinear_filter);
@ -1259,8 +1270,8 @@ void GenericCAO::processMessage(const std::string &data)
collision_box.MinEdge *= BS;
collision_box.MaxEdge *= BS;
player->setCollisionbox(collision_box);
player->setCanZoom(m_prop.can_zoom);
player->setEyeHeight(m_prop.eye_height);
player->setZoomFOV(m_prop.zoom_fov);
}
if ((m_is_player && !m_is_local_player) && m_prop.nametag.empty())

View File

@ -124,7 +124,10 @@ public:
{
return ACTIVEOBJECT_TYPE_GENERIC;
}
inline const ItemGroupList &getGroups() const
{
return m_armor_groups;
}
void initialize(const std::string &data);
void processInitData(const std::string &data);
@ -143,6 +146,8 @@ public:
return m_yaw;
}
const bool isImmortal();
scene::ISceneNode *getSceneNode();
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode();

View File

@ -385,9 +385,16 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing()
getSpecialTile(1, &tile_liquid);
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
c_flowing = nodedef->getId(f->liquid_alternative_flowing);
c_source = nodedef->getId(f->liquid_alternative_source);
top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
if (draw_liquid_bottom) {
const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
if (f2.solidness > 1)
draw_liquid_bottom = false;
}
if (data->m_smooth_lighting)
return; // don't need to pre-compute anything in this case
@ -595,6 +602,24 @@ void MapblockMeshGenerator::drawLiquidTop()
collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
}
void MapblockMeshGenerator::drawLiquidBottom()
{
video::S3DVertex vertices[4] = {
video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
};
for (int i = 0; i < 4; i++) {
if (data->m_smooth_lighting)
vertices[i].Color = blendLightColor(vertices[i].Pos);
vertices[i].Pos += origin;
}
collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
}
void MapblockMeshGenerator::drawLiquidNode()
{
prepareLiquidNodeDrawing();
@ -603,6 +628,8 @@ void MapblockMeshGenerator::drawLiquidNode()
drawLiquidSides();
if (!top_is_same_liquid)
drawLiquidTop();
if (draw_liquid_bottom)
drawLiquidBottom();
}
void MapblockMeshGenerator::drawGlasslikeNode()

View File

@ -92,6 +92,7 @@ public:
// liquid-specific
bool top_is_same_liquid;
bool draw_liquid_bottom;
TileSpec tile_liquid;
TileSpec tile_liquid_top;
content_t c_flowing;
@ -112,6 +113,7 @@ public:
f32 getCornerLevel(int i, int k);
void drawLiquidSides();
void drawLiquidTop();
void drawLiquidBottom();
// raillike-specific
// name of the group that enables connecting to raillike nodes of different kind

View File

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h"
#include "scripting_server.h"
#include "genericobject.h"
#include "settings.h"
#include <algorithm>
std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
@ -372,20 +373,20 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_velocity += dtime * m_acceleration;
}
if((m_prop.automatic_face_movement_dir) &&
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001))
{
float optimal_yaw = atan2(m_velocity.Z,m_velocity.X) * 180 / M_PI
+ m_prop.automatic_face_movement_dir_offset;
if (m_prop.automatic_face_movement_dir &&
(fabs(m_velocity.Z) > 0.001 || fabs(m_velocity.X) > 0.001)) {
float target_yaw = atan2(m_velocity.Z, m_velocity.X) * 180 / M_PI
+ m_prop.automatic_face_movement_dir_offset;
float max_rotation_delta =
dtime * m_prop.automatic_face_movement_max_rotation_per_sec;
float delta = wrapDegrees_0_360(target_yaw - m_yaw);
if ((m_prop.automatic_face_movement_max_rotation_per_sec > 0) &&
(fabs(m_yaw - optimal_yaw) > max_rotation_delta)) {
m_yaw = optimal_yaw < m_yaw ? m_yaw - max_rotation_delta : m_yaw + max_rotation_delta;
if (delta > max_rotation_delta && 360 - delta > max_rotation_delta) {
m_yaw += (delta < 180) ? max_rotation_delta : -max_rotation_delta;
m_yaw = wrapDegrees_0_360(m_yaw);
} else {
m_yaw = optimal_yaw;
m_yaw = target_yaw;
}
}
}
@ -801,7 +802,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p
m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
m_prop.pointable = true;
// start of default appearance, this should be overwritten by LUA
// Start of default appearance, this should be overwritten by Lua
m_prop.visual = "upright_sprite";
m_prop.visual_size = v2f(1, 2);
m_prop.textures.clear();
@ -811,13 +812,14 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p
m_prop.colors.emplace_back(255, 255, 255, 255);
m_prop.spritediv = v2s16(1,1);
m_prop.eye_height = 1.625f;
// end of default appearance
// End of default appearance
m_prop.is_visible = true;
m_prop.makes_footstep_sound = true;
m_prop.stepheight = PLAYER_DEFAULT_STEPHEIGHT * BS;
m_prop.can_zoom = true;
m_hp = m_prop.hp_max;
m_breath = m_prop.breath_max;
// Disable zoom in survival mode using a value of 0
m_prop.zoom_fov = g_settings->getBool("creative_mode") ? 15.0f : 0.0f;
}
PlayerSAO::~PlayerSAO()
@ -1399,26 +1401,38 @@ bool PlayerSAO::checkMovementCheat()
too, and much more lightweight.
*/
float player_max_speed = 0;
float player_max_walk = 0; // horizontal movement
float player_max_jump = 0; // vertical upwards movement
if (m_privs.count("fast") != 0) {
// Fast speed
player_max_speed = m_player->movement_speed_fast * m_physics_override_speed;
} else {
// Normal speed
player_max_speed = m_player->movement_speed_walk * m_physics_override_speed;
}
// Tolerance. The lag pool does this a bit.
//player_max_speed *= 2.5;
if (m_privs.count("fast") != 0)
player_max_walk = m_player->movement_speed_fast; // Fast speed
else
player_max_walk = m_player->movement_speed_walk; // Normal speed
player_max_walk *= m_physics_override_speed;
player_max_jump = m_player->movement_speed_jump * m_physics_override_jump;
// FIXME: Bouncy nodes cause practically unbound increase in Y speed,
// until this can be verified correctly, tolerate higher jumping speeds
player_max_jump *= 2.0;
// Don't divide by zero!
if (player_max_walk < 0.0001f)
player_max_walk = 0.0001f;
if (player_max_jump < 0.0001f)
player_max_jump = 0.0001f;
v3f diff = (m_base_position - m_last_good_position);
float d_vert = diff.Y;
diff.Y = 0;
float d_horiz = diff.getLength();
float required_time = d_horiz / player_max_speed;
float required_time = d_horiz / player_max_walk;
if (d_vert > 0 && d_vert / player_max_speed > required_time)
required_time = d_vert / player_max_speed; // Moving upwards
// FIXME: Checking downwards movement is not easily possible currently,
// the server could calculate speed differences to examine the gravity
if (d_vert > 0) {
// In certain cases (water, ladders) walking speed is applied vertically
float s = MYMAX(player_max_jump, player_max_walk);
required_time = MYMAX(required_time, d_vert / s);
}
if (m_move_pool.grab(required_time)) {
m_last_good_position = m_base_position;

View File

@ -58,6 +58,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_remote_media_server", "true");
settings->setDefault("enable_client_modding", "false");
settings->setDefault("max_out_chat_queue_size", "20");
settings->setDefault("pause_on_lost_focus", "false");
// Keymap
settings->setDefault("remote_port", "30000");
@ -147,7 +148,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("3d_paralax_strength", "0.025");
settings->setDefault("tooltip_show_delay", "400");
settings->setDefault("tooltip_append_itemname", "false");
settings->setDefault("zoom_fov", "15");
settings->setDefault("fps_max", "60");
settings->setDefault("pause_fps_max", "20");
settings->setDefault("viewing_range", "100");
@ -163,8 +163,11 @@ void set_default_settings(Settings *settings)
settings->setDefault("connected_glass", "false");
settings->setDefault("smooth_lighting", "true");
settings->setDefault("lighting_alpha", "0.0");
settings->setDefault("lighting_beta", "0.0");
settings->setDefault("lighting_beta", "1.5");
settings->setDefault("display_gamma", "1.0");
settings->setDefault("lighting_boost", "0.2");
settings->setDefault("lighting_boost_center", "0.5");
settings->setDefault("lighting_boost_spread", "0.2");
settings->setDefault("texture_path", "");
settings->setDefault("shader_path", "");
settings->setDefault("video_driver", "opengl");
@ -178,7 +181,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("cloud_radius", "12");
settings->setDefault("menu_clouds", "true");
settings->setDefault("opaque_water", "false");
settings->setDefault("console_height", "1.0");
settings->setDefault("console_height", "0.6");
settings->setDefault("console_color", "(0,0,0)");
settings->setDefault("console_alpha", "200");
settings->setDefault("formspec_fullscreen_bg_color", "(0,0,0)");
@ -327,7 +330,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_block_send_distance", "9");
settings->setDefault("block_send_optimize_distance", "4");
settings->setDefault("server_side_occlusion_culling", "true");
settings->setDefault("csm_flavour_limits", "3");
settings->setDefault("csm_flavour_limits", "18");
settings->setDefault("csm_flavour_noderange_limit", "8");
settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
settings->setDefault("time_speed", "72");
@ -347,6 +350,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("ignore_world_load_errors", "false");
settings->setDefault("remote_media", "");
settings->setDefault("debug_log_level", "action");
settings->setDefault("log_color", "detect");
settings->setDefault("emergequeue_limit_total", "256");
settings->setDefault("emergequeue_limit_diskonly", "32");
settings->setDefault("emergequeue_limit_generate", "32");

File diff suppressed because it is too large Load Diff

View File

@ -26,16 +26,19 @@ class InputHandler;
class ChatBackend; /* to avoid having to include chat.h */
struct SubgameSpec;
// Flags that can, or may, change during main game loop
struct GameUIFlags
{
bool show_chat;
bool show_hud;
bool show_minimap;
bool force_fog_off;
bool show_debug;
bool show_profiler_graph;
bool disable_camera_update;
struct Jitter {
f32 max, min, avg, counter, max_sample, min_sample, max_fraction;
};
struct RunStats {
u32 drawtime;
Jitter dtime_jitter, busy_time_jitter;
};
struct CameraOrientation {
f32 camera_yaw; // "right/left"
f32 camera_pitch; // "up/down"
};
void the_game(bool *kill,

View File

@ -1,5 +1,6 @@
set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
@ -9,5 +10,6 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp
${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp
PARENT_SCOPE
)

View File

@ -0,0 +1,232 @@
/*
Minetest
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "guiConfirmRegistration.h"
#include "client.h"
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include <IGUIButton.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
#include "gettext.h"
// Continuing from guiPasswordChange.cpp
const int ID_confirmPassword = 262;
const int ID_confirm = 263;
const int ID_message = 264;
const int ID_cancel = 265;
GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
const std::string &address, bool *aborted) :
GUIModalMenu(env, parent, id, menumgr),
m_client(client), m_playername(playername), m_password(password),
m_address(address), m_aborted(aborted)
{
}
GUIConfirmRegistration::~GUIConfirmRegistration()
{
removeChildren();
}
void GUIConfirmRegistration::removeChildren()
{
const core::list<gui::IGUIElement *> &children = getChildren();
core::list<gui::IGUIElement *> children_copy;
for (gui::IGUIElement *i : children)
children_copy.push_back(i);
for (gui::IGUIElement *i : children_copy)
i->remove();
}
void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
{
acceptInput();
removeChildren();
/*
Calculate new sizes and positions
*/
core::rect<s32> rect(screensize.X / 2 - 600 / 2, screensize.Y / 2 - 300 / 2,
screensize.X / 2 + 600 / 2, screensize.Y / 2 + 300 / 2);
DesiredRect = rect;
recalculateAbsolutePosition(false);
v2s32 size = rect.getSize();
v2s32 topleft_client(0, 0);
const wchar_t *text;
/*
Add stuff
*/
s32 ypos = 30;
{
std::string address = m_address;
if (address.empty())
address = "localhost";
core::rect<s32> rect(0, 0, 540, 90);
rect += topleft_client + v2s32(30, ypos);
static const std::string info_text_template = strgettext(
"You are about to join the server at %1$s with the "
"name \"%2$s\" for the first time. If you proceed, a "
"new account using your credentials will be created "
"on this server.\n"
"Please retype your password and click Register and "
"Join to confirm account creation or click Cancel to "
"abort.");
char info_text_buf[1024];
snprintf(info_text_buf, sizeof(info_text_buf), info_text_template.c_str(),
address.c_str(), m_playername.c_str());
Environment->addStaticText(narrow_to_wide_c(info_text_buf), rect, false,
true, this, -1);
}
ypos += 120;
{
core::rect<s32> rect(0, 0, 540, 30);
rect += topleft_client + v2s32(30, ypos);
gui::IGUIEditBox *e = Environment->addEditBox(m_pass_confirm.c_str(),
rect, true, this, ID_confirmPassword);
e->setPasswordBox(true);
}
ypos += 90;
{
core::rect<s32> rect(0, 0, 230, 35);
rect = rect + v2s32(size.X / 2 - 220, ypos);
text = wgettext("Register and Join");
Environment->addButton(rect, this, ID_confirm, text);
delete[] text;
}
{
core::rect<s32> rect(0, 0, 120, 35);
rect = rect + v2s32(size.X / 2 + 70, ypos);
text = wgettext("Cancel");
Environment->addButton(rect, this, ID_cancel, text);
delete[] text;
}
{
core::rect<s32> rect(0, 0, 200, 20);
rect += topleft_client + v2s32(30, ypos - 40);
text = wgettext("Passwords do not match!");
IGUIElement *e = Environment->addStaticText(
text, rect, false, true, this, ID_message);
e->setVisible(false);
delete[] text;
}
}
void GUIConfirmRegistration::drawMenu()
{
gui::IGUISkin *skin = Environment->getSkin();
if (!skin)
return;
video::IVideoDriver *driver = Environment->getVideoDriver();
video::SColor bgcolor(140, 0, 0, 0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
gui::IGUIElement::draw();
}
void GUIConfirmRegistration::closeMenu(bool goNext)
{
quitMenu();
if (goNext) {
m_client->confirmRegistration();
} else {
*m_aborted = true;
infostream << "Connect aborted [Escape]" << std::endl;
}
}
void GUIConfirmRegistration::acceptInput()
{
gui::IGUIElement *e;
e = getElementFromId(ID_confirmPassword);
if (e)
m_pass_confirm = e->getText();
}
bool GUIConfirmRegistration::processInput()
{
std::wstring m_password_ws = narrow_to_wide(m_password);
if (m_password_ws != m_pass_confirm) {
gui::IGUIElement *e = getElementFromId(ID_message);
if (e)
e->setVisible(true);
return false;
}
return true;
}
bool GUIConfirmRegistration::OnEvent(const SEvent &event)
{
if (event.EventType == EET_KEY_INPUT_EVENT) {
if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
closeMenu(false);
return true;
}
if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
acceptInput();
if (processInput())
closeMenu(true);
return true;
}
}
if (event.EventType != EET_GUI_EVENT)
return Parent ? Parent->OnEvent(event) : false;
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) {
if (!canTakeFocus(event.GUIEvent.Element)) {
dstream << "GUIConfirmRegistration: Not allowing focus "
"change."
<< std::endl;
// Returning true disables focus change
return true;
}
} else if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
switch (event.GUIEvent.Caller->getID()) {
case ID_confirm:
acceptInput();
if (processInput())
closeMenu(true);
return true;
case ID_cancel:
closeMenu(false);
return true;
}
} else if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
switch (event.GUIEvent.Caller->getID()) {
case ID_confirmPassword:
acceptInput();
if (processInput())
closeMenu(true);
return true;
}
}
return false;
}

View File

@ -0,0 +1,61 @@
/*
Minetest
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
<muhammadrifqipriyosusanto@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include "irrlichttypes_extrabloated.h"
#include "modalMenu.h"
#include <string>
class Client;
class GUIConfirmRegistration : public GUIModalMenu
{
public:
GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
s32 id, IMenuManager *menumgr, Client *client,
const std::string &playername, const std::string &password,
const std::string &address, bool *aborted);
~GUIConfirmRegistration();
void removeChildren();
/*
Remove and re-add (or reposition) stuff
*/
void regenerateGui(v2u32 screensize);
void drawMenu();
void closeMenu(bool goNext);
void acceptInput();
bool processInput();
bool OnEvent(const SEvent &event);
private:
Client *m_client = nullptr;
const std::string &m_playername;
const std::string &m_password;
const std::string &m_address;
bool *m_aborted = nullptr;
std::wstring m_pass_confirm = L"";
};

View File

@ -149,9 +149,8 @@ GUIEngine::GUIEngine(JoystickController *joystick,
g_fontengine->getTextHeight());
rect += v2s32(4, 0);
m_irr_toplefttext =
addStaticText(RenderingEngine::get_gui_env(), m_toplefttext,
rect, false, true, 0, -1);
m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(),
m_toplefttext, rect, false, true, 0, -1);
//create formspecsource
m_formspecgui = new FormspecFormSource("");
@ -560,9 +559,8 @@ void GUIEngine::updateTopLeftTextSize()
rect += v2s32(4, 0);
m_irr_toplefttext->remove();
m_irr_toplefttext =
addStaticText(RenderingEngine::get_gui_env(), m_toplefttext,
rect, false, true, 0, -1);
m_irr_toplefttext = gui::StaticText::add(RenderingEngine::get_gui_env(),
m_toplefttext, rect, false, true, 0, -1);
}
/******************************************************************************/

View File

@ -39,11 +39,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "client/renderingengine.h"
#include "log.h"
#include "client/tile.h" // ITextureSource
#include "hud.h" // drawItemStack
#include "client/hud.h" // drawItemStack
#include "filesys.h"
#include "gettime.h"
#include "gettext.h"
#include "scripting_server.h"
#include "mainmenumanager.h"
#include "porting.h"
#include "settings.h"
#include "client.h"
@ -57,6 +58,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
#include "intlGUIEditBox.h"
#include "mainmenumanager.h"
#endif
#define MY_CHECKPOS(a,b) \
@ -129,6 +132,28 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
delete m_text_dst;
}
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest)
{
if (cur_formspec == nullptr) {
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
client, client->getTextureSource(), fs_src, txt_dest);
cur_formspec->doPause = false;
/*
Caution: do not call (*cur_formspec)->drop() here --
the reference might outlive the menu, so we will
periodically check if *cur_formspec is the only
remaining reference (i.e. the menu was removed)
and delete it in that case.
*/
} else {
cur_formspec->setFormSource(fs_src);
cur_formspec->setTextDest(txt_dest);
}
}
void GUIFormSpecMenu::removeChildren()
{
const core::list<gui::IGUIElement*> &children = getChildren();
@ -950,12 +975,12 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
Environment->setFocus(e);
}
if (label.length() >= 1)
{
if (label.length() >= 1) {
int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
this, 0);
}
e->setPasswordBox(true,L'*');
@ -1017,7 +1042,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
if (name.empty()) {
// spec field id to 0, this stops submit searching for a value that isn't there
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true, this,
spec.fid);
} else {
spec.send = true;
gui::IGUIElement *e;
@ -1050,7 +1076,8 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
this, 0);
}
}
@ -1162,7 +1189,8 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>&
int font_height = g_fontengine->getTextHeight();
rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
this, 0);
}
if (parts.size() >= 6) {
@ -1237,11 +1265,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
L"",
258+m_fields.size()
);
gui::IGUIStaticText *e =
addStaticText(Environment, spec.flabel.c_str(),
rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT,
gui::EGUIA_CENTER);
gui::IGUIStaticText *e = gui::StaticText::add(Environment,
spec.flabel.c_str(), rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
m_fields.push_back(spec);
}
@ -1291,8 +1317,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
L"",
258+m_fields.size()
);
gui::IGUIStaticText *t =
addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid);
gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
rect, false, false, this, spec.fid);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_fields.push_back(spec);
return;
@ -2024,7 +2050,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
assert(!m_tooltip_element);
// Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18));
m_tooltip_element = gui::StaticText::add(Environment, L"",
core::rect<s32>(0, 0, 110, 18));
m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setDrawBackground(true);
@ -3668,18 +3695,24 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
} else if (craft_amount > 0) {
m_selected_content_guess = ItemStack(); // Clear
// Send IAction::Craft
assert(s.isValid());
assert(inv_s);
infostream << "Handing IAction::Craft to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
m_invmgr->inventoryAction(a);
// if there are no items selected or the selected item
// belongs to craftresult list, proceed with crafting
if (m_selected_item == NULL ||
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
m_selected_content_guess = ItemStack(); // Clear
assert(inv_s);
// Send IACTION_CRAFT
infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
m_invmgr->inventoryAction(a);
}
}
// If m_selected_amount has been decreased to zero, deselect

View File

@ -291,6 +291,9 @@ public:
~GUIFormSpecMenu();
static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest);
void setFormSpec(const std::string &formspec_string,
const InventoryLocation &current_inventory_location)
{

View File

@ -1430,14 +1430,14 @@ void intlGUIEditBox::calculateScrollPos()
// todo: adjust scrollbar
}
// vertical scroll position
if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
if (!WordWrap && !MultiLine)
return;
else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
else
VScrollPos = 0;
// vertical scroll position
if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y)
VScrollPos += CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y; // scrolling downwards
else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y)
VScrollPos += CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y; // scrolling upwards
// todo: adjust scrollbar
if (m_vscrollbar)

166
src/gui/profilergraph.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "profilergraph.h"
#include "util/string.h"
void ProfilerGraph::put(const Profiler::GraphValues &values)
{
m_log.emplace_back(values);
while (m_log.size() > m_log_max_size)
m_log.erase(m_log.begin());
}
void ProfilerGraph::draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
gui::IGUIFont *font) const
{
// Do *not* use UNORDERED_MAP here as the order needs
// to be the same for each call to prevent flickering
std::map<std::string, Meta> m_meta;
for (const Piece &piece : m_log) {
for (const auto &i : piece.values) {
const std::string &id = i.first;
const float &value = i.second;
std::map<std::string, Meta>::iterator j = m_meta.find(id);
if (j == m_meta.end()) {
m_meta[id] = Meta(value);
continue;
}
if (value < j->second.min)
j->second.min = value;
if (value > j->second.max)
j->second.max = value;
}
}
// Assign colors
static const video::SColor usable_colors[] = {video::SColor(255, 255, 100, 100),
video::SColor(255, 90, 225, 90),
video::SColor(255, 100, 100, 255),
video::SColor(255, 255, 150, 50),
video::SColor(255, 220, 220, 100)};
static const u32 usable_colors_count =
sizeof(usable_colors) / sizeof(*usable_colors);
u32 next_color_i = 0;
for (auto &i : m_meta) {
Meta &meta = i.second;
video::SColor color(255, 200, 200, 200);
if (next_color_i < usable_colors_count)
color = usable_colors[next_color_i++];
meta.color = color;
}
s32 graphh = 50;
s32 textx = x_left + m_log_max_size + 15;
s32 textx2 = textx + 200 - 15;
s32 meta_i = 0;
for (const auto &p : m_meta) {
const std::string &id = p.first;
const Meta &meta = p.second;
s32 x = x_left;
s32 y = y_bottom - meta_i * 50;
float show_min = meta.min;
float show_max = meta.max;
if (show_min >= -0.0001 && show_max >= -0.0001) {
if (show_min <= show_max * 0.5)
show_min = 0;
}
s32 texth = 15;
char buf[10];
snprintf(buf, 10, "%.3g", show_max);
font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - graphh, textx2,
y - graphh + texth),
meta.color);
snprintf(buf, 10, "%.3g", show_min);
font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - texth, textx2, y), meta.color);
font->draw(utf8_to_wide(id).c_str(),
core::rect<s32>(textx, y - graphh / 2 - texth / 2, textx2,
y - graphh / 2 + texth / 2),
meta.color);
s32 graph1y = y;
s32 graph1h = graphh;
bool relativegraph = (show_min != 0 && show_min != show_max);
float lastscaledvalue = 0.0;
bool lastscaledvalue_exists = false;
for (const Piece &piece : m_log) {
float value = 0;
bool value_exists = false;
Profiler::GraphValues::const_iterator k = piece.values.find(id);
if (k != piece.values.end()) {
value = k->second;
value_exists = true;
}
if (!value_exists) {
x++;
lastscaledvalue_exists = false;
continue;
}
float scaledvalue = 1.0;
if (show_max != show_min)
scaledvalue = (value - show_min) / (show_max - show_min);
if (scaledvalue == 1.0 && value == 0) {
x++;
lastscaledvalue_exists = false;
continue;
}
if (relativegraph) {
if (lastscaledvalue_exists) {
s32 ivalue1 = lastscaledvalue * graph1h;
s32 ivalue2 = scaledvalue * graph1h;
driver->draw2DLine(
v2s32(x - 1, graph1y - ivalue1),
v2s32(x, graph1y - ivalue2),
meta.color);
}
lastscaledvalue = scaledvalue;
lastscaledvalue_exists = true;
} else {
s32 ivalue = scaledvalue * graph1h;
driver->draw2DLine(v2s32(x, graph1y),
v2s32(x, graph1y - ivalue), meta.color);
}
x++;
}
meta_i++;
}
}

61
src/gui/profilergraph.h Normal file
View File

@ -0,0 +1,61 @@
/*
Minetest
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <SColor.h>
#include <deque>
#include <utility>
#include <IGUIFont.h>
#include <IVideoDriver.h>
#include "profiler.h"
/* Profiler display */
class ProfilerGraph
{
private:
struct Piece
{
Piece(Profiler::GraphValues v) : values(std::move(v)) {}
Profiler::GraphValues values;
};
struct Meta
{
float min;
float max;
video::SColor color;
Meta(float initial = 0,
video::SColor color = video::SColor(255, 255, 255, 255)) :
min(initial),
max(initial), color(color)
{
}
};
std::deque<Piece> m_log;
public:
u32 m_log_max_size = 200;
ProfilerGraph() = default;
void put(const Profiler::GraphValues &values);
void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
gui::IGUIFont *font) const;
};

View File

@ -1,775 +1,39 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "hud.h"
#include "settings.h"
#include "util/numeric.h"
#include "log.h"
#include "client.h"
#include "inventory.h"
#include "shader.h"
#include "client/tile.h"
#include "localplayer.h"
#include "camera.h"
#include "porting.h"
#include "fontengine.h"
#include "guiscalingfilter.h"
#include "mesh.h"
#include "wieldmesh.h"
#include "client/renderingengine.h"
#ifdef HAVE_TOUCHSCREENGUI
#include "gui/touchscreengui.h"
#endif
Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory)
const struct EnumString es_HudElementType[] =
{
driver = RenderingEngine::get_video_driver();
this->guienv = guienv;
this->client = client;
this->player = player;
this->inventory = inventory;
m_hud_scaling = g_settings->getFloat("hud_scaling");
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5f);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
for (auto &hbar_color : hbar_colors)
hbar_color = video::SColor(255, 255, 255, 255);
tsrc = client->getTextureSource();
v3f crosshair_color = g_settings->getV3F("crosshair_color");
u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
m_selection_boxes.clear();
m_halo_boxes.clear();
std::string mode_setting = g_settings->get("node_highlighting");
if (mode_setting == "halo") {
m_mode = HIGHLIGHT_HALO;
} else if (mode_setting == "none") {
m_mode = HIGHLIGHT_NONE;
} else {
m_mode = HIGHLIGHT_BOX;
}
m_selection_material.Lighting = false;
if (g_settings->getBool("enable_shaders")) {
IShaderSource *shdrsrc = client->getShaderSource();
u16 shader_id = shdrsrc->getShader(
m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
} else {
m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
}
if (m_mode == HIGHLIGHT_BOX) {
m_selection_material.Thickness =
rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
} else if (m_mode == HIGHLIGHT_HALO) {
m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
} else {
m_selection_material.MaterialType = video::EMT_SOLID;
}
}
Hud::~Hud()
{
if (m_selection_mesh)
m_selection_mesh->drop();
}
void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
bool selected)
{
if (selected) {
/* draw hihlighting around selected item */
if (use_hotbar_selected_image) {
core::rect<s32> imgrect2 = rect;
imgrect2.UpperLeftCorner.X -= (m_padding*2);
imgrect2.UpperLeftCorner.Y -= (m_padding*2);
imgrect2.LowerRightCorner.X += (m_padding*2);
imgrect2.LowerRightCorner.Y += (m_padding*2);
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, imgrect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
} else {
video::SColor c_outside(255,255,0,0);
//video::SColor c_outside(255,0,0,0);
//video::SColor c_inside(255,192,192,192);
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
// Black base borders
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1 - m_padding),
v2s32(x2 + m_padding, y1)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y2),
v2s32(x2 + m_padding, y2 + m_padding)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x1 - m_padding, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_outside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + m_padding, y2)
), NULL);
/*// Light inside borders
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1 - padding/2),
v2s32(x2 + padding/2, y1)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y2),
v2s32(x2 + padding/2, y2 + padding/2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x1 - padding/2, y1),
v2s32(x1, y2)
), NULL);
driver->draw2DRectangle(c_inside,
core::rect<s32>(
v2s32(x2, y1),
v2s32(x2 + padding/2, y2)
), NULL);
*/
}
}
video::SColor bgcolor2(128, 0, 0, 0);
if (!use_hotbar_image)
driver->draw2DRectangle(bgcolor2, rect, NULL);
drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
}
//NOTE: selectitem = 0 -> no selected; selectitem 1-based
void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
{
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui && inv_offset == 0)
g_touchscreengui->resetHud();
#endif
s32 height = m_hotbar_imagesize + m_padding * 2;
s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
s32 tmp = height;
height = width;
width = tmp;
}
// Position of upper left corner of bar
v2s32 pos = screen_offset;
pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity();
pos += upperleftpos;
// Store hotbar_image in member variable, used by drawItem()
if (hotbar_image != player->hotbar_image) {
hotbar_image = player->hotbar_image;
if (!hotbar_image.empty())
use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
else
use_hotbar_image = false;
}
// Store hotbar_selected_image in member variable, used by drawItem()
if (hotbar_selected_image != player->hotbar_selected_image) {
hotbar_selected_image = player->hotbar_selected_image;
if (!hotbar_selected_image.empty())
use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
else
use_hotbar_selected_image = false;
}
// draw customized item background
if (use_hotbar_image) {
core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
width+m_padding/2, height+m_padding/2);
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
}
// Draw items
core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
v2s32 steppos;
switch (direction) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
break;
default:
steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
break;
}
drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
#ifdef HAVE_TOUCHSCREENGUI
if (g_touchscreengui)
g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
#endif
}
}
void Hud::drawLuaElements(const v3s16 &camera_offset)
{
u32 text_height = g_fontengine->getTextHeight();
irr::gui::IGUIFont* font = g_fontengine->getFont();
for (size_t i = 0; i != player->maxHudId(); i++) {
HudElement *e = player->getHud(i);
if (!e)
continue;
v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
floor(e->pos.Y * (float) m_screensize.Y + 0.5));
switch (e->type) {
case HUD_ELEM_IMAGE: {
video::ITexture *texture = tsrc->getTexture(e->text);
if (!texture)
continue;
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
core::dimension2di imgsize(texture->getOriginalSize());
v2s32 dstsize(imgsize.Width * e->scale.X,
imgsize.Height * e->scale.Y);
if (e->scale.X < 0)
dstsize.X = m_screensize.X * (e->scale.X * -0.01);
if (e->scale.Y < 0)
dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
(e->align.Y - 1.0) * dstsize.Y / 2);
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, colors, true);
break; }
case HUD_ELEM_TEXT: {
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
std::wstring text = unescape_translate(utf8_to_wide(e->text));
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
v2s32 offs(e->offset.X, e->offset.Y);
font->draw(text.c_str(), size + pos + offset + offs, color);
break; }
case HUD_ELEM_STATBAR: {
v2s32 offs(e->offset.X, e->offset.Y);
drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
break; }
case HUD_ELEM_INVENTORY: {
InventoryList *inv = inventory->getList(e->text);
drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
inv, e->item, e->dir);
break; }
case HUD_ELEM_WAYPOINT: {
v3f p_pos = player->getPosition() / BS;
v3f w_pos = e->world_pos * BS;
float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
scene::ICameraSceneNode* camera =
RenderingEngine::get_scene_manager()->getActiveCamera();
w_pos -= intToFloat(camera_offset, BS);
core::matrix4 trans = camera->getProjectionMatrix();
trans *= camera->getViewMatrix();
f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
trans.multiplyWith1x4Matrix(transformed_pos);
if (transformed_pos[3] < 0)
break;
f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
core::reciprocal(transformed_pos[3]);
pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
video::SColor color(255, (e->number >> 16) & 0xFF,
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
std::wstring text = unescape_translate(utf8_to_wide(e->name));
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
os << distance << e->text;
text = unescape_translate(utf8_to_wide(os.str()));
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }
default:
infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
" of hud element ID " << i << " due to unrecognized type" << std::endl;
}
}
}
void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size)
{
const video::SColor color(255, 255, 255, 255);
const video::SColor colors[] = {color, color, color, color};
video::ITexture *stat_texture = tsrc->getTexture(texture);
if (!stat_texture)
return;
core::dimension2di srcd(stat_texture->getOriginalSize());
core::dimension2di dstd;
if (size == v2s32()) {
dstd = srcd;
} else {
float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
dstd.Height = size.Y * size_factor;
dstd.Width = size.X * size_factor;
offset.X *= size_factor;
offset.Y *= size_factor;
}
v2s32 p = pos;
if (corner & HUD_CORNER_LOWER)
p -= dstd.Height;
p += offset;
v2s32 steppos;
core::rect<s32> srchalfrect, dsthalfrect;
switch (drawdir) {
case HUD_DIR_RIGHT_LEFT:
steppos = v2s32(-1, 0);
srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
break;
case HUD_DIR_TOP_BOTTOM:
steppos = v2s32(0, 1);
srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
break;
case HUD_DIR_BOTTOM_TOP:
steppos = v2s32(0, -1);
srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
break;
default:
steppos = v2s32(1, 0);
srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
}
steppos.X *= dstd.Width;
steppos.Y *= dstd.Height;
for (s32 i = 0; i < count / 2; i++) {
core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
dstrect += p;
draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
p += steppos;
}
if (count % 2 == 1) {
dsthalfrect += p;
draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
}
}
void Hud::drawHotbar(u16 playeritem) {
v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
InventoryList *mainlist = inventory->getList("main");
if (mainlist == NULL) {
//silently ignore this we may not be initialized completely
return;
}
s32 hotbar_itemcount = player->hud_hotbar_itemcount;
s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if ( (float) width / (float) window_size.X <=
g_settings->getFloat("hud_hotbar_max_width")) {
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
}
} else {
pos.X += width/4;
v2s32 secondpos = pos;
pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
mainlist, playeritem + 1, 0);
drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
}
}
//////////////////////////// compatibility code to be removed //////////////
// this is ugly as hell but there's no other way to keep compatibility to
// old servers
if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "heart.png",
player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
}
if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
(player->getBreath() < 11)) {
drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
floor(1 * (float) m_screensize.Y + 0.5)),
HUD_CORNER_UPPER, 0, "bubble.png",
player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
}
////////////////////////////////////////////////////////////////////////////
}
void Hud::drawCrosshair()
{
if (use_crosshair_image) {
video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
v2u32 size = crosshair->getOriginalSize();
v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
m_displaycenter.Y - (size.Y / 2));
driver->draw2DImage(crosshair, lsize,
core::rect<s32>(0, 0, size.X, size.Y),
0, crosshair_argb, true);
} else {
driver->draw2DLine(m_displaycenter - v2s32(10, 0),
m_displaycenter + v2s32(10, 0), crosshair_argb);
driver->draw2DLine(m_displaycenter - v2s32(0, 10),
m_displaycenter + v2s32(0, 10), crosshair_argb);
}
}
void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
m_selection_pos = pos;
m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
}
void Hud::drawSelectionMesh()
{
if (m_mode == HIGHLIGHT_BOX) {
// Draw 3D selection boxes
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
for (std::vector<aabb3f>::const_iterator
i = m_selection_boxes.begin();
i != m_selection_boxes.end(); ++i) {
aabb3f box = aabb3f(
i->MinEdge + m_selection_pos_with_offset,
i->MaxEdge + m_selection_pos_with_offset);
u32 r = (selectionbox_argb.getRed() *
m_selection_mesh_color.getRed() / 255);
u32 g = (selectionbox_argb.getGreen() *
m_selection_mesh_color.getGreen() / 255);
u32 b = (selectionbox_argb.getBlue() *
m_selection_mesh_color.getBlue() / 255);
driver->draw3DBox(box, video::SColor(255, r, g, b));
}
driver->setMaterial(oldmaterial);
} else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
// Draw selection mesh
video::SMaterial oldmaterial = driver->getMaterial2D();
driver->setMaterial(m_selection_material);
setMeshColor(m_selection_mesh, m_selection_mesh_color);
video::SColor face_color(0,
MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
face_color);
scene::IMesh* mesh = cloneMesh(m_selection_mesh);
translateMesh(mesh, m_selection_pos_with_offset);
u32 mc = m_selection_mesh->getMeshBufferCount();
for (u32 i = 0; i < mc; i++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
driver->drawMeshBuffer(buf);
}
mesh->drop();
driver->setMaterial(oldmaterial);
}
}
void Hud::updateSelectionMesh(const v3s16 &camera_offset)
{
m_camera_offset = camera_offset;
if (m_mode != HIGHLIGHT_HALO)
return;
if (m_selection_mesh) {
m_selection_mesh->drop();
m_selection_mesh = NULL;
}
if (m_selection_boxes.empty()) {
// No pointed object
return;
}
// New pointed object, create new mesh.
// Texture UV coordinates for selection boxes
static f32 texture_uv[24] = {
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1,
0,0,1,1
};
// Use single halo box instead of multiple overlapping boxes.
// Temporary solution - problem can be solved with multiple
// rendering targets, or some method to remove inner surfaces.
// Thats because of halo transparency.
aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
m_halo_boxes.clear();
for (const auto &selection_box : m_selection_boxes) {
halo_box.addInternalBox(selection_box);
}
m_halo_boxes.push_back(halo_box);
m_selection_mesh = convertNodeboxesToMesh(
m_halo_boxes, texture_uv, 0.5);
}
void Hud::resizeHotbar() {
const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
if (m_screensize != window_size) {
m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
RenderingEngine::getDisplayDensity() + 0.5);
m_hotbar_imagesize *= m_hud_scaling;
m_padding = m_hotbar_imagesize / 12;
m_screensize = window_size;
m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
}
}
struct MeshTimeInfo {
u64 time;
scene::IMesh *mesh;
{HUD_ELEM_IMAGE, "image"},
{HUD_ELEM_TEXT, "text"},
{HUD_ELEM_STATBAR, "statbar"},
{HUD_ELEM_INVENTORY, "inventory"},
{HUD_ELEM_WAYPOINT, "waypoint"},
{0, NULL},
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind)
const struct EnumString es_HudElementStat[] =
{
static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations");
{HUD_STAT_POS, "position"},
{HUD_STAT_POS, "pos"}, /* Deprecated, only for compatibility's sake */
{HUD_STAT_NAME, "name"},
{HUD_STAT_SCALE, "scale"},
{HUD_STAT_TEXT, "text"},
{HUD_STAT_NUMBER, "number"},
{HUD_STAT_ITEM, "item"},
{HUD_STAT_DIR, "direction"},
{HUD_STAT_ALIGN, "alignment"},
{HUD_STAT_OFFSET, "offset"},
{HUD_STAT_WORLD_POS, "world_pos"},
{0, NULL},
};
if (item.empty()) {
if (rotation_kind < IT_ROT_NONE) {
rotation_time_infos[rotation_kind].mesh = NULL;
}
return;
}
const ItemDefinition &def = item.getDefinition(client->idef());
ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
if (imesh && imesh->mesh) {
scene::IMesh *mesh = imesh->mesh;
driver->clearZBuffer();
s32 delta = 0;
if (rotation_kind < IT_ROT_NONE) {
MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
if (mesh != ti.mesh) {
ti.mesh = mesh;
ti.time = porting::getTimeMs();
} else {
delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
}
}
core::rect<s32> oldViewPort = driver->getViewPort();
core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
core::matrix4 ProjMatrix;
ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
driver->setTransform(video::ETS_VIEW, ProjMatrix);
core::matrix4 matrix;
matrix.makeIdentity();
if (enable_animations) {
float timer_f = (float) delta / 5000.0;
matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
}
driver->setTransform(video::ETS_WORLD, matrix);
driver->setViewPort(rect);
video::SColor basecolor =
client->idef()->getItemstackColor(item, client);
u32 mc = mesh->getMeshBufferCount();
for (u32 j = 0; j < mc; ++j) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
// we can modify vertices relatively fast,
// because these meshes are not buffered.
assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
video::SColor c = basecolor;
if (imesh->buffer_colors.size() > j) {
ItemPartColor *p = &imesh->buffer_colors[j];
if (p->override_base)
c = p->color;
}
if (imesh->needs_shading)
colorizeMeshBuffer(buf, &c);
else
setMeshBufferColor(buf, c);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
driver->setMaterial(material);
driver->drawMeshBuffer(buf);
}
driver->setTransform(video::ETS_VIEW, oldViewMat);
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort);
}
if(def.type == ITEM_TOOL && item.wear != 0)
{
// Draw a progressbar
float barheight = rect.getHeight()/16;
float barpad_x = rect.getWidth()/16;
float barpad_y = rect.getHeight()/16;
core::rect<s32> progressrect(
rect.UpperLeftCorner.X + barpad_x,
rect.LowerRightCorner.Y - barpad_y - barheight,
rect.LowerRightCorner.X - barpad_x,
rect.LowerRightCorner.Y - barpad_y);
// Shrink progressrect by amount of tool damage
float wear = item.wear / 65535.0;
int progressmid =
wear * progressrect.UpperLeftCorner.X +
(1-wear) * progressrect.LowerRightCorner.X;
// Compute progressbar color
// wear = 0.0: green
// wear = 0.5: yellow
// wear = 1.0: red
video::SColor color(255,255,255,255);
int wear_i = MYMIN(floor(wear * 600), 511);
wear_i = MYMIN(wear_i + 10, 511);
if(wear_i <= 255)
color.set(255, wear_i, 255, 0);
else
color.set(255, 255, 511-wear_i, 0);
core::rect<s32> progressrect2 = progressrect;
progressrect2.LowerRightCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
color = video::SColor(255,0,0,0);
progressrect2 = progressrect;
progressrect2.UpperLeftCorner.X = progressmid;
driver->draw2DRectangle(color, progressrect2, clip);
}
if(font != NULL && item.count >= 2)
{
// Get the item count as a string
std::string text = itos(item.count);
v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
v2s32 sdim(dim.X,dim.Y);
core::rect<s32> rect2(
/*rect.UpperLeftCorner,
core::dimension2d<u32>(rect.getWidth(), 15)*/
rect.LowerRightCorner - sdim,
sdim
);
video::SColor bgcolor(128,0,0,0);
driver->draw2DRectangle(bgcolor, rect2, clip);
video::SColor color(255,255,255,255);
font->draw(text.c_str(), rect2, color, false, false, clip);
}
}
const struct EnumString es_HudBuiltinElement[] =
{
{HUD_FLAG_HOTBAR_VISIBLE, "hotbar"},
{HUD_FLAG_HEALTHBAR_VISIBLE, "healthbar"},
{HUD_FLAG_CROSSHAIR_VISIBLE, "crosshair"},
{HUD_FLAG_WIELDITEM_VISIBLE, "wielditem"},
{HUD_FLAG_BREATHBAR_VISIBLE, "breathbar"},
{HUD_FLAG_MINIMAP_VISIBLE, "minimap"},
{0, NULL},
};

114
src/hud.h
View File

@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2017 red-001 <red-001@outlook.ie>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@ -17,10 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#ifndef HUD_HEADER
#define HUD_HEADER
#include "irrlichttypes_extrabloated.h"
#include <string>
#include "common/c_types.h"
#define HUD_DIR_LEFT_RIGHT 0
#define HUD_DIR_RIGHT_LEFT 1
@ -89,111 +92,8 @@ struct HudElement {
v2s32 size;
};
#ifndef SERVER
#include <vector>
#include <IGUIFont.h>
#include "irr_aabb3d.h"
class Client;
class ITextureSource;
class Inventory;
class InventoryList;
class LocalPlayer;
struct ItemStack;
class Hud {
public:
video::IVideoDriver *driver;
scene::ISceneManager* smgr;
gui::IGUIEnvironment *guienv;
Client *client;
LocalPlayer *player;
Inventory *inventory;
ITextureSource *tsrc;
video::SColor crosshair_argb;
video::SColor selectionbox_argb;
bool use_crosshair_image = false;
std::string hotbar_image = "";
bool use_hotbar_image = false;
std::string hotbar_selected_image = "";
bool use_hotbar_selected_image = false;
Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
Inventory *inventory);
~Hud();
void drawHotbar(u16 playeritem);
void resizeHotbar();
void drawCrosshair();
void drawSelectionMesh();
void updateSelectionMesh(const v3s16 &camera_offset);
std::vector<aabb3f> *getSelectionBoxes()
{ return &m_selection_boxes; }
void setSelectionPos(const v3f &pos, const v3s16 &camera_offset);
v3f getSelectionPos() const
{ return m_selection_pos; }
void setSelectionMeshColor(const video::SColor &color)
{ m_selection_mesh_color = color; }
void setSelectedFaceNormal(const v3f &face_normal)
{ m_selected_face_normal = face_normal; }
void drawLuaElements(const v3s16 &camera_offset);
private:
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size=v2s32());
void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction);
void drawItem(const ItemStack &item, const core::rect<s32>& rect,
bool selected);
float m_hud_scaling; // cached minetest setting
v3s16 m_camera_offset;
v2u32 m_screensize;
v2s32 m_displaycenter;
s32 m_hotbar_imagesize; // Takes hud_scaling into account, updated by resizeHotbar()
s32 m_padding; // Takes hud_scaling into account, updated by resizeHotbar()
video::SColor hbar_colors[4];
std::vector<aabb3f> m_selection_boxes;
std::vector<aabb3f> m_halo_boxes;
v3f m_selection_pos;
v3f m_selection_pos_with_offset;
scene::IMesh *m_selection_mesh = nullptr;
video::SColor m_selection_mesh_color;
v3f m_selected_face_normal;
video::SMaterial m_selection_material;
enum {
HIGHLIGHT_BOX,
HIGHLIGHT_HALO,
HIGHLIGHT_NONE } m_mode;
};
enum ItemRotationKind {
IT_ROT_SELECTED,
IT_ROT_HOVERED,
IT_ROT_DRAGGED,
IT_ROT_NONE, // Must be last, also serves as number
};
void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font,
const ItemStack &item,
const core::rect<s32> &rect,
const core::rect<s32> *clip,
Client *client,
ItemRotationKind rotation_kind);
extern const EnumString es_HudElementType[];
extern const EnumString es_HudElementStat[];
extern const EnumString es_HudBuiltinElement[];
#endif

View File

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector3d.h>
typedef core::vector3df v3f;
typedef core::vector3d<double> v3d;
typedef core::vector3d<s16> v3s16;
typedef core::vector3d<u16> v3u16;
typedef core::vector3d<s32> v3s32;

View File

@ -42,6 +42,60 @@ namespace gui
//! destructor
virtual ~StaticText();
static irr::gui::IGUIStaticText *add(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
if (parent == NULL) {
// parent is NULL, so we must find one, or we need not to drop
// result, but then there will be a memory leak.
//
// What Irrlicht does is to use guienv as a parent, but the problem
// is that guienv is here only an IGUIEnvironment, while it is a
// CGUIEnvironment in Irrlicht, which inherits from both IGUIElement
// and IGUIEnvironment.
//
// A solution would be to dynamic_cast guienv to a
// IGUIElement*, but Irrlicht is shipped without rtti support
// in some distributions, causing the dymanic_cast to segfault.
//
// Thus, to find the parent, we create a dummy StaticText and ask
// for its parent, and then remove it.
irr::gui::IGUIStaticText *dummy_text =
guienv->addStaticText(L"", rectangle, border, wordWrap,
parent, id, fillBackground);
parent = dummy_text->getParent();
dummy_text->remove();
}
irr::gui::IGUIStaticText *result = new irr::gui::StaticText(
text, border, guienv, parent,
id, rectangle, fillBackground);
result->setWordWrap(wordWrap);
result->drop();
return result;
}
static irr::gui::IGUIStaticText *add(
irr::gui::IGUIEnvironment *guienv,
const wchar_t *text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
return add(guienv, EnrichedString(text), rectangle, border, wordWrap, parent,
id, fillBackground);
}
//! draws the element and its children
virtual void draw();
@ -171,46 +225,6 @@ namespace gui
} // end namespace irr
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
if (parent == NULL) {
// parent is NULL, so we must find one, or we need not to drop
// result, but then there will be a memory leak.
//
// What Irrlicht does is to use guienv as a parent, but the problem
// is that guienv is here only an IGUIEnvironment, while it is a
// CGUIEnvironment in Irrlicht, which inherits from both IGUIElement
// and IGUIEnvironment.
//
// A solution would be to dynamic_cast guienv to a
// IGUIElement*, but Irrlicht is shipped without rtti support
// in some distributions, causing the dymanic_cast to segfault.
//
// Thus, to find the parent, we create a dummy StaticText and ask
// for its parent, and then remove it.
irr::gui::IGUIStaticText *dummy_text =
guienv->addStaticText(L"", rectangle, border, wordWrap,
parent, id, fillBackground);
parent = dummy_text->getParent();
dummy_text->remove();
}
irr::gui::IGUIStaticText *result = new irr::gui::StaticText(
text, border, guienv, parent,
id, rectangle, fillBackground);
result->setWordWrap(wordWrap);
result->drop();
return result;
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{
// dynamic_cast not possible due to some distributions shipped
@ -225,7 +239,15 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS
#else // USE_FREETYPE
inline irr::gui::IGUIStaticText *addStaticText(
namespace irr
{
namespace gui
{
class StaticText
{
public:
static irr::gui::IGUIStaticText *add(
irr::gui::IGUIEnvironment *guienv,
const EnrichedString &text,
const core::rect< s32 > &rectangle,
@ -234,9 +256,14 @@ inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false)
{
return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground);
}
{
return guienv->addStaticText(text.c_str(), rectangle, border, wordWrap, parent, id, fillBackground);
}
};
} // end namespace gui
} // end namespace irr
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedString &text)
{
@ -245,18 +272,6 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS
#endif
inline irr::gui::IGUIStaticText *addStaticText(
irr::gui::IGUIEnvironment *guienv,
const wchar_t *text,
const core::rect< s32 > &rectangle,
bool border = false,
bool wordWrap = true,
irr::gui::IGUIElement *parent = NULL,
s32 id = -1,
bool fillBackground = false) {
return addStaticText(guienv, EnrichedString(text), rectangle, border, wordWrap, parent, id, fillBackground);
}
inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text)
{
setStaticText(static_text, EnrichedString(text));

View File

@ -24,38 +24,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER
// Length of LIGHT_MAX+1 means LIGHT_MAX is the last value.
// LIGHT_SUN is read as LIGHT_MAX from here.
static u8 light_LUT[LIGHT_SUN + 1];
u8 light_LUT[LIGHT_MAX+1];
// the const ref to light_LUT is what is actually used in the code.
// The const ref to light_LUT is what is actually used in the code
const u8 *light_decode_table = light_LUT;
/** Initialize or update the light value tables using the specified \p gamma.
*/
// Initialize or update the light value tables using the specified gamma
void set_light_table(float gamma)
{
// lighting curve derivatives
// Lighting curve derivatives
const float alpha = g_settings->getFloat("lighting_alpha");
const float beta = g_settings->getFloat("lighting_beta");
// lighting curve coefficients
const float a = alpha + beta - 2;
const float b = 3 - 2 * alpha - beta;
// Lighting curve coefficients
const float a = alpha + beta - 2.0f;
const float b = 3.0f - 2.0f * alpha - beta;
const float c = alpha;
// gamma correction
gamma = rangelim(gamma, 0.5, 3.0);
// Mid boost
const float d = g_settings->getFloat("lighting_boost");
const float e = g_settings->getFloat("lighting_boost_center");
const float f = g_settings->getFloat("lighting_boost_spread");
// Gamma correction
gamma = rangelim(gamma, 0.5f, 3.0f);
for (size_t i = 0; i < LIGHT_MAX; i++) {
for (size_t i = 0; i < LIGHT_SUN; i++) {
float x = i;
x /= LIGHT_MAX;
x /= LIGHT_SUN;
float brightness = a * x * x * x + b * x * x + c * x;
brightness = powf(brightness, 1.0 / gamma);
light_LUT[i] = rangelim((u32)(255 * brightness), 0, 255);
float boost = d * std::exp(-((x - e) * (x - e)) / (2.0f * f * f));
brightness = powf(brightness + boost, 1.0f / gamma);
light_LUT[i] = rangelim((u32)(255.0f * brightness), 0, 255);
if (i > 1 && light_LUT[i] <= light_LUT[i - 1])
light_LUT[i] = light_LUT[i - 1] + 1;
}
light_LUT[LIGHT_MAX] = 255;
light_LUT[LIGHT_SUN] = 255;
}
#endif

View File

@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#pragma once
#include <cassert>
#include "irrlichttypes.h"
/*
@ -54,11 +54,12 @@ inline u8 diminish_light(u8 light, u8 distance)
inline u8 undiminish_light(u8 light)
{
assert(light <= LIGHT_SUN);
// We don't know if light should undiminish from this particular 0.
// Thus, keep it at 0.
if (light == 0)
return 0;
if (light == LIGHT_MAX)
if (light >= LIGHT_MAX)
return light;
return light + 1;
@ -84,9 +85,9 @@ extern const u8 *light_decode_table;
// 0 <= return value <= 255
inline u8 decode_light(u8 light)
{
if (light > LIGHT_MAX)
light = LIGHT_MAX;
// assert(light <= LIGHT_SUN);
if (light > LIGHT_SUN)
light = LIGHT_SUN;
return light_decode_table[light];
}
@ -98,8 +99,8 @@ inline float decode_light_f(float light_f)
if (i <= 0)
return (float)light_decode_table[0] / 255.0;
if (i >= LIGHT_MAX)
return (float)light_decode_table[LIGHT_MAX] / 255.0;
if (i >= LIGHT_SUN)
return (float)light_decode_table[LIGHT_SUN] / 255.0;
float v1 = (float)light_decode_table[i - 1] / 255.0;
float v2 = (float)light_decode_table[i] / 255.0;

View File

@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
#include "environment.h"
#include "constants.h"
#include "settings.h"
#include <list>
class Client;
@ -142,8 +143,8 @@ public:
void setCollisionbox(const aabb3f &box) { m_collisionbox = box; }
bool getCanZoom() const { return m_can_zoom; }
void setCanZoom(bool can_zoom) { m_can_zoom = can_zoom; }
float getZoomFOV() const { return m_zoom_fov; }
void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; }
private:
void accelerateHorizontal(const v3f &target_speed, const f32 max_increase);
@ -181,8 +182,8 @@ private:
bool camera_barely_in_ceiling = false;
aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f,
BS * 1.75f, BS * 0.30f);
bool m_can_zoom = true;
float m_eye_height = 1.625f;
float m_zoom_fov = 0.0f;
GenericCAO *m_cao = nullptr;
Client *m_client;

View File

@ -347,13 +347,10 @@ void StringBuffer::push_back(char c)
flush(std::string(buffer, buffer_index));
buffer_index = 0;
} else {
int index = buffer_index;
buffer[index++] = c;
if (index >= BUFFER_LENGTH) {
buffer[buffer_index++] = c;
if (buffer_index >= BUFFER_LENGTH) {
flush(std::string(buffer, buffer_index));
buffer_index = 0;
} else {
buffer_index = index;
}
}
}

View File

@ -25,6 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <fstream>
#include <thread>
#include <mutex>
#if !defined(_WIN32) // POSIX
#include <unistd.h>
#endif
#include "settings.h"
#include "irrlichttypes.h"
class ILogOutput;
@ -106,15 +110,51 @@ public:
StreamLogOutput(std::ostream &stream) :
m_stream(stream)
{
#if !defined(_WIN32)
is_tty = isatty(fileno(stdout));
#else
is_tty = false;
#endif
}
void logRaw(LogLevel lev, const std::string &line)
{
static const std::string use_logcolor = g_settings->get("log_color");
bool colored = use_logcolor == "detect" ? is_tty : use_logcolor == "yes";
if (colored)
switch (lev) {
case LL_ERROR:
// error is red
m_stream << "\033[91m";
break;
case LL_WARNING:
// warning is yellow
m_stream << "\033[93m";
break;
case LL_INFO:
// info is a bit dark
m_stream << "\033[37m";
break;
case LL_VERBOSE:
// verbose is darker than info
m_stream << "\033[2m";
break;
default:
// action is white
colored = false;
}
m_stream << line << std::endl;
if (colored)
// reset to white color
m_stream << "\033[0m";
}
private:
std::ostream &m_stream;
bool is_tty;
};
class FileLogOutput : public ICombinedLogOutput {

View File

@ -132,198 +132,6 @@ std::string MapBlock::getModifiedReasonString()
return reason;
}
/*
Propagates sunlight down through the block.
Doesn't modify nodes that are not affected by sunlight.
Returns false if sunlight at bottom block is invalid.
Returns true if sunlight at bottom block is valid.
Returns true if bottom block doesn't exist.
If there is a block above, continues from it.
If there is no block above, assumes there is sunlight, unless
is_underground is set or highest node is water.
All sunlighted nodes are added to light_sources.
if remove_light==true, sets non-sunlighted nodes black.
if black_air_left!=NULL, it is set to true if non-sunlighted
air is left in block.
*/
bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
bool remove_light, bool *black_air_left)
{
INodeDefManager *nodemgr = m_gamedef->ndef();
// Whether the sunlight at the top of the bottom block is valid
bool block_below_is_valid = true;
v3s16 pos_relative = getPosRelative();
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
{
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
{
#if 1
bool no_sunlight = false;
//bool no_top_block = false;
// Check if node above block has sunlight
bool is_valid_position;
MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
&is_valid_position);
if (is_valid_position)
{
if(n.getContent() == CONTENT_IGNORE)
{
// Trust heuristics
no_sunlight = is_underground;
}
else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
{
no_sunlight = true;
}
}
else
{
//no_top_block = true;
// NOTE: This makes over-ground roofed places sunlighted
// Assume sunlight, unless is_underground==true
if(is_underground)
{
no_sunlight = true;
}
else
{
MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
if (!m_gamedef->ndef()->get(n).sunlight_propagates) {
no_sunlight = true;
}
}
// NOTE: As of now, this just would make everything dark.
// No sunlight here
//no_sunlight = true;
}
#endif
#if 0 // Doesn't work; nothing gets light.
bool no_sunlight = true;
bool no_top_block = false;
// Check if node above block has sunlight
try{
MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
{
no_sunlight = false;
}
}
catch(InvalidPositionException &e)
{
no_top_block = true;
}
#endif
/*std::cout<<"("<<x<<","<<z<<"): "
<<"no_top_block="<<no_top_block
<<", is_underground="<<is_underground
<<", no_sunlight="<<no_sunlight
<<std::endl;*/
s16 y = MAP_BLOCKSIZE-1;
// This makes difference to diminishing in water.
bool stopped_to_solid_object = false;
u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
for(; y >= 0; y--)
{
v3s16 pos(x, y, z);
MapNode &n = getNodeRef(pos);
if(current_light == 0)
{
// Do nothing
}
else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
{
// Do nothing: Sunlight is continued
} else if (!nodemgr->get(n).light_propagates) {
// A solid object is on the way.
stopped_to_solid_object = true;
// Light stops.
current_light = 0;
}
else
{
// Diminish light
current_light = diminish_light(current_light);
}
u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
if(current_light > old_light || remove_light)
{
n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
}
if(diminish_light(current_light) != 0)
{
light_sources.insert(pos_relative + pos);
}
if(current_light == 0 && stopped_to_solid_object)
{
if(black_air_left)
{
*black_air_left = true;
}
}
}
// Whether or not the block below should see LIGHT_SUN
bool sunlight_should_go_down = (current_light == LIGHT_SUN);
/*
If the block below hasn't already been marked invalid:
Check if the node below the block has proper sunlight at top.
If not, the block below is invalid.
Ignore non-transparent nodes as they always have no light
*/
if(block_below_is_valid)
{
MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
if (is_valid_position) {
if(nodemgr->get(n).light_propagates)
{
if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
&& !sunlight_should_go_down)
block_below_is_valid = false;
else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
&& sunlight_should_go_down)
block_below_is_valid = false;
}
}
else
{
/*std::cout<<"InvalidBlockException for bottom block node"
<<std::endl;*/
// Just no block below, no need to panic.
}
}
}
}
return block_below_is_valid;
}
void MapBlock::copyTo(VoxelManipulator &dst)
{
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);

View File

@ -348,10 +348,6 @@ public:
setNode(x0 + x, y0 + y, z0 + z, node);
}
// See comments in mapblock.cpp
bool propagateSunlight(std::set<v3s16> &light_sources,
bool remove_light=false, bool *black_air_left=NULL);
// Copies data to VoxelManipulator to getPosRelative()
void copyTo(VoxelManipulator &dst);

View File

@ -196,7 +196,7 @@ u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
Both light banks
*/
static u16 getSmoothLightCombined(const v3s16 &p,
const std::array<v3s16,8> &dirs, MeshMakeData *data, bool node_solid)
const std::array<v3s16,8> &dirs, MeshMakeData *data)
{
INodeDefManager *ndef = data->m_client->ndef();
@ -206,8 +206,14 @@ static u16 getSmoothLightCombined(const v3s16 &p,
u16 light_day = 0;
u16 light_night = 0;
auto add_node = [&] (int i) -> const ContentFeatures& {
auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
if (obstructed) {
ambient_occlusion++;
return false;
}
MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
if (n.getContent() == CONTENT_IGNORE)
return true;
const ContentFeatures &f = ndef->get(n);
if (f.light_source > light_source_max)
light_source_max = f.light_source;
@ -219,37 +225,24 @@ static u16 getSmoothLightCombined(const v3s16 &p,
} else {
ambient_occlusion++;
}
return f;
return f.light_propagates;
};
if (node_solid) {
ambient_occlusion = 3;
bool corner_obstructed = true;
for (int i = 0; i < 2; ++i) {
if (add_node(i).light_propagates)
corner_obstructed = false;
}
add_node(2);
add_node(3);
if (corner_obstructed)
ambient_occlusion++;
else
add_node(4);
} else {
std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
add_node(0);
bool opaque1 = !add_node(1).light_propagates;
bool opaque2 = !add_node(2).light_propagates;
bool opaque3 = !add_node(3).light_propagates;
obstructed[0] = opaque1 && opaque2;
obstructed[1] = opaque1 && opaque3;
obstructed[2] = opaque2 && opaque3;
for (int k = 0; k < 4; ++k) {
if (obstructed[k])
ambient_occlusion++;
else if (add_node(k + 4).light_propagates)
obstructed[3] = false;
}
std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
add_node(0);
bool opaque1 = !add_node(1);
bool opaque2 = !add_node(2);
bool opaque3 = !add_node(3);
obstructed[0] = opaque1 && opaque2;
obstructed[1] = opaque1 && opaque3;
obstructed[2] = opaque2 && opaque3;
for (u8 k = 0; k < 3; ++k)
if (add_node(k + 4, obstructed[k]))
obstructed[3] = false;
if (add_node(7, obstructed[3])) { // wrap light around nodes
ambient_occlusion -= 3;
for (u8 k = 0; k < 3; ++k)
add_node(k + 4, !obstructed[k]);
}
if (light_count == 0) {
@ -277,7 +270,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
// Table of gamma space multiply factors.
static const float light_amount[3] = {
static thread_local const float light_amount[3] = {
powf(0.75, 1.0 / ao_gamma),
powf(0.5, 1.0 / ao_gamma),
powf(0.25, 1.0 / ao_gamma)
@ -304,43 +297,7 @@ static u16 getSmoothLightCombined(const v3s16 &p,
*/
u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
{
v3s16 neighbor_offset1, neighbor_offset2;
/*
* face_dir, neighbor_offset1 and neighbor_offset2 define an
* orthonormal basis which is used to define the offsets of the 8
* surrounding nodes and to differentiate the "distance" (by going only
* along directly neighboring nodes) relative to the node at p.
* Apart from the node at p, only the 4 nodes which contain face_dir
* can contribute light.
*/
if (face_dir.X != 0) {
neighbor_offset1 = v3s16(0, corner.Y, 0);
neighbor_offset2 = v3s16(0, 0, corner.Z);
} else if (face_dir.Y != 0) {
neighbor_offset1 = v3s16(0, 0, corner.Z);
neighbor_offset2 = v3s16(corner.X, 0, 0);
} else if (face_dir.Z != 0) {
neighbor_offset1 = v3s16(corner.X,0,0);
neighbor_offset2 = v3s16(0,corner.Y,0);
}
const std::array<v3s16,8> dirs = {{
// Always shine light
neighbor_offset1 + face_dir,
neighbor_offset2 + face_dir,
v3s16(0,0,0),
face_dir,
// Can be obstructed
neighbor_offset1 + neighbor_offset2 + face_dir,
// Do not shine light, only for ambient occlusion
neighbor_offset1,
neighbor_offset2,
neighbor_offset1 + neighbor_offset2
}};
return getSmoothLightCombined(p, dirs, data, true);
return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
}
/*
@ -363,7 +320,7 @@ u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData
v3s16(0,corner.Y,corner.Z),
v3s16(corner.X,corner.Y,corner.Z)
}};
return getSmoothLightCombined(p, dirs, data, false);
return getSmoothLightCombined(p, dirs, data);
}
void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){

View File

@ -1,6 +1,8 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,6 +1,7 @@
/*
Minetest
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,8 +1,8 @@
/*
Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@ -301,10 +301,8 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings)
std::vector<s16> &floors, std::vector<s16> &ceilings)
{
u16 floor_i = 0;
u16 ceiling_i = 0;
const v3s16 &em = vm->m_area.getExtent();
bool is_walkable = false;
@ -318,19 +316,14 @@ void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
is_walkable = ndef->get(mn).walkable;
if (is_walkable && !walkable_above) {
floors[floor_i] = y;
floor_i++;
floors.push_back(y);
} else if (!is_walkable && walkable_above) {
ceilings[ceiling_i] = y + 1;
ceiling_i++;
ceilings.push_back(y + 1);
}
vm->m_area.add_y(em, vi, -1);
walkable_above = is_walkable;
}
*num_floors = floor_i;
*num_ceilings = ceiling_i;
}

View File

@ -1,8 +1,8 @@
/*
Minetest
Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat
Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
@ -194,7 +194,7 @@ public:
s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax);
void updateHeightmap(v3s16 nmin, v3s16 nmax);
void getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings);
std::vector<s16> &floors, std::vector<s16> &ceilings);
void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);

View File

@ -1,8 +1,8 @@
/*
Minetest
Copyright (C) 2010-2016 paramat, Matt Gregory
Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
Copyright (C) 2017-2018 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
Copyright (C) 2010-2018 paramat
Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,8 +1,8 @@
/*
Minetest
Copyright (C) 2010-2016 paramat, Matt Gregory
Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
Copyright (C) 2017-2018 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
Copyright (C) 2010-2018 paramat
Copyright (C) 2010-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Minetest
Copyright (C) 2015-2017 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Minetest
Copyright (C) 2015-2017 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Minetest
Copyright (C) 2015-2017 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Minetest
Copyright (C) 2015-2017 paramat
Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
Copyright (C) 2015-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
by Paul Nylander, and from http://www.fractalforums.com, thank you.

View File

@ -1,8 +1,8 @@
/*
Minetest
Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat
Copyright (C) 2013-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,8 +1,8 @@
/*
Minetest
Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2017 paramat
Copyright (C) 2013-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2015-2018 paramat
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Minetest
Copyright (C) 2014-2017 paramat
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2014-2018 paramat
Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

View File

@ -1,7 +1,7 @@
/*
Minetest
Copyright (C) 2014-2017 paramat
Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
Copyright (C) 2014-2018 paramat
Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by

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