game.cpp 122 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "game.h"
  17. #include <iomanip>
  18. #include <cmath>
  19. #include "client/renderingengine.h"
  20. #include "camera.h"
  21. #include "client.h"
  22. #include "client/clientevent.h"
  23. #include "client/gameui.h"
  24. #include "client/inputhandler.h"
  25. #include "client/tile.h" // For TextureSource
  26. #include "client/keys.h"
  27. #include "client/joystick_controller.h"
  28. #include "clientmap.h"
  29. #include "clouds.h"
  30. #include "config.h"
  31. #include "content_cao.h"
  32. #include "content/subgames.h"
  33. #include "client/event_manager.h"
  34. #include "fontengine.h"
  35. #include "itemdef.h"
  36. #include "log.h"
  37. #include "filesys.h"
  38. #include "gameparams.h"
  39. #include "gettext.h"
  40. #include "gui/guiChatConsole.h"
  41. #include "gui/guiConfirmRegistration.h"
  42. #include "gui/guiFormSpecMenu.h"
  43. #include "gui/guiKeyChangeMenu.h"
  44. #include "gui/guiPasswordChange.h"
  45. #include "gui/guiVolumeChange.h"
  46. #include "gui/mainmenumanager.h"
  47. #include "gui/profilergraph.h"
  48. #include "mapblock.h"
  49. #include "minimap.h"
  50. #include "nodedef.h" // Needed for determining pointing to nodes
  51. #include "nodemetadata.h"
  52. #include "particles.h"
  53. #include "porting.h"
  54. #include "profiler.h"
  55. #include "raycast.h"
  56. #include "server.h"
  57. #include "settings.h"
  58. #include "shader.h"
  59. #include "sky.h"
  60. #include "translation.h"
  61. #include "util/basic_macros.h"
  62. #include "util/directiontables.h"
  63. #include "util/pointedthing.h"
  64. #include "util/quicktune_shortcutter.h"
  65. #include "irrlicht_changes/static_text.h"
  66. #include "version.h"
  67. #include "script/scripting_client.h"
  68. #include "hud.h"
  69. #if USE_SOUND
  70. #include "client/sound_openal.h"
  71. #else
  72. #include "client/sound.h"
  73. #endif
  74. /*
  75. Text input system
  76. */
  77. struct TextDestNodeMetadata : public TextDest
  78. {
  79. TextDestNodeMetadata(v3s16 p, Client *client)
  80. {
  81. m_p = p;
  82. m_client = client;
  83. }
  84. // This is deprecated I guess? -celeron55
  85. void gotText(const std::wstring &text)
  86. {
  87. std::string ntext = wide_to_utf8(text);
  88. infostream << "Submitting 'text' field of node at (" << m_p.X << ","
  89. << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
  90. StringMap fields;
  91. fields["text"] = ntext;
  92. m_client->sendNodemetaFields(m_p, "", fields);
  93. }
  94. void gotText(const StringMap &fields)
  95. {
  96. m_client->sendNodemetaFields(m_p, "", fields);
  97. }
  98. v3s16 m_p;
  99. Client *m_client;
  100. };
  101. struct TextDestPlayerInventory : public TextDest
  102. {
  103. TextDestPlayerInventory(Client *client)
  104. {
  105. m_client = client;
  106. m_formname = "";
  107. }
  108. TextDestPlayerInventory(Client *client, const std::string &formname)
  109. {
  110. m_client = client;
  111. m_formname = formname;
  112. }
  113. void gotText(const StringMap &fields)
  114. {
  115. m_client->sendInventoryFields(m_formname, fields);
  116. }
  117. Client *m_client;
  118. };
  119. struct LocalFormspecHandler : public TextDest
  120. {
  121. LocalFormspecHandler(const std::string &formname)
  122. {
  123. m_formname = formname;
  124. }
  125. LocalFormspecHandler(const std::string &formname, Client *client):
  126. m_client(client)
  127. {
  128. m_formname = formname;
  129. }
  130. void gotText(const StringMap &fields)
  131. {
  132. if (m_formname == "MT_PAUSE_MENU") {
  133. if (fields.find("btn_sound") != fields.end()) {
  134. g_gamecallback->changeVolume();
  135. return;
  136. }
  137. if (fields.find("btn_key_config") != fields.end()) {
  138. g_gamecallback->keyConfig();
  139. return;
  140. }
  141. if (fields.find("btn_exit_menu") != fields.end()) {
  142. g_gamecallback->disconnect();
  143. return;
  144. }
  145. if (fields.find("btn_exit_os") != fields.end()) {
  146. g_gamecallback->exitToOS();
  147. #ifndef __ANDROID__
  148. RenderingEngine::get_raw_device()->closeDevice();
  149. #endif
  150. return;
  151. }
  152. if (fields.find("btn_change_password") != fields.end()) {
  153. g_gamecallback->changePassword();
  154. return;
  155. }
  156. if (fields.find("quit") != fields.end()) {
  157. return;
  158. }
  159. if (fields.find("btn_continue") != fields.end()) {
  160. return;
  161. }
  162. }
  163. if (m_formname == "MT_DEATH_SCREEN") {
  164. assert(m_client != 0);
  165. m_client->sendRespawn();
  166. return;
  167. }
  168. if (m_client->modsLoaded())
  169. m_client->getScript()->on_formspec_input(m_formname, fields);
  170. }
  171. Client *m_client = nullptr;
  172. };
  173. /* Form update callback */
  174. class NodeMetadataFormSource: public IFormSource
  175. {
  176. public:
  177. NodeMetadataFormSource(ClientMap *map, v3s16 p):
  178. m_map(map),
  179. m_p(p)
  180. {
  181. }
  182. const std::string &getForm() const
  183. {
  184. static const std::string empty_string = "";
  185. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  186. if (!meta)
  187. return empty_string;
  188. return meta->getString("formspec");
  189. }
  190. virtual std::string resolveText(const std::string &str)
  191. {
  192. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  193. if (!meta)
  194. return str;
  195. return meta->resolveString(str);
  196. }
  197. ClientMap *m_map;
  198. v3s16 m_p;
  199. };
  200. class PlayerInventoryFormSource: public IFormSource
  201. {
  202. public:
  203. PlayerInventoryFormSource(Client *client):
  204. m_client(client)
  205. {
  206. }
  207. const std::string &getForm() const
  208. {
  209. LocalPlayer *player = m_client->getEnv().getLocalPlayer();
  210. return player->inventory_formspec;
  211. }
  212. Client *m_client;
  213. };
  214. class NodeDugEvent: public MtEvent
  215. {
  216. public:
  217. v3s16 p;
  218. MapNode n;
  219. NodeDugEvent(v3s16 p, MapNode n):
  220. p(p),
  221. n(n)
  222. {}
  223. MtEvent::Type getType() const
  224. {
  225. return MtEvent::NODE_DUG;
  226. }
  227. };
  228. class SoundMaker
  229. {
  230. ISoundManager *m_sound;
  231. const NodeDefManager *m_ndef;
  232. public:
  233. bool makes_footstep_sound;
  234. float m_player_step_timer;
  235. float m_player_jump_timer;
  236. SimpleSoundSpec m_player_step_sound;
  237. SimpleSoundSpec m_player_leftpunch_sound;
  238. SimpleSoundSpec m_player_rightpunch_sound;
  239. SoundMaker(ISoundManager *sound, const NodeDefManager *ndef):
  240. m_sound(sound),
  241. m_ndef(ndef),
  242. makes_footstep_sound(true),
  243. m_player_step_timer(0.0f),
  244. m_player_jump_timer(0.0f)
  245. {
  246. }
  247. void playPlayerStep()
  248. {
  249. if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
  250. m_player_step_timer = 0.03;
  251. if (makes_footstep_sound)
  252. m_sound->playSound(m_player_step_sound, false);
  253. }
  254. }
  255. void playPlayerJump()
  256. {
  257. if (m_player_jump_timer <= 0.0f) {
  258. m_player_jump_timer = 0.2f;
  259. m_sound->playSound(SimpleSoundSpec("player_jump", 0.5f), false);
  260. }
  261. }
  262. static void viewBobbingStep(MtEvent *e, void *data)
  263. {
  264. SoundMaker *sm = (SoundMaker *)data;
  265. sm->playPlayerStep();
  266. }
  267. static void playerRegainGround(MtEvent *e, void *data)
  268. {
  269. SoundMaker *sm = (SoundMaker *)data;
  270. sm->playPlayerStep();
  271. }
  272. static void playerJump(MtEvent *e, void *data)
  273. {
  274. SoundMaker *sm = (SoundMaker *)data;
  275. sm->playPlayerJump();
  276. }
  277. static void cameraPunchLeft(MtEvent *e, void *data)
  278. {
  279. SoundMaker *sm = (SoundMaker *)data;
  280. sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
  281. }
  282. static void cameraPunchRight(MtEvent *e, void *data)
  283. {
  284. SoundMaker *sm = (SoundMaker *)data;
  285. sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
  286. }
  287. static void nodeDug(MtEvent *e, void *data)
  288. {
  289. SoundMaker *sm = (SoundMaker *)data;
  290. NodeDugEvent *nde = (NodeDugEvent *)e;
  291. sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
  292. }
  293. static void playerDamage(MtEvent *e, void *data)
  294. {
  295. SoundMaker *sm = (SoundMaker *)data;
  296. sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
  297. }
  298. static void playerFallingDamage(MtEvent *e, void *data)
  299. {
  300. SoundMaker *sm = (SoundMaker *)data;
  301. sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
  302. }
  303. void registerReceiver(MtEventManager *mgr)
  304. {
  305. mgr->reg(MtEvent::VIEW_BOBBING_STEP, SoundMaker::viewBobbingStep, this);
  306. mgr->reg(MtEvent::PLAYER_REGAIN_GROUND, SoundMaker::playerRegainGround, this);
  307. mgr->reg(MtEvent::PLAYER_JUMP, SoundMaker::playerJump, this);
  308. mgr->reg(MtEvent::CAMERA_PUNCH_LEFT, SoundMaker::cameraPunchLeft, this);
  309. mgr->reg(MtEvent::CAMERA_PUNCH_RIGHT, SoundMaker::cameraPunchRight, this);
  310. mgr->reg(MtEvent::NODE_DUG, SoundMaker::nodeDug, this);
  311. mgr->reg(MtEvent::PLAYER_DAMAGE, SoundMaker::playerDamage, this);
  312. mgr->reg(MtEvent::PLAYER_FALLING_DAMAGE, SoundMaker::playerFallingDamage, this);
  313. }
  314. void step(float dtime)
  315. {
  316. m_player_step_timer -= dtime;
  317. m_player_jump_timer -= dtime;
  318. }
  319. };
  320. // Locally stored sounds don't need to be preloaded because of this
  321. class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
  322. {
  323. std::set<std::string> m_fetched;
  324. private:
  325. void paths_insert(std::set<std::string> &dst_paths,
  326. const std::string &base,
  327. const std::string &name)
  328. {
  329. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".ogg");
  330. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".0.ogg");
  331. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".1.ogg");
  332. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".2.ogg");
  333. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".3.ogg");
  334. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".4.ogg");
  335. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".5.ogg");
  336. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".6.ogg");
  337. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".7.ogg");
  338. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".8.ogg");
  339. dst_paths.insert(base + DIR_DELIM + "sounds" + DIR_DELIM + name + ".9.ogg");
  340. }
  341. public:
  342. void fetchSounds(const std::string &name,
  343. std::set<std::string> &dst_paths,
  344. std::set<std::string> &dst_datas)
  345. {
  346. if (m_fetched.count(name))
  347. return;
  348. m_fetched.insert(name);
  349. paths_insert(dst_paths, porting::path_share, name);
  350. paths_insert(dst_paths, porting::path_user, name);
  351. }
  352. };
  353. // before 1.8 there isn't a "integer interface", only float
  354. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
  355. typedef f32 SamplerLayer_t;
  356. #else
  357. typedef s32 SamplerLayer_t;
  358. #endif
  359. class GameGlobalShaderConstantSetter : public IShaderConstantSetter
  360. {
  361. Sky *m_sky;
  362. bool *m_force_fog_off;
  363. f32 *m_fog_range;
  364. bool m_fog_enabled;
  365. CachedPixelShaderSetting<float, 4> m_sky_bg_color;
  366. CachedPixelShaderSetting<float> m_fog_distance;
  367. CachedVertexShaderSetting<float> m_animation_timer_vertex;
  368. CachedPixelShaderSetting<float> m_animation_timer_pixel;
  369. CachedPixelShaderSetting<float, 3> m_day_light;
  370. CachedPixelShaderSetting<float, 4> m_star_color;
  371. CachedPixelShaderSetting<float, 3> m_eye_position_pixel;
  372. CachedVertexShaderSetting<float, 3> m_eye_position_vertex;
  373. CachedPixelShaderSetting<float, 3> m_minimap_yaw;
  374. CachedPixelShaderSetting<float, 3> m_camera_offset_pixel;
  375. CachedPixelShaderSetting<float, 3> m_camera_offset_vertex;
  376. CachedPixelShaderSetting<SamplerLayer_t> m_base_texture;
  377. Client *m_client;
  378. public:
  379. void onSettingsChange(const std::string &name)
  380. {
  381. if (name == "enable_fog")
  382. m_fog_enabled = g_settings->getBool("enable_fog");
  383. }
  384. static void settingsCallback(const std::string &name, void *userdata)
  385. {
  386. reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
  387. }
  388. void setSky(Sky *sky) { m_sky = sky; }
  389. GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
  390. f32 *fog_range, Client *client) :
  391. m_sky(sky),
  392. m_force_fog_off(force_fog_off),
  393. m_fog_range(fog_range),
  394. m_sky_bg_color("skyBgColor"),
  395. m_fog_distance("fogDistance"),
  396. m_animation_timer_vertex("animationTimer"),
  397. m_animation_timer_pixel("animationTimer"),
  398. m_day_light("dayLight"),
  399. m_star_color("starColor"),
  400. m_eye_position_pixel("eyePosition"),
  401. m_eye_position_vertex("eyePosition"),
  402. m_minimap_yaw("yawVec"),
  403. m_camera_offset_pixel("cameraOffset"),
  404. m_camera_offset_vertex("cameraOffset"),
  405. m_base_texture("baseTexture"),
  406. m_client(client)
  407. {
  408. g_settings->registerChangedCallback("enable_fog", settingsCallback, this);
  409. m_fog_enabled = g_settings->getBool("enable_fog");
  410. }
  411. ~GameGlobalShaderConstantSetter()
  412. {
  413. g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this);
  414. }
  415. void onSetConstants(video::IMaterialRendererServices *services) override
  416. {
  417. // Background color
  418. video::SColor bgcolor = m_sky->getBgColor();
  419. video::SColorf bgcolorf(bgcolor);
  420. float bgcolorfa[4] = {
  421. bgcolorf.r,
  422. bgcolorf.g,
  423. bgcolorf.b,
  424. bgcolorf.a,
  425. };
  426. m_sky_bg_color.set(bgcolorfa, services);
  427. // Fog distance
  428. float fog_distance = 10000 * BS;
  429. if (m_fog_enabled && !*m_force_fog_off)
  430. fog_distance = *m_fog_range;
  431. m_fog_distance.set(&fog_distance, services);
  432. u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio();
  433. video::SColorf sunlight;
  434. get_sunlight_color(&sunlight, daynight_ratio);
  435. float dnc[3] = {
  436. sunlight.r,
  437. sunlight.g,
  438. sunlight.b };
  439. m_day_light.set(dnc, services);
  440. video::SColorf star_color = m_sky->getCurrentStarColor();
  441. float clr[4] = {star_color.r, star_color.g, star_color.b, star_color.a};
  442. m_star_color.set(clr, services);
  443. u32 animation_timer = porting::getTimeMs() % 1000000;
  444. float animation_timer_f = (float)animation_timer / 100000.f;
  445. m_animation_timer_vertex.set(&animation_timer_f, services);
  446. m_animation_timer_pixel.set(&animation_timer_f, services);
  447. float eye_position_array[3];
  448. v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition();
  449. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
  450. eye_position_array[0] = epos.X;
  451. eye_position_array[1] = epos.Y;
  452. eye_position_array[2] = epos.Z;
  453. #else
  454. epos.getAs3Values(eye_position_array);
  455. #endif
  456. m_eye_position_pixel.set(eye_position_array, services);
  457. m_eye_position_vertex.set(eye_position_array, services);
  458. if (m_client->getMinimap()) {
  459. float minimap_yaw_array[3];
  460. v3f minimap_yaw = m_client->getMinimap()->getYawVec();
  461. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
  462. minimap_yaw_array[0] = minimap_yaw.X;
  463. minimap_yaw_array[1] = minimap_yaw.Y;
  464. minimap_yaw_array[2] = minimap_yaw.Z;
  465. #else
  466. minimap_yaw.getAs3Values(minimap_yaw_array);
  467. #endif
  468. m_minimap_yaw.set(minimap_yaw_array, services);
  469. }
  470. float camera_offset_array[3];
  471. v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS);
  472. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
  473. camera_offset_array[0] = offset.X;
  474. camera_offset_array[1] = offset.Y;
  475. camera_offset_array[2] = offset.Z;
  476. #else
  477. offset.getAs3Values(camera_offset_array);
  478. #endif
  479. m_camera_offset_pixel.set(camera_offset_array, services);
  480. m_camera_offset_vertex.set(camera_offset_array, services);
  481. SamplerLayer_t base_tex = 0;
  482. m_base_texture.set(&base_tex, services);
  483. }
  484. };
  485. class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory
  486. {
  487. Sky *m_sky;
  488. bool *m_force_fog_off;
  489. f32 *m_fog_range;
  490. Client *m_client;
  491. std::vector<GameGlobalShaderConstantSetter *> created_nosky;
  492. public:
  493. GameGlobalShaderConstantSetterFactory(bool *force_fog_off,
  494. f32 *fog_range, Client *client) :
  495. m_sky(NULL),
  496. m_force_fog_off(force_fog_off),
  497. m_fog_range(fog_range),
  498. m_client(client)
  499. {}
  500. void setSky(Sky *sky) {
  501. m_sky = sky;
  502. for (GameGlobalShaderConstantSetter *ggscs : created_nosky) {
  503. ggscs->setSky(m_sky);
  504. }
  505. created_nosky.clear();
  506. }
  507. virtual IShaderConstantSetter* create()
  508. {
  509. auto *scs = new GameGlobalShaderConstantSetter(
  510. m_sky, m_force_fog_off, m_fog_range, m_client);
  511. if (!m_sky)
  512. created_nosky.push_back(scs);
  513. return scs;
  514. }
  515. };
  516. #ifdef __ANDROID__
  517. #define SIZE_TAG "size[11,5.5]"
  518. #else
  519. #define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
  520. #endif
  521. /****************************************************************************
  522. ****************************************************************************/
  523. const float object_hit_delay = 0.2;
  524. struct FpsControl {
  525. u32 last_time, busy_time, sleep_time;
  526. };
  527. /* The reason the following structs are not anonymous structs within the
  528. * class is that they are not used by the majority of member functions and
  529. * many functions that do require objects of thse types do not modify them
  530. * (so they can be passed as a const qualified parameter)
  531. */
  532. struct GameRunData {
  533. u16 dig_index;
  534. u16 new_playeritem;
  535. PointedThing pointed_old;
  536. bool digging;
  537. bool punching;
  538. bool btn_down_for_dig;
  539. bool dig_instantly;
  540. bool digging_blocked;
  541. bool reset_jump_timer;
  542. float nodig_delay_timer;
  543. float dig_time;
  544. float dig_time_complete;
  545. float repeat_place_timer;
  546. float object_hit_delay_timer;
  547. float time_from_last_punch;
  548. ClientActiveObject *selected_object;
  549. float jump_timer;
  550. float damage_flash;
  551. float update_draw_list_timer;
  552. f32 fog_range;
  553. v3f update_draw_list_last_cam_dir;
  554. float time_of_day_smooth;
  555. };
  556. class Game;
  557. struct ClientEventHandler
  558. {
  559. void (Game::*handler)(ClientEvent *, CameraOrientation *);
  560. };
  561. /****************************************************************************
  562. THE GAME
  563. ****************************************************************************/
  564. /* This is not intended to be a public class. If a public class becomes
  565. * desirable then it may be better to create another 'wrapper' class that
  566. * hides most of the stuff in this class (nothing in this class is required
  567. * by any other file) but exposes the public methods/data only.
  568. */
  569. class Game {
  570. public:
  571. Game();
  572. ~Game();
  573. bool startup(bool *kill,
  574. InputHandler *input,
  575. const GameStartData &game_params,
  576. std::string &error_message,
  577. bool *reconnect,
  578. ChatBackend *chat_backend);
  579. void run();
  580. void shutdown();
  581. protected:
  582. void extendedResourceCleanup();
  583. // Basic initialisation
  584. bool init(const std::string &map_dir, const std::string &address,
  585. u16 port, const SubgameSpec &gamespec);
  586. bool initSound();
  587. bool createSingleplayerServer(const std::string &map_dir,
  588. const SubgameSpec &gamespec, u16 port);
  589. // Client creation
  590. bool createClient(const GameStartData &start_data);
  591. bool initGui();
  592. // Client connection
  593. bool connectToServer(const GameStartData &start_data,
  594. bool *connect_ok, bool *aborted);
  595. bool getServerContent(bool *aborted);
  596. // Main loop
  597. void updateInteractTimers(f32 dtime);
  598. bool checkConnection();
  599. bool handleCallbacks();
  600. void processQueues();
  601. void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime);
  602. void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
  603. void updateProfilerGraphs(ProfilerGraph *graph);
  604. // Input related
  605. void processUserInput(f32 dtime);
  606. void processKeyInput();
  607. void processItemSelection(u16 *new_playeritem);
  608. void dropSelectedItem(bool single_item = false);
  609. void openInventory();
  610. void openConsole(float scale, const wchar_t *line=NULL);
  611. void toggleFreeMove();
  612. void toggleFreeMoveAlt();
  613. void togglePitchMove();
  614. void toggleFast();
  615. void toggleNoClip();
  616. void toggleCinematic();
  617. void toggleAutoforward();
  618. void toggleMinimap(bool shift_pressed);
  619. void toggleFog();
  620. void toggleDebug();
  621. void toggleUpdateCamera();
  622. void increaseViewRange();
  623. void decreaseViewRange();
  624. void toggleFullViewRange();
  625. void checkZoomEnabled();
  626. void updateCameraDirection(CameraOrientation *cam, float dtime);
  627. void updateCameraOrientation(CameraOrientation *cam, float dtime);
  628. void updatePlayerControl(const CameraOrientation &cam);
  629. void step(f32 *dtime);
  630. void processClientEvents(CameraOrientation *cam);
  631. void updateCamera(u32 busy_time, f32 dtime);
  632. void updateSound(f32 dtime);
  633. void processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug);
  634. /*!
  635. * Returns the object or node the player is pointing at.
  636. * Also updates the selected thing in the Hud.
  637. *
  638. * @param[in] shootline the shootline, starting from
  639. * the camera position. This also gives the maximal distance
  640. * of the search.
  641. * @param[in] liquids_pointable if false, liquids are ignored
  642. * @param[in] look_for_object if false, objects are ignored
  643. * @param[in] camera_offset offset of the camera
  644. * @param[out] selected_object the selected object or
  645. * NULL if not found
  646. */
  647. PointedThing updatePointedThing(
  648. const core::line3d<f32> &shootline, bool liquids_pointable,
  649. bool look_for_object, const v3s16 &camera_offset);
  650. void handlePointingAtNothing(const ItemStack &playerItem);
  651. void handlePointingAtNode(const PointedThing &pointed,
  652. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
  653. void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
  654. const v3f &player_position, bool show_debug);
  655. void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
  656. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime);
  657. void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
  658. const CameraOrientation &cam);
  659. // Misc
  660. void limitFps(FpsControl *fps_timings, f32 *dtime);
  661. void showOverlayMessage(const char *msg, float dtime, int percent,
  662. bool draw_clouds = true);
  663. static void settingChangedCallback(const std::string &setting_name, void *data);
  664. void readSettings();
  665. inline bool isKeyDown(GameKeyType k)
  666. {
  667. return input->isKeyDown(k);
  668. }
  669. inline bool wasKeyDown(GameKeyType k)
  670. {
  671. return input->wasKeyDown(k);
  672. }
  673. inline bool wasKeyPressed(GameKeyType k)
  674. {
  675. return input->wasKeyPressed(k);
  676. }
  677. inline bool wasKeyReleased(GameKeyType k)
  678. {
  679. return input->wasKeyReleased(k);
  680. }
  681. #ifdef __ANDROID__
  682. void handleAndroidChatInput();
  683. #endif
  684. private:
  685. struct Flags {
  686. bool force_fog_off = false;
  687. bool disable_camera_update = false;
  688. };
  689. void showDeathFormspec();
  690. void showPauseMenu();
  691. // ClientEvent handlers
  692. void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam);
  693. void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam);
  694. void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam);
  695. void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam);
  696. void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam);
  697. void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam);
  698. void handleClientEvent_HandleParticleEvent(ClientEvent *event,
  699. CameraOrientation *cam);
  700. void handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam);
  701. void handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam);
  702. void handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam);
  703. void handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam);
  704. void handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam);
  705. void handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam);
  706. void handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam);
  707. void handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
  708. CameraOrientation *cam);
  709. void handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam);
  710. void updateChat(f32 dtime, const v2u32 &screensize);
  711. bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item,
  712. const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed,
  713. const NodeMetadata *meta);
  714. static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX];
  715. InputHandler *input = nullptr;
  716. Client *client = nullptr;
  717. Server *server = nullptr;
  718. IWritableTextureSource *texture_src = nullptr;
  719. IWritableShaderSource *shader_src = nullptr;
  720. // When created, these will be filled with data received from the server
  721. IWritableItemDefManager *itemdef_manager = nullptr;
  722. NodeDefManager *nodedef_manager = nullptr;
  723. GameOnDemandSoundFetcher soundfetcher; // useful when testing
  724. ISoundManager *sound = nullptr;
  725. bool sound_is_dummy = false;
  726. SoundMaker *soundmaker = nullptr;
  727. ChatBackend *chat_backend = nullptr;
  728. LogOutputBuffer m_chat_log_buf;
  729. EventManager *eventmgr = nullptr;
  730. QuicktuneShortcutter *quicktune = nullptr;
  731. bool registration_confirmation_shown = false;
  732. std::unique_ptr<GameUI> m_game_ui;
  733. GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop()
  734. MapDrawControl *draw_control = nullptr;
  735. Camera *camera = nullptr;
  736. Clouds *clouds = nullptr; // Free using ->Drop()
  737. Sky *sky = nullptr; // Free using ->Drop()
  738. Hud *hud = nullptr;
  739. Minimap *mapper = nullptr;
  740. GameRunData runData;
  741. Flags m_flags;
  742. /* 'cache'
  743. This class does take ownership/responsibily for cleaning up etc of any of
  744. these items (e.g. device)
  745. */
  746. IrrlichtDevice *device;
  747. video::IVideoDriver *driver;
  748. scene::ISceneManager *smgr;
  749. bool *kill;
  750. std::string *error_message;
  751. bool *reconnect_requested;
  752. scene::ISceneNode *skybox;
  753. bool simple_singleplayer_mode;
  754. /* End 'cache' */
  755. /* Pre-calculated values
  756. */
  757. int crack_animation_length;
  758. IntervalLimiter profiler_interval;
  759. /*
  760. * TODO: Local caching of settings is not optimal and should at some stage
  761. * be updated to use a global settings object for getting thse values
  762. * (as opposed to the this local caching). This can be addressed in
  763. * a later release.
  764. */
  765. bool m_cache_doubletap_jump;
  766. bool m_cache_enable_clouds;
  767. bool m_cache_enable_joysticks;
  768. bool m_cache_enable_particles;
  769. bool m_cache_enable_fog;
  770. bool m_cache_enable_noclip;
  771. bool m_cache_enable_free_move;
  772. f32 m_cache_mouse_sensitivity;
  773. f32 m_cache_joystick_frustum_sensitivity;
  774. f32 m_repeat_place_time;
  775. f32 m_cache_cam_smoothing;
  776. f32 m_cache_fog_start;
  777. bool m_invert_mouse = false;
  778. bool m_first_loop_after_window_activation = false;
  779. bool m_camera_offset_changed = false;
  780. bool m_does_lost_focus_pause_game = false;
  781. int m_reset_HW_buffer_counter = 0;
  782. #ifdef __ANDROID__
  783. bool m_cache_hold_aux1;
  784. bool m_android_chat_open;
  785. #endif
  786. };
  787. Game::Game() :
  788. m_chat_log_buf(g_logger),
  789. m_game_ui(new GameUI())
  790. {
  791. g_settings->registerChangedCallback("doubletap_jump",
  792. &settingChangedCallback, this);
  793. g_settings->registerChangedCallback("enable_clouds",
  794. &settingChangedCallback, this);
  795. g_settings->registerChangedCallback("doubletap_joysticks",
  796. &settingChangedCallback, this);
  797. g_settings->registerChangedCallback("enable_particles",
  798. &settingChangedCallback, this);
  799. g_settings->registerChangedCallback("enable_fog",
  800. &settingChangedCallback, this);
  801. g_settings->registerChangedCallback("mouse_sensitivity",
  802. &settingChangedCallback, this);
  803. g_settings->registerChangedCallback("joystick_frustum_sensitivity",
  804. &settingChangedCallback, this);
  805. g_settings->registerChangedCallback("repeat_place_time",
  806. &settingChangedCallback, this);
  807. g_settings->registerChangedCallback("noclip",
  808. &settingChangedCallback, this);
  809. g_settings->registerChangedCallback("free_move",
  810. &settingChangedCallback, this);
  811. g_settings->registerChangedCallback("cinematic",
  812. &settingChangedCallback, this);
  813. g_settings->registerChangedCallback("cinematic_camera_smoothing",
  814. &settingChangedCallback, this);
  815. g_settings->registerChangedCallback("camera_smoothing",
  816. &settingChangedCallback, this);
  817. readSettings();
  818. #ifdef __ANDROID__
  819. m_cache_hold_aux1 = false; // This is initialised properly later
  820. #endif
  821. }
  822. /****************************************************************************
  823. MinetestApp Public
  824. ****************************************************************************/
  825. Game::~Game()
  826. {
  827. delete client;
  828. delete soundmaker;
  829. if (!sound_is_dummy)
  830. delete sound;
  831. delete server; // deleted first to stop all server threads
  832. delete hud;
  833. delete camera;
  834. delete quicktune;
  835. delete eventmgr;
  836. delete texture_src;
  837. delete shader_src;
  838. delete nodedef_manager;
  839. delete itemdef_manager;
  840. delete draw_control;
  841. extendedResourceCleanup();
  842. g_settings->deregisterChangedCallback("doubletap_jump",
  843. &settingChangedCallback, this);
  844. g_settings->deregisterChangedCallback("enable_clouds",
  845. &settingChangedCallback, this);
  846. g_settings->deregisterChangedCallback("enable_particles",
  847. &settingChangedCallback, this);
  848. g_settings->deregisterChangedCallback("enable_fog",
  849. &settingChangedCallback, this);
  850. g_settings->deregisterChangedCallback("mouse_sensitivity",
  851. &settingChangedCallback, this);
  852. g_settings->deregisterChangedCallback("repeat_place_time",
  853. &settingChangedCallback, this);
  854. g_settings->deregisterChangedCallback("noclip",
  855. &settingChangedCallback, this);
  856. g_settings->deregisterChangedCallback("free_move",
  857. &settingChangedCallback, this);
  858. g_settings->deregisterChangedCallback("cinematic",
  859. &settingChangedCallback, this);
  860. g_settings->deregisterChangedCallback("cinematic_camera_smoothing",
  861. &settingChangedCallback, this);
  862. g_settings->deregisterChangedCallback("camera_smoothing",
  863. &settingChangedCallback, this);
  864. }
  865. bool Game::startup(bool *kill,
  866. InputHandler *input,
  867. const GameStartData &start_data,
  868. std::string &error_message,
  869. bool *reconnect,
  870. ChatBackend *chat_backend)
  871. {
  872. // "cache"
  873. this->device = RenderingEngine::get_raw_device();
  874. this->kill = kill;
  875. this->error_message = &error_message;
  876. this->reconnect_requested = reconnect;
  877. this->input = input;
  878. this->chat_backend = chat_backend;
  879. this->simple_singleplayer_mode = start_data.isSinglePlayer();
  880. input->keycache.populate();
  881. driver = device->getVideoDriver();
  882. smgr = RenderingEngine::get_scene_manager();
  883. RenderingEngine::get_scene_manager()->getParameters()->
  884. setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
  885. // Reinit runData
  886. runData = GameRunData();
  887. runData.time_from_last_punch = 10.0;
  888. m_game_ui->initFlags();
  889. m_invert_mouse = g_settings->getBool("invert_mouse");
  890. m_first_loop_after_window_activation = true;
  891. g_client_translations->clear();
  892. // address can change if simple_singleplayer_mode
  893. if (!init(start_data.world_spec.path, start_data.address,
  894. start_data.socket_port, start_data.game_spec))
  895. return false;
  896. if (!createClient(start_data))
  897. return false;
  898. RenderingEngine::initialize(client, hud);
  899. return true;
  900. }
  901. void Game::run()
  902. {
  903. ProfilerGraph graph;
  904. RunStats stats = { 0 };
  905. CameraOrientation cam_view_target = { 0 };
  906. CameraOrientation cam_view = { 0 };
  907. FpsControl draw_times = { 0 };
  908. f32 dtime; // in seconds
  909. /* Clear the profiler */
  910. Profiler::GraphValues dummyvalues;
  911. g_profiler->graphGet(dummyvalues);
  912. draw_times.last_time = RenderingEngine::get_timer_time();
  913. set_light_table(g_settings->getFloat("display_gamma"));
  914. #ifdef __ANDROID__
  915. m_cache_hold_aux1 = g_settings->getBool("fast_move")
  916. && client->checkPrivilege("fast");
  917. #endif
  918. irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"),
  919. g_settings->getU16("screen_h"));
  920. while (RenderingEngine::run()
  921. && !(*kill || g_gamecallback->shutdown_requested
  922. || (server && server->isShutdownRequested()))) {
  923. const irr::core::dimension2d<u32> &current_screen_size =
  924. RenderingEngine::get_video_driver()->getScreenSize();
  925. // Verify if window size has changed and save it if it's the case
  926. // Ensure evaluating settings->getBool after verifying screensize
  927. // First condition is cheaper
  928. if (previous_screen_size != current_screen_size &&
  929. current_screen_size != irr::core::dimension2d<u32>(0,0) &&
  930. g_settings->getBool("autosave_screensize")) {
  931. g_settings->setU16("screen_w", current_screen_size.Width);
  932. g_settings->setU16("screen_h", current_screen_size.Height);
  933. previous_screen_size = current_screen_size;
  934. }
  935. // Calculate dtime =
  936. // RenderingEngine::run() from this iteration
  937. // + Sleep time until the wanted FPS are reached
  938. limitFps(&draw_times, &dtime);
  939. // Prepare render data for next iteration
  940. updateStats(&stats, draw_times, dtime);
  941. updateInteractTimers(dtime);
  942. if (!checkConnection())
  943. break;
  944. if (!handleCallbacks())
  945. break;
  946. processQueues();
  947. m_game_ui->clearInfoText();
  948. hud->resizeHotbar();
  949. updateProfilers(stats, draw_times, dtime);
  950. processUserInput(dtime);
  951. // Update camera before player movement to avoid camera lag of one frame
  952. updateCameraDirection(&cam_view_target, dtime);
  953. cam_view.camera_yaw += (cam_view_target.camera_yaw -
  954. cam_view.camera_yaw) * m_cache_cam_smoothing;
  955. cam_view.camera_pitch += (cam_view_target.camera_pitch -
  956. cam_view.camera_pitch) * m_cache_cam_smoothing;
  957. updatePlayerControl(cam_view);
  958. step(&dtime);
  959. processClientEvents(&cam_view_target);
  960. updateCamera(draw_times.busy_time, dtime);
  961. updateSound(dtime);
  962. processPlayerInteraction(dtime, m_game_ui->m_flags.show_hud,
  963. m_game_ui->m_flags.show_debug);
  964. updateFrame(&graph, &stats, dtime, cam_view);
  965. updateProfilerGraphs(&graph);
  966. // Update if minimap has been disabled by the server
  967. m_game_ui->m_flags.show_minimap &= client->shouldShowMinimap();
  968. if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) {
  969. showPauseMenu();
  970. }
  971. }
  972. }
  973. void Game::shutdown()
  974. {
  975. RenderingEngine::finalize();
  976. #if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8
  977. if (g_settings->get("3d_mode") == "pageflip") {
  978. driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS);
  979. }
  980. #endif
  981. auto formspec = m_game_ui->getFormspecGUI();
  982. if (formspec)
  983. formspec->quitMenu();
  984. #ifdef HAVE_TOUCHSCREENGUI
  985. g_touchscreengui->hide();
  986. #endif
  987. showOverlayMessage(N_("Shutting down..."), 0, 0, false);
  988. if (clouds)
  989. clouds->drop();
  990. if (gui_chat_console)
  991. gui_chat_console->drop();
  992. if (sky)
  993. sky->drop();
  994. /* cleanup menus */
  995. while (g_menumgr.menuCount() > 0) {
  996. g_menumgr.m_stack.front()->setVisible(false);
  997. g_menumgr.deletingMenu(g_menumgr.m_stack.front());
  998. }
  999. m_game_ui->deleteFormspec();
  1000. chat_backend->addMessage(L"", L"# Disconnected.");
  1001. chat_backend->addMessage(L"", L"");
  1002. m_chat_log_buf.clear();
  1003. if (client) {
  1004. client->Stop();
  1005. while (!client->isShutdown()) {
  1006. assert(texture_src != NULL);
  1007. assert(shader_src != NULL);
  1008. texture_src->processQueue();
  1009. shader_src->processQueue();
  1010. sleep_ms(100);
  1011. }
  1012. }
  1013. }
  1014. /****************************************************************************/
  1015. /****************************************************************************
  1016. Startup
  1017. ****************************************************************************/
  1018. /****************************************************************************/
  1019. bool Game::init(
  1020. const std::string &map_dir,
  1021. const std::string &address,
  1022. u16 port,
  1023. const SubgameSpec &gamespec)
  1024. {
  1025. texture_src = createTextureSource();
  1026. showOverlayMessage(N_("Loading..."), 0, 0);
  1027. shader_src = createShaderSource();
  1028. itemdef_manager = createItemDefManager();
  1029. nodedef_manager = createNodeDefManager();
  1030. eventmgr = new EventManager();
  1031. quicktune = new QuicktuneShortcutter();
  1032. if (!(texture_src && shader_src && itemdef_manager && nodedef_manager
  1033. && eventmgr && quicktune))
  1034. return false;
  1035. if (!initSound())
  1036. return false;
  1037. // Create a server if not connecting to an existing one
  1038. if (address.empty()) {
  1039. if (!createSingleplayerServer(map_dir, gamespec, port))
  1040. return false;
  1041. }
  1042. return true;
  1043. }
  1044. bool Game::initSound()
  1045. {
  1046. #if USE_SOUND
  1047. if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) {
  1048. infostream << "Attempting to use OpenAL audio" << std::endl;
  1049. sound = createOpenALSoundManager(g_sound_manager_singleton.get(), &soundfetcher);
  1050. if (!sound)
  1051. infostream << "Failed to initialize OpenAL audio" << std::endl;
  1052. } else
  1053. infostream << "Sound disabled." << std::endl;
  1054. #endif
  1055. if (!sound) {
  1056. infostream << "Using dummy audio." << std::endl;
  1057. sound = &dummySoundManager;
  1058. sound_is_dummy = true;
  1059. }
  1060. soundmaker = new SoundMaker(sound, nodedef_manager);
  1061. if (!soundmaker)
  1062. return false;
  1063. soundmaker->registerReceiver(eventmgr);
  1064. return true;
  1065. }
  1066. bool Game::createSingleplayerServer(const std::string &map_dir,
  1067. const SubgameSpec &gamespec, u16 port)
  1068. {
  1069. showOverlayMessage(N_("Creating server..."), 0, 5);
  1070. std::string bind_str = g_settings->get("bind_address");
  1071. Address bind_addr(0, 0, 0, 0, port);
  1072. if (g_settings->getBool("ipv6_server")) {
  1073. bind_addr.setAddress((IPv6AddressBytes *) NULL);
  1074. }
  1075. try {
  1076. bind_addr.Resolve(bind_str.c_str());
  1077. } catch (ResolveError &e) {
  1078. infostream << "Resolving bind address \"" << bind_str
  1079. << "\" failed: " << e.what()
  1080. << " -- Listening on all addresses." << std::endl;
  1081. }
  1082. if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1083. *error_message = "Unable to listen on " +
  1084. bind_addr.serializeString() +
  1085. " because IPv6 is disabled";
  1086. errorstream << *error_message << std::endl;
  1087. return false;
  1088. }
  1089. server = new Server(map_dir, gamespec, simple_singleplayer_mode, bind_addr,
  1090. false, nullptr, error_message);
  1091. server->start();
  1092. return true;
  1093. }
  1094. bool Game::createClient(const GameStartData &start_data)
  1095. {
  1096. showOverlayMessage(N_("Creating client..."), 0, 10);
  1097. draw_control = new MapDrawControl;
  1098. if (!draw_control)
  1099. return false;
  1100. bool could_connect, connect_aborted;
  1101. #ifdef HAVE_TOUCHSCREENGUI
  1102. if (g_touchscreengui) {
  1103. g_touchscreengui->init(texture_src);
  1104. g_touchscreengui->hide();
  1105. }
  1106. #endif
  1107. if (!connectToServer(start_data, &could_connect, &connect_aborted))
  1108. return false;
  1109. if (!could_connect) {
  1110. if (error_message->empty() && !connect_aborted) {
  1111. // Should not happen if error messages are set properly
  1112. *error_message = "Connection failed for unknown reason";
  1113. errorstream << *error_message << std::endl;
  1114. }
  1115. return false;
  1116. }
  1117. if (!getServerContent(&connect_aborted)) {
  1118. if (error_message->empty() && !connect_aborted) {
  1119. // Should not happen if error messages are set properly
  1120. *error_message = "Connection failed for unknown reason";
  1121. errorstream << *error_message << std::endl;
  1122. }
  1123. return false;
  1124. }
  1125. auto *scsf = new GameGlobalShaderConstantSetterFactory(
  1126. &m_flags.force_fog_off, &runData.fog_range, client);
  1127. shader_src->addShaderConstantSetterFactory(scsf);
  1128. // Update cached textures, meshes and materials
  1129. client->afterContentReceived();
  1130. /* Camera
  1131. */
  1132. camera = new Camera(*draw_control, client);
  1133. if (!camera->successfullyCreated(*error_message))
  1134. return false;
  1135. client->setCamera(camera);
  1136. /* Clouds
  1137. */
  1138. if (m_cache_enable_clouds)
  1139. clouds = new Clouds(smgr, -1, time(0));
  1140. /* Skybox
  1141. */
  1142. sky = new Sky(-1, texture_src, shader_src);
  1143. scsf->setSky(sky);
  1144. skybox = NULL; // This is used/set later on in the main run loop
  1145. /* Pre-calculated values
  1146. */
  1147. video::ITexture *t = texture_src->getTexture("crack_anylength.png");
  1148. if (t) {
  1149. v2u32 size = t->getOriginalSize();
  1150. crack_animation_length = size.Y / size.X;
  1151. } else {
  1152. crack_animation_length = 5;
  1153. }
  1154. if (!initGui())
  1155. return false;
  1156. /* Set window caption
  1157. */
  1158. std::wstring str = utf8_to_wide(PROJECT_NAME_C);
  1159. str += L" ";
  1160. str += utf8_to_wide(g_version_hash);
  1161. str += L" [";
  1162. str += driver->getName();
  1163. str += L"]";
  1164. device->setWindowCaption(str.c_str());
  1165. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1166. player->hurt_tilt_timer = 0;
  1167. player->hurt_tilt_strength = 0;
  1168. hud = new Hud(guienv, client, player, &player->inventory);
  1169. mapper = client->getMinimap();
  1170. if (mapper && client->modsLoaded())
  1171. client->getScript()->on_minimap_ready(mapper);
  1172. return true;
  1173. }
  1174. bool Game::initGui()
  1175. {
  1176. m_game_ui->init();
  1177. // Remove stale "recent" chat messages from previous connections
  1178. chat_backend->clearRecentChat();
  1179. // Make sure the size of the recent messages buffer is right
  1180. chat_backend->applySettings();
  1181. // Chat backend and console
  1182. gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
  1183. -1, chat_backend, client, &g_menumgr);
  1184. #ifdef HAVE_TOUCHSCREENGUI
  1185. if (g_touchscreengui)
  1186. g_touchscreengui->show();
  1187. #endif
  1188. return true;
  1189. }
  1190. bool Game::connectToServer(const GameStartData &start_data,
  1191. bool *connect_ok, bool *connection_aborted)
  1192. {
  1193. *connect_ok = false; // Let's not be overly optimistic
  1194. *connection_aborted = false;
  1195. bool local_server_mode = false;
  1196. showOverlayMessage(N_("Resolving address..."), 0, 15);
  1197. Address connect_address(0, 0, 0, 0, start_data.socket_port);
  1198. try {
  1199. connect_address.Resolve(start_data.address.c_str());
  1200. if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
  1201. //connect_address.Resolve("localhost");
  1202. if (connect_address.isIPv6()) {
  1203. IPv6AddressBytes addr_bytes;
  1204. addr_bytes.bytes[15] = 1;
  1205. connect_address.setAddress(&addr_bytes);
  1206. } else {
  1207. connect_address.setAddress(127, 0, 0, 1);
  1208. }
  1209. local_server_mode = true;
  1210. }
  1211. } catch (ResolveError &e) {
  1212. *error_message = std::string("Couldn't resolve address: ") + e.what();
  1213. errorstream << *error_message << std::endl;
  1214. return false;
  1215. }
  1216. if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1217. *error_message = "Unable to connect to " +
  1218. connect_address.serializeString() +
  1219. " because IPv6 is disabled";
  1220. errorstream << *error_message << std::endl;
  1221. return false;
  1222. }
  1223. client = new Client(start_data.name.c_str(),
  1224. start_data.password, start_data.address,
  1225. *draw_control, texture_src, shader_src,
  1226. itemdef_manager, nodedef_manager, sound, eventmgr,
  1227. connect_address.isIPv6(), m_game_ui.get());
  1228. client->m_simple_singleplayer_mode = simple_singleplayer_mode;
  1229. infostream << "Connecting to server at ";
  1230. connect_address.print(&infostream);
  1231. infostream << std::endl;
  1232. client->connect(connect_address,
  1233. simple_singleplayer_mode || local_server_mode);
  1234. /*
  1235. Wait for server to accept connection
  1236. */
  1237. try {
  1238. input->clear();
  1239. FpsControl fps_control = { 0 };
  1240. f32 dtime;
  1241. f32 wait_time = 0; // in seconds
  1242. fps_control.last_time = RenderingEngine::get_timer_time();
  1243. while (RenderingEngine::run()) {
  1244. limitFps(&fps_control, &dtime);
  1245. // Update client and server
  1246. client->step(dtime);
  1247. if (server != NULL)
  1248. server->step(dtime);
  1249. // End condition
  1250. if (client->getState() == LC_Init) {
  1251. *connect_ok = true;
  1252. break;
  1253. }
  1254. // Break conditions
  1255. if (*connection_aborted)
  1256. break;
  1257. if (client->accessDenied()) {
  1258. *error_message = "Access denied. Reason: "
  1259. + client->accessDeniedReason();
  1260. *reconnect_requested = client->reconnectRequested();
  1261. errorstream << *error_message << std::endl;
  1262. break;
  1263. }
  1264. if (input->cancelPressed()) {
  1265. *connection_aborted = true;
  1266. infostream << "Connect aborted [Escape]" << std::endl;
  1267. break;
  1268. }
  1269. if (client->m_is_registration_confirmation_state) {
  1270. if (registration_confirmation_shown) {
  1271. // Keep drawing the GUI
  1272. RenderingEngine::draw_menu_scene(guienv, dtime, true);
  1273. } else {
  1274. registration_confirmation_shown = true;
  1275. (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
  1276. &g_menumgr, client, start_data.name, start_data.password,
  1277. connection_aborted, texture_src))->drop();
  1278. }
  1279. } else {
  1280. wait_time += dtime;
  1281. // Only time out if we aren't waiting for the server we started
  1282. if (!start_data.isSinglePlayer() && wait_time > 10) {
  1283. *error_message = "Connection timed out.";
  1284. errorstream << *error_message << std::endl;
  1285. break;
  1286. }
  1287. // Update status
  1288. showOverlayMessage(N_("Connecting to server..."), dtime, 20);
  1289. }
  1290. }
  1291. } catch (con::PeerNotFoundException &e) {
  1292. // TODO: Should something be done here? At least an info/error
  1293. // message?
  1294. return false;
  1295. }
  1296. return true;
  1297. }
  1298. bool Game::getServerContent(bool *aborted)
  1299. {
  1300. input->clear();
  1301. FpsControl fps_control = { 0 };
  1302. f32 dtime; // in seconds
  1303. fps_control.last_time = RenderingEngine::get_timer_time();
  1304. while (RenderingEngine::run()) {
  1305. limitFps(&fps_control, &dtime);
  1306. // Update client and server
  1307. client->step(dtime);
  1308. if (server != NULL)
  1309. server->step(dtime);
  1310. // End condition
  1311. if (client->mediaReceived() && client->itemdefReceived() &&
  1312. client->nodedefReceived()) {
  1313. break;
  1314. }
  1315. // Error conditions
  1316. if (!checkConnection())
  1317. return false;
  1318. if (client->getState() < LC_Init) {
  1319. *error_message = "Client disconnected";
  1320. errorstream << *error_message << std::endl;
  1321. return false;
  1322. }
  1323. if (input->cancelPressed()) {
  1324. *aborted = true;
  1325. infostream << "Connect aborted [Escape]" << std::endl;
  1326. return false;
  1327. }
  1328. // Display status
  1329. int progress = 25;
  1330. if (!client->itemdefReceived()) {
  1331. const wchar_t *text = wgettext("Item definitions...");
  1332. progress = 25;
  1333. RenderingEngine::draw_load_screen(text, guienv, texture_src,
  1334. dtime, progress);
  1335. delete[] text;
  1336. } else if (!client->nodedefReceived()) {
  1337. const wchar_t *text = wgettext("Node definitions...");
  1338. progress = 30;
  1339. RenderingEngine::draw_load_screen(text, guienv, texture_src,
  1340. dtime, progress);
  1341. delete[] text;
  1342. } else {
  1343. std::stringstream message;
  1344. std::fixed(message);
  1345. message.precision(0);
  1346. float receive = client->mediaReceiveProgress() * 100;
  1347. message << gettext("Media...");
  1348. if (receive > 0)
  1349. message << " " << receive << "%";
  1350. message.precision(2);
  1351. if ((USE_CURL == 0) ||
  1352. (!g_settings->getBool("enable_remote_media_server"))) {
  1353. float cur = client->getCurRate();
  1354. std::string cur_unit = gettext("KiB/s");
  1355. if (cur > 900) {
  1356. cur /= 1024.0;
  1357. cur_unit = gettext("MiB/s");
  1358. }
  1359. message << " (" << cur << ' ' << cur_unit << ")";
  1360. }
  1361. progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
  1362. RenderingEngine::draw_load_screen(utf8_to_wide(message.str()), guienv,
  1363. texture_src, dtime, progress);
  1364. }
  1365. }
  1366. return true;
  1367. }
  1368. /****************************************************************************/
  1369. /****************************************************************************
  1370. Run
  1371. ****************************************************************************/
  1372. /****************************************************************************/
  1373. inline void Game::updateInteractTimers(f32 dtime)
  1374. {
  1375. if (runData.nodig_delay_timer >= 0)
  1376. runData.nodig_delay_timer -= dtime;
  1377. if (runData.object_hit_delay_timer >= 0)
  1378. runData.object_hit_delay_timer -= dtime;
  1379. runData.time_from_last_punch += dtime;
  1380. }
  1381. /* returns false if game should exit, otherwise true
  1382. */
  1383. inline bool Game::checkConnection()
  1384. {
  1385. if (client->accessDenied()) {
  1386. *error_message = "Access denied. Reason: "
  1387. + client->accessDeniedReason();
  1388. *reconnect_requested = client->reconnectRequested();
  1389. errorstream << *error_message << std::endl;
  1390. return false;
  1391. }
  1392. return true;
  1393. }
  1394. /* returns false if game should exit, otherwise true
  1395. */
  1396. inline bool Game::handleCallbacks()
  1397. {
  1398. if (g_gamecallback->disconnect_requested) {
  1399. g_gamecallback->disconnect_requested = false;
  1400. return false;
  1401. }
  1402. if (g_gamecallback->changepassword_requested) {
  1403. (new GUIPasswordChange(guienv, guiroot, -1,
  1404. &g_menumgr, client, texture_src))->drop();
  1405. g_gamecallback->changepassword_requested = false;
  1406. }
  1407. if (g_gamecallback->changevolume_requested) {
  1408. (new GUIVolumeChange(guienv, guiroot, -1,
  1409. &g_menumgr, texture_src))->drop();
  1410. g_gamecallback->changevolume_requested = false;
  1411. }
  1412. if (g_gamecallback->keyconfig_requested) {
  1413. (new GUIKeyChangeMenu(guienv, guiroot, -1,
  1414. &g_menumgr, texture_src))->drop();
  1415. g_gamecallback->keyconfig_requested = false;
  1416. }
  1417. if (g_gamecallback->keyconfig_changed) {
  1418. input->keycache.populate(); // update the cache with new settings
  1419. g_gamecallback->keyconfig_changed = false;
  1420. }
  1421. return true;
  1422. }
  1423. void Game::processQueues()
  1424. {
  1425. texture_src->processQueue();
  1426. itemdef_manager->processQueue(client);
  1427. shader_src->processQueue();
  1428. }
  1429. void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
  1430. f32 dtime)
  1431. {
  1432. float profiler_print_interval =
  1433. g_settings->getFloat("profiler_print_interval");
  1434. bool print_to_log = true;
  1435. if (profiler_print_interval == 0) {
  1436. print_to_log = false;
  1437. profiler_print_interval = 3;
  1438. }
  1439. if (profiler_interval.step(dtime, profiler_print_interval)) {
  1440. if (print_to_log) {
  1441. infostream << "Profiler:" << std::endl;
  1442. g_profiler->print(infostream);
  1443. }
  1444. m_game_ui->updateProfiler();
  1445. g_profiler->clear();
  1446. }
  1447. // Update update graphs
  1448. g_profiler->graphAdd("Time non-rendering [ms]",
  1449. draw_times.busy_time - stats.drawtime);
  1450. g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time);
  1451. g_profiler->graphAdd("FPS", 1.0f / dtime);
  1452. }
  1453. void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
  1454. f32 dtime)
  1455. {
  1456. f32 jitter;
  1457. Jitter *jp;
  1458. /* Time average and jitter calculation
  1459. */
  1460. jp = &stats->dtime_jitter;
  1461. jp->avg = jp->avg * 0.96 + dtime * 0.04;
  1462. jitter = dtime - jp->avg;
  1463. if (jitter > jp->max)
  1464. jp->max = jitter;
  1465. jp->counter += dtime;
  1466. if (jp->counter > 0.0) {
  1467. jp->counter -= 3.0;
  1468. jp->max_sample = jp->max;
  1469. jp->max_fraction = jp->max_sample / (jp->avg + 0.001);
  1470. jp->max = 0.0;
  1471. }
  1472. /* Busytime average and jitter calculation
  1473. */
  1474. jp = &stats->busy_time_jitter;
  1475. jp->avg = jp->avg + draw_times.busy_time * 0.02;
  1476. jitter = draw_times.busy_time - jp->avg;
  1477. if (jitter > jp->max)
  1478. jp->max = jitter;
  1479. if (jitter < jp->min)
  1480. jp->min = jitter;
  1481. jp->counter += dtime;
  1482. if (jp->counter > 0.0) {
  1483. jp->counter -= 3.0;
  1484. jp->max_sample = jp->max;
  1485. jp->min_sample = jp->min;
  1486. jp->max = 0.0;
  1487. jp->min = 0.0;
  1488. }
  1489. }
  1490. /****************************************************************************
  1491. Input handling
  1492. ****************************************************************************/
  1493. void Game::processUserInput(f32 dtime)
  1494. {
  1495. // Reset input if window not active or some menu is active
  1496. if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) {
  1497. input->clear();
  1498. #ifdef HAVE_TOUCHSCREENGUI
  1499. g_touchscreengui->hide();
  1500. #endif
  1501. }
  1502. #ifdef HAVE_TOUCHSCREENGUI
  1503. else if (g_touchscreengui) {
  1504. /* on touchscreengui step may generate own input events which ain't
  1505. * what we want in case we just did clear them */
  1506. g_touchscreengui->step(dtime);
  1507. }
  1508. #endif
  1509. if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
  1510. gui_chat_console->closeConsoleAtOnce();
  1511. }
  1512. // Input handler step() (used by the random input generator)
  1513. input->step(dtime);
  1514. #ifdef __ANDROID__
  1515. auto formspec = m_game_ui->getFormspecGUI();
  1516. if (formspec)
  1517. formspec->getAndroidUIInput();
  1518. else
  1519. handleAndroidChatInput();
  1520. #endif
  1521. // Increase timer for double tap of "keymap_jump"
  1522. if (m_cache_doubletap_jump && runData.jump_timer <= 0.2f)
  1523. runData.jump_timer += dtime;
  1524. processKeyInput();
  1525. processItemSelection(&runData.new_playeritem);
  1526. }
  1527. void Game::processKeyInput()
  1528. {
  1529. if (wasKeyDown(KeyType::DROP)) {
  1530. dropSelectedItem(isKeyDown(KeyType::SNEAK));
  1531. } else if (wasKeyDown(KeyType::AUTOFORWARD)) {
  1532. toggleAutoforward();
  1533. } else if (wasKeyDown(KeyType::BACKWARD)) {
  1534. if (g_settings->getBool("continuous_forward"))
  1535. toggleAutoforward();
  1536. } else if (wasKeyDown(KeyType::INVENTORY)) {
  1537. openInventory();
  1538. } else if (input->cancelPressed()) {
  1539. #ifdef __ANDROID__
  1540. m_android_chat_open = false;
  1541. #endif
  1542. if (!gui_chat_console->isOpenInhibited()) {
  1543. showPauseMenu();
  1544. }
  1545. } else if (wasKeyDown(KeyType::CHAT)) {
  1546. openConsole(0.2, L"");
  1547. } else if (wasKeyDown(KeyType::CMD)) {
  1548. openConsole(0.2, L"/");
  1549. } else if (wasKeyDown(KeyType::CMD_LOCAL)) {
  1550. if (client->modsLoaded())
  1551. openConsole(0.2, L".");
  1552. else
  1553. m_game_ui->showStatusText(wgettext("Client side scripting is disabled"));
  1554. } else if (wasKeyDown(KeyType::CONSOLE)) {
  1555. openConsole(core::clamp(g_settings->getFloat("console_height"), 0.1f, 1.0f));
  1556. } else if (wasKeyDown(KeyType::FREEMOVE)) {
  1557. toggleFreeMove();
  1558. } else if (wasKeyDown(KeyType::JUMP)) {
  1559. toggleFreeMoveAlt();
  1560. } else if (wasKeyDown(KeyType::PITCHMOVE)) {
  1561. togglePitchMove();
  1562. } else if (wasKeyDown(KeyType::FASTMOVE)) {
  1563. toggleFast();
  1564. } else if (wasKeyDown(KeyType::NOCLIP)) {
  1565. toggleNoClip();
  1566. #if USE_SOUND
  1567. } else if (wasKeyDown(KeyType::MUTE)) {
  1568. if (g_settings->getBool("enable_sound")) {
  1569. bool new_mute_sound = !g_settings->getBool("mute_sound");
  1570. g_settings->setBool("mute_sound", new_mute_sound);
  1571. if (new_mute_sound)
  1572. m_game_ui->showTranslatedStatusText("Sound muted");
  1573. else
  1574. m_game_ui->showTranslatedStatusText("Sound unmuted");
  1575. } else {
  1576. m_game_ui->showTranslatedStatusText("Sound system is disabled");
  1577. }
  1578. } else if (wasKeyDown(KeyType::INC_VOLUME)) {
  1579. if (g_settings->getBool("enable_sound")) {
  1580. float new_volume = rangelim(g_settings->getFloat("sound_volume") + 0.1f, 0.0f, 1.0f);
  1581. wchar_t buf[100];
  1582. g_settings->setFloat("sound_volume", new_volume);
  1583. const wchar_t *str = wgettext("Volume changed to %d%%");
  1584. swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100));
  1585. delete[] str;
  1586. m_game_ui->showStatusText(buf);
  1587. } else {
  1588. m_game_ui->showTranslatedStatusText("Sound system is disabled");
  1589. }
  1590. } else if (wasKeyDown(KeyType::DEC_VOLUME)) {
  1591. if (g_settings->getBool("enable_sound")) {
  1592. float new_volume = rangelim(g_settings->getFloat("sound_volume") - 0.1f, 0.0f, 1.0f);
  1593. wchar_t buf[100];
  1594. g_settings->setFloat("sound_volume", new_volume);
  1595. const wchar_t *str = wgettext("Volume changed to %d%%");
  1596. swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, myround(new_volume * 100));
  1597. delete[] str;
  1598. m_game_ui->showStatusText(buf);
  1599. } else {
  1600. m_game_ui->showTranslatedStatusText("Sound system is disabled");
  1601. }
  1602. #else
  1603. } else if (wasKeyDown(KeyType::MUTE) || wasKeyDown(KeyType::INC_VOLUME)
  1604. || wasKeyDown(KeyType::DEC_VOLUME)) {
  1605. m_game_ui->showTranslatedStatusText("Sound system is not supported on this build");
  1606. #endif
  1607. } else if (wasKeyDown(KeyType::CINEMATIC)) {
  1608. toggleCinematic();
  1609. } else if (wasKeyDown(KeyType::SCREENSHOT)) {
  1610. client->makeScreenshot();
  1611. } else if (wasKeyDown(KeyType::TOGGLE_HUD)) {
  1612. m_game_ui->toggleHud();
  1613. } else if (wasKeyDown(KeyType::MINIMAP)) {
  1614. toggleMinimap(isKeyDown(KeyType::SNEAK));
  1615. } else if (wasKeyDown(KeyType::TOGGLE_CHAT)) {
  1616. m_game_ui->toggleChat();
  1617. } else if (wasKeyDown(KeyType::TOGGLE_FOG)) {
  1618. toggleFog();
  1619. } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) {
  1620. toggleUpdateCamera();
  1621. } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) {
  1622. toggleDebug();
  1623. } else if (wasKeyDown(KeyType::TOGGLE_PROFILER)) {
  1624. m_game_ui->toggleProfiler();
  1625. } else if (wasKeyDown(KeyType::INCREASE_VIEWING_RANGE)) {
  1626. increaseViewRange();
  1627. } else if (wasKeyDown(KeyType::DECREASE_VIEWING_RANGE)) {
  1628. decreaseViewRange();
  1629. } else if (wasKeyDown(KeyType::RANGESELECT)) {
  1630. toggleFullViewRange();
  1631. } else if (wasKeyDown(KeyType::ZOOM)) {
  1632. checkZoomEnabled();
  1633. } else if (wasKeyDown(KeyType::QUICKTUNE_NEXT)) {
  1634. quicktune->next();
  1635. } else if (wasKeyDown(KeyType::QUICKTUNE_PREV)) {
  1636. quicktune->prev();
  1637. } else if (wasKeyDown(KeyType::QUICKTUNE_INC)) {
  1638. quicktune->inc();
  1639. } else if (wasKeyDown(KeyType::QUICKTUNE_DEC)) {
  1640. quicktune->dec();
  1641. }
  1642. if (!isKeyDown(KeyType::JUMP) && runData.reset_jump_timer) {
  1643. runData.reset_jump_timer = false;
  1644. runData.jump_timer = 0.0f;
  1645. }
  1646. if (quicktune->hasMessage()) {
  1647. m_game_ui->showStatusText(utf8_to_wide(quicktune->getMessage()));
  1648. }
  1649. }
  1650. void Game::processItemSelection(u16 *new_playeritem)
  1651. {
  1652. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1653. /* Item selection using mouse wheel
  1654. */
  1655. *new_playeritem = player->getWieldIndex();
  1656. s32 wheel = input->getMouseWheel();
  1657. u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
  1658. player->hud_hotbar_itemcount - 1);
  1659. s32 dir = wheel;
  1660. if (wasKeyDown(KeyType::HOTBAR_NEXT))
  1661. dir = -1;
  1662. if (wasKeyDown(KeyType::HOTBAR_PREV))
  1663. dir = 1;
  1664. if (dir < 0)
  1665. *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
  1666. else if (dir > 0)
  1667. *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
  1668. // else dir == 0
  1669. /* Item selection using hotbar slot keys
  1670. */
  1671. for (u16 i = 0; i <= max_item; i++) {
  1672. if (wasKeyDown((GameKeyType) (KeyType::SLOT_1 + i))) {
  1673. *new_playeritem = i;
  1674. break;
  1675. }
  1676. }
  1677. }
  1678. void Game::dropSelectedItem(bool single_item)
  1679. {
  1680. IDropAction *a = new IDropAction();
  1681. a->count = single_item ? 1 : 0;
  1682. a->from_inv.setCurrentPlayer();
  1683. a->from_list = "main";
  1684. a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex();
  1685. client->inventoryAction(a);
  1686. }
  1687. void Game::openInventory()
  1688. {
  1689. /*
  1690. * Don't permit to open inventory is CAO or player doesn't exists.
  1691. * This prevent showing an empty inventory at player load
  1692. */
  1693. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1694. if (!player || !player->getCAO())
  1695. return;
  1696. infostream << "Game: Launching inventory" << std::endl;
  1697. PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
  1698. InventoryLocation inventoryloc;
  1699. inventoryloc.setCurrentPlayer();
  1700. if (!client->modsLoaded()
  1701. || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
  1702. TextDest *txt_dst = new TextDestPlayerInventory(client);
  1703. auto *&formspec = m_game_ui->updateFormspec("");
  1704. GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src,
  1705. txt_dst, client->getFormspecPrepend(), sound);
  1706. formspec->setFormSpec(fs_src->getForm(), inventoryloc);
  1707. }
  1708. }
  1709. void Game::openConsole(float scale, const wchar_t *line)
  1710. {
  1711. assert(scale > 0.0f && scale <= 1.0f);
  1712. #ifdef __ANDROID__
  1713. porting::showInputDialog(gettext("ok"), "", "", 2);
  1714. m_android_chat_open = true;
  1715. #else
  1716. if (gui_chat_console->isOpenInhibited())
  1717. return;
  1718. gui_chat_console->openConsole(scale);
  1719. if (line) {
  1720. gui_chat_console->setCloseOnEnter(true);
  1721. gui_chat_console->replaceAndAddToHistory(line);
  1722. }
  1723. #endif
  1724. }
  1725. #ifdef __ANDROID__
  1726. void Game::handleAndroidChatInput()
  1727. {
  1728. if (m_android_chat_open && porting::getInputDialogState() == 0) {
  1729. std::string text = porting::getInputDialogValue();
  1730. client->typeChatMessage(utf8_to_wide(text));
  1731. m_android_chat_open = false;
  1732. }
  1733. }
  1734. #endif
  1735. void Game::toggleFreeMove()
  1736. {
  1737. bool free_move = !g_settings->getBool("free_move");
  1738. g_settings->set("free_move", bool_to_cstr(free_move));
  1739. if (free_move) {
  1740. if (client->checkPrivilege("fly")) {
  1741. m_game_ui->showTranslatedStatusText("Fly mode enabled");
  1742. } else {
  1743. m_game_ui->showTranslatedStatusText("Fly mode enabled (note: no 'fly' privilege)");
  1744. }
  1745. } else {
  1746. m_game_ui->showTranslatedStatusText("Fly mode disabled");
  1747. }
  1748. }
  1749. void Game::toggleFreeMoveAlt()
  1750. {
  1751. if (m_cache_doubletap_jump && runData.jump_timer < 0.2f)
  1752. toggleFreeMove();
  1753. runData.reset_jump_timer = true;
  1754. }
  1755. void Game::togglePitchMove()
  1756. {
  1757. bool pitch_move = !g_settings->getBool("pitch_move");
  1758. g_settings->set("pitch_move", bool_to_cstr(pitch_move));
  1759. if (pitch_move) {
  1760. m_game_ui->showTranslatedStatusText("Pitch move mode enabled");
  1761. } else {
  1762. m_game_ui->showTranslatedStatusText("Pitch move mode disabled");
  1763. }
  1764. }
  1765. void Game::toggleFast()
  1766. {
  1767. bool fast_move = !g_settings->getBool("fast_move");
  1768. bool has_fast_privs = client->checkPrivilege("fast");
  1769. g_settings->set("fast_move", bool_to_cstr(fast_move));
  1770. if (fast_move) {
  1771. if (has_fast_privs) {
  1772. m_game_ui->showTranslatedStatusText("Fast mode enabled");
  1773. } else {
  1774. m_game_ui->showTranslatedStatusText("Fast mode enabled (note: no 'fast' privilege)");
  1775. }
  1776. } else {
  1777. m_game_ui->showTranslatedStatusText("Fast mode disabled");
  1778. }
  1779. #ifdef __ANDROID__
  1780. m_cache_hold_aux1 = fast_move && has_fast_privs;
  1781. #endif
  1782. }
  1783. void Game::toggleNoClip()
  1784. {
  1785. bool noclip = !g_settings->getBool("noclip");
  1786. g_settings->set("noclip", bool_to_cstr(noclip));
  1787. if (noclip) {
  1788. if (client->checkPrivilege("noclip")) {
  1789. m_game_ui->showTranslatedStatusText("Noclip mode enabled");
  1790. } else {
  1791. m_game_ui->showTranslatedStatusText("Noclip mode enabled (note: no 'noclip' privilege)");
  1792. }
  1793. } else {
  1794. m_game_ui->showTranslatedStatusText("Noclip mode disabled");
  1795. }
  1796. }
  1797. void Game::toggleCinematic()
  1798. {
  1799. bool cinematic = !g_settings->getBool("cinematic");
  1800. g_settings->set("cinematic", bool_to_cstr(cinematic));
  1801. if (cinematic)
  1802. m_game_ui->showTranslatedStatusText("Cinematic mode enabled");
  1803. else
  1804. m_game_ui->showTranslatedStatusText("Cinematic mode disabled");
  1805. }
  1806. // Autoforward by toggling continuous forward.
  1807. void Game::toggleAutoforward()
  1808. {
  1809. bool autorun_enabled = !g_settings->getBool("continuous_forward");
  1810. g_settings->set("continuous_forward", bool_to_cstr(autorun_enabled));
  1811. if (autorun_enabled)
  1812. m_game_ui->showTranslatedStatusText("Automatic forward enabled");
  1813. else
  1814. m_game_ui->showTranslatedStatusText("Automatic forward disabled");
  1815. }
  1816. void Game::toggleMinimap(bool shift_pressed)
  1817. {
  1818. if (!mapper || !m_game_ui->m_flags.show_hud || !g_settings->getBool("enable_minimap"))
  1819. return;
  1820. if (shift_pressed)
  1821. mapper->toggleMinimapShape();
  1822. else
  1823. mapper->nextMode();
  1824. // TODO: When legacy minimap is deprecated, keep only HUD minimap stuff here
  1825. // Not so satisying code to keep compatibility with old fixed mode system
  1826. // -->
  1827. u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags;
  1828. if (!(hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) {
  1829. m_game_ui->m_flags.show_minimap = false;
  1830. } else {
  1831. // If radar is disabled, try to find a non radar mode or fall back to 0
  1832. if (!(hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE))
  1833. while (mapper->getModeIndex() &&
  1834. mapper->getModeDef().type == MINIMAP_TYPE_RADAR)
  1835. mapper->nextMode();
  1836. m_game_ui->m_flags.show_minimap = mapper->getModeDef().type !=
  1837. MINIMAP_TYPE_OFF;
  1838. }
  1839. // <--
  1840. // End of 'not so satifying code'
  1841. if ((hud_flags & HUD_FLAG_MINIMAP_VISIBLE) ||
  1842. (hud && hud->hasElementOfType(HUD_ELEM_MINIMAP)))
  1843. m_game_ui->showStatusText(utf8_to_wide(mapper->getModeDef().label));
  1844. else
  1845. m_game_ui->showTranslatedStatusText("Minimap currently disabled by game or mod");
  1846. }
  1847. void Game::toggleFog()
  1848. {
  1849. bool fog_enabled = g_settings->getBool("enable_fog");
  1850. g_settings->setBool("enable_fog", !fog_enabled);
  1851. if (fog_enabled)
  1852. m_game_ui->showTranslatedStatusText("Fog disabled");
  1853. else
  1854. m_game_ui->showTranslatedStatusText("Fog enabled");
  1855. }
  1856. void Game::toggleDebug()
  1857. {
  1858. // Initial / 4x toggle: Chat only
  1859. // 1x toggle: Debug text with chat
  1860. // 2x toggle: Debug text with profiler graph
  1861. // 3x toggle: Debug text and wireframe
  1862. if (!m_game_ui->m_flags.show_debug) {
  1863. m_game_ui->m_flags.show_debug = true;
  1864. m_game_ui->m_flags.show_profiler_graph = false;
  1865. draw_control->show_wireframe = false;
  1866. m_game_ui->showTranslatedStatusText("Debug info shown");
  1867. } else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) {
  1868. m_game_ui->m_flags.show_profiler_graph = true;
  1869. m_game_ui->showTranslatedStatusText("Profiler graph shown");
  1870. } else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) {
  1871. m_game_ui->m_flags.show_profiler_graph = false;
  1872. draw_control->show_wireframe = true;
  1873. m_game_ui->showTranslatedStatusText("Wireframe shown");
  1874. } else {
  1875. m_game_ui->m_flags.show_debug = false;
  1876. m_game_ui->m_flags.show_profiler_graph = false;
  1877. draw_control->show_wireframe = false;
  1878. if (client->checkPrivilege("debug")) {
  1879. m_game_ui->showTranslatedStatusText("Debug info, profiler graph, and wireframe hidden");
  1880. } else {
  1881. m_game_ui->showTranslatedStatusText("Debug info and profiler graph hidden");
  1882. }
  1883. }
  1884. }
  1885. void Game::toggleUpdateCamera()
  1886. {
  1887. m_flags.disable_camera_update = !m_flags.disable_camera_update;
  1888. if (m_flags.disable_camera_update)
  1889. m_game_ui->showTranslatedStatusText("Camera update disabled");
  1890. else
  1891. m_game_ui->showTranslatedStatusText("Camera update enabled");
  1892. }
  1893. void Game::increaseViewRange()
  1894. {
  1895. s16 range = g_settings->getS16("viewing_range");
  1896. s16 range_new = range + 10;
  1897. wchar_t buf[255];
  1898. const wchar_t *str;
  1899. if (range_new > 4000) {
  1900. range_new = 4000;
  1901. str = wgettext("Viewing range is at maximum: %d");
  1902. swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new);
  1903. delete[] str;
  1904. m_game_ui->showStatusText(buf);
  1905. } else {
  1906. str = wgettext("Viewing range changed to %d");
  1907. swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new);
  1908. delete[] str;
  1909. m_game_ui->showStatusText(buf);
  1910. }
  1911. g_settings->set("viewing_range", itos(range_new));
  1912. }
  1913. void Game::decreaseViewRange()
  1914. {
  1915. s16 range = g_settings->getS16("viewing_range");
  1916. s16 range_new = range - 10;
  1917. wchar_t buf[255];
  1918. const wchar_t *str;
  1919. if (range_new < 20) {
  1920. range_new = 20;
  1921. str = wgettext("Viewing range is at minimum: %d");
  1922. swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new);
  1923. delete[] str;
  1924. m_game_ui->showStatusText(buf);
  1925. } else {
  1926. str = wgettext("Viewing range changed to %d");
  1927. swprintf(buf, sizeof(buf) / sizeof(wchar_t), str, range_new);
  1928. delete[] str;
  1929. m_game_ui->showStatusText(buf);
  1930. }
  1931. g_settings->set("viewing_range", itos(range_new));
  1932. }
  1933. void Game::toggleFullViewRange()
  1934. {
  1935. draw_control->range_all = !draw_control->range_all;
  1936. if (draw_control->range_all)
  1937. m_game_ui->showTranslatedStatusText("Enabled unlimited viewing range");
  1938. else
  1939. m_game_ui->showTranslatedStatusText("Disabled unlimited viewing range");
  1940. }
  1941. void Game::checkZoomEnabled()
  1942. {
  1943. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1944. if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f)
  1945. m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod");
  1946. }
  1947. void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
  1948. {
  1949. if ((device->isWindowActive() && device->isWindowFocused()
  1950. && !isMenuActive()) || input->isRandom()) {
  1951. #ifndef __ANDROID__
  1952. if (!input->isRandom()) {
  1953. // Mac OSX gets upset if this is set every frame
  1954. if (device->getCursorControl()->isVisible())
  1955. device->getCursorControl()->setVisible(false);
  1956. }
  1957. #endif
  1958. if (m_first_loop_after_window_activation) {
  1959. m_first_loop_after_window_activation = false;
  1960. input->setMousePos(driver->getScreenSize().Width / 2,
  1961. driver->getScreenSize().Height / 2);
  1962. } else {
  1963. updateCameraOrientation(cam, dtime);
  1964. }
  1965. } else {
  1966. #ifndef ANDROID
  1967. // Mac OSX gets upset if this is set every frame
  1968. if (!device->getCursorControl()->isVisible())
  1969. device->getCursorControl()->setVisible(true);
  1970. #endif
  1971. m_first_loop_after_window_activation = true;
  1972. }
  1973. }
  1974. void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
  1975. {
  1976. #ifdef HAVE_TOUCHSCREENGUI
  1977. if (g_touchscreengui) {
  1978. cam->camera_yaw += g_touchscreengui->getYawChange();
  1979. cam->camera_pitch = g_touchscreengui->getPitch();
  1980. } else {
  1981. #endif
  1982. v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
  1983. v2s32 dist = input->getMousePos() - center;
  1984. if (m_invert_mouse || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
  1985. dist.Y = -dist.Y;
  1986. }
  1987. cam->camera_yaw -= dist.X * m_cache_mouse_sensitivity;
  1988. cam->camera_pitch += dist.Y * m_cache_mouse_sensitivity;
  1989. if (dist.X != 0 || dist.Y != 0)
  1990. input->setMousePos(center.X, center.Y);
  1991. #ifdef HAVE_TOUCHSCREENGUI
  1992. }
  1993. #endif
  1994. if (m_cache_enable_joysticks) {
  1995. f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime;
  1996. cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * c;
  1997. cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * c;
  1998. }
  1999. cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
  2000. }
  2001. void Game::updatePlayerControl(const CameraOrientation &cam)
  2002. {
  2003. //TimeTaker tt("update player control", NULL, PRECISION_NANO);
  2004. // DO NOT use the isKeyDown method for the forward, backward, left, right
  2005. // buttons, as the code that uses the controls needs to be able to
  2006. // distinguish between the two in order to know when to use joysticks.
  2007. PlayerControl control(
  2008. input->isKeyDown(KeyType::FORWARD),
  2009. input->isKeyDown(KeyType::BACKWARD),
  2010. input->isKeyDown(KeyType::LEFT),
  2011. input->isKeyDown(KeyType::RIGHT),
  2012. isKeyDown(KeyType::JUMP),
  2013. isKeyDown(KeyType::SPECIAL1),
  2014. isKeyDown(KeyType::SNEAK),
  2015. isKeyDown(KeyType::ZOOM),
  2016. isKeyDown(KeyType::DIG),
  2017. isKeyDown(KeyType::PLACE),
  2018. cam.camera_pitch,
  2019. cam.camera_yaw,
  2020. input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE),
  2021. input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE)
  2022. );
  2023. u32 keypress_bits = (
  2024. ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) |
  2025. ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) |
  2026. ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) |
  2027. ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) |
  2028. ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) |
  2029. ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) |
  2030. ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) |
  2031. ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) |
  2032. ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) |
  2033. ( (u32)(isKeyDown(KeyType::ZOOM) & 0x1) << 9)
  2034. );
  2035. #ifdef ANDROID
  2036. /* For Android, simulate holding down AUX1 (fast move) if the user has
  2037. * the fast_move setting toggled on. If there is an aux1 key defined for
  2038. * Android then its meaning is inverted (i.e. holding aux1 means walk and
  2039. * not fast)
  2040. */
  2041. if (m_cache_hold_aux1) {
  2042. control.aux1 = control.aux1 ^ true;
  2043. keypress_bits ^= ((u32)(1U << 5));
  2044. }
  2045. #endif
  2046. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2047. // autojump if set: simulate "jump" key
  2048. if (player->getAutojump()) {
  2049. control.jump = true;
  2050. keypress_bits |= 1U << 4;
  2051. }
  2052. // autoforward if set: simulate "up" key
  2053. if (player->getPlayerSettings().continuous_forward &&
  2054. client->activeObjectsReceived() && !player->isDead()) {
  2055. control.up = true;
  2056. keypress_bits |= 1U << 0;
  2057. }
  2058. client->setPlayerControl(control);
  2059. player->keyPressed = keypress_bits;
  2060. //tt.stop();
  2061. }
  2062. inline void Game::step(f32 *dtime)
  2063. {
  2064. bool can_be_and_is_paused =
  2065. (simple_singleplayer_mode && g_menumgr.pausesGame());
  2066. if (can_be_and_is_paused) { // This is for a singleplayer server
  2067. *dtime = 0; // No time passes
  2068. } else {
  2069. if (server)
  2070. server->step(*dtime);
  2071. client->step(*dtime);
  2072. }
  2073. }
  2074. const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = {
  2075. {&Game::handleClientEvent_None},
  2076. {&Game::handleClientEvent_PlayerDamage},
  2077. {&Game::handleClientEvent_PlayerForceMove},
  2078. {&Game::handleClientEvent_Deathscreen},
  2079. {&Game::handleClientEvent_ShowFormSpec},
  2080. {&Game::handleClientEvent_ShowLocalFormSpec},
  2081. {&Game::handleClientEvent_HandleParticleEvent},
  2082. {&Game::handleClientEvent_HandleParticleEvent},
  2083. {&Game::handleClientEvent_HandleParticleEvent},
  2084. {&Game::handleClientEvent_HudAdd},
  2085. {&Game::handleClientEvent_HudRemove},
  2086. {&Game::handleClientEvent_HudChange},
  2087. {&Game::handleClientEvent_SetSky},
  2088. {&Game::handleClientEvent_SetSun},
  2089. {&Game::handleClientEvent_SetMoon},
  2090. {&Game::handleClientEvent_SetStars},
  2091. {&Game::handleClientEvent_OverrideDayNigthRatio},
  2092. {&Game::handleClientEvent_CloudParams},
  2093. };
  2094. void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam)
  2095. {
  2096. FATAL_ERROR("ClientEvent type None received");
  2097. }
  2098. void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam)
  2099. {
  2100. if (client->modsLoaded())
  2101. client->getScript()->on_damage_taken(event->player_damage.amount);
  2102. // Damage flash and hurt tilt are not used at death
  2103. if (client->getHP() > 0) {
  2104. runData.damage_flash += 95.0f + 3.2f * event->player_damage.amount;
  2105. runData.damage_flash = MYMIN(runData.damage_flash, 127.0f);
  2106. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2107. player->hurt_tilt_timer = 1.5f;
  2108. player->hurt_tilt_strength =
  2109. rangelim(event->player_damage.amount / 4.0f, 1.0f, 4.0f);
  2110. }
  2111. // Play damage sound
  2112. client->getEventManager()->put(new SimpleTriggerEvent(MtEvent::PLAYER_DAMAGE));
  2113. }
  2114. void Game::handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam)
  2115. {
  2116. cam->camera_yaw = event->player_force_move.yaw;
  2117. cam->camera_pitch = event->player_force_move.pitch;
  2118. }
  2119. void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam)
  2120. {
  2121. // If client scripting is enabled, deathscreen is handled by CSM code in
  2122. // builtin/client/init.lua
  2123. if (client->modsLoaded())
  2124. client->getScript()->on_death();
  2125. else
  2126. showDeathFormspec();
  2127. /* Handle visualization */
  2128. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2129. runData.damage_flash = 0;
  2130. player->hurt_tilt_timer = 0;
  2131. player->hurt_tilt_strength = 0;
  2132. }
  2133. void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam)
  2134. {
  2135. if (event->show_formspec.formspec->empty()) {
  2136. auto formspec = m_game_ui->getFormspecGUI();
  2137. if (formspec && (event->show_formspec.formname->empty()
  2138. || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) {
  2139. formspec->quitMenu();
  2140. }
  2141. } else {
  2142. FormspecFormSource *fs_src =
  2143. new FormspecFormSource(*(event->show_formspec.formspec));
  2144. TextDestPlayerInventory *txt_dst =
  2145. new TextDestPlayerInventory(client, *(event->show_formspec.formname));
  2146. auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname));
  2147. GUIFormSpecMenu::create(formspec, client, &input->joystick,
  2148. fs_src, txt_dst, client->getFormspecPrepend(), sound);
  2149. }
  2150. delete event->show_formspec.formspec;
  2151. delete event->show_formspec.formname;
  2152. }
  2153. void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam)
  2154. {
  2155. FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
  2156. LocalFormspecHandler *txt_dst =
  2157. new LocalFormspecHandler(*event->show_formspec.formname, client);
  2158. GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick,
  2159. fs_src, txt_dst, client->getFormspecPrepend(), sound);
  2160. delete event->show_formspec.formspec;
  2161. delete event->show_formspec.formname;
  2162. }
  2163. void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event,
  2164. CameraOrientation *cam)
  2165. {
  2166. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2167. client->getParticleManager()->handleParticleEvent(event, client, player);
  2168. }
  2169. void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam)
  2170. {
  2171. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2172. auto &hud_server_to_client = client->getHUDTranslationMap();
  2173. u32 server_id = event->hudadd.server_id;
  2174. // ignore if we already have a HUD with that ID
  2175. auto i = hud_server_to_client.find(server_id);
  2176. if (i != hud_server_to_client.end()) {
  2177. delete event->hudadd.pos;
  2178. delete event->hudadd.name;
  2179. delete event->hudadd.scale;
  2180. delete event->hudadd.text;
  2181. delete event->hudadd.align;
  2182. delete event->hudadd.offset;
  2183. delete event->hudadd.world_pos;
  2184. delete event->hudadd.size;
  2185. delete event->hudadd.text2;
  2186. return;
  2187. }
  2188. HudElement *e = new HudElement;
  2189. e->type = (HudElementType)event->hudadd.type;
  2190. e->pos = *event->hudadd.pos;
  2191. e->name = *event->hudadd.name;
  2192. e->scale = *event->hudadd.scale;
  2193. e->text = *event->hudadd.text;
  2194. e->number = event->hudadd.number;
  2195. e->item = event->hudadd.item;
  2196. e->dir = event->hudadd.dir;
  2197. e->align = *event->hudadd.align;
  2198. e->offset = *event->hudadd.offset;
  2199. e->world_pos = *event->hudadd.world_pos;
  2200. e->size = *event->hudadd.size;
  2201. e->z_index = event->hudadd.z_index;
  2202. e->text2 = *event->hudadd.text2;
  2203. hud_server_to_client[server_id] = player->addHud(e);
  2204. delete event->hudadd.pos;
  2205. delete event->hudadd.name;
  2206. delete event->hudadd.scale;
  2207. delete event->hudadd.text;
  2208. delete event->hudadd.align;
  2209. delete event->hudadd.offset;
  2210. delete event->hudadd.world_pos;
  2211. delete event->hudadd.size;
  2212. delete event->hudadd.text2;
  2213. }
  2214. void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam)
  2215. {
  2216. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2217. HudElement *e = player->removeHud(event->hudrm.id);
  2218. delete e;
  2219. }
  2220. void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam)
  2221. {
  2222. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2223. u32 id = event->hudchange.id;
  2224. HudElement *e = player->getHud(id);
  2225. if (e == NULL) {
  2226. delete event->hudchange.v3fdata;
  2227. delete event->hudchange.v2fdata;
  2228. delete event->hudchange.sdata;
  2229. delete event->hudchange.v2s32data;
  2230. return;
  2231. }
  2232. switch (event->hudchange.stat) {
  2233. case HUD_STAT_POS:
  2234. e->pos = *event->hudchange.v2fdata;
  2235. break;
  2236. case HUD_STAT_NAME:
  2237. e->name = *event->hudchange.sdata;
  2238. break;
  2239. case HUD_STAT_SCALE:
  2240. e->scale = *event->hudchange.v2fdata;
  2241. break;
  2242. case HUD_STAT_TEXT:
  2243. e->text = *event->hudchange.sdata;
  2244. break;
  2245. case HUD_STAT_NUMBER:
  2246. e->number = event->hudchange.data;
  2247. break;
  2248. case HUD_STAT_ITEM:
  2249. e->item = event->hudchange.data;
  2250. break;
  2251. case HUD_STAT_DIR:
  2252. e->dir = event->hudchange.data;
  2253. break;
  2254. case HUD_STAT_ALIGN:
  2255. e->align = *event->hudchange.v2fdata;
  2256. break;
  2257. case HUD_STAT_OFFSET:
  2258. e->offset = *event->hudchange.v2fdata;
  2259. break;
  2260. case HUD_STAT_WORLD_POS:
  2261. e->world_pos = *event->hudchange.v3fdata;
  2262. break;
  2263. case HUD_STAT_SIZE:
  2264. e->size = *event->hudchange.v2s32data;
  2265. break;
  2266. case HUD_STAT_Z_INDEX:
  2267. e->z_index = event->hudchange.data;
  2268. break;
  2269. case HUD_STAT_TEXT2:
  2270. e->text2 = *event->hudchange.sdata;
  2271. break;
  2272. }
  2273. delete event->hudchange.v3fdata;
  2274. delete event->hudchange.v2fdata;
  2275. delete event->hudchange.sdata;
  2276. delete event->hudchange.v2s32data;
  2277. }
  2278. void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
  2279. {
  2280. sky->setVisible(false);
  2281. // Whether clouds are visible in front of a custom skybox.
  2282. sky->setCloudsEnabled(event->set_sky->clouds);
  2283. if (skybox) {
  2284. skybox->remove();
  2285. skybox = NULL;
  2286. }
  2287. // Clear the old textures out in case we switch rendering type.
  2288. sky->clearSkyboxTextures();
  2289. // Handle according to type
  2290. if (event->set_sky->type == "regular") {
  2291. // Shows the mesh skybox
  2292. sky->setVisible(true);
  2293. // Update mesh based skybox colours if applicable.
  2294. sky->setSkyColors(event->set_sky->sky_color);
  2295. sky->setHorizonTint(
  2296. event->set_sky->fog_sun_tint,
  2297. event->set_sky->fog_moon_tint,
  2298. event->set_sky->fog_tint_type
  2299. );
  2300. } else if (event->set_sky->type == "skybox" &&
  2301. event->set_sky->textures.size() == 6) {
  2302. // Disable the dyanmic mesh skybox:
  2303. sky->setVisible(false);
  2304. // Set fog colors:
  2305. sky->setFallbackBgColor(event->set_sky->bgcolor);
  2306. // Set sunrise and sunset fog tinting:
  2307. sky->setHorizonTint(
  2308. event->set_sky->fog_sun_tint,
  2309. event->set_sky->fog_moon_tint,
  2310. event->set_sky->fog_tint_type
  2311. );
  2312. // Add textures to skybox.
  2313. for (int i = 0; i < 6; i++)
  2314. sky->addTextureToSkybox(event->set_sky->textures[i], i, texture_src);
  2315. } else {
  2316. // Handle everything else as plain color.
  2317. if (event->set_sky->type != "plain")
  2318. infostream << "Unknown sky type: "
  2319. << (event->set_sky->type) << std::endl;
  2320. sky->setVisible(false);
  2321. sky->setFallbackBgColor(event->set_sky->bgcolor);
  2322. // Disable directional sun/moon tinting on plain or invalid skyboxes.
  2323. sky->setHorizonTint(
  2324. event->set_sky->bgcolor,
  2325. event->set_sky->bgcolor,
  2326. "custom"
  2327. );
  2328. }
  2329. delete event->set_sky;
  2330. }
  2331. void Game::handleClientEvent_SetSun(ClientEvent *event, CameraOrientation *cam)
  2332. {
  2333. sky->setSunVisible(event->sun_params->visible);
  2334. sky->setSunTexture(event->sun_params->texture,
  2335. event->sun_params->tonemap, texture_src);
  2336. sky->setSunScale(event->sun_params->scale);
  2337. sky->setSunriseVisible(event->sun_params->sunrise_visible);
  2338. sky->setSunriseTexture(event->sun_params->sunrise, texture_src);
  2339. delete event->sun_params;
  2340. }
  2341. void Game::handleClientEvent_SetMoon(ClientEvent *event, CameraOrientation *cam)
  2342. {
  2343. sky->setMoonVisible(event->moon_params->visible);
  2344. sky->setMoonTexture(event->moon_params->texture,
  2345. event->moon_params->tonemap, texture_src);
  2346. sky->setMoonScale(event->moon_params->scale);
  2347. delete event->moon_params;
  2348. }
  2349. void Game::handleClientEvent_SetStars(ClientEvent *event, CameraOrientation *cam)
  2350. {
  2351. sky->setStarsVisible(event->star_params->visible);
  2352. sky->setStarCount(event->star_params->count, false);
  2353. sky->setStarColor(event->star_params->starcolor);
  2354. sky->setStarScale(event->star_params->scale);
  2355. delete event->star_params;
  2356. }
  2357. void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event,
  2358. CameraOrientation *cam)
  2359. {
  2360. client->getEnv().setDayNightRatioOverride(
  2361. event->override_day_night_ratio.do_override,
  2362. event->override_day_night_ratio.ratio_f * 1000.0f);
  2363. }
  2364. void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam)
  2365. {
  2366. if (!clouds)
  2367. return;
  2368. clouds->setDensity(event->cloud_params.density);
  2369. clouds->setColorBright(video::SColor(event->cloud_params.color_bright));
  2370. clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient));
  2371. clouds->setHeight(event->cloud_params.height);
  2372. clouds->setThickness(event->cloud_params.thickness);
  2373. clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y));
  2374. }
  2375. void Game::processClientEvents(CameraOrientation *cam)
  2376. {
  2377. while (client->hasClientEvents()) {
  2378. std::unique_ptr<ClientEvent> event(client->getClientEvent());
  2379. FATAL_ERROR_IF(event->type >= CLIENTEVENT_MAX, "Invalid clientevent type");
  2380. const ClientEventHandler& evHandler = clientEventHandler[event->type];
  2381. (this->*evHandler.handler)(event.get(), cam);
  2382. }
  2383. }
  2384. void Game::updateChat(f32 dtime, const v2u32 &screensize)
  2385. {
  2386. // Get new messages from error log buffer
  2387. while (!m_chat_log_buf.empty())
  2388. chat_backend->addMessage(L"", utf8_to_wide(m_chat_log_buf.get()));
  2389. // Get new messages from client
  2390. std::wstring message;
  2391. while (client->getChatMessage(message)) {
  2392. chat_backend->addUnparsedMessage(message);
  2393. }
  2394. // Remove old messages
  2395. chat_backend->step(dtime);
  2396. // Display all messages in a static text element
  2397. m_game_ui->setChatText(chat_backend->getRecentChat(),
  2398. chat_backend->getRecentBuffer().getLineCount());
  2399. }
  2400. void Game::updateCamera(u32 busy_time, f32 dtime)
  2401. {
  2402. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2403. /*
  2404. For interaction purposes, get info about the held item
  2405. - What item is it?
  2406. - Is it a usable item?
  2407. - Can it point to liquids?
  2408. */
  2409. ItemStack playeritem;
  2410. {
  2411. ItemStack selected, hand;
  2412. playeritem = player->getWieldedItem(&selected, &hand);
  2413. }
  2414. ToolCapabilities playeritem_toolcap =
  2415. playeritem.getToolCapabilities(itemdef_manager);
  2416. v3s16 old_camera_offset = camera->getOffset();
  2417. if (wasKeyDown(KeyType::CAMERA_MODE)) {
  2418. GenericCAO *playercao = player->getCAO();
  2419. // If playercao not loaded, don't change camera
  2420. if (!playercao)
  2421. return;
  2422. camera->toggleCameraMode();
  2423. // Make the player visible depending on camera mode.
  2424. playercao->updateMeshCulling();
  2425. playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
  2426. }
  2427. float full_punch_interval = playeritem_toolcap.full_punch_interval;
  2428. float tool_reload_ratio = runData.time_from_last_punch / full_punch_interval;
  2429. tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
  2430. camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio);
  2431. camera->step(dtime);
  2432. v3f camera_position = camera->getPosition();
  2433. v3f camera_direction = camera->getDirection();
  2434. f32 camera_fov = camera->getFovMax();
  2435. v3s16 camera_offset = camera->getOffset();
  2436. m_camera_offset_changed = (camera_offset != old_camera_offset);
  2437. if (!m_flags.disable_camera_update) {
  2438. client->getEnv().getClientMap().updateCamera(camera_position,
  2439. camera_direction, camera_fov, camera_offset);
  2440. if (m_camera_offset_changed) {
  2441. client->updateCameraOffset(camera_offset);
  2442. client->getEnv().updateCameraOffset(camera_offset);
  2443. if (clouds)
  2444. clouds->updateCameraOffset(camera_offset);
  2445. }
  2446. }
  2447. }
  2448. void Game::updateSound(f32 dtime)
  2449. {
  2450. // Update sound listener
  2451. v3s16 camera_offset = camera->getOffset();
  2452. sound->updateListener(camera->getCameraNode()->getPosition() + intToFloat(camera_offset, BS),
  2453. v3f(0, 0, 0), // velocity
  2454. camera->getDirection(),
  2455. camera->getCameraNode()->getUpVector());
  2456. bool mute_sound = g_settings->getBool("mute_sound");
  2457. if (mute_sound) {
  2458. sound->setListenerGain(0.0f);
  2459. } else {
  2460. // Check if volume is in the proper range, else fix it.
  2461. float old_volume = g_settings->getFloat("sound_volume");
  2462. float new_volume = rangelim(old_volume, 0.0f, 1.0f);
  2463. sound->setListenerGain(new_volume);
  2464. if (old_volume != new_volume) {
  2465. g_settings->setFloat("sound_volume", new_volume);
  2466. }
  2467. }
  2468. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2469. // Tell the sound maker whether to make footstep sounds
  2470. soundmaker->makes_footstep_sound = player->makes_footstep_sound;
  2471. // Update sound maker
  2472. if (player->makes_footstep_sound)
  2473. soundmaker->step(dtime);
  2474. ClientMap &map = client->getEnv().getClientMap();
  2475. MapNode n = map.getNode(player->getFootstepNodePos());
  2476. soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
  2477. }
  2478. void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
  2479. {
  2480. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2481. const v3f camera_direction = camera->getDirection();
  2482. const v3s16 camera_offset = camera->getOffset();
  2483. /*
  2484. Calculate what block is the crosshair pointing to
  2485. */
  2486. ItemStack selected_item, hand_item;
  2487. const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
  2488. const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager);
  2489. f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager));
  2490. core::line3d<f32> shootline;
  2491. switch (camera->getCameraMode()) {
  2492. case CAMERA_MODE_FIRST:
  2493. // Shoot from camera position, with bobbing
  2494. shootline.start = camera->getPosition();
  2495. break;
  2496. case CAMERA_MODE_THIRD:
  2497. // Shoot from player head, no bobbing
  2498. shootline.start = camera->getHeadPosition();
  2499. break;
  2500. case CAMERA_MODE_THIRD_FRONT:
  2501. shootline.start = camera->getHeadPosition();
  2502. // prevent player pointing anything in front-view
  2503. d = 0;
  2504. break;
  2505. }
  2506. shootline.end = shootline.start + camera_direction * BS * d;
  2507. #ifdef HAVE_TOUCHSCREENGUI
  2508. if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
  2509. shootline = g_touchscreengui->getShootline();
  2510. // Scale shootline to the acual distance the player can reach
  2511. shootline.end = shootline.start
  2512. + shootline.getVector().normalize() * BS * d;
  2513. shootline.start += intToFloat(camera_offset, BS);
  2514. shootline.end += intToFloat(camera_offset, BS);
  2515. }
  2516. #endif
  2517. PointedThing pointed = updatePointedThing(shootline,
  2518. selected_def.liquids_pointable,
  2519. !runData.btn_down_for_dig,
  2520. camera_offset);
  2521. if (pointed != runData.pointed_old) {
  2522. infostream << "Pointing at " << pointed.dump() << std::endl;
  2523. hud->updateSelectionMesh(camera_offset);
  2524. }
  2525. // Allow digging again if button is not pressed
  2526. if (runData.digging_blocked && !isKeyDown(KeyType::DIG))
  2527. runData.digging_blocked = false;
  2528. /*
  2529. Stop digging when
  2530. - releasing dig button
  2531. - pointing away from node
  2532. */
  2533. if (runData.digging) {
  2534. if (wasKeyReleased(KeyType::DIG)) {
  2535. infostream << "Dig button released (stopped digging)" << std::endl;
  2536. runData.digging = false;
  2537. } else if (pointed != runData.pointed_old) {
  2538. if (pointed.type == POINTEDTHING_NODE
  2539. && runData.pointed_old.type == POINTEDTHING_NODE
  2540. && pointed.node_undersurface
  2541. == runData.pointed_old.node_undersurface) {
  2542. // Still pointing to the same node, but a different face.
  2543. // Don't reset.
  2544. } else {
  2545. infostream << "Pointing away from node (stopped digging)" << std::endl;
  2546. runData.digging = false;
  2547. hud->updateSelectionMesh(camera_offset);
  2548. }
  2549. }
  2550. if (!runData.digging) {
  2551. client->interact(INTERACT_STOP_DIGGING, runData.pointed_old);
  2552. client->setCrack(-1, v3s16(0, 0, 0));
  2553. runData.dig_time = 0.0;
  2554. }
  2555. } else if (runData.dig_instantly && wasKeyReleased(KeyType::DIG)) {
  2556. // Remove e.g. torches faster when clicking instead of holding dig button
  2557. runData.nodig_delay_timer = 0;
  2558. runData.dig_instantly = false;
  2559. }
  2560. if (!runData.digging && runData.btn_down_for_dig && !isKeyDown(KeyType::DIG))
  2561. runData.btn_down_for_dig = false;
  2562. runData.punching = false;
  2563. soundmaker->m_player_leftpunch_sound.name = "";
  2564. // Prepare for repeating, unless we're not supposed to
  2565. if (isKeyDown(KeyType::PLACE) && !g_settings->getBool("safe_dig_and_place"))
  2566. runData.repeat_place_timer += dtime;
  2567. else
  2568. runData.repeat_place_timer = 0;
  2569. if (selected_def.usable && isKeyDown(KeyType::DIG)) {
  2570. if (wasKeyPressed(KeyType::DIG) && (!client->modsLoaded() ||
  2571. !client->getScript()->on_item_use(selected_item, pointed)))
  2572. client->interact(INTERACT_USE, pointed);
  2573. } else if (pointed.type == POINTEDTHING_NODE) {
  2574. handlePointingAtNode(pointed, selected_item, hand_item, dtime);
  2575. } else if (pointed.type == POINTEDTHING_OBJECT) {
  2576. v3f player_position = player->getPosition();
  2577. handlePointingAtObject(pointed, tool_item, player_position, show_debug);
  2578. } else if (isKeyDown(KeyType::DIG)) {
  2579. // When button is held down in air, show continuous animation
  2580. runData.punching = true;
  2581. // Run callback even though item is not usable
  2582. if (wasKeyPressed(KeyType::DIG) && client->modsLoaded())
  2583. client->getScript()->on_item_use(selected_item, pointed);
  2584. } else if (wasKeyPressed(KeyType::PLACE)) {
  2585. handlePointingAtNothing(selected_item);
  2586. }
  2587. runData.pointed_old = pointed;
  2588. if (runData.punching || wasKeyPressed(KeyType::DIG))
  2589. camera->setDigging(0); // dig animation
  2590. input->clearWasKeyPressed();
  2591. input->clearWasKeyReleased();
  2592. // Ensure DIG & PLACE are marked as handled
  2593. wasKeyDown(KeyType::DIG);
  2594. wasKeyDown(KeyType::PLACE);
  2595. input->joystick.clearWasKeyPressed(KeyType::DIG);
  2596. input->joystick.clearWasKeyPressed(KeyType::PLACE);
  2597. input->joystick.clearWasKeyReleased(KeyType::DIG);
  2598. input->joystick.clearWasKeyReleased(KeyType::PLACE);
  2599. }
  2600. PointedThing Game::updatePointedThing(
  2601. const core::line3d<f32> &shootline,
  2602. bool liquids_pointable,
  2603. bool look_for_object,
  2604. const v3s16 &camera_offset)
  2605. {
  2606. std::vector<aabb3f> *selectionboxes = hud->getSelectionBoxes();
  2607. selectionboxes->clear();
  2608. hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0));
  2609. static thread_local const bool show_entity_selectionbox = g_settings->getBool(
  2610. "show_entity_selectionbox");
  2611. ClientEnvironment &env = client->getEnv();
  2612. ClientMap &map = env.getClientMap();
  2613. const NodeDefManager *nodedef = map.getNodeDefManager();
  2614. runData.selected_object = NULL;
  2615. hud->pointing_at_object = false;
  2616. RaycastState s(shootline, look_for_object, liquids_pointable);
  2617. PointedThing result;
  2618. env.continueRaycast(&s, &result);
  2619. if (result.type == POINTEDTHING_OBJECT) {
  2620. hud->pointing_at_object = true;
  2621. runData.selected_object = client->getEnv().getActiveObject(result.object_id);
  2622. aabb3f selection_box;
  2623. if (show_entity_selectionbox && runData.selected_object->doShowSelectionBox() &&
  2624. runData.selected_object->getSelectionBox(&selection_box)) {
  2625. v3f pos = runData.selected_object->getPosition();
  2626. selectionboxes->push_back(aabb3f(selection_box));
  2627. hud->setSelectionPos(pos, camera_offset);
  2628. }
  2629. } else if (result.type == POINTEDTHING_NODE) {
  2630. // Update selection boxes
  2631. MapNode n = map.getNode(result.node_undersurface);
  2632. std::vector<aabb3f> boxes;
  2633. n.getSelectionBoxes(nodedef, &boxes,
  2634. n.getNeighbors(result.node_undersurface, &map));
  2635. f32 d = 0.002 * BS;
  2636. for (std::vector<aabb3f>::const_iterator i = boxes.begin();
  2637. i != boxes.end(); ++i) {
  2638. aabb3f box = *i;
  2639. box.MinEdge -= v3f(d, d, d);
  2640. box.MaxEdge += v3f(d, d, d);
  2641. selectionboxes->push_back(box);
  2642. }
  2643. hud->setSelectionPos(intToFloat(result.node_undersurface, BS),
  2644. camera_offset);
  2645. hud->setSelectedFaceNormal(v3f(
  2646. result.intersection_normal.X,
  2647. result.intersection_normal.Y,
  2648. result.intersection_normal.Z));
  2649. }
  2650. // Update selection mesh light level and vertex colors
  2651. if (!selectionboxes->empty()) {
  2652. v3f pf = hud->getSelectionPos();
  2653. v3s16 p = floatToInt(pf, BS);
  2654. // Get selection mesh light level
  2655. MapNode n = map.getNode(p);
  2656. u16 node_light = getInteriorLight(n, -1, nodedef);
  2657. u16 light_level = node_light;
  2658. for (const v3s16 &dir : g_6dirs) {
  2659. n = map.getNode(p + dir);
  2660. node_light = getInteriorLight(n, -1, nodedef);
  2661. if (node_light > light_level)
  2662. light_level = node_light;
  2663. }
  2664. u32 daynight_ratio = client->getEnv().getDayNightRatio();
  2665. video::SColor c;
  2666. final_color_blend(&c, light_level, daynight_ratio);
  2667. // Modify final color a bit with time
  2668. u32 timer = porting::getTimeMs() % 5000;
  2669. float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5));
  2670. float sin_r = 0.08f * std::sin(timerf);
  2671. float sin_g = 0.08f * std::sin(timerf + irr::core::PI * 0.5f);
  2672. float sin_b = 0.08f * std::sin(timerf + irr::core::PI);
  2673. c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255));
  2674. c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255));
  2675. c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255));
  2676. // Set mesh final color
  2677. hud->setSelectionMeshColor(c);
  2678. }
  2679. return result;
  2680. }
  2681. void Game::handlePointingAtNothing(const ItemStack &playerItem)
  2682. {
  2683. infostream << "Attempted to place item while pointing at nothing" << std::endl;
  2684. PointedThing fauxPointed;
  2685. fauxPointed.type = POINTEDTHING_NOTHING;
  2686. client->interact(INTERACT_ACTIVATE, fauxPointed);
  2687. }
  2688. void Game::handlePointingAtNode(const PointedThing &pointed,
  2689. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
  2690. {
  2691. v3s16 nodepos = pointed.node_undersurface;
  2692. v3s16 neighbourpos = pointed.node_abovesurface;
  2693. /*
  2694. Check information text of node
  2695. */
  2696. ClientMap &map = client->getEnv().getClientMap();
  2697. if (runData.nodig_delay_timer <= 0.0 && isKeyDown(KeyType::DIG)
  2698. && !runData.digging_blocked
  2699. && client->checkPrivilege("interact")) {
  2700. handleDigging(pointed, nodepos, selected_item, hand_item, dtime);
  2701. }
  2702. // This should be done after digging handling
  2703. NodeMetadata *meta = map.getNodeMetadata(nodepos);
  2704. if (meta) {
  2705. m_game_ui->setInfoText(unescape_translate(utf8_to_wide(
  2706. meta->getString("infotext"))));
  2707. } else {
  2708. MapNode n = map.getNode(nodepos);
  2709. if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
  2710. m_game_ui->setInfoText(L"Unknown node: " +
  2711. utf8_to_wide(nodedef_manager->get(n).name));
  2712. }
  2713. }
  2714. if ((wasKeyPressed(KeyType::PLACE) ||
  2715. runData.repeat_place_timer >= m_repeat_place_time) &&
  2716. client->checkPrivilege("interact")) {
  2717. runData.repeat_place_timer = 0;
  2718. infostream << "Place button pressed while looking at ground" << std::endl;
  2719. // Placing animation (always shown for feedback)
  2720. camera->setDigging(1);
  2721. soundmaker->m_player_rightpunch_sound = SimpleSoundSpec();
  2722. // If the wielded item has node placement prediction,
  2723. // make that happen
  2724. // And also set the sound and send the interact
  2725. // But first check for meta formspec and rightclickable
  2726. auto &def = selected_item.getDefinition(itemdef_manager);
  2727. bool placed = nodePlacement(def, selected_item, nodepos, neighbourpos,
  2728. pointed, meta);
  2729. if (placed && client->modsLoaded())
  2730. client->getScript()->on_placenode(pointed, def);
  2731. }
  2732. }
  2733. bool Game::nodePlacement(const ItemDefinition &selected_def,
  2734. const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos,
  2735. const PointedThing &pointed, const NodeMetadata *meta)
  2736. {
  2737. std::string prediction = selected_def.node_placement_prediction;
  2738. const NodeDefManager *nodedef = client->ndef();
  2739. ClientMap &map = client->getEnv().getClientMap();
  2740. MapNode node;
  2741. bool is_valid_position;
  2742. node = map.getNode(nodepos, &is_valid_position);
  2743. if (!is_valid_position) {
  2744. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2745. return false;
  2746. }
  2747. // formspec in meta
  2748. if (meta && !meta->getString("formspec").empty() && !input->isRandom()
  2749. && !isKeyDown(KeyType::SNEAK)) {
  2750. // on_rightclick callbacks are called anyway
  2751. if (nodedef_manager->get(map.getNode(nodepos)).rightclickable)
  2752. client->interact(INTERACT_PLACE, pointed);
  2753. infostream << "Launching custom inventory view" << std::endl;
  2754. InventoryLocation inventoryloc;
  2755. inventoryloc.setNodeMeta(nodepos);
  2756. NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
  2757. &client->getEnv().getClientMap(), nodepos);
  2758. TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
  2759. auto *&formspec = m_game_ui->updateFormspec("");
  2760. GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src,
  2761. txt_dst, client->getFormspecPrepend(), sound);
  2762. formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
  2763. return false;
  2764. }
  2765. // on_rightclick callback
  2766. if (prediction.empty() || (nodedef->get(node).rightclickable &&
  2767. !isKeyDown(KeyType::SNEAK))) {
  2768. // Report to server
  2769. client->interact(INTERACT_PLACE, pointed);
  2770. return false;
  2771. }
  2772. verbosestream << "Node placement prediction for "
  2773. << selected_def.name << " is " << prediction << std::endl;
  2774. v3s16 p = neighbourpos;
  2775. // Place inside node itself if buildable_to
  2776. MapNode n_under = map.getNode(nodepos, &is_valid_position);
  2777. if (is_valid_position) {
  2778. if (nodedef->get(n_under).buildable_to) {
  2779. p = nodepos;
  2780. } else {
  2781. node = map.getNode(p, &is_valid_position);
  2782. if (is_valid_position && !nodedef->get(node).buildable_to) {
  2783. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2784. // Report to server
  2785. client->interact(INTERACT_PLACE, pointed);
  2786. return false;
  2787. }
  2788. }
  2789. }
  2790. // Find id of predicted node
  2791. content_t id;
  2792. bool found = nodedef->getId(prediction, id);
  2793. if (!found) {
  2794. errorstream << "Node placement prediction failed for "
  2795. << selected_def.name << " (places "
  2796. << prediction
  2797. << ") - Name not known" << std::endl;
  2798. // Handle this as if prediction was empty
  2799. // Report to server
  2800. client->interact(INTERACT_PLACE, pointed);
  2801. return false;
  2802. }
  2803. const ContentFeatures &predicted_f = nodedef->get(id);
  2804. // Predict param2 for facedir and wallmounted nodes
  2805. u8 param2 = 0;
  2806. if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
  2807. predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
  2808. v3s16 dir = nodepos - neighbourpos;
  2809. if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
  2810. param2 = dir.Y < 0 ? 1 : 0;
  2811. } else if (abs(dir.X) > abs(dir.Z)) {
  2812. param2 = dir.X < 0 ? 3 : 2;
  2813. } else {
  2814. param2 = dir.Z < 0 ? 5 : 4;
  2815. }
  2816. }
  2817. if (predicted_f.param_type_2 == CPT2_FACEDIR ||
  2818. predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
  2819. v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS);
  2820. if (abs(dir.X) > abs(dir.Z)) {
  2821. param2 = dir.X < 0 ? 3 : 1;
  2822. } else {
  2823. param2 = dir.Z < 0 ? 2 : 0;
  2824. }
  2825. }
  2826. assert(param2 <= 5);
  2827. //Check attachment if node is in group attached_node
  2828. if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) {
  2829. static v3s16 wallmounted_dirs[8] = {
  2830. v3s16(0, 1, 0),
  2831. v3s16(0, -1, 0),
  2832. v3s16(1, 0, 0),
  2833. v3s16(-1, 0, 0),
  2834. v3s16(0, 0, 1),
  2835. v3s16(0, 0, -1),
  2836. };
  2837. v3s16 pp;
  2838. if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
  2839. predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
  2840. pp = p + wallmounted_dirs[param2];
  2841. else
  2842. pp = p + v3s16(0, -1, 0);
  2843. if (!nodedef->get(map.getNode(pp)).walkable) {
  2844. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2845. // Report to server
  2846. client->interact(INTERACT_PLACE, pointed);
  2847. return false;
  2848. }
  2849. }
  2850. // Apply color
  2851. if ((predicted_f.param_type_2 == CPT2_COLOR
  2852. || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
  2853. || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
  2854. const std::string &indexstr = selected_item.metadata.getString(
  2855. "palette_index", 0);
  2856. if (!indexstr.empty()) {
  2857. s32 index = mystoi(indexstr);
  2858. if (predicted_f.param_type_2 == CPT2_COLOR) {
  2859. param2 = index;
  2860. } else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
  2861. // param2 = pure palette index + other
  2862. param2 = (index & 0xf8) | (param2 & 0x07);
  2863. } else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
  2864. // param2 = pure palette index + other
  2865. param2 = (index & 0xe0) | (param2 & 0x1f);
  2866. }
  2867. }
  2868. }
  2869. // Add node to client map
  2870. MapNode n(id, 0, param2);
  2871. try {
  2872. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2873. // Dont place node when player would be inside new node
  2874. // NOTE: This is to be eventually implemented by a mod as client-side Lua
  2875. if (!nodedef->get(n).walkable ||
  2876. g_settings->getBool("enable_build_where_you_stand") ||
  2877. (client->checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
  2878. (nodedef->get(n).walkable &&
  2879. neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
  2880. neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
  2881. // This triggers the required mesh update too
  2882. client->addNode(p, n);
  2883. // Report to server
  2884. client->interact(INTERACT_PLACE, pointed);
  2885. // A node is predicted, also play a sound
  2886. soundmaker->m_player_rightpunch_sound = selected_def.sound_place;
  2887. return true;
  2888. } else {
  2889. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2890. return false;
  2891. }
  2892. } catch (InvalidPositionException &e) {
  2893. errorstream << "Node placement prediction failed for "
  2894. << selected_def.name << " (places "
  2895. << prediction
  2896. << ") - Position not loaded" << std::endl;
  2897. soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed;
  2898. return false;
  2899. }
  2900. }
  2901. void Game::handlePointingAtObject(const PointedThing &pointed,
  2902. const ItemStack &tool_item, const v3f &player_position, bool show_debug)
  2903. {
  2904. std::wstring infotext = unescape_translate(
  2905. utf8_to_wide(runData.selected_object->infoText()));
  2906. if (show_debug) {
  2907. if (!infotext.empty()) {
  2908. infotext += L"\n";
  2909. }
  2910. infotext += utf8_to_wide(runData.selected_object->debugInfoText());
  2911. }
  2912. m_game_ui->setInfoText(infotext);
  2913. if (isKeyDown(KeyType::DIG)) {
  2914. bool do_punch = false;
  2915. bool do_punch_damage = false;
  2916. if (runData.object_hit_delay_timer <= 0.0) {
  2917. do_punch = true;
  2918. do_punch_damage = true;
  2919. runData.object_hit_delay_timer = object_hit_delay;
  2920. }
  2921. if (wasKeyPressed(KeyType::DIG))
  2922. do_punch = true;
  2923. if (do_punch) {
  2924. infostream << "Punched object" << std::endl;
  2925. runData.punching = true;
  2926. }
  2927. if (do_punch_damage) {
  2928. // Report direct punch
  2929. v3f objpos = runData.selected_object->getPosition();
  2930. v3f dir = (objpos - player_position).normalize();
  2931. bool disable_send = runData.selected_object->directReportPunch(
  2932. dir, &tool_item, runData.time_from_last_punch);
  2933. runData.time_from_last_punch = 0;
  2934. if (!disable_send)
  2935. client->interact(INTERACT_START_DIGGING, pointed);
  2936. }
  2937. } else if (wasKeyDown(KeyType::PLACE)) {
  2938. infostream << "Pressed place button while pointing at object" << std::endl;
  2939. client->interact(INTERACT_PLACE, pointed); // place
  2940. }
  2941. }
  2942. void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
  2943. const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime)
  2944. {
  2945. // See also: serverpackethandle.cpp, action == 2
  2946. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2947. ClientMap &map = client->getEnv().getClientMap();
  2948. MapNode n = client->getEnv().getClientMap().getNode(nodepos);
  2949. // NOTE: Similar piece of code exists on the server side for
  2950. // cheat detection.
  2951. // Get digging parameters
  2952. DigParams params = getDigParams(nodedef_manager->get(n).groups,
  2953. &selected_item.getToolCapabilities(itemdef_manager));
  2954. // If can't dig, try hand
  2955. if (!params.diggable) {
  2956. params = getDigParams(nodedef_manager->get(n).groups,
  2957. &hand_item.getToolCapabilities(itemdef_manager));
  2958. }
  2959. if (!params.diggable) {
  2960. // I guess nobody will wait for this long
  2961. runData.dig_time_complete = 10000000.0;
  2962. } else {
  2963. runData.dig_time_complete = params.time;
  2964. if (m_cache_enable_particles) {
  2965. const ContentFeatures &features = client->getNodeDefManager()->get(n);
  2966. client->getParticleManager()->addNodeParticle(client,
  2967. player, nodepos, n, features);
  2968. }
  2969. }
  2970. if (!runData.digging) {
  2971. infostream << "Started digging" << std::endl;
  2972. runData.dig_instantly = runData.dig_time_complete == 0;
  2973. if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n))
  2974. return;
  2975. client->interact(INTERACT_START_DIGGING, pointed);
  2976. runData.digging = true;
  2977. runData.btn_down_for_dig = true;
  2978. }
  2979. if (!runData.dig_instantly) {
  2980. runData.dig_index = (float)crack_animation_length
  2981. * runData.dig_time
  2982. / runData.dig_time_complete;
  2983. } else {
  2984. // This is for e.g. torches
  2985. runData.dig_index = crack_animation_length;
  2986. }
  2987. SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig;
  2988. if (sound_dig.exists() && params.diggable) {
  2989. if (sound_dig.name == "__group") {
  2990. if (!params.main_group.empty()) {
  2991. soundmaker->m_player_leftpunch_sound.gain = 0.5;
  2992. soundmaker->m_player_leftpunch_sound.name =
  2993. std::string("default_dig_") +
  2994. params.main_group;
  2995. }
  2996. } else {
  2997. soundmaker->m_player_leftpunch_sound = sound_dig;
  2998. }
  2999. }
  3000. // Don't show cracks if not diggable
  3001. if (runData.dig_time_complete >= 100000.0) {
  3002. } else if (runData.dig_index < crack_animation_length) {
  3003. //TimeTaker timer("client.setTempMod");
  3004. //infostream<<"dig_index="<<dig_index<<std::endl;
  3005. client->setCrack(runData.dig_index, nodepos);
  3006. } else {
  3007. infostream << "Digging completed" << std::endl;
  3008. client->setCrack(-1, v3s16(0, 0, 0));
  3009. runData.dig_time = 0;
  3010. runData.digging = false;
  3011. // we successfully dug, now block it from repeating if we want to be safe
  3012. if (g_settings->getBool("safe_dig_and_place"))
  3013. runData.digging_blocked = true;
  3014. runData.nodig_delay_timer =
  3015. runData.dig_time_complete / (float)crack_animation_length;
  3016. // We don't want a corresponding delay to very time consuming nodes
  3017. // and nodes without digging time (e.g. torches) get a fixed delay.
  3018. if (runData.nodig_delay_timer > 0.3)
  3019. runData.nodig_delay_timer = 0.3;
  3020. else if (runData.dig_instantly)
  3021. runData.nodig_delay_timer = 0.15;
  3022. bool is_valid_position;
  3023. MapNode wasnode = map.getNode(nodepos, &is_valid_position);
  3024. if (is_valid_position) {
  3025. if (client->modsLoaded() &&
  3026. client->getScript()->on_dignode(nodepos, wasnode)) {
  3027. return;
  3028. }
  3029. const ContentFeatures &f = client->ndef()->get(wasnode);
  3030. if (f.node_dig_prediction == "air") {
  3031. client->removeNode(nodepos);
  3032. } else if (!f.node_dig_prediction.empty()) {
  3033. content_t id;
  3034. bool found = client->ndef()->getId(f.node_dig_prediction, id);
  3035. if (found)
  3036. client->addNode(nodepos, id, true);
  3037. }
  3038. // implicit else: no prediction
  3039. }
  3040. client->interact(INTERACT_DIGGING_COMPLETED, pointed);
  3041. if (m_cache_enable_particles) {
  3042. const ContentFeatures &features =
  3043. client->getNodeDefManager()->get(wasnode);
  3044. client->getParticleManager()->addDiggingParticles(client,
  3045. player, nodepos, wasnode, features);
  3046. }
  3047. // Send event to trigger sound
  3048. client->getEventManager()->put(new NodeDugEvent(nodepos, wasnode));
  3049. }
  3050. if (runData.dig_time_complete < 100000.0) {
  3051. runData.dig_time += dtime;
  3052. } else {
  3053. runData.dig_time = 0;
  3054. client->setCrack(-1, nodepos);
  3055. }
  3056. camera->setDigging(0); // Dig animation
  3057. }
  3058. void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
  3059. const CameraOrientation &cam)
  3060. {
  3061. TimeTaker tt_update("Game::updateFrame()");
  3062. LocalPlayer *player = client->getEnv().getLocalPlayer();
  3063. /*
  3064. Fog range
  3065. */
  3066. if (draw_control->range_all) {
  3067. runData.fog_range = 100000 * BS;
  3068. } else {
  3069. runData.fog_range = draw_control->wanted_range * BS;
  3070. }
  3071. /*
  3072. Calculate general brightness
  3073. */
  3074. u32 daynight_ratio = client->getEnv().getDayNightRatio();
  3075. float time_brightness = decode_light_f((float)daynight_ratio / 1000.0);
  3076. float direct_brightness;
  3077. bool sunlight_seen;
  3078. if (m_cache_enable_noclip && m_cache_enable_free_move) {
  3079. direct_brightness = time_brightness;
  3080. sunlight_seen = true;
  3081. } else {
  3082. float old_brightness = sky->getBrightness();
  3083. direct_brightness = client->getEnv().getClientMap()
  3084. .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS),
  3085. daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
  3086. / 255.0;
  3087. }
  3088. float time_of_day_smooth = runData.time_of_day_smooth;
  3089. float time_of_day = client->getEnv().getTimeOfDayF();
  3090. static const float maxsm = 0.05f;
  3091. static const float todsm = 0.05f;
  3092. if (std::fabs(time_of_day - time_of_day_smooth) > maxsm &&
  3093. std::fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
  3094. std::fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
  3095. time_of_day_smooth = time_of_day;
  3096. if (time_of_day_smooth > 0.8 && time_of_day < 0.2)
  3097. time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
  3098. + (time_of_day + 1.0) * todsm;
  3099. else
  3100. time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
  3101. + time_of_day * todsm;
  3102. runData.time_of_day_smooth = time_of_day_smooth;
  3103. sky->update(time_of_day_smooth, time_brightness, direct_brightness,
  3104. sunlight_seen, camera->getCameraMode(), player->getYaw(),
  3105. player->getPitch());
  3106. /*
  3107. Update clouds
  3108. */
  3109. if (clouds) {
  3110. if (sky->getCloudsVisible()) {
  3111. clouds->setVisible(true);
  3112. clouds->step(dtime);
  3113. // camera->getPosition is not enough for 3rd person views
  3114. v3f camera_node_position = camera->getCameraNode()->getPosition();
  3115. v3s16 camera_offset = camera->getOffset();
  3116. camera_node_position.X = camera_node_position.X + camera_offset.X * BS;
  3117. camera_node_position.Y = camera_node_position.Y + camera_offset.Y * BS;
  3118. camera_node_position.Z = camera_node_position.Z + camera_offset.Z * BS;
  3119. clouds->update(camera_node_position,
  3120. sky->getCloudColor());
  3121. if (clouds->isCameraInsideCloud() && m_cache_enable_fog) {
  3122. // if inside clouds, and fog enabled, use that as sky
  3123. // color(s)
  3124. video::SColor clouds_dark = clouds->getColor()
  3125. .getInterpolated(video::SColor(255, 0, 0, 0), 0.9);
  3126. sky->overrideColors(clouds_dark, clouds->getColor());
  3127. sky->setInClouds(true);
  3128. runData.fog_range = std::fmin(runData.fog_range * 0.5f, 32.0f * BS);
  3129. // do not draw clouds after all
  3130. clouds->setVisible(false);
  3131. }
  3132. } else {
  3133. clouds->setVisible(false);
  3134. }
  3135. }
  3136. /*
  3137. Update particles
  3138. */
  3139. client->getParticleManager()->step(dtime);
  3140. /*
  3141. Fog
  3142. */
  3143. if (m_cache_enable_fog) {
  3144. driver->setFog(
  3145. sky->getBgColor(),
  3146. video::EFT_FOG_LINEAR,
  3147. runData.fog_range * m_cache_fog_start,
  3148. runData.fog_range * 1.0,
  3149. 0.01,
  3150. false, // pixel fog
  3151. true // range fog
  3152. );
  3153. } else {
  3154. driver->setFog(
  3155. sky->getBgColor(),
  3156. video::EFT_FOG_LINEAR,
  3157. 100000 * BS,
  3158. 110000 * BS,
  3159. 0.01f,
  3160. false, // pixel fog
  3161. false // range fog
  3162. );
  3163. }
  3164. /*
  3165. Get chat messages from client
  3166. */
  3167. v2u32 screensize = driver->getScreenSize();
  3168. updateChat(dtime, screensize);
  3169. /*
  3170. Inventory
  3171. */
  3172. if (player->getWieldIndex() != runData.new_playeritem)
  3173. client->setPlayerItem(runData.new_playeritem);
  3174. if (client->updateWieldedItem()) {
  3175. // Update wielded tool
  3176. ItemStack selected_item, hand_item;
  3177. ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item);
  3178. camera->wield(tool_item);
  3179. }
  3180. /*
  3181. Update block draw list every 200ms or when camera direction has
  3182. changed much
  3183. */
  3184. runData.update_draw_list_timer += dtime;
  3185. v3f camera_direction = camera->getDirection();
  3186. if (runData.update_draw_list_timer >= 0.2
  3187. || runData.update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
  3188. || m_camera_offset_changed) {
  3189. runData.update_draw_list_timer = 0;
  3190. client->getEnv().getClientMap().updateDrawList();
  3191. runData.update_draw_list_last_cam_dir = camera_direction;
  3192. }
  3193. m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime);
  3194. /*
  3195. make sure menu is on top
  3196. 1. Delete formspec menu reference if menu was removed
  3197. 2. Else, make sure formspec menu is on top
  3198. */
  3199. auto formspec = m_game_ui->getFormspecGUI();
  3200. do { // breakable. only runs for one iteration
  3201. if (!formspec)
  3202. break;
  3203. if (formspec->getReferenceCount() == 1) {
  3204. m_game_ui->deleteFormspec();
  3205. break;
  3206. }
  3207. auto &loc = formspec->getFormspecLocation();
  3208. if (loc.type == InventoryLocation::NODEMETA) {
  3209. NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p);
  3210. if (!meta || meta->getString("formspec").empty()) {
  3211. formspec->quitMenu();
  3212. break;
  3213. }
  3214. }
  3215. if (isMenuActive())
  3216. guiroot->bringToFront(formspec);
  3217. } while (false);
  3218. /*
  3219. Drawing begins
  3220. */
  3221. const video::SColor &skycolor = sky->getSkyColor();
  3222. TimeTaker tt_draw("Draw scene");
  3223. driver->beginScene(true, true, skycolor);
  3224. bool draw_wield_tool = (m_game_ui->m_flags.show_hud &&
  3225. (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
  3226. (camera->getCameraMode() == CAMERA_MODE_FIRST));
  3227. bool draw_crosshair = (
  3228. (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
  3229. (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
  3230. #ifdef HAVE_TOUCHSCREENGUI
  3231. try {
  3232. draw_crosshair = !g_settings->getBool("touchtarget");
  3233. } catch (SettingNotFoundException) {
  3234. }
  3235. #endif
  3236. RenderingEngine::draw_scene(skycolor, m_game_ui->m_flags.show_hud,
  3237. m_game_ui->m_flags.show_minimap, draw_wield_tool, draw_crosshair);
  3238. /*
  3239. Profiler graph
  3240. */
  3241. if (m_game_ui->m_flags.show_profiler_graph)
  3242. graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
  3243. /*
  3244. Damage flash
  3245. */
  3246. if (runData.damage_flash > 0.0f) {
  3247. video::SColor color(runData.damage_flash, 180, 0, 0);
  3248. driver->draw2DRectangle(color,
  3249. core::rect<s32>(0, 0, screensize.X, screensize.Y),
  3250. NULL);
  3251. runData.damage_flash -= 384.0f * dtime;
  3252. }
  3253. /*
  3254. Damage camera tilt
  3255. */
  3256. if (player->hurt_tilt_timer > 0.0f) {
  3257. player->hurt_tilt_timer -= dtime * 6.0f;
  3258. if (player->hurt_tilt_timer < 0.0f)
  3259. player->hurt_tilt_strength = 0.0f;
  3260. }
  3261. /*
  3262. Update minimap pos and rotation
  3263. */
  3264. if (mapper && m_game_ui->m_flags.show_hud) {
  3265. mapper->setPos(floatToInt(player->getPosition(), BS));
  3266. mapper->setAngle(player->getYaw());
  3267. }
  3268. /*
  3269. End scene
  3270. */
  3271. if (++m_reset_HW_buffer_counter > 500) {
  3272. /*
  3273. Periodically remove all mesh HW buffers.
  3274. Work around for a quirk in Irrlicht where a HW buffer is only
  3275. released after 20000 iterations (triggered from endScene()).
  3276. Without this, all loaded but unused meshes will retain their HW
  3277. buffers for at least 5 minutes, at which point looking up the HW buffers
  3278. becomes a bottleneck and the framerate drops (as much as 30%).
  3279. Tests showed that numbers between 50 and 1000 are good, so picked 500.
  3280. There are no other public Irrlicht APIs that allow interacting with the
  3281. HW buffers without tracking the status of every individual mesh.
  3282. The HW buffers for _visible_ meshes will be reinitialized in the next frame.
  3283. */
  3284. infostream << "Game::updateFrame(): Removing all HW buffers." << std::endl;
  3285. driver->removeAllHardwareBuffers();
  3286. m_reset_HW_buffer_counter = 0;
  3287. }
  3288. driver->endScene();
  3289. stats->drawtime = tt_draw.stop(true);
  3290. g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime);
  3291. g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true));
  3292. }
  3293. /* Log times and stuff for visualization */
  3294. inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
  3295. {
  3296. Profiler::GraphValues values;
  3297. g_profiler->graphGet(values);
  3298. graph->put(values);
  3299. }
  3300. /****************************************************************************
  3301. Misc
  3302. ****************************************************************************/
  3303. /* On some computers framerate doesn't seem to be automatically limited
  3304. */
  3305. inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
  3306. {
  3307. // not using getRealTime is necessary for wine
  3308. device->getTimer()->tick(); // Maker sure device time is up-to-date
  3309. u32 time = device->getTimer()->getTime();
  3310. u32 last_time = fps_timings->last_time;
  3311. if (time > last_time) // Make sure time hasn't overflowed
  3312. fps_timings->busy_time = time - last_time;
  3313. else
  3314. fps_timings->busy_time = 0;
  3315. u32 frametime_min = 1000 / (
  3316. device->isWindowFocused() && !g_menumgr.pausesGame()
  3317. ? g_settings->getFloat("fps_max")
  3318. : g_settings->getFloat("fps_max_unfocused"));
  3319. if (fps_timings->busy_time < frametime_min) {
  3320. fps_timings->sleep_time = frametime_min - fps_timings->busy_time;
  3321. device->sleep(fps_timings->sleep_time);
  3322. } else {
  3323. fps_timings->sleep_time = 0;
  3324. }
  3325. /* Get the new value of the device timer. Note that device->sleep() may
  3326. * not sleep for the entire requested time as sleep may be interrupted and
  3327. * therefore it is arguably more accurate to get the new time from the
  3328. * device rather than calculating it by adding sleep_time to time.
  3329. */
  3330. device->getTimer()->tick(); // Update device timer
  3331. time = device->getTimer()->getTime();
  3332. if (time > last_time) // Make sure last_time hasn't overflowed
  3333. *dtime = (time - last_time) / 1000.0;
  3334. else
  3335. *dtime = 0;
  3336. fps_timings->last_time = time;
  3337. }
  3338. void Game::showOverlayMessage(const char *msg, float dtime, int percent, bool draw_clouds)
  3339. {
  3340. const wchar_t *wmsg = wgettext(msg);
  3341. RenderingEngine::draw_load_screen(wmsg, guienv, texture_src, dtime, percent,
  3342. draw_clouds);
  3343. delete[] wmsg;
  3344. }
  3345. void Game::settingChangedCallback(const std::string &setting_name, void *data)
  3346. {
  3347. ((Game *)data)->readSettings();
  3348. }
  3349. void Game::readSettings()
  3350. {
  3351. m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
  3352. m_cache_enable_clouds = g_settings->getBool("enable_clouds");
  3353. m_cache_enable_joysticks = g_settings->getBool("enable_joysticks");
  3354. m_cache_enable_particles = g_settings->getBool("enable_particles");
  3355. m_cache_enable_fog = g_settings->getBool("enable_fog");
  3356. m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
  3357. m_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity");
  3358. m_repeat_place_time = g_settings->getFloat("repeat_place_time");
  3359. m_cache_enable_noclip = g_settings->getBool("noclip");
  3360. m_cache_enable_free_move = g_settings->getBool("free_move");
  3361. m_cache_fog_start = g_settings->getFloat("fog_start");
  3362. m_cache_cam_smoothing = 0;
  3363. if (g_settings->getBool("cinematic"))
  3364. m_cache_cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
  3365. else
  3366. m_cache_cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
  3367. m_cache_fog_start = rangelim(m_cache_fog_start, 0.0f, 0.99f);
  3368. m_cache_cam_smoothing = rangelim(m_cache_cam_smoothing, 0.01f, 1.0f);
  3369. m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
  3370. m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus");
  3371. }
  3372. /****************************************************************************/
  3373. /****************************************************************************
  3374. Shutdown / cleanup
  3375. ****************************************************************************/
  3376. /****************************************************************************/
  3377. void Game::extendedResourceCleanup()
  3378. {
  3379. // Extended resource accounting
  3380. infostream << "Irrlicht resources after cleanup:" << std::endl;
  3381. infostream << "\tRemaining meshes : "
  3382. << RenderingEngine::get_mesh_cache()->getMeshCount() << std::endl;
  3383. infostream << "\tRemaining textures : "
  3384. << driver->getTextureCount() << std::endl;
  3385. for (unsigned int i = 0; i < driver->getTextureCount(); i++) {
  3386. irr::video::ITexture *texture = driver->getTextureByIndex(i);
  3387. infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str()
  3388. << std::endl;
  3389. }
  3390. clearTextureNameCache();
  3391. infostream << "\tRemaining materials: "
  3392. << driver-> getMaterialRendererCount()
  3393. << " (note: irrlicht doesn't support removing renderers)" << std::endl;
  3394. }
  3395. void Game::showDeathFormspec()
  3396. {
  3397. static std::string formspec_str =
  3398. std::string("formspec_version[1]") +
  3399. SIZE_TAG
  3400. "bgcolor[#320000b4;true]"
  3401. "label[4.85,1.35;" + gettext("You died") + "]"
  3402. "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
  3403. ;
  3404. /* Create menu */
  3405. /* Note: FormspecFormSource and LocalFormspecHandler *
  3406. * are deleted by guiFormSpecMenu */
  3407. FormspecFormSource *fs_src = new FormspecFormSource(formspec_str);
  3408. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
  3409. auto *&formspec = m_game_ui->getFormspecGUI();
  3410. GUIFormSpecMenu::create(formspec, client, &input->joystick,
  3411. fs_src, txt_dst, client->getFormspecPrepend(), sound);
  3412. formspec->setFocus("btn_respawn");
  3413. }
  3414. #define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
  3415. void Game::showPauseMenu()
  3416. {
  3417. #ifdef __ANDROID__
  3418. static const std::string control_text = strgettext("Default Controls:\n"
  3419. "No menu visible:\n"
  3420. "- single tap: button activate\n"
  3421. "- double tap: place/use\n"
  3422. "- slide finger: look around\n"
  3423. "Menu/Inventory visible:\n"
  3424. "- double tap (outside):\n"
  3425. " -->close\n"
  3426. "- touch stack, touch slot:\n"
  3427. " --> move stack\n"
  3428. "- touch&drag, tap 2nd finger\n"
  3429. " --> place single item to slot\n"
  3430. );
  3431. #else
  3432. static const std::string control_text_template = strgettext("Controls:\n"
  3433. "- %s: move forwards\n"
  3434. "- %s: move backwards\n"
  3435. "- %s: move left\n"
  3436. "- %s: move right\n"
  3437. "- %s: jump/climb up\n"
  3438. "- %s: dig/punch\n"
  3439. "- %s: place/use\n"
  3440. "- %s: sneak/climb down\n"
  3441. "- %s: drop item\n"
  3442. "- %s: inventory\n"
  3443. "- Mouse: turn/look\n"
  3444. "- Mouse wheel: select item\n"
  3445. "- %s: chat\n"
  3446. );
  3447. char control_text_buf[600];
  3448. porting::mt_snprintf(control_text_buf, sizeof(control_text_buf), control_text_template.c_str(),
  3449. GET_KEY_NAME(keymap_forward),
  3450. GET_KEY_NAME(keymap_backward),
  3451. GET_KEY_NAME(keymap_left),
  3452. GET_KEY_NAME(keymap_right),
  3453. GET_KEY_NAME(keymap_jump),
  3454. GET_KEY_NAME(keymap_dig),
  3455. GET_KEY_NAME(keymap_place),
  3456. GET_KEY_NAME(keymap_sneak),
  3457. GET_KEY_NAME(keymap_drop),
  3458. GET_KEY_NAME(keymap_inventory),
  3459. GET_KEY_NAME(keymap_chat)
  3460. );
  3461. std::string control_text = std::string(control_text_buf);
  3462. str_formspec_escape(control_text);
  3463. #endif
  3464. float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
  3465. std::ostringstream os;
  3466. os << "formspec_version[1]" << SIZE_TAG
  3467. << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
  3468. << strgettext("Continue") << "]";
  3469. if (!simple_singleplayer_mode) {
  3470. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
  3471. << strgettext("Change Password") << "]";
  3472. } else {
  3473. os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]";
  3474. }
  3475. #ifndef __ANDROID__
  3476. #if USE_SOUND
  3477. if (g_settings->getBool("enable_sound")) {
  3478. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
  3479. << strgettext("Sound Volume") << "]";
  3480. }
  3481. #endif
  3482. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
  3483. << strgettext("Change Keys") << "]";
  3484. #endif
  3485. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
  3486. << strgettext("Exit to Menu") << "]";
  3487. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
  3488. << strgettext("Exit to OS") << "]"
  3489. << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
  3490. << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
  3491. << "\n"
  3492. << strgettext("Game info:") << "\n";
  3493. const std::string &address = client->getAddressName();
  3494. static const std::string mode = strgettext("- Mode: ");
  3495. if (!simple_singleplayer_mode) {
  3496. Address serverAddress = client->getServerAddress();
  3497. if (!address.empty()) {
  3498. os << mode << strgettext("Remote server") << "\n"
  3499. << strgettext("- Address: ") << address;
  3500. } else {
  3501. os << mode << strgettext("Hosting server");
  3502. }
  3503. os << "\n" << strgettext("- Port: ") << serverAddress.getPort() << "\n";
  3504. } else {
  3505. os << mode << strgettext("Singleplayer") << "\n";
  3506. }
  3507. if (simple_singleplayer_mode || address.empty()) {
  3508. static const std::string on = strgettext("On");
  3509. static const std::string off = strgettext("Off");
  3510. const std::string &damage = g_settings->getBool("enable_damage") ? on : off;
  3511. const std::string &creative = g_settings->getBool("creative_mode") ? on : off;
  3512. const std::string &announced = g_settings->getBool("server_announce") ? on : off;
  3513. os << strgettext("- Damage: ") << damage << "\n"
  3514. << strgettext("- Creative Mode: ") << creative << "\n";
  3515. if (!simple_singleplayer_mode) {
  3516. const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off;
  3517. //~ PvP = Player versus Player
  3518. os << strgettext("- PvP: ") << pvp << "\n"
  3519. << strgettext("- Public: ") << announced << "\n";
  3520. std::string server_name = g_settings->get("server_name");
  3521. str_formspec_escape(server_name);
  3522. if (announced == on && !server_name.empty())
  3523. os << strgettext("- Server Name: ") << server_name;
  3524. }
  3525. }
  3526. os << ";]";
  3527. /* Create menu */
  3528. /* Note: FormspecFormSource and LocalFormspecHandler *
  3529. * are deleted by guiFormSpecMenu */
  3530. FormspecFormSource *fs_src = new FormspecFormSource(os.str());
  3531. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
  3532. auto *&formspec = m_game_ui->getFormspecGUI();
  3533. GUIFormSpecMenu::create(formspec, client, &input->joystick,
  3534. fs_src, txt_dst, client->getFormspecPrepend(), sound);
  3535. formspec->setFocus("btn_continue");
  3536. formspec->doPause = true;
  3537. }
  3538. /****************************************************************************/
  3539. /****************************************************************************
  3540. extern function for launching the game
  3541. ****************************************************************************/
  3542. /****************************************************************************/
  3543. void the_game(bool *kill,
  3544. InputHandler *input,
  3545. const GameStartData &start_data,
  3546. std::string &error_message,
  3547. ChatBackend &chat_backend,
  3548. bool *reconnect_requested) // Used for local game
  3549. {
  3550. Game game;
  3551. /* Make a copy of the server address because if a local singleplayer server
  3552. * is created then this is updated and we don't want to change the value
  3553. * passed to us by the calling function
  3554. */
  3555. try {
  3556. if (game.startup(kill, input, start_data, error_message,
  3557. reconnect_requested, &chat_backend)) {
  3558. game.run();
  3559. }
  3560. } catch (SerializationError &e) {
  3561. error_message = std::string("A serialization error occurred:\n")
  3562. + e.what() + "\n\nThe server is probably "
  3563. " running a different version of " PROJECT_NAME_C ".";
  3564. errorstream << error_message << std::endl;
  3565. } catch (ServerError &e) {
  3566. error_message = e.what();
  3567. errorstream << "ServerError: " << error_message << std::endl;
  3568. } catch (ModError &e) {
  3569. error_message = std::string("ModError: ") + e.what() +
  3570. strgettext("\nCheck debug.txt for details.");
  3571. errorstream << error_message << std::endl;
  3572. }
  3573. game.shutdown();
  3574. }