game.cpp 131 KB

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