game.cpp 95 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575
  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 "irrlichttypes_extrabloated.h"
  18. #include <IGUICheckBox.h>
  19. #include <IGUIEditBox.h>
  20. #include <IGUIButton.h>
  21. #include <IGUIStaticText.h>
  22. #include <IGUIFont.h>
  23. #include <IMaterialRendererServices.h>
  24. #include "IMeshCache.h"
  25. #include "client.h"
  26. #include "server.h"
  27. #include "guiPasswordChange.h"
  28. #include "guiVolumeChange.h"
  29. #include "guiFormSpecMenu.h"
  30. #include "tool.h"
  31. #include "guiChatConsole.h"
  32. #include "config.h"
  33. #include "version.h"
  34. #include "clouds.h"
  35. #include "particles.h"
  36. #include "camera.h"
  37. #include "mapblock.h"
  38. #include "settings.h"
  39. #include "profiler.h"
  40. #include "mainmenumanager.h"
  41. #include "gettext.h"
  42. #include "log.h"
  43. #include "filesys.h"
  44. // Needed for determining pointing to nodes
  45. #include "nodedef.h"
  46. #include "nodemetadata.h"
  47. #include "main.h" // For g_settings
  48. #include "itemdef.h"
  49. #include "tile.h" // For TextureSource
  50. #include "shader.h" // For ShaderSource
  51. #include "logoutputbuffer.h"
  52. #include "subgame.h"
  53. #include "quicktune_shortcutter.h"
  54. #include "clientmap.h"
  55. #include "hud.h"
  56. #include "sky.h"
  57. #include "sound.h"
  58. #if USE_SOUND
  59. #include "sound_openal.h"
  60. #endif
  61. #include "event_manager.h"
  62. #include <iomanip>
  63. #include <list>
  64. #include "util/directiontables.h"
  65. #include "util/pointedthing.h"
  66. #include "drawscene.h"
  67. #include "content_cao.h"
  68. #ifdef HAVE_TOUCHSCREENGUI
  69. #include "touchscreengui.h"
  70. #endif
  71. /*
  72. Text input system
  73. */
  74. struct TextDestNodeMetadata : public TextDest
  75. {
  76. TextDestNodeMetadata(v3s16 p, Client *client)
  77. {
  78. m_p = p;
  79. m_client = client;
  80. }
  81. // This is deprecated I guess? -celeron55
  82. void gotText(std::wstring text)
  83. {
  84. std::string ntext = wide_to_narrow(text);
  85. infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
  86. <<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
  87. std::map<std::string, std::string> fields;
  88. fields["text"] = ntext;
  89. m_client->sendNodemetaFields(m_p, "", fields);
  90. }
  91. void gotText(std::map<std::string, std::string> fields)
  92. {
  93. m_client->sendNodemetaFields(m_p, "", fields);
  94. }
  95. v3s16 m_p;
  96. Client *m_client;
  97. };
  98. struct TextDestPlayerInventory : public TextDest
  99. {
  100. TextDestPlayerInventory(Client *client)
  101. {
  102. m_client = client;
  103. m_formname = "";
  104. }
  105. TextDestPlayerInventory(Client *client, std::string formname)
  106. {
  107. m_client = client;
  108. m_formname = formname;
  109. }
  110. void gotText(std::map<std::string, std::string> fields)
  111. {
  112. m_client->sendInventoryFields(m_formname, fields);
  113. }
  114. Client *m_client;
  115. };
  116. struct LocalFormspecHandler : public TextDest
  117. {
  118. LocalFormspecHandler();
  119. LocalFormspecHandler(std::string formname) :
  120. m_client(0)
  121. {
  122. m_formname = formname;
  123. }
  124. LocalFormspecHandler(std::string formname, Client *client) :
  125. m_client(client)
  126. {
  127. m_formname = formname;
  128. }
  129. void gotText(std::wstring message) {
  130. errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
  131. }
  132. void gotText(std::map<std::string, std::string> fields)
  133. {
  134. if (m_formname == "MT_PAUSE_MENU") {
  135. if (fields.find("btn_sound") != fields.end()) {
  136. g_gamecallback->changeVolume();
  137. return;
  138. }
  139. if (fields.find("btn_exit_menu") != fields.end()) {
  140. g_gamecallback->disconnect();
  141. return;
  142. }
  143. if (fields.find("btn_exit_os") != fields.end()) {
  144. g_gamecallback->exitToOS();
  145. return;
  146. }
  147. if (fields.find("btn_change_password") != fields.end()) {
  148. g_gamecallback->changePassword();
  149. return;
  150. }
  151. if (fields.find("quit") != fields.end()) {
  152. return;
  153. }
  154. if (fields.find("btn_continue") != fields.end()) {
  155. return;
  156. }
  157. }
  158. if (m_formname == "MT_CHAT_MENU") {
  159. assert(m_client != 0);
  160. if ((fields.find("btn_send") != fields.end()) ||
  161. (fields.find("quit") != fields.end())) {
  162. if (fields.find("f_text") != fields.end()) {
  163. m_client->typeChatMessage(narrow_to_wide(fields["f_text"]));
  164. }
  165. return;
  166. }
  167. }
  168. if (m_formname == "MT_DEATH_SCREEN") {
  169. assert(m_client != 0);
  170. if ((fields.find("btn_respawn") != fields.end())) {
  171. m_client->sendRespawn();
  172. return;
  173. }
  174. if (fields.find("quit") != fields.end()) {
  175. m_client->sendRespawn();
  176. return;
  177. }
  178. }
  179. // don't show error message for unhandled cursor keys
  180. if ( (fields.find("key_up") != fields.end()) ||
  181. (fields.find("key_down") != fields.end()) ||
  182. (fields.find("key_left") != fields.end()) ||
  183. (fields.find("key_right") != fields.end())) {
  184. return;
  185. }
  186. errorstream << "LocalFormspecHandler::gotText unhandled >" << m_formname << "< event" << std::endl;
  187. int i = 0;
  188. for (std::map<std::string,std::string>::iterator iter = fields.begin();
  189. iter != fields.end(); iter++) {
  190. errorstream << "\t"<< i << ": " << iter->first << "=" << iter->second << std::endl;
  191. i++;
  192. }
  193. }
  194. Client *m_client;
  195. };
  196. /* Form update callback */
  197. class NodeMetadataFormSource: public IFormSource
  198. {
  199. public:
  200. NodeMetadataFormSource(ClientMap *map, v3s16 p):
  201. m_map(map),
  202. m_p(p)
  203. {
  204. }
  205. std::string getForm()
  206. {
  207. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  208. if(!meta)
  209. return "";
  210. return meta->getString("formspec");
  211. }
  212. std::string resolveText(std::string str)
  213. {
  214. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  215. if(!meta)
  216. return str;
  217. return meta->resolveString(str);
  218. }
  219. ClientMap *m_map;
  220. v3s16 m_p;
  221. };
  222. class PlayerInventoryFormSource: public IFormSource
  223. {
  224. public:
  225. PlayerInventoryFormSource(Client *client):
  226. m_client(client)
  227. {
  228. }
  229. std::string getForm()
  230. {
  231. LocalPlayer* player = m_client->getEnv().getLocalPlayer();
  232. return player->inventory_formspec;
  233. }
  234. Client *m_client;
  235. };
  236. /*
  237. Check if a node is pointable
  238. */
  239. inline bool isPointableNode(const MapNode& n,
  240. Client *client, bool liquids_pointable)
  241. {
  242. const ContentFeatures &features = client->getNodeDefManager()->get(n);
  243. return features.pointable ||
  244. (liquids_pointable && features.isLiquid());
  245. }
  246. /*
  247. Find what the player is pointing at
  248. */
  249. PointedThing getPointedThing(Client *client, v3f player_position,
  250. v3f camera_direction, v3f camera_position, core::line3d<f32> shootline,
  251. f32 d, bool liquids_pointable, bool look_for_object, v3s16 camera_offset,
  252. std::vector<aabb3f> &hilightboxes, ClientActiveObject *&selected_object)
  253. {
  254. PointedThing result;
  255. hilightboxes.clear();
  256. selected_object = NULL;
  257. INodeDefManager *nodedef = client->getNodeDefManager();
  258. ClientMap &map = client->getEnv().getClientMap();
  259. f32 mindistance = BS * 1001;
  260. // First try to find a pointed at active object
  261. if(look_for_object)
  262. {
  263. selected_object = client->getSelectedActiveObject(d*BS,
  264. camera_position, shootline);
  265. if(selected_object != NULL)
  266. {
  267. if(selected_object->doShowSelectionBox())
  268. {
  269. aabb3f *selection_box = selected_object->getSelectionBox();
  270. // Box should exist because object was
  271. // returned in the first place
  272. assert(selection_box);
  273. v3f pos = selected_object->getPosition();
  274. hilightboxes.push_back(aabb3f(
  275. selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
  276. selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
  277. }
  278. mindistance = (selected_object->getPosition() - camera_position).getLength();
  279. result.type = POINTEDTHING_OBJECT;
  280. result.object_id = selected_object->getId();
  281. }
  282. }
  283. // That didn't work, try to find a pointed at node
  284. v3s16 pos_i = floatToInt(player_position, BS);
  285. /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
  286. <<std::endl;*/
  287. s16 a = d;
  288. s16 ystart = pos_i.Y + 0 - (camera_direction.Y<0 ? a : 1);
  289. s16 zstart = pos_i.Z - (camera_direction.Z<0 ? a : 1);
  290. s16 xstart = pos_i.X - (camera_direction.X<0 ? a : 1);
  291. s16 yend = pos_i.Y + 1 + (camera_direction.Y>0 ? a : 1);
  292. s16 zend = pos_i.Z + (camera_direction.Z>0 ? a : 1);
  293. s16 xend = pos_i.X + (camera_direction.X>0 ? a : 1);
  294. // Prevent signed number overflow
  295. if(yend==32767)
  296. yend=32766;
  297. if(zend==32767)
  298. zend=32766;
  299. if(xend==32767)
  300. xend=32766;
  301. for(s16 y = ystart; y <= yend; y++)
  302. for(s16 z = zstart; z <= zend; z++)
  303. for(s16 x = xstart; x <= xend; x++)
  304. {
  305. MapNode n;
  306. try
  307. {
  308. n = map.getNode(v3s16(x,y,z));
  309. }
  310. catch(InvalidPositionException &e)
  311. {
  312. continue;
  313. }
  314. if(!isPointableNode(n, client, liquids_pointable))
  315. continue;
  316. std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
  317. v3s16 np(x,y,z);
  318. v3f npf = intToFloat(np, BS);
  319. for(std::vector<aabb3f>::const_iterator
  320. i = boxes.begin();
  321. i != boxes.end(); i++)
  322. {
  323. aabb3f box = *i;
  324. box.MinEdge += npf;
  325. box.MaxEdge += npf;
  326. for(u16 j=0; j<6; j++)
  327. {
  328. v3s16 facedir = g_6dirs[j];
  329. aabb3f facebox = box;
  330. f32 d = 0.001*BS;
  331. if(facedir.X > 0)
  332. facebox.MinEdge.X = facebox.MaxEdge.X-d;
  333. else if(facedir.X < 0)
  334. facebox.MaxEdge.X = facebox.MinEdge.X+d;
  335. else if(facedir.Y > 0)
  336. facebox.MinEdge.Y = facebox.MaxEdge.Y-d;
  337. else if(facedir.Y < 0)
  338. facebox.MaxEdge.Y = facebox.MinEdge.Y+d;
  339. else if(facedir.Z > 0)
  340. facebox.MinEdge.Z = facebox.MaxEdge.Z-d;
  341. else if(facedir.Z < 0)
  342. facebox.MaxEdge.Z = facebox.MinEdge.Z+d;
  343. v3f centerpoint = facebox.getCenter();
  344. f32 distance = (centerpoint - camera_position).getLength();
  345. if(distance >= mindistance)
  346. continue;
  347. if(!facebox.intersectsWithLine(shootline))
  348. continue;
  349. v3s16 np_above = np + facedir;
  350. result.type = POINTEDTHING_NODE;
  351. result.node_undersurface = np;
  352. result.node_abovesurface = np_above;
  353. mindistance = distance;
  354. hilightboxes.clear();
  355. if (!g_settings->getBool("enable_node_highlighting")) {
  356. for(std::vector<aabb3f>::const_iterator
  357. i2 = boxes.begin();
  358. i2 != boxes.end(); i2++)
  359. {
  360. aabb3f box = *i2;
  361. box.MinEdge += npf + v3f(-d,-d,-d) - intToFloat(camera_offset, BS);
  362. box.MaxEdge += npf + v3f(d,d,d) - intToFloat(camera_offset, BS);
  363. hilightboxes.push_back(box);
  364. }
  365. }
  366. }
  367. }
  368. } // for coords
  369. return result;
  370. }
  371. /* Profiler display */
  372. void update_profiler_gui(gui::IGUIStaticText *guitext_profiler,
  373. gui::IGUIFont *font, u32 text_height, u32 show_profiler,
  374. u32 show_profiler_max)
  375. {
  376. if(show_profiler == 0)
  377. {
  378. guitext_profiler->setVisible(false);
  379. }
  380. else
  381. {
  382. std::ostringstream os(std::ios_base::binary);
  383. g_profiler->printPage(os, show_profiler, show_profiler_max);
  384. std::wstring text = narrow_to_wide(os.str());
  385. guitext_profiler->setText(text.c_str());
  386. guitext_profiler->setVisible(true);
  387. s32 w = font->getDimension(text.c_str()).Width;
  388. if(w < 400)
  389. w = 400;
  390. core::rect<s32> rect(6, 4+(text_height+5)*2, 12+w,
  391. 8+(text_height+5)*2 +
  392. font->getDimension(text.c_str()).Height);
  393. guitext_profiler->setRelativePosition(rect);
  394. guitext_profiler->setVisible(true);
  395. }
  396. }
  397. class ProfilerGraph
  398. {
  399. private:
  400. struct Piece{
  401. Profiler::GraphValues values;
  402. };
  403. struct Meta{
  404. float min;
  405. float max;
  406. video::SColor color;
  407. Meta(float initial=0, video::SColor color=
  408. video::SColor(255,255,255,255)):
  409. min(initial),
  410. max(initial),
  411. color(color)
  412. {}
  413. };
  414. std::list<Piece> m_log;
  415. public:
  416. u32 m_log_max_size;
  417. ProfilerGraph():
  418. m_log_max_size(200)
  419. {}
  420. void put(const Profiler::GraphValues &values)
  421. {
  422. Piece piece;
  423. piece.values = values;
  424. m_log.push_back(piece);
  425. while(m_log.size() > m_log_max_size)
  426. m_log.erase(m_log.begin());
  427. }
  428. void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
  429. gui::IGUIFont* font) const
  430. {
  431. std::map<std::string, Meta> m_meta;
  432. for(std::list<Piece>::const_iterator k = m_log.begin();
  433. k != m_log.end(); k++)
  434. {
  435. const Piece &piece = *k;
  436. for(Profiler::GraphValues::const_iterator i = piece.values.begin();
  437. i != piece.values.end(); i++){
  438. const std::string &id = i->first;
  439. const float &value = i->second;
  440. std::map<std::string, Meta>::iterator j =
  441. m_meta.find(id);
  442. if(j == m_meta.end()){
  443. m_meta[id] = Meta(value);
  444. continue;
  445. }
  446. if(value < j->second.min)
  447. j->second.min = value;
  448. if(value > j->second.max)
  449. j->second.max = value;
  450. }
  451. }
  452. // Assign colors
  453. static const video::SColor usable_colors[] = {
  454. video::SColor(255,255,100,100),
  455. video::SColor(255,90,225,90),
  456. video::SColor(255,100,100,255),
  457. video::SColor(255,255,150,50),
  458. video::SColor(255,220,220,100)
  459. };
  460. static const u32 usable_colors_count =
  461. sizeof(usable_colors) / sizeof(*usable_colors);
  462. u32 next_color_i = 0;
  463. for(std::map<std::string, Meta>::iterator i = m_meta.begin();
  464. i != m_meta.end(); i++){
  465. Meta &meta = i->second;
  466. video::SColor color(255,200,200,200);
  467. if(next_color_i < usable_colors_count)
  468. color = usable_colors[next_color_i++];
  469. meta.color = color;
  470. }
  471. s32 graphh = 50;
  472. s32 textx = x_left + m_log_max_size + 15;
  473. s32 textx2 = textx + 200 - 15;
  474. // Draw background
  475. /*{
  476. u32 num_graphs = m_meta.size();
  477. core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
  478. textx2, y_bottom);
  479. video::SColor bgcolor(120,0,0,0);
  480. driver->draw2DRectangle(bgcolor, rect, NULL);
  481. }*/
  482. s32 meta_i = 0;
  483. for(std::map<std::string, Meta>::const_iterator i = m_meta.begin();
  484. i != m_meta.end(); i++){
  485. const std::string &id = i->first;
  486. const Meta &meta = i->second;
  487. s32 x = x_left;
  488. s32 y = y_bottom - meta_i * 50;
  489. float show_min = meta.min;
  490. float show_max = meta.max;
  491. if(show_min >= -0.0001 && show_max >= -0.0001){
  492. if(show_min <= show_max * 0.5)
  493. show_min = 0;
  494. }
  495. s32 texth = 15;
  496. char buf[10];
  497. snprintf(buf, 10, "%.3g", show_max);
  498. font->draw(narrow_to_wide(buf).c_str(),
  499. core::rect<s32>(textx, y - graphh,
  500. textx2, y - graphh + texth),
  501. meta.color);
  502. snprintf(buf, 10, "%.3g", show_min);
  503. font->draw(narrow_to_wide(buf).c_str(),
  504. core::rect<s32>(textx, y - texth,
  505. textx2, y),
  506. meta.color);
  507. font->draw(narrow_to_wide(id).c_str(),
  508. core::rect<s32>(textx, y - graphh/2 - texth/2,
  509. textx2, y - graphh/2 + texth/2),
  510. meta.color);
  511. s32 graph1y = y;
  512. s32 graph1h = graphh;
  513. bool relativegraph = (show_min != 0 && show_min != show_max);
  514. float lastscaledvalue = 0.0;
  515. bool lastscaledvalue_exists = false;
  516. for(std::list<Piece>::const_iterator j = m_log.begin();
  517. j != m_log.end(); j++)
  518. {
  519. const Piece &piece = *j;
  520. float value = 0;
  521. bool value_exists = false;
  522. Profiler::GraphValues::const_iterator k =
  523. piece.values.find(id);
  524. if(k != piece.values.end()){
  525. value = k->second;
  526. value_exists = true;
  527. }
  528. if(!value_exists){
  529. x++;
  530. lastscaledvalue_exists = false;
  531. continue;
  532. }
  533. float scaledvalue = 1.0;
  534. if(show_max != show_min)
  535. scaledvalue = (value - show_min) / (show_max - show_min);
  536. if(scaledvalue == 1.0 && value == 0){
  537. x++;
  538. lastscaledvalue_exists = false;
  539. continue;
  540. }
  541. if(relativegraph){
  542. if(lastscaledvalue_exists){
  543. s32 ivalue1 = lastscaledvalue * graph1h;
  544. s32 ivalue2 = scaledvalue * graph1h;
  545. driver->draw2DLine(v2s32(x-1, graph1y - ivalue1),
  546. v2s32(x, graph1y - ivalue2), meta.color);
  547. }
  548. lastscaledvalue = scaledvalue;
  549. lastscaledvalue_exists = true;
  550. } else{
  551. s32 ivalue = scaledvalue * graph1h;
  552. driver->draw2DLine(v2s32(x, graph1y),
  553. v2s32(x, graph1y - ivalue), meta.color);
  554. }
  555. x++;
  556. }
  557. meta_i++;
  558. }
  559. }
  560. };
  561. class NodeDugEvent: public MtEvent
  562. {
  563. public:
  564. v3s16 p;
  565. MapNode n;
  566. NodeDugEvent(v3s16 p, MapNode n):
  567. p(p),
  568. n(n)
  569. {}
  570. const char* getType() const
  571. {return "NodeDug";}
  572. };
  573. class SoundMaker
  574. {
  575. ISoundManager *m_sound;
  576. INodeDefManager *m_ndef;
  577. public:
  578. float m_player_step_timer;
  579. SimpleSoundSpec m_player_step_sound;
  580. SimpleSoundSpec m_player_leftpunch_sound;
  581. SimpleSoundSpec m_player_rightpunch_sound;
  582. SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
  583. m_sound(sound),
  584. m_ndef(ndef),
  585. m_player_step_timer(0)
  586. {
  587. }
  588. void playPlayerStep()
  589. {
  590. if(m_player_step_timer <= 0 && m_player_step_sound.exists()){
  591. m_player_step_timer = 0.03;
  592. m_sound->playSound(m_player_step_sound, false);
  593. }
  594. }
  595. static void viewBobbingStep(MtEvent *e, void *data)
  596. {
  597. SoundMaker *sm = (SoundMaker*)data;
  598. sm->playPlayerStep();
  599. }
  600. static void playerRegainGround(MtEvent *e, void *data)
  601. {
  602. SoundMaker *sm = (SoundMaker*)data;
  603. sm->playPlayerStep();
  604. }
  605. static void playerJump(MtEvent *e, void *data)
  606. {
  607. //SoundMaker *sm = (SoundMaker*)data;
  608. }
  609. static void cameraPunchLeft(MtEvent *e, void *data)
  610. {
  611. SoundMaker *sm = (SoundMaker*)data;
  612. sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
  613. }
  614. static void cameraPunchRight(MtEvent *e, void *data)
  615. {
  616. SoundMaker *sm = (SoundMaker*)data;
  617. sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
  618. }
  619. static void nodeDug(MtEvent *e, void *data)
  620. {
  621. SoundMaker *sm = (SoundMaker*)data;
  622. NodeDugEvent *nde = (NodeDugEvent*)e;
  623. sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
  624. }
  625. static void playerDamage(MtEvent *e, void *data)
  626. {
  627. SoundMaker *sm = (SoundMaker*)data;
  628. sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
  629. }
  630. static void playerFallingDamage(MtEvent *e, void *data)
  631. {
  632. SoundMaker *sm = (SoundMaker*)data;
  633. sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
  634. }
  635. void registerReceiver(MtEventManager *mgr)
  636. {
  637. mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this);
  638. mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this);
  639. mgr->reg("PlayerJump", SoundMaker::playerJump, this);
  640. mgr->reg("CameraPunchLeft", SoundMaker::cameraPunchLeft, this);
  641. mgr->reg("CameraPunchRight", SoundMaker::cameraPunchRight, this);
  642. mgr->reg("NodeDug", SoundMaker::nodeDug, this);
  643. mgr->reg("PlayerDamage", SoundMaker::playerDamage, this);
  644. mgr->reg("PlayerFallingDamage", SoundMaker::playerFallingDamage, this);
  645. }
  646. void step(float dtime)
  647. {
  648. m_player_step_timer -= dtime;
  649. }
  650. };
  651. // Locally stored sounds don't need to be preloaded because of this
  652. class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
  653. {
  654. std::set<std::string> m_fetched;
  655. public:
  656. void fetchSounds(const std::string &name,
  657. std::set<std::string> &dst_paths,
  658. std::set<std::string> &dst_datas)
  659. {
  660. if(m_fetched.count(name))
  661. return;
  662. m_fetched.insert(name);
  663. std::string base = porting::path_share + DIR_DELIM + "testsounds";
  664. dst_paths.insert(base + DIR_DELIM + name + ".ogg");
  665. dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
  666. dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
  667. dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
  668. dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
  669. dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
  670. dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
  671. dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
  672. dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
  673. dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
  674. dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
  675. }
  676. };
  677. class GameGlobalShaderConstantSetter : public IShaderConstantSetter
  678. {
  679. Sky *m_sky;
  680. bool *m_force_fog_off;
  681. f32 *m_fog_range;
  682. Client *m_client;
  683. public:
  684. GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
  685. f32 *fog_range, Client *client):
  686. m_sky(sky),
  687. m_force_fog_off(force_fog_off),
  688. m_fog_range(fog_range),
  689. m_client(client)
  690. {}
  691. ~GameGlobalShaderConstantSetter() {}
  692. virtual void onSetConstants(video::IMaterialRendererServices *services,
  693. bool is_highlevel)
  694. {
  695. if(!is_highlevel)
  696. return;
  697. // Background color
  698. video::SColor bgcolor = m_sky->getBgColor();
  699. video::SColorf bgcolorf(bgcolor);
  700. float bgcolorfa[4] = {
  701. bgcolorf.r,
  702. bgcolorf.g,
  703. bgcolorf.b,
  704. bgcolorf.a,
  705. };
  706. services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4);
  707. // Fog distance
  708. float fog_distance = 10000*BS;
  709. if(g_settings->getBool("enable_fog") && !*m_force_fog_off)
  710. fog_distance = *m_fog_range;
  711. services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
  712. // Day-night ratio
  713. u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
  714. float daynight_ratio_f = (float)daynight_ratio / 1000.0;
  715. services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1);
  716. u32 animation_timer = porting::getTimeMs() % 100000;
  717. float animation_timer_f = (float)animation_timer / 100000.0;
  718. services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1);
  719. services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1);
  720. LocalPlayer* player = m_client->getEnv().getLocalPlayer();
  721. v3f eye_position = player->getEyePosition();
  722. services->setPixelShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
  723. services->setVertexShaderConstant("eyePosition", (irr::f32*)&eye_position, 3);
  724. // Uniform sampler layers
  725. int layer0 = 0;
  726. int layer1 = 1;
  727. int layer2 = 2;
  728. // before 1.8 there isn't a "integer interface", only float
  729. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
  730. services->setPixelShaderConstant("baseTexture" , (irr::f32*)&layer0, 1);
  731. services->setPixelShaderConstant("normalTexture" , (irr::f32*)&layer1, 1);
  732. services->setPixelShaderConstant("useNormalmap" , (irr::f32*)&layer2, 1);
  733. #else
  734. services->setPixelShaderConstant("baseTexture" , (irr::s32*)&layer0, 1);
  735. services->setPixelShaderConstant("normalTexture" , (irr::s32*)&layer1, 1);
  736. services->setPixelShaderConstant("useNormalmap" , (irr::s32*)&layer2, 1);
  737. #endif
  738. }
  739. };
  740. bool nodePlacementPrediction(Client &client,
  741. const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
  742. {
  743. std::string prediction = playeritem_def.node_placement_prediction;
  744. INodeDefManager *nodedef = client.ndef();
  745. ClientMap &map = client.getEnv().getClientMap();
  746. if(prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable)
  747. {
  748. verbosestream<<"Node placement prediction for "
  749. <<playeritem_def.name<<" is "
  750. <<prediction<<std::endl;
  751. v3s16 p = neighbourpos;
  752. // Place inside node itself if buildable_to
  753. try{
  754. MapNode n_under = map.getNode(nodepos);
  755. if(nodedef->get(n_under).buildable_to)
  756. p = nodepos;
  757. else if (!nodedef->get(map.getNode(p)).buildable_to)
  758. return false;
  759. }catch(InvalidPositionException &e){}
  760. // Find id of predicted node
  761. content_t id;
  762. bool found = nodedef->getId(prediction, id);
  763. if(!found){
  764. errorstream<<"Node placement prediction failed for "
  765. <<playeritem_def.name<<" (places "
  766. <<prediction
  767. <<") - Name not known"<<std::endl;
  768. return false;
  769. }
  770. // Predict param2 for facedir and wallmounted nodes
  771. u8 param2 = 0;
  772. if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED){
  773. v3s16 dir = nodepos - neighbourpos;
  774. if(abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))){
  775. param2 = dir.Y < 0 ? 1 : 0;
  776. } else if(abs(dir.X) > abs(dir.Z)){
  777. param2 = dir.X < 0 ? 3 : 2;
  778. } else {
  779. param2 = dir.Z < 0 ? 5 : 4;
  780. }
  781. }
  782. if(nodedef->get(id).param_type_2 == CPT2_FACEDIR){
  783. v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
  784. if(abs(dir.X) > abs(dir.Z)){
  785. param2 = dir.X < 0 ? 3 : 1;
  786. } else {
  787. param2 = dir.Z < 0 ? 2 : 0;
  788. }
  789. }
  790. assert(param2 >= 0 && param2 <= 5);
  791. //Check attachment if node is in group attached_node
  792. if(((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0){
  793. static v3s16 wallmounted_dirs[8] = {
  794. v3s16(0,1,0),
  795. v3s16(0,-1,0),
  796. v3s16(1,0,0),
  797. v3s16(-1,0,0),
  798. v3s16(0,0,1),
  799. v3s16(0,0,-1),
  800. };
  801. v3s16 pp;
  802. if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED)
  803. pp = p + wallmounted_dirs[param2];
  804. else
  805. pp = p + v3s16(0,-1,0);
  806. if(!nodedef->get(map.getNode(pp)).walkable)
  807. return false;
  808. }
  809. // Add node to client map
  810. MapNode n(id, 0, param2);
  811. try{
  812. LocalPlayer* player = client.getEnv().getLocalPlayer();
  813. // Dont place node when player would be inside new node
  814. // NOTE: This is to be eventually implemented by a mod as client-side Lua
  815. if (!nodedef->get(n).walkable ||
  816. (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
  817. (nodedef->get(n).walkable &&
  818. neighbourpos != player->getStandingNodePos() + v3s16(0,1,0) &&
  819. neighbourpos != player->getStandingNodePos() + v3s16(0,2,0))) {
  820. // This triggers the required mesh update too
  821. client.addNode(p, n);
  822. return true;
  823. }
  824. }catch(InvalidPositionException &e){
  825. errorstream<<"Node placement prediction failed for "
  826. <<playeritem_def.name<<" (places "
  827. <<prediction
  828. <<") - Position not loaded"<<std::endl;
  829. }
  830. }
  831. return false;
  832. }
  833. static inline void create_formspec_menu(GUIFormSpecMenu** cur_formspec,
  834. InventoryManager *invmgr, IGameDef *gamedef,
  835. IWritableTextureSource* tsrc, IrrlichtDevice * device,
  836. IFormSource* fs_src, TextDest* txt_dest
  837. ) {
  838. if (*cur_formspec == 0) {
  839. *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr,
  840. invmgr, gamedef, tsrc, fs_src, txt_dest, cur_formspec );
  841. (*cur_formspec)->doPause = false;
  842. (*cur_formspec)->drop();
  843. }
  844. else {
  845. (*cur_formspec)->setFormSource(fs_src);
  846. (*cur_formspec)->setTextDest(txt_dest);
  847. }
  848. }
  849. #ifdef __ANDROID__
  850. #define SIZE_TAG "size[11,5.5]"
  851. #else
  852. #define SIZE_TAG "size[11,5.5,true]"
  853. #endif
  854. static void show_chat_menu(GUIFormSpecMenu** cur_formspec,
  855. InventoryManager *invmgr, IGameDef *gamedef,
  856. IWritableTextureSource* tsrc, IrrlichtDevice * device,
  857. Client* client, std::string text)
  858. {
  859. std::string formspec =
  860. FORMSPEC_VERSION_STRING
  861. SIZE_TAG
  862. "field[3,2.35;6,0.5;f_text;;" + text + "]"
  863. "button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
  864. ;
  865. /* Create menu */
  866. /* Note: FormspecFormSource and LocalFormspecHandler
  867. * are deleted by guiFormSpecMenu */
  868. FormspecFormSource* fs_src = new FormspecFormSource(formspec);
  869. LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client);
  870. create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst);
  871. }
  872. static void show_deathscreen(GUIFormSpecMenu** cur_formspec,
  873. InventoryManager *invmgr, IGameDef *gamedef,
  874. IWritableTextureSource* tsrc, IrrlichtDevice * device, Client* client)
  875. {
  876. std::string formspec =
  877. std::string(FORMSPEC_VERSION_STRING) +
  878. SIZE_TAG
  879. "bgcolor[#320000b4;true]"
  880. "label[4.85,1.35;You died.]"
  881. "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
  882. ;
  883. /* Create menu */
  884. /* Note: FormspecFormSource and LocalFormspecHandler
  885. * are deleted by guiFormSpecMenu */
  886. FormspecFormSource* fs_src = new FormspecFormSource(formspec);
  887. LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
  888. create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst);
  889. }
  890. /******************************************************************************/
  891. static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
  892. InventoryManager *invmgr, IGameDef *gamedef,
  893. IWritableTextureSource* tsrc, IrrlichtDevice * device,
  894. bool singleplayermode)
  895. {
  896. #ifdef __ANDROID__
  897. std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
  898. "No menu visible:\n"
  899. "- single tap: button activate\n"
  900. "- double tap: place/use\n"
  901. "- slide finger: look around\n"
  902. "Menu/Inventory visible:\n"
  903. "- double tap (outside):\n"
  904. " -->close\n"
  905. "- touch stack, touch slot:\n"
  906. " --> move stack\n"
  907. "- touch&drag, tap 2nd finger\n"
  908. " --> place single item to slot\n"
  909. ));
  910. #else
  911. std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
  912. "- WASD: move\n"
  913. "- Space: jump/climb\n"
  914. "- Shift: sneak/go down\n"
  915. "- Q: drop item\n"
  916. "- I: inventory\n"
  917. "- Mouse: turn/look\n"
  918. "- Mouse left: dig/punch\n"
  919. "- Mouse right: place/use\n"
  920. "- Mouse wheel: select item\n"
  921. "- T: chat\n"
  922. ));
  923. #endif
  924. float ypos = singleplayermode ? 1.0 : 0.5;
  925. std::ostringstream os;
  926. os << FORMSPEC_VERSION_STRING << SIZE_TAG
  927. << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
  928. << wide_to_narrow(wstrgettext("Continue")) << "]";
  929. if (!singleplayermode) {
  930. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
  931. << wide_to_narrow(wstrgettext("Change Password")) << "]";
  932. }
  933. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
  934. << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
  935. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
  936. << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
  937. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
  938. << wide_to_narrow(wstrgettext("Exit to OS")) << "]"
  939. << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
  940. << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
  941. << minetest_build_info << "\n"
  942. << "path_user = " << wrap_rows(porting::path_user, 20)
  943. << "\n;]";
  944. /* Create menu */
  945. /* Note: FormspecFormSource and LocalFormspecHandler *
  946. * are deleted by guiFormSpecMenu */
  947. FormspecFormSource* fs_src = new FormspecFormSource(os.str());
  948. LocalFormspecHandler* txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
  949. create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst);
  950. (*cur_formspec)->doPause = true;
  951. }
  952. /******************************************************************************/
  953. static void updateChat(Client& client, f32 dtime, bool show_debug,
  954. const v2u32& screensize, bool show_chat, u32 show_profiler,
  955. ChatBackend& chat_backend, gui::IGUIStaticText* guitext_chat,
  956. gui::IGUIFont* font)
  957. {
  958. // Add chat log output for errors to be shown in chat
  959. static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
  960. // Get new messages from error log buffer
  961. while(!chat_log_error_buf.empty()) {
  962. chat_backend.addMessage(L"", narrow_to_wide(chat_log_error_buf.get()));
  963. }
  964. // Get new messages from client
  965. std::wstring message;
  966. while (client.getChatMessage(message)) {
  967. chat_backend.addUnparsedMessage(message);
  968. }
  969. // Remove old messages
  970. chat_backend.step(dtime);
  971. // Display all messages in a static text element
  972. unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
  973. std::wstring recent_chat = chat_backend.getRecentChat();
  974. // TODO replace by fontengine fcts
  975. unsigned int line_height = font->getDimension(L"Ay").Height + font->getKerningHeight();
  976. guitext_chat->setText(recent_chat.c_str());
  977. // Update gui element size and position
  978. s32 chat_y = 5 + line_height;
  979. if (show_debug)
  980. chat_y += line_height;
  981. core::rect<s32> rect(10, chat_y, font->getDimension(recent_chat.c_str()).Width +10,
  982. chat_y + (recent_chat_count * line_height));
  983. guitext_chat->setRelativePosition(rect);
  984. // Don't show chat if disabled or empty or profiler is enabled
  985. guitext_chat->setVisible(
  986. show_chat && recent_chat_count != 0 && !show_profiler);
  987. }
  988. /******************************************************************************/
  989. void the_game(bool &kill, bool random_input, InputHandler *input,
  990. IrrlichtDevice *device, gui::IGUIFont* font, std::string map_dir,
  991. std::string playername, std::string password,
  992. std::string address /* If "", local server is used */,
  993. u16 port, std::wstring &error_message, ChatBackend &chat_backend,
  994. const SubgameSpec &gamespec /* Used for local game */,
  995. bool simple_singleplayer_mode)
  996. {
  997. GUIFormSpecMenu* current_formspec = 0;
  998. video::IVideoDriver* driver = device->getVideoDriver();
  999. scene::ISceneManager* smgr = device->getSceneManager();
  1000. // Calculate text height using the font
  1001. u32 text_height = font->getDimension(L"Random test string").Height;
  1002. /*
  1003. Draw "Loading" screen
  1004. */
  1005. {
  1006. wchar_t* text = wgettext("Loading...");
  1007. draw_load_screen(text, device, guienv, font, 0, 0);
  1008. delete[] text;
  1009. }
  1010. // Create texture source
  1011. IWritableTextureSource *tsrc = createTextureSource(device);
  1012. // Create shader source
  1013. IWritableShaderSource *shsrc = createShaderSource(device);
  1014. // These will be filled by data received from the server
  1015. // Create item definition manager
  1016. IWritableItemDefManager *itemdef = createItemDefManager();
  1017. // Create node definition manager
  1018. IWritableNodeDefManager *nodedef = createNodeDefManager();
  1019. // Sound fetcher (useful when testing)
  1020. GameOnDemandSoundFetcher soundfetcher;
  1021. // Sound manager
  1022. ISoundManager *sound = NULL;
  1023. bool sound_is_dummy = false;
  1024. #if USE_SOUND
  1025. if(g_settings->getBool("enable_sound")){
  1026. infostream<<"Attempting to use OpenAL audio"<<std::endl;
  1027. sound = createOpenALSoundManager(&soundfetcher);
  1028. if(!sound)
  1029. infostream<<"Failed to initialize OpenAL audio"<<std::endl;
  1030. } else {
  1031. infostream<<"Sound disabled."<<std::endl;
  1032. }
  1033. #endif
  1034. if(!sound){
  1035. infostream<<"Using dummy audio."<<std::endl;
  1036. sound = &dummySoundManager;
  1037. sound_is_dummy = true;
  1038. }
  1039. Server *server = NULL;
  1040. try{
  1041. // Event manager
  1042. EventManager eventmgr;
  1043. // Sound maker
  1044. SoundMaker soundmaker(sound, nodedef);
  1045. soundmaker.registerReceiver(&eventmgr);
  1046. // Create UI for modifying quicktune values
  1047. QuicktuneShortcutter quicktune;
  1048. /*
  1049. Create server.
  1050. */
  1051. if(address == ""){
  1052. wchar_t* text = wgettext("Creating server....");
  1053. draw_load_screen(text, device, guienv, font, 0, 25);
  1054. delete[] text;
  1055. infostream<<"Creating server"<<std::endl;
  1056. std::string bind_str = g_settings->get("bind_address");
  1057. Address bind_addr(0,0,0,0, port);
  1058. if (g_settings->getBool("ipv6_server")) {
  1059. bind_addr.setAddress((IPv6AddressBytes*) NULL);
  1060. }
  1061. try {
  1062. bind_addr.Resolve(bind_str.c_str());
  1063. address = bind_str;
  1064. } catch (ResolveError &e) {
  1065. infostream << "Resolving bind address \"" << bind_str
  1066. << "\" failed: " << e.what()
  1067. << " -- Listening on all addresses." << std::endl;
  1068. }
  1069. if(bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1070. error_message = L"Unable to listen on " +
  1071. narrow_to_wide(bind_addr.serializeString()) +
  1072. L" because IPv6 is disabled";
  1073. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1074. // Break out of client scope
  1075. return;
  1076. }
  1077. server = new Server(map_dir, gamespec,
  1078. simple_singleplayer_mode,
  1079. bind_addr.isIPv6());
  1080. server->start(bind_addr);
  1081. }
  1082. do{ // Client scope (breakable do-while(0))
  1083. /*
  1084. Create client
  1085. */
  1086. {
  1087. wchar_t* text = wgettext("Creating client...");
  1088. draw_load_screen(text, device, guienv, font, 0, 50);
  1089. delete[] text;
  1090. }
  1091. infostream<<"Creating client"<<std::endl;
  1092. MapDrawControl draw_control;
  1093. {
  1094. wchar_t* text = wgettext("Resolving address...");
  1095. draw_load_screen(text, device, guienv, font, 0, 75);
  1096. delete[] text;
  1097. }
  1098. Address connect_address(0,0,0,0, port);
  1099. try {
  1100. connect_address.Resolve(address.c_str());
  1101. if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
  1102. //connect_address.Resolve("localhost");
  1103. if (connect_address.isIPv6()) {
  1104. IPv6AddressBytes addr_bytes;
  1105. addr_bytes.bytes[15] = 1;
  1106. connect_address.setAddress(&addr_bytes);
  1107. } else {
  1108. connect_address.setAddress(127,0,0,1);
  1109. }
  1110. }
  1111. }
  1112. catch(ResolveError &e) {
  1113. error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
  1114. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1115. // Break out of client scope
  1116. break;
  1117. }
  1118. if(connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1119. error_message = L"Unable to connect to " +
  1120. narrow_to_wide(connect_address.serializeString()) +
  1121. L" because IPv6 is disabled";
  1122. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1123. // Break out of client scope
  1124. break;
  1125. }
  1126. /*
  1127. Create client
  1128. */
  1129. Client client(device, playername.c_str(), password, draw_control,
  1130. tsrc, shsrc, itemdef, nodedef, sound, &eventmgr,
  1131. connect_address.isIPv6());
  1132. // Client acts as our GameDef
  1133. IGameDef *gamedef = &client;
  1134. /*
  1135. Attempt to connect to the server
  1136. */
  1137. infostream<<"Connecting to server at ";
  1138. connect_address.print(&infostream);
  1139. infostream<<std::endl;
  1140. client.connect(connect_address);
  1141. /*
  1142. Wait for server to accept connection
  1143. */
  1144. bool could_connect = false;
  1145. bool connect_aborted = false;
  1146. try{
  1147. float time_counter = 0.0;
  1148. input->clear();
  1149. float fps_max = g_settings->getFloat("fps_max");
  1150. bool cloud_menu_background = g_settings->getBool("menu_clouds");
  1151. u32 lasttime = device->getTimer()->getTime();
  1152. while(device->run())
  1153. {
  1154. f32 dtime = 0.033; // in seconds
  1155. if (cloud_menu_background) {
  1156. u32 time = device->getTimer()->getTime();
  1157. if(time > lasttime)
  1158. dtime = (time - lasttime) / 1000.0;
  1159. else
  1160. dtime = 0;
  1161. lasttime = time;
  1162. }
  1163. // Update client and server
  1164. client.step(dtime);
  1165. if(server != NULL)
  1166. server->step(dtime);
  1167. // End condition
  1168. if(client.getState() == LC_Init) {
  1169. could_connect = true;
  1170. break;
  1171. }
  1172. // Break conditions
  1173. if(client.accessDenied()) {
  1174. error_message = L"Access denied. Reason: "
  1175. +client.accessDeniedReason();
  1176. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1177. break;
  1178. }
  1179. if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
  1180. connect_aborted = true;
  1181. infostream<<"Connect aborted [Escape]"<<std::endl;
  1182. break;
  1183. }
  1184. // Display status
  1185. {
  1186. wchar_t* text = wgettext("Connecting to server...");
  1187. draw_load_screen(text, device, guienv, font, dtime, 100);
  1188. delete[] text;
  1189. }
  1190. // On some computers framerate doesn't seem to be
  1191. // automatically limited
  1192. if (cloud_menu_background) {
  1193. // Time of frame without fps limit
  1194. float busytime;
  1195. u32 busytime_u32;
  1196. // not using getRealTime is necessary for wine
  1197. u32 time = device->getTimer()->getTime();
  1198. if(time > lasttime)
  1199. busytime_u32 = time - lasttime;
  1200. else
  1201. busytime_u32 = 0;
  1202. busytime = busytime_u32 / 1000.0;
  1203. // FPS limiter
  1204. u32 frametime_min = 1000./fps_max;
  1205. if(busytime_u32 < frametime_min) {
  1206. u32 sleeptime = frametime_min - busytime_u32;
  1207. device->sleep(sleeptime);
  1208. }
  1209. } else {
  1210. sleep_ms(25);
  1211. }
  1212. time_counter += dtime;
  1213. }
  1214. }
  1215. catch(con::PeerNotFoundException &e)
  1216. {}
  1217. /*
  1218. Handle failure to connect
  1219. */
  1220. if(!could_connect) {
  1221. if(error_message == L"" && !connect_aborted) {
  1222. error_message = L"Connection failed";
  1223. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1224. }
  1225. // Break out of client scope
  1226. break;
  1227. }
  1228. /*
  1229. Wait until content has been received
  1230. */
  1231. bool got_content = false;
  1232. bool content_aborted = false;
  1233. {
  1234. float time_counter = 0.0;
  1235. input->clear();
  1236. float fps_max = g_settings->getFloat("fps_max");
  1237. bool cloud_menu_background = g_settings->getBool("menu_clouds");
  1238. u32 lasttime = device->getTimer()->getTime();
  1239. while (device->run()) {
  1240. f32 dtime = 0.033; // in seconds
  1241. if (cloud_menu_background) {
  1242. u32 time = device->getTimer()->getTime();
  1243. if(time > lasttime)
  1244. dtime = (time - lasttime) / 1000.0;
  1245. else
  1246. dtime = 0;
  1247. lasttime = time;
  1248. }
  1249. // Update client and server
  1250. client.step(dtime);
  1251. if (server != NULL)
  1252. server->step(dtime);
  1253. // End condition
  1254. if (client.mediaReceived() &&
  1255. client.itemdefReceived() &&
  1256. client.nodedefReceived()) {
  1257. got_content = true;
  1258. break;
  1259. }
  1260. // Break conditions
  1261. if (client.accessDenied()) {
  1262. error_message = L"Access denied. Reason: "
  1263. +client.accessDeniedReason();
  1264. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1265. break;
  1266. }
  1267. if (client.getState() < LC_Init) {
  1268. error_message = L"Client disconnected";
  1269. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1270. break;
  1271. }
  1272. if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
  1273. content_aborted = true;
  1274. infostream<<"Connect aborted [Escape]"<<std::endl;
  1275. break;
  1276. }
  1277. // Display status
  1278. int progress=0;
  1279. if (!client.itemdefReceived())
  1280. {
  1281. wchar_t* text = wgettext("Item definitions...");
  1282. progress = 0;
  1283. draw_load_screen(text, device, guienv, font, dtime, progress);
  1284. delete[] text;
  1285. }
  1286. else if (!client.nodedefReceived())
  1287. {
  1288. wchar_t* text = wgettext("Node definitions...");
  1289. progress = 25;
  1290. draw_load_screen(text, device, guienv, font, dtime, progress);
  1291. delete[] text;
  1292. }
  1293. else
  1294. {
  1295. std::stringstream message;
  1296. message.precision(3);
  1297. message << gettext("Media...");
  1298. if ( ( USE_CURL == 0) ||
  1299. (!g_settings->getBool("enable_remote_media_server"))) {
  1300. float cur = client.getCurRate();
  1301. std::string cur_unit = gettext(" KB/s");
  1302. if (cur > 900) {
  1303. cur /= 1024.0;
  1304. cur_unit = gettext(" MB/s");
  1305. }
  1306. message << " ( " << cur << cur_unit << " )";
  1307. }
  1308. progress = 50+client.mediaReceiveProgress()*50+0.5;
  1309. draw_load_screen(narrow_to_wide(message.str().c_str()), device,
  1310. guienv, font, dtime, progress);
  1311. }
  1312. // On some computers framerate doesn't seem to be
  1313. // automatically limited
  1314. if (cloud_menu_background) {
  1315. // Time of frame without fps limit
  1316. float busytime;
  1317. u32 busytime_u32;
  1318. // not using getRealTime is necessary for wine
  1319. u32 time = device->getTimer()->getTime();
  1320. if(time > lasttime)
  1321. busytime_u32 = time - lasttime;
  1322. else
  1323. busytime_u32 = 0;
  1324. busytime = busytime_u32 / 1000.0;
  1325. // FPS limiter
  1326. u32 frametime_min = 1000./fps_max;
  1327. if(busytime_u32 < frametime_min) {
  1328. u32 sleeptime = frametime_min - busytime_u32;
  1329. device->sleep(sleeptime);
  1330. }
  1331. } else {
  1332. sleep_ms(25);
  1333. }
  1334. time_counter += dtime;
  1335. }
  1336. }
  1337. if(!got_content){
  1338. if(error_message == L"" && !content_aborted){
  1339. error_message = L"Something failed";
  1340. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1341. }
  1342. // Break out of client scope
  1343. break;
  1344. }
  1345. /*
  1346. After all content has been received:
  1347. Update cached textures, meshes and materials
  1348. */
  1349. client.afterContentReceived(device,font);
  1350. /*
  1351. Create the camera node
  1352. */
  1353. Camera camera(smgr, draw_control, gamedef);
  1354. if (!camera.successfullyCreated(error_message))
  1355. return;
  1356. f32 camera_yaw = 0; // "right/left"
  1357. f32 camera_pitch = 0; // "up/down"
  1358. /*
  1359. Clouds
  1360. */
  1361. Clouds *clouds = NULL;
  1362. if(g_settings->getBool("enable_clouds"))
  1363. {
  1364. clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
  1365. }
  1366. /*
  1367. Skybox thingy
  1368. */
  1369. Sky *sky = NULL;
  1370. sky = new Sky(smgr->getRootSceneNode(), smgr, -1);
  1371. scene::ISceneNode* skybox = NULL;
  1372. /*
  1373. A copy of the local inventory
  1374. */
  1375. Inventory local_inventory(itemdef);
  1376. /*
  1377. Find out size of crack animation
  1378. */
  1379. int crack_animation_length = 5;
  1380. {
  1381. video::ITexture *t = tsrc->getTexture("crack_anylength.png");
  1382. v2u32 size = t->getOriginalSize();
  1383. crack_animation_length = size.Y / size.X;
  1384. }
  1385. /*
  1386. Add some gui stuff
  1387. */
  1388. // First line of debug text
  1389. gui::IGUIStaticText *guitext = guienv->addStaticText(
  1390. L"Minetest",
  1391. core::rect<s32>(0, 0, 0, 0),
  1392. false, false, guiroot);
  1393. // Second line of debug text
  1394. gui::IGUIStaticText *guitext2 = guienv->addStaticText(
  1395. L"",
  1396. core::rect<s32>(0, 0, 0, 0),
  1397. false, false, guiroot);
  1398. // At the middle of the screen
  1399. // Object infos are shown in this
  1400. gui::IGUIStaticText *guitext_info = guienv->addStaticText(
  1401. L"",
  1402. core::rect<s32>(0,0,400,text_height*5+5) + v2s32(100,200),
  1403. false, true, guiroot);
  1404. // Status text (displays info when showing and hiding GUI stuff, etc.)
  1405. gui::IGUIStaticText *guitext_status = guienv->addStaticText(
  1406. L"<Status>",
  1407. core::rect<s32>(0,0,0,0),
  1408. false, false, guiroot);
  1409. guitext_status->setVisible(false);
  1410. std::wstring statustext;
  1411. float statustext_time = 0;
  1412. // Chat text
  1413. gui::IGUIStaticText *guitext_chat = guienv->addStaticText(
  1414. L"",
  1415. core::rect<s32>(0,0,0,0),
  1416. //false, false); // Disable word wrap as of now
  1417. false, true, guiroot);
  1418. // Remove stale "recent" chat messages from previous connections
  1419. chat_backend.clearRecentChat();
  1420. // Chat backend and console
  1421. GUIChatConsole *gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, &chat_backend, &client);
  1422. // Profiler text (size is updated when text is updated)
  1423. gui::IGUIStaticText *guitext_profiler = guienv->addStaticText(
  1424. L"<Profiler>",
  1425. core::rect<s32>(0,0,0,0),
  1426. false, false, guiroot);
  1427. guitext_profiler->setBackgroundColor(video::SColor(120,0,0,0));
  1428. guitext_profiler->setVisible(false);
  1429. guitext_profiler->setWordWrap(true);
  1430. #ifdef HAVE_TOUCHSCREENGUI
  1431. if (g_touchscreengui)
  1432. g_touchscreengui->init(tsrc,porting::getDisplayDensity());
  1433. #endif
  1434. /*
  1435. Some statistics are collected in these
  1436. */
  1437. u32 drawtime = 0;
  1438. u32 beginscenetime = 0;
  1439. u32 endscenetime = 0;
  1440. float recent_turn_speed = 0.0;
  1441. ProfilerGraph graph;
  1442. // Initially clear the profiler
  1443. Profiler::GraphValues dummyvalues;
  1444. g_profiler->graphGet(dummyvalues);
  1445. float nodig_delay_timer = 0.0;
  1446. float dig_time = 0.0;
  1447. u16 dig_index = 0;
  1448. PointedThing pointed_old;
  1449. bool digging = false;
  1450. bool ldown_for_dig = false;
  1451. float damage_flash = 0;
  1452. float jump_timer = 0;
  1453. bool reset_jump_timer = false;
  1454. const float object_hit_delay = 0.2;
  1455. float object_hit_delay_timer = 0.0;
  1456. float time_from_last_punch = 10;
  1457. float update_draw_list_timer = 0.0;
  1458. v3f update_draw_list_last_cam_dir;
  1459. bool invert_mouse = g_settings->getBool("invert_mouse");
  1460. bool update_wielded_item_trigger = true;
  1461. bool show_hud = true;
  1462. bool show_chat = true;
  1463. bool force_fog_off = false;
  1464. f32 fog_range = 100*BS;
  1465. bool disable_camera_update = false;
  1466. bool show_debug = g_settings->getBool("show_debug");
  1467. bool show_profiler_graph = false;
  1468. u32 show_profiler = 0;
  1469. u32 show_profiler_max = 3; // Number of pages
  1470. float time_of_day = 0;
  1471. float time_of_day_smooth = 0;
  1472. float repeat_rightclick_timer = 0;
  1473. /*
  1474. Shader constants
  1475. */
  1476. shsrc->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
  1477. sky, &force_fog_off, &fog_range, &client));
  1478. /*
  1479. Main loop
  1480. */
  1481. bool first_loop_after_window_activation = true;
  1482. // TODO: Convert the static interval timers to these
  1483. // Interval limiter for profiler
  1484. IntervalLimiter m_profiler_interval;
  1485. // Time is in milliseconds
  1486. // NOTE: getRealTime() causes strange problems in wine (imprecision?)
  1487. // NOTE: So we have to use getTime() and call run()s between them
  1488. u32 lasttime = device->getTimer()->getTime();
  1489. LocalPlayer* player = client.getEnv().getLocalPlayer();
  1490. player->hurt_tilt_timer = 0;
  1491. player->hurt_tilt_strength = 0;
  1492. /*
  1493. HUD object
  1494. */
  1495. Hud hud(driver, smgr, guienv, font, text_height,
  1496. gamedef, player, &local_inventory);
  1497. core::stringw str = L"Minetest [";
  1498. str += driver->getName();
  1499. str += "]";
  1500. device->setWindowCaption(str.c_str());
  1501. // Info text
  1502. std::wstring infotext;
  1503. for(;;)
  1504. {
  1505. if(device->run() == false || kill == true ||
  1506. g_gamecallback->shutdown_requested)
  1507. break;
  1508. v2u32 screensize = driver->getScreenSize();
  1509. // Time of frame without fps limit
  1510. float busytime;
  1511. u32 busytime_u32;
  1512. {
  1513. // not using getRealTime is necessary for wine
  1514. u32 time = device->getTimer()->getTime();
  1515. if(time > lasttime)
  1516. busytime_u32 = time - lasttime;
  1517. else
  1518. busytime_u32 = 0;
  1519. busytime = busytime_u32 / 1000.0;
  1520. }
  1521. g_profiler->graphAdd("mainloop_other", busytime - (float)drawtime/1000.0f);
  1522. // Necessary for device->getTimer()->getTime()
  1523. device->run();
  1524. /*
  1525. FPS limiter
  1526. */
  1527. {
  1528. float fps_max = g_menumgr.pausesGame() ?
  1529. g_settings->getFloat("pause_fps_max") :
  1530. g_settings->getFloat("fps_max");
  1531. u32 frametime_min = 1000./fps_max;
  1532. if(busytime_u32 < frametime_min)
  1533. {
  1534. u32 sleeptime = frametime_min - busytime_u32;
  1535. device->sleep(sleeptime);
  1536. g_profiler->graphAdd("mainloop_sleep", (float)sleeptime/1000.0f);
  1537. }
  1538. }
  1539. // Necessary for device->getTimer()->getTime()
  1540. device->run();
  1541. /*
  1542. Time difference calculation
  1543. */
  1544. f32 dtime; // in seconds
  1545. u32 time = device->getTimer()->getTime();
  1546. if(time > lasttime)
  1547. dtime = (time - lasttime) / 1000.0;
  1548. else
  1549. dtime = 0;
  1550. lasttime = time;
  1551. g_profiler->graphAdd("mainloop_dtime", dtime);
  1552. /* Run timers */
  1553. if(nodig_delay_timer >= 0)
  1554. nodig_delay_timer -= dtime;
  1555. if(object_hit_delay_timer >= 0)
  1556. object_hit_delay_timer -= dtime;
  1557. time_from_last_punch += dtime;
  1558. g_profiler->add("Elapsed time", dtime);
  1559. g_profiler->avg("FPS", 1./dtime);
  1560. /*
  1561. Time average and jitter calculation
  1562. */
  1563. static f32 dtime_avg1 = 0.0;
  1564. dtime_avg1 = dtime_avg1 * 0.96 + dtime * 0.04;
  1565. f32 dtime_jitter1 = dtime - dtime_avg1;
  1566. static f32 dtime_jitter1_max_sample = 0.0;
  1567. static f32 dtime_jitter1_max_fraction = 0.0;
  1568. {
  1569. static f32 jitter1_max = 0.0;
  1570. static f32 counter = 0.0;
  1571. if(dtime_jitter1 > jitter1_max)
  1572. jitter1_max = dtime_jitter1;
  1573. counter += dtime;
  1574. if(counter > 0.0)
  1575. {
  1576. counter -= 3.0;
  1577. dtime_jitter1_max_sample = jitter1_max;
  1578. dtime_jitter1_max_fraction
  1579. = dtime_jitter1_max_sample / (dtime_avg1+0.001);
  1580. jitter1_max = 0.0;
  1581. }
  1582. }
  1583. /*
  1584. Busytime average and jitter calculation
  1585. */
  1586. static f32 busytime_avg1 = 0.0;
  1587. busytime_avg1 = busytime_avg1 * 0.98 + busytime * 0.02;
  1588. f32 busytime_jitter1 = busytime - busytime_avg1;
  1589. static f32 busytime_jitter1_max_sample = 0.0;
  1590. static f32 busytime_jitter1_min_sample = 0.0;
  1591. {
  1592. static f32 jitter1_max = 0.0;
  1593. static f32 jitter1_min = 0.0;
  1594. static f32 counter = 0.0;
  1595. if(busytime_jitter1 > jitter1_max)
  1596. jitter1_max = busytime_jitter1;
  1597. if(busytime_jitter1 < jitter1_min)
  1598. jitter1_min = busytime_jitter1;
  1599. counter += dtime;
  1600. if(counter > 0.0){
  1601. counter -= 3.0;
  1602. busytime_jitter1_max_sample = jitter1_max;
  1603. busytime_jitter1_min_sample = jitter1_min;
  1604. jitter1_max = 0.0;
  1605. jitter1_min = 0.0;
  1606. }
  1607. }
  1608. /*
  1609. Handle miscellaneous stuff
  1610. */
  1611. if(client.accessDenied())
  1612. {
  1613. error_message = L"Access denied. Reason: "
  1614. +client.accessDeniedReason();
  1615. errorstream<<wide_to_narrow(error_message)<<std::endl;
  1616. break;
  1617. }
  1618. if(g_gamecallback->disconnect_requested)
  1619. {
  1620. g_gamecallback->disconnect_requested = false;
  1621. break;
  1622. }
  1623. if(g_gamecallback->changepassword_requested)
  1624. {
  1625. (new GUIPasswordChange(guienv, guiroot, -1,
  1626. &g_menumgr, &client))->drop();
  1627. g_gamecallback->changepassword_requested = false;
  1628. }
  1629. if(g_gamecallback->changevolume_requested)
  1630. {
  1631. (new GUIVolumeChange(guienv, guiroot, -1,
  1632. &g_menumgr, &client))->drop();
  1633. g_gamecallback->changevolume_requested = false;
  1634. }
  1635. /* Process TextureSource's queue */
  1636. tsrc->processQueue();
  1637. /* Process ItemDefManager's queue */
  1638. itemdef->processQueue(gamedef);
  1639. /*
  1640. Process ShaderSource's queue
  1641. */
  1642. shsrc->processQueue();
  1643. /*
  1644. Random calculations
  1645. */
  1646. hud.resizeHotbar();
  1647. // Hilight boxes collected during the loop and displayed
  1648. std::vector<aabb3f> hilightboxes;
  1649. /* reset infotext */
  1650. infotext = L"";
  1651. /*
  1652. Profiler
  1653. */
  1654. float profiler_print_interval =
  1655. g_settings->getFloat("profiler_print_interval");
  1656. bool print_to_log = true;
  1657. if(profiler_print_interval == 0){
  1658. print_to_log = false;
  1659. profiler_print_interval = 5;
  1660. }
  1661. if(m_profiler_interval.step(dtime, profiler_print_interval))
  1662. {
  1663. if(print_to_log){
  1664. infostream<<"Profiler:"<<std::endl;
  1665. g_profiler->print(infostream);
  1666. }
  1667. update_profiler_gui(guitext_profiler, font, text_height,
  1668. show_profiler, show_profiler_max);
  1669. g_profiler->clear();
  1670. }
  1671. /*
  1672. Direct handling of user input
  1673. */
  1674. // Reset input if window not active or some menu is active
  1675. if(device->isWindowActive() == false
  1676. || noMenuActive() == false
  1677. || guienv->hasFocus(gui_chat_console))
  1678. {
  1679. input->clear();
  1680. }
  1681. if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen())
  1682. {
  1683. gui_chat_console->closeConsoleAtOnce();
  1684. }
  1685. // Input handler step() (used by the random input generator)
  1686. input->step(dtime);
  1687. #ifdef HAVE_TOUCHSCREENGUI
  1688. if (g_touchscreengui) {
  1689. g_touchscreengui->step(dtime);
  1690. }
  1691. #endif
  1692. #ifdef __ANDROID__
  1693. if (current_formspec != 0)
  1694. current_formspec->getAndroidUIInput();
  1695. #endif
  1696. // Increase timer for doubleclick of "jump"
  1697. if(g_settings->getBool("doubletap_jump") && jump_timer <= 0.2)
  1698. jump_timer += dtime;
  1699. /*
  1700. Launch menus and trigger stuff according to keys
  1701. */
  1702. if(input->wasKeyDown(getKeySetting("keymap_drop")))
  1703. {
  1704. // drop selected item
  1705. IDropAction *a = new IDropAction();
  1706. a->count = 0;
  1707. a->from_inv.setCurrentPlayer();
  1708. a->from_list = "main";
  1709. a->from_i = client.getPlayerItem();
  1710. client.inventoryAction(a);
  1711. }
  1712. else if(input->wasKeyDown(getKeySetting("keymap_inventory")))
  1713. {
  1714. infostream<<"the_game: "
  1715. <<"Launching inventory"<<std::endl;
  1716. PlayerInventoryFormSource* fs_src = new PlayerInventoryFormSource(&client);
  1717. TextDest* txt_dst = new TextDestPlayerInventory(&client);
  1718. create_formspec_menu(&current_formspec, &client, gamedef, tsrc, device, fs_src, txt_dst);
  1719. InventoryLocation inventoryloc;
  1720. inventoryloc.setCurrentPlayer();
  1721. current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
  1722. }
  1723. else if(input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey))
  1724. {
  1725. show_pause_menu(&current_formspec, &client, gamedef, tsrc, device,
  1726. simple_singleplayer_mode);
  1727. }
  1728. else if(input->wasKeyDown(getKeySetting("keymap_chat")))
  1729. {
  1730. show_chat_menu(&current_formspec, &client, gamedef, tsrc, device,
  1731. &client,"");
  1732. }
  1733. else if(input->wasKeyDown(getKeySetting("keymap_cmd")))
  1734. {
  1735. show_chat_menu(&current_formspec, &client, gamedef, tsrc, device,
  1736. &client,"/");
  1737. }
  1738. else if(input->wasKeyDown(getKeySetting("keymap_console")))
  1739. {
  1740. if (!gui_chat_console->isOpenInhibited())
  1741. {
  1742. // Open up to over half of the screen
  1743. gui_chat_console->openConsole(0.6);
  1744. guienv->setFocus(gui_chat_console);
  1745. }
  1746. }
  1747. else if(input->wasKeyDown(getKeySetting("keymap_freemove")))
  1748. {
  1749. if(g_settings->getBool("free_move"))
  1750. {
  1751. g_settings->set("free_move","false");
  1752. statustext = L"free_move disabled";
  1753. statustext_time = 0;
  1754. }
  1755. else
  1756. {
  1757. g_settings->set("free_move","true");
  1758. statustext = L"free_move enabled";
  1759. statustext_time = 0;
  1760. if(!client.checkPrivilege("fly"))
  1761. statustext += L" (note: no 'fly' privilege)";
  1762. }
  1763. }
  1764. else if(input->wasKeyDown(getKeySetting("keymap_jump")))
  1765. {
  1766. if(g_settings->getBool("doubletap_jump") && jump_timer < 0.2)
  1767. {
  1768. if(g_settings->getBool("free_move"))
  1769. {
  1770. g_settings->set("free_move","false");
  1771. statustext = L"free_move disabled";
  1772. statustext_time = 0;
  1773. }
  1774. else
  1775. {
  1776. g_settings->set("free_move","true");
  1777. statustext = L"free_move enabled";
  1778. statustext_time = 0;
  1779. if(!client.checkPrivilege("fly"))
  1780. statustext += L" (note: no 'fly' privilege)";
  1781. }
  1782. }
  1783. reset_jump_timer = true;
  1784. }
  1785. else if(input->wasKeyDown(getKeySetting("keymap_fastmove")))
  1786. {
  1787. if(g_settings->getBool("fast_move"))
  1788. {
  1789. g_settings->set("fast_move","false");
  1790. statustext = L"fast_move disabled";
  1791. statustext_time = 0;
  1792. }
  1793. else
  1794. {
  1795. g_settings->set("fast_move","true");
  1796. statustext = L"fast_move enabled";
  1797. statustext_time = 0;
  1798. if(!client.checkPrivilege("fast"))
  1799. statustext += L" (note: no 'fast' privilege)";
  1800. }
  1801. }
  1802. else if(input->wasKeyDown(getKeySetting("keymap_noclip")))
  1803. {
  1804. if(g_settings->getBool("noclip"))
  1805. {
  1806. g_settings->set("noclip","false");
  1807. statustext = L"noclip disabled";
  1808. statustext_time = 0;
  1809. }
  1810. else
  1811. {
  1812. g_settings->set("noclip","true");
  1813. statustext = L"noclip enabled";
  1814. statustext_time = 0;
  1815. if(!client.checkPrivilege("noclip"))
  1816. statustext += L" (note: no 'noclip' privilege)";
  1817. }
  1818. }
  1819. else if(input->wasKeyDown(getKeySetting("keymap_screenshot")))
  1820. {
  1821. irr::video::IImage* const raw_image = driver->createScreenShot();
  1822. if (raw_image) {
  1823. irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
  1824. raw_image->getDimension());
  1825. if (image) {
  1826. raw_image->copyTo(image);
  1827. irr::c8 filename[256];
  1828. snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
  1829. g_settings->get("screenshot_path").c_str(),
  1830. device->getTimer()->getRealTime());
  1831. if (driver->writeImageToFile(image, filename)) {
  1832. std::wstringstream sstr;
  1833. sstr << "Saved screenshot to '" << filename << "'";
  1834. infostream << "Saved screenshot to '" << filename << "'" << std::endl;
  1835. statustext = sstr.str();
  1836. statustext_time = 0;
  1837. } else {
  1838. infostream << "Failed to save screenshot '" << filename << "'" << std::endl;
  1839. }
  1840. image->drop();
  1841. }
  1842. raw_image->drop();
  1843. }
  1844. }
  1845. else if(input->wasKeyDown(getKeySetting("keymap_toggle_hud")))
  1846. {
  1847. show_hud = !show_hud;
  1848. if(show_hud) {
  1849. statustext = L"HUD shown";
  1850. client.setHighlighted(client.getHighlighted(), true);
  1851. } else {
  1852. statustext = L"HUD hidden";
  1853. client.setHighlighted(client.getHighlighted(), false);
  1854. }
  1855. statustext_time = 0;
  1856. }
  1857. else if(input->wasKeyDown(getKeySetting("keymap_toggle_chat")))
  1858. {
  1859. show_chat = !show_chat;
  1860. if(show_chat)
  1861. statustext = L"Chat shown";
  1862. else
  1863. statustext = L"Chat hidden";
  1864. statustext_time = 0;
  1865. }
  1866. else if(input->wasKeyDown(getKeySetting("keymap_toggle_force_fog_off")))
  1867. {
  1868. force_fog_off = !force_fog_off;
  1869. if(force_fog_off)
  1870. statustext = L"Fog disabled";
  1871. else
  1872. statustext = L"Fog enabled";
  1873. statustext_time = 0;
  1874. }
  1875. else if(input->wasKeyDown(getKeySetting("keymap_toggle_update_camera")))
  1876. {
  1877. disable_camera_update = !disable_camera_update;
  1878. if(disable_camera_update)
  1879. statustext = L"Camera update disabled";
  1880. else
  1881. statustext = L"Camera update enabled";
  1882. statustext_time = 0;
  1883. }
  1884. else if(input->wasKeyDown(getKeySetting("keymap_toggle_debug")))
  1885. {
  1886. // Initial / 3x toggle: Chat only
  1887. // 1x toggle: Debug text with chat
  1888. // 2x toggle: Debug text with profiler graph
  1889. if(!show_debug)
  1890. {
  1891. show_debug = true;
  1892. show_profiler_graph = false;
  1893. statustext = L"Debug info shown";
  1894. statustext_time = 0;
  1895. }
  1896. else if(show_profiler_graph)
  1897. {
  1898. show_debug = false;
  1899. show_profiler_graph = false;
  1900. statustext = L"Debug info and profiler graph hidden";
  1901. statustext_time = 0;
  1902. }
  1903. else
  1904. {
  1905. show_profiler_graph = true;
  1906. statustext = L"Profiler graph shown";
  1907. statustext_time = 0;
  1908. }
  1909. }
  1910. else if(input->wasKeyDown(getKeySetting("keymap_toggle_profiler")))
  1911. {
  1912. show_profiler = (show_profiler + 1) % (show_profiler_max + 1);
  1913. // FIXME: This updates the profiler with incomplete values
  1914. update_profiler_gui(guitext_profiler, font, text_height,
  1915. show_profiler, show_profiler_max);
  1916. if(show_profiler != 0)
  1917. {
  1918. std::wstringstream sstr;
  1919. sstr<<"Profiler shown (page "<<show_profiler
  1920. <<" of "<<show_profiler_max<<")";
  1921. statustext = sstr.str();
  1922. statustext_time = 0;
  1923. }
  1924. else
  1925. {
  1926. statustext = L"Profiler hidden";
  1927. statustext_time = 0;
  1928. }
  1929. }
  1930. else if(input->wasKeyDown(getKeySetting("keymap_increase_viewing_range_min")))
  1931. {
  1932. s16 range = g_settings->getS16("viewing_range_nodes_min");
  1933. s16 range_new = range + 10;
  1934. g_settings->set("viewing_range_nodes_min", itos(range_new));
  1935. statustext = narrow_to_wide(
  1936. "Minimum viewing range changed to "
  1937. + itos(range_new));
  1938. statustext_time = 0;
  1939. }
  1940. else if(input->wasKeyDown(getKeySetting("keymap_decrease_viewing_range_min")))
  1941. {
  1942. s16 range = g_settings->getS16("viewing_range_nodes_min");
  1943. s16 range_new = range - 10;
  1944. if(range_new < 0)
  1945. range_new = range;
  1946. g_settings->set("viewing_range_nodes_min",
  1947. itos(range_new));
  1948. statustext = narrow_to_wide(
  1949. "Minimum viewing range changed to "
  1950. + itos(range_new));
  1951. statustext_time = 0;
  1952. }
  1953. // Reset jump_timer
  1954. if(!input->isKeyDown(getKeySetting("keymap_jump")) && reset_jump_timer)
  1955. {
  1956. reset_jump_timer = false;
  1957. jump_timer = 0.0;
  1958. }
  1959. // Handle QuicktuneShortcutter
  1960. if(input->wasKeyDown(getKeySetting("keymap_quicktune_next")))
  1961. quicktune.next();
  1962. if(input->wasKeyDown(getKeySetting("keymap_quicktune_prev")))
  1963. quicktune.prev();
  1964. if(input->wasKeyDown(getKeySetting("keymap_quicktune_inc")))
  1965. quicktune.inc();
  1966. if(input->wasKeyDown(getKeySetting("keymap_quicktune_dec")))
  1967. quicktune.dec();
  1968. {
  1969. std::string msg = quicktune.getMessage();
  1970. if(msg != ""){
  1971. statustext = narrow_to_wide(msg);
  1972. statustext_time = 0;
  1973. }
  1974. }
  1975. // Item selection with mouse wheel
  1976. u16 new_playeritem = client.getPlayerItem();
  1977. {
  1978. s32 wheel = input->getMouseWheel();
  1979. u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1,
  1980. player->hud_hotbar_itemcount-1);
  1981. if(wheel < 0)
  1982. {
  1983. if(new_playeritem < max_item)
  1984. new_playeritem++;
  1985. else
  1986. new_playeritem = 0;
  1987. }
  1988. else if(wheel > 0)
  1989. {
  1990. if(new_playeritem > 0)
  1991. new_playeritem--;
  1992. else
  1993. new_playeritem = max_item;
  1994. }
  1995. }
  1996. // Item selection
  1997. for(u16 i=0; i<10; i++)
  1998. {
  1999. const KeyPress *kp = NumberKey + (i + 1) % 10;
  2000. if(input->wasKeyDown(*kp))
  2001. {
  2002. if(i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount)
  2003. {
  2004. new_playeritem = i;
  2005. infostream<<"Selected item: "
  2006. <<new_playeritem<<std::endl;
  2007. }
  2008. }
  2009. }
  2010. // Viewing range selection
  2011. if(input->wasKeyDown(getKeySetting("keymap_rangeselect")))
  2012. {
  2013. draw_control.range_all = !draw_control.range_all;
  2014. if(draw_control.range_all)
  2015. {
  2016. infostream<<"Enabled full viewing range"<<std::endl;
  2017. statustext = L"Enabled full viewing range";
  2018. statustext_time = 0;
  2019. }
  2020. else
  2021. {
  2022. infostream<<"Disabled full viewing range"<<std::endl;
  2023. statustext = L"Disabled full viewing range";
  2024. statustext_time = 0;
  2025. }
  2026. }
  2027. // Print debug stacks
  2028. if(input->wasKeyDown(getKeySetting("keymap_print_debug_stacks")))
  2029. {
  2030. dstream<<"-----------------------------------------"
  2031. <<std::endl;
  2032. dstream<<DTIME<<"Printing debug stacks:"<<std::endl;
  2033. dstream<<"-----------------------------------------"
  2034. <<std::endl;
  2035. debug_stacks_print();
  2036. }
  2037. /*
  2038. Mouse and camera control
  2039. NOTE: Do this before client.setPlayerControl() to not cause a camera lag of one frame
  2040. */
  2041. float turn_amount = 0;
  2042. if((device->isWindowActive() && noMenuActive()) || random_input)
  2043. {
  2044. #ifndef __ANDROID__
  2045. if(!random_input)
  2046. {
  2047. // Mac OSX gets upset if this is set every frame
  2048. if(device->getCursorControl()->isVisible())
  2049. device->getCursorControl()->setVisible(false);
  2050. }
  2051. #endif
  2052. if(first_loop_after_window_activation){
  2053. //infostream<<"window active, first loop"<<std::endl;
  2054. first_loop_after_window_activation = false;
  2055. } else {
  2056. #ifdef HAVE_TOUCHSCREENGUI
  2057. if (g_touchscreengui) {
  2058. camera_yaw = g_touchscreengui->getYaw();
  2059. camera_pitch = g_touchscreengui->getPitch();
  2060. } else {
  2061. #endif
  2062. s32 dx = input->getMousePos().X - (driver->getScreenSize().Width/2);
  2063. s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height/2);
  2064. if ((invert_mouse)
  2065. || (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)) {
  2066. dy = -dy;
  2067. }
  2068. //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
  2069. /*const float keyspeed = 500;
  2070. if(input->isKeyDown(irr::KEY_UP))
  2071. dy -= dtime * keyspeed;
  2072. if(input->isKeyDown(irr::KEY_DOWN))
  2073. dy += dtime * keyspeed;
  2074. if(input->isKeyDown(irr::KEY_LEFT))
  2075. dx -= dtime * keyspeed;
  2076. if(input->isKeyDown(irr::KEY_RIGHT))
  2077. dx += dtime * keyspeed;*/
  2078. float d = g_settings->getFloat("mouse_sensitivity");
  2079. d = rangelim(d, 0.01, 100.0);
  2080. camera_yaw -= dx*d;
  2081. camera_pitch += dy*d;
  2082. turn_amount = v2f(dx, dy).getLength() * d;
  2083. #ifdef HAVE_TOUCHSCREENGUI
  2084. }
  2085. #endif
  2086. if(camera_pitch < -89.5) camera_pitch = -89.5;
  2087. if(camera_pitch > 89.5) camera_pitch = 89.5;
  2088. }
  2089. input->setMousePos((driver->getScreenSize().Width/2),
  2090. (driver->getScreenSize().Height/2));
  2091. }
  2092. else{
  2093. #ifndef ANDROID
  2094. // Mac OSX gets upset if this is set every frame
  2095. if(device->getCursorControl()->isVisible() == false)
  2096. device->getCursorControl()->setVisible(true);
  2097. #endif
  2098. //infostream<<"window inactive"<<std::endl;
  2099. first_loop_after_window_activation = true;
  2100. }
  2101. recent_turn_speed = recent_turn_speed * 0.9 + turn_amount * 0.1;
  2102. //std::cerr<<"recent_turn_speed = "<<recent_turn_speed<<std::endl;
  2103. /*
  2104. Player speed control
  2105. */
  2106. {
  2107. /*bool a_up,
  2108. bool a_down,
  2109. bool a_left,
  2110. bool a_right,
  2111. bool a_jump,
  2112. bool a_superspeed,
  2113. bool a_sneak,
  2114. bool a_LMB,
  2115. bool a_RMB,
  2116. float a_pitch,
  2117. float a_yaw*/
  2118. PlayerControl control(
  2119. input->isKeyDown(getKeySetting("keymap_forward")),
  2120. input->isKeyDown(getKeySetting("keymap_backward")),
  2121. input->isKeyDown(getKeySetting("keymap_left")),
  2122. input->isKeyDown(getKeySetting("keymap_right")),
  2123. input->isKeyDown(getKeySetting("keymap_jump")),
  2124. input->isKeyDown(getKeySetting("keymap_special1")),
  2125. input->isKeyDown(getKeySetting("keymap_sneak")),
  2126. input->getLeftState(),
  2127. input->getRightState(),
  2128. camera_pitch,
  2129. camera_yaw
  2130. );
  2131. client.setPlayerControl(control);
  2132. LocalPlayer* player = client.getEnv().getLocalPlayer();
  2133. player->keyPressed=
  2134. (((int)input->isKeyDown(getKeySetting("keymap_forward")) & 0x1) << 0) |
  2135. (((int)input->isKeyDown(getKeySetting("keymap_backward")) & 0x1) << 1) |
  2136. (((int)input->isKeyDown(getKeySetting("keymap_left")) & 0x1) << 2) |
  2137. (((int)input->isKeyDown(getKeySetting("keymap_right")) & 0x1) << 3) |
  2138. (((int)input->isKeyDown(getKeySetting("keymap_jump")) & 0x1) << 4) |
  2139. (((int)input->isKeyDown(getKeySetting("keymap_special1")) & 0x1) << 5) |
  2140. (((int)input->isKeyDown(getKeySetting("keymap_sneak")) & 0x1) << 6) |
  2141. (((int)input->getLeftState() & 0x1) << 7) |
  2142. (((int)input->getRightState() & 0x1) << 8);
  2143. }
  2144. /*
  2145. Run server, client (and process environments)
  2146. */
  2147. bool can_be_and_is_paused =
  2148. (simple_singleplayer_mode && g_menumgr.pausesGame());
  2149. if(can_be_and_is_paused)
  2150. {
  2151. // No time passes
  2152. dtime = 0;
  2153. }
  2154. else
  2155. {
  2156. if(server != NULL)
  2157. {
  2158. //TimeTaker timer("server->step(dtime)");
  2159. server->step(dtime);
  2160. }
  2161. {
  2162. //TimeTaker timer("client.step(dtime)");
  2163. client.step(dtime);
  2164. }
  2165. }
  2166. {
  2167. // Read client events
  2168. for(;;) {
  2169. ClientEvent event = client.getClientEvent();
  2170. if(event.type == CE_NONE) {
  2171. break;
  2172. }
  2173. else if(event.type == CE_PLAYER_DAMAGE &&
  2174. client.getHP() != 0) {
  2175. //u16 damage = event.player_damage.amount;
  2176. //infostream<<"Player damage: "<<damage<<std::endl;
  2177. damage_flash += 100.0;
  2178. damage_flash += 8.0 * event.player_damage.amount;
  2179. player->hurt_tilt_timer = 1.5;
  2180. player->hurt_tilt_strength = event.player_damage.amount/4;
  2181. player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0);
  2182. MtEvent *e = new SimpleTriggerEvent("PlayerDamage");
  2183. gamedef->event()->put(e);
  2184. }
  2185. else if(event.type == CE_PLAYER_FORCE_MOVE) {
  2186. camera_yaw = event.player_force_move.yaw;
  2187. camera_pitch = event.player_force_move.pitch;
  2188. }
  2189. else if(event.type == CE_DEATHSCREEN) {
  2190. show_deathscreen(&current_formspec, &client, gamedef, tsrc,
  2191. device, &client);
  2192. chat_backend.addMessage(L"", L"You died.");
  2193. /* Handle visualization */
  2194. damage_flash = 0;
  2195. LocalPlayer* player = client.getEnv().getLocalPlayer();
  2196. player->hurt_tilt_timer = 0;
  2197. player->hurt_tilt_strength = 0;
  2198. }
  2199. else if (event.type == CE_SHOW_FORMSPEC) {
  2200. FormspecFormSource* fs_src =
  2201. new FormspecFormSource(*(event.show_formspec.formspec));
  2202. TextDestPlayerInventory* txt_dst =
  2203. new TextDestPlayerInventory(&client,*(event.show_formspec.formname));
  2204. create_formspec_menu(&current_formspec, &client, gamedef,
  2205. tsrc, device, fs_src, txt_dst);
  2206. delete(event.show_formspec.formspec);
  2207. delete(event.show_formspec.formname);
  2208. }
  2209. else if(event.type == CE_SPAWN_PARTICLE) {
  2210. LocalPlayer* player = client.getEnv().getLocalPlayer();
  2211. video::ITexture *texture =
  2212. gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
  2213. new Particle(gamedef, smgr, player, client.getEnv(),
  2214. *event.spawn_particle.pos,
  2215. *event.spawn_particle.vel,
  2216. *event.spawn_particle.acc,
  2217. event.spawn_particle.expirationtime,
  2218. event.spawn_particle.size,
  2219. event.spawn_particle.collisiondetection,
  2220. event.spawn_particle.vertical,
  2221. texture,
  2222. v2f(0.0, 0.0),
  2223. v2f(1.0, 1.0));
  2224. }
  2225. else if(event.type == CE_ADD_PARTICLESPAWNER) {
  2226. LocalPlayer* player = client.getEnv().getLocalPlayer();
  2227. video::ITexture *texture =
  2228. gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
  2229. new ParticleSpawner(gamedef, smgr, player,
  2230. event.add_particlespawner.amount,
  2231. event.add_particlespawner.spawntime,
  2232. *event.add_particlespawner.minpos,
  2233. *event.add_particlespawner.maxpos,
  2234. *event.add_particlespawner.minvel,
  2235. *event.add_particlespawner.maxvel,
  2236. *event.add_particlespawner.minacc,
  2237. *event.add_particlespawner.maxacc,
  2238. event.add_particlespawner.minexptime,
  2239. event.add_particlespawner.maxexptime,
  2240. event.add_particlespawner.minsize,
  2241. event.add_particlespawner.maxsize,
  2242. event.add_particlespawner.collisiondetection,
  2243. event.add_particlespawner.vertical,
  2244. texture,
  2245. event.add_particlespawner.id);
  2246. }
  2247. else if(event.type == CE_DELETE_PARTICLESPAWNER) {
  2248. delete_particlespawner (event.delete_particlespawner.id);
  2249. }
  2250. else if (event.type == CE_HUDADD) {
  2251. u32 id = event.hudadd.id;
  2252. HudElement *e = player->getHud(id);
  2253. if (e != NULL) {
  2254. delete event.hudadd.pos;
  2255. delete event.hudadd.name;
  2256. delete event.hudadd.scale;
  2257. delete event.hudadd.text;
  2258. delete event.hudadd.align;
  2259. delete event.hudadd.offset;
  2260. delete event.hudadd.world_pos;
  2261. delete event.hudadd.size;
  2262. continue;
  2263. }
  2264. e = new HudElement;
  2265. e->type = (HudElementType)event.hudadd.type;
  2266. e->pos = *event.hudadd.pos;
  2267. e->name = *event.hudadd.name;
  2268. e->scale = *event.hudadd.scale;
  2269. e->text = *event.hudadd.text;
  2270. e->number = event.hudadd.number;
  2271. e->item = event.hudadd.item;
  2272. e->dir = event.hudadd.dir;
  2273. e->align = *event.hudadd.align;
  2274. e->offset = *event.hudadd.offset;
  2275. e->world_pos = *event.hudadd.world_pos;
  2276. e->size = *event.hudadd.size;
  2277. u32 new_id = player->addHud(e);
  2278. //if this isn't true our huds aren't consistent
  2279. assert(new_id == id);
  2280. delete event.hudadd.pos;
  2281. delete event.hudadd.name;
  2282. delete event.hudadd.scale;
  2283. delete event.hudadd.text;
  2284. delete event.hudadd.align;
  2285. delete event.hudadd.offset;
  2286. delete event.hudadd.world_pos;
  2287. delete event.hudadd.size;
  2288. }
  2289. else if (event.type == CE_HUDRM) {
  2290. HudElement* e = player->removeHud(event.hudrm.id);
  2291. if (e != NULL)
  2292. delete (e);
  2293. }
  2294. else if (event.type == CE_HUDCHANGE) {
  2295. u32 id = event.hudchange.id;
  2296. HudElement* e = player->getHud(id);
  2297. if (e == NULL)
  2298. {
  2299. delete event.hudchange.v3fdata;
  2300. delete event.hudchange.v2fdata;
  2301. delete event.hudchange.sdata;
  2302. delete event.hudchange.v2s32data;
  2303. continue;
  2304. }
  2305. switch (event.hudchange.stat) {
  2306. case HUD_STAT_POS:
  2307. e->pos = *event.hudchange.v2fdata;
  2308. break;
  2309. case HUD_STAT_NAME:
  2310. e->name = *event.hudchange.sdata;
  2311. break;
  2312. case HUD_STAT_SCALE:
  2313. e->scale = *event.hudchange.v2fdata;
  2314. break;
  2315. case HUD_STAT_TEXT:
  2316. e->text = *event.hudchange.sdata;
  2317. break;
  2318. case HUD_STAT_NUMBER:
  2319. e->number = event.hudchange.data;
  2320. break;
  2321. case HUD_STAT_ITEM:
  2322. e->item = event.hudchange.data;
  2323. break;
  2324. case HUD_STAT_DIR:
  2325. e->dir = event.hudchange.data;
  2326. break;
  2327. case HUD_STAT_ALIGN:
  2328. e->align = *event.hudchange.v2fdata;
  2329. break;
  2330. case HUD_STAT_OFFSET:
  2331. e->offset = *event.hudchange.v2fdata;
  2332. break;
  2333. case HUD_STAT_WORLD_POS:
  2334. e->world_pos = *event.hudchange.v3fdata;
  2335. break;
  2336. case HUD_STAT_SIZE:
  2337. e->size = *event.hudchange.v2s32data;
  2338. break;
  2339. }
  2340. delete event.hudchange.v3fdata;
  2341. delete event.hudchange.v2fdata;
  2342. delete event.hudchange.sdata;
  2343. delete event.hudchange.v2s32data;
  2344. }
  2345. else if (event.type == CE_SET_SKY) {
  2346. sky->setVisible(false);
  2347. if(skybox){
  2348. skybox->remove();
  2349. skybox = NULL;
  2350. }
  2351. // Handle according to type
  2352. if(*event.set_sky.type == "regular") {
  2353. sky->setVisible(true);
  2354. }
  2355. else if(*event.set_sky.type == "skybox" &&
  2356. event.set_sky.params->size() == 6) {
  2357. sky->setFallbackBgColor(*event.set_sky.bgcolor);
  2358. skybox = smgr->addSkyBoxSceneNode(
  2359. tsrc->getTexture((*event.set_sky.params)[0]),
  2360. tsrc->getTexture((*event.set_sky.params)[1]),
  2361. tsrc->getTexture((*event.set_sky.params)[2]),
  2362. tsrc->getTexture((*event.set_sky.params)[3]),
  2363. tsrc->getTexture((*event.set_sky.params)[4]),
  2364. tsrc->getTexture((*event.set_sky.params)[5]));
  2365. }
  2366. // Handle everything else as plain color
  2367. else {
  2368. if(*event.set_sky.type != "plain")
  2369. infostream<<"Unknown sky type: "
  2370. <<(*event.set_sky.type)<<std::endl;
  2371. sky->setFallbackBgColor(*event.set_sky.bgcolor);
  2372. }
  2373. delete event.set_sky.bgcolor;
  2374. delete event.set_sky.type;
  2375. delete event.set_sky.params;
  2376. }
  2377. else if (event.type == CE_OVERRIDE_DAY_NIGHT_RATIO) {
  2378. bool enable = event.override_day_night_ratio.do_override;
  2379. u32 value = event.override_day_night_ratio.ratio_f * 1000;
  2380. client.getEnv().setDayNightRatioOverride(enable, value);
  2381. }
  2382. }
  2383. }
  2384. //TimeTaker //timer2("//timer2");
  2385. /*
  2386. For interaction purposes, get info about the held item
  2387. - What item is it?
  2388. - Is it a usable item?
  2389. - Can it point to liquids?
  2390. */
  2391. ItemStack playeritem;
  2392. {
  2393. InventoryList *mlist = local_inventory.getList("main");
  2394. if((mlist != NULL) && (client.getPlayerItem() < mlist->getSize()))
  2395. playeritem = mlist->getItem(client.getPlayerItem());
  2396. }
  2397. const ItemDefinition &playeritem_def =
  2398. playeritem.getDefinition(itemdef);
  2399. ToolCapabilities playeritem_toolcap =
  2400. playeritem.getToolCapabilities(itemdef);
  2401. /*
  2402. Update camera
  2403. */
  2404. v3s16 old_camera_offset = camera.getOffset();
  2405. LocalPlayer* player = client.getEnv().getLocalPlayer();
  2406. float full_punch_interval = playeritem_toolcap.full_punch_interval;
  2407. float tool_reload_ratio = time_from_last_punch / full_punch_interval;
  2408. if(input->wasKeyDown(getKeySetting("keymap_camera_mode"))) {
  2409. camera.toggleCameraMode();
  2410. GenericCAO* playercao = player->getCAO();
  2411. assert( playercao != NULL );
  2412. if (camera.getCameraMode() > CAMERA_MODE_FIRST) {
  2413. playercao->setVisible(true);
  2414. }
  2415. else {
  2416. playercao->setVisible(false);
  2417. }
  2418. }
  2419. tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
  2420. camera.update(player, dtime, busytime, tool_reload_ratio,
  2421. client.getEnv());
  2422. camera.step(dtime);
  2423. v3f player_position = player->getPosition();
  2424. v3f camera_position = camera.getPosition();
  2425. v3f camera_direction = camera.getDirection();
  2426. f32 camera_fov = camera.getFovMax();
  2427. v3s16 camera_offset = camera.getOffset();
  2428. bool camera_offset_changed = (camera_offset != old_camera_offset);
  2429. if(!disable_camera_update){
  2430. client.getEnv().getClientMap().updateCamera(camera_position,
  2431. camera_direction, camera_fov, camera_offset);
  2432. if (camera_offset_changed){
  2433. client.updateCameraOffset(camera_offset);
  2434. client.getEnv().updateCameraOffset(camera_offset);
  2435. if (clouds)
  2436. clouds->updateCameraOffset(camera_offset);
  2437. }
  2438. }
  2439. // Update sound listener
  2440. sound->updateListener(camera.getCameraNode()->getPosition()+intToFloat(camera_offset, BS),
  2441. v3f(0,0,0), // velocity
  2442. camera.getDirection(),
  2443. camera.getCameraNode()->getUpVector());
  2444. sound->setListenerGain(g_settings->getFloat("sound_volume"));
  2445. /*
  2446. Update sound maker
  2447. */
  2448. {
  2449. soundmaker.step(dtime);
  2450. ClientMap &map = client.getEnv().getClientMap();
  2451. MapNode n = map.getNodeNoEx(player->getStandingNodePos());
  2452. soundmaker.m_player_step_sound = nodedef->get(n).sound_footstep;
  2453. }
  2454. /*
  2455. Calculate what block is the crosshair pointing to
  2456. */
  2457. //u32 t1 = device->getTimer()->getRealTime();
  2458. f32 d = playeritem_def.range; // max. distance
  2459. f32 d_hand = itemdef->get("").range;
  2460. if(d < 0 && d_hand >= 0)
  2461. d = d_hand;
  2462. else if(d < 0)
  2463. d = 4.0;
  2464. core::line3d<f32> shootline(camera_position,
  2465. camera_position + camera_direction * BS * (d+1));
  2466. // prevent player pointing anything in front-view
  2467. if (camera.getCameraMode() == CAMERA_MODE_THIRD_FRONT)
  2468. shootline = core::line3d<f32>(0,0,0,0,0,0);
  2469. #ifdef HAVE_TOUCHSCREENGUI
  2470. if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
  2471. shootline = g_touchscreengui->getShootline();
  2472. shootline.start += intToFloat(camera_offset,BS);
  2473. shootline.end += intToFloat(camera_offset,BS);
  2474. }
  2475. #endif
  2476. ClientActiveObject *selected_object = NULL;
  2477. PointedThing pointed = getPointedThing(
  2478. // input
  2479. &client, player_position, camera_direction,
  2480. camera_position, shootline, d,
  2481. playeritem_def.liquids_pointable, !ldown_for_dig,
  2482. camera_offset,
  2483. // output
  2484. hilightboxes,
  2485. selected_object);
  2486. if(pointed != pointed_old)
  2487. {
  2488. infostream<<"Pointing at "<<pointed.dump()<<std::endl;
  2489. if (g_settings->getBool("enable_node_highlighting"))
  2490. client.setHighlighted(pointed.node_undersurface, show_hud);
  2491. }
  2492. /*
  2493. Stop digging when
  2494. - releasing left mouse button
  2495. - pointing away from node
  2496. */
  2497. if(digging)
  2498. {
  2499. if(input->getLeftReleased())
  2500. {
  2501. infostream<<"Left button released"
  2502. <<" (stopped digging)"<<std::endl;
  2503. digging = false;
  2504. }
  2505. else if(pointed != pointed_old)
  2506. {
  2507. if (pointed.type == POINTEDTHING_NODE
  2508. && pointed_old.type == POINTEDTHING_NODE
  2509. && pointed.node_undersurface == pointed_old.node_undersurface)
  2510. {
  2511. // Still pointing to the same node,
  2512. // but a different face. Don't reset.
  2513. }
  2514. else
  2515. {
  2516. infostream<<"Pointing away from node"
  2517. <<" (stopped digging)"<<std::endl;
  2518. digging = false;
  2519. }
  2520. }
  2521. if(!digging)
  2522. {
  2523. client.interact(1, pointed_old);
  2524. client.setCrack(-1, v3s16(0,0,0));
  2525. dig_time = 0.0;
  2526. }
  2527. }
  2528. if(!digging && ldown_for_dig && !input->getLeftState())
  2529. {
  2530. ldown_for_dig = false;
  2531. }
  2532. bool left_punch = false;
  2533. soundmaker.m_player_leftpunch_sound.name = "";
  2534. if(input->getRightState())
  2535. repeat_rightclick_timer += dtime;
  2536. else
  2537. repeat_rightclick_timer = 0;
  2538. if(playeritem_def.usable && input->getLeftState())
  2539. {
  2540. if(input->getLeftClicked())
  2541. client.interact(4, pointed);
  2542. }
  2543. else if(pointed.type == POINTEDTHING_NODE)
  2544. {
  2545. v3s16 nodepos = pointed.node_undersurface;
  2546. v3s16 neighbourpos = pointed.node_abovesurface;
  2547. /*
  2548. Check information text of node
  2549. */
  2550. ClientMap &map = client.getEnv().getClientMap();
  2551. NodeMetadata *meta = map.getNodeMetadata(nodepos);
  2552. if(meta){
  2553. infotext = narrow_to_wide(meta->getString("infotext"));
  2554. } else {
  2555. MapNode n = map.getNode(nodepos);
  2556. if(nodedef->get(n).tiledef[0].name == "unknown_node.png"){
  2557. infotext = L"Unknown node: ";
  2558. infotext += narrow_to_wide(nodedef->get(n).name);
  2559. }
  2560. }
  2561. /*
  2562. Handle digging
  2563. */
  2564. if(nodig_delay_timer <= 0.0 && input->getLeftState()
  2565. && client.checkPrivilege("interact"))
  2566. {
  2567. if(!digging)
  2568. {
  2569. infostream<<"Started digging"<<std::endl;
  2570. client.interact(0, pointed);
  2571. digging = true;
  2572. ldown_for_dig = true;
  2573. }
  2574. MapNode n = client.getEnv().getClientMap().getNode(nodepos);
  2575. // NOTE: Similar piece of code exists on the server side for
  2576. // cheat detection.
  2577. // Get digging parameters
  2578. DigParams params = getDigParams(nodedef->get(n).groups,
  2579. &playeritem_toolcap);
  2580. // If can't dig, try hand
  2581. if(!params.diggable){
  2582. const ItemDefinition &hand = itemdef->get("");
  2583. const ToolCapabilities *tp = hand.tool_capabilities;
  2584. if(tp)
  2585. params = getDigParams(nodedef->get(n).groups, tp);
  2586. }
  2587. float dig_time_complete = 0.0;
  2588. if(params.diggable == false)
  2589. {
  2590. // I guess nobody will wait for this long
  2591. dig_time_complete = 10000000.0;
  2592. }
  2593. else
  2594. {
  2595. dig_time_complete = params.time;
  2596. if (g_settings->getBool("enable_particles"))
  2597. {
  2598. const ContentFeatures &features =
  2599. client.getNodeDefManager()->get(n);
  2600. addPunchingParticles
  2601. (gamedef, smgr, player, client.getEnv(),
  2602. nodepos, features.tiles);
  2603. }
  2604. }
  2605. if(dig_time_complete >= 0.001)
  2606. {
  2607. dig_index = (u16)((float)crack_animation_length
  2608. * dig_time/dig_time_complete);
  2609. }
  2610. // This is for torches
  2611. else
  2612. {
  2613. dig_index = crack_animation_length;
  2614. }
  2615. SimpleSoundSpec sound_dig = nodedef->get(n).sound_dig;
  2616. if(sound_dig.exists() && params.diggable){
  2617. if(sound_dig.name == "__group"){
  2618. if(params.main_group != ""){
  2619. soundmaker.m_player_leftpunch_sound.gain = 0.5;
  2620. soundmaker.m_player_leftpunch_sound.name =
  2621. std::string("default_dig_") +
  2622. params.main_group;
  2623. }
  2624. } else{
  2625. soundmaker.m_player_leftpunch_sound = sound_dig;
  2626. }
  2627. }
  2628. // Don't show cracks if not diggable
  2629. if(dig_time_complete >= 100000.0)
  2630. {
  2631. }
  2632. else if(dig_index < crack_animation_length)
  2633. {
  2634. //TimeTaker timer("client.setTempMod");
  2635. //infostream<<"dig_index="<<dig_index<<std::endl;
  2636. client.setCrack(dig_index, nodepos);
  2637. }
  2638. else
  2639. {
  2640. infostream<<"Digging completed"<<std::endl;
  2641. client.interact(2, pointed);
  2642. client.setCrack(-1, v3s16(0,0,0));
  2643. MapNode wasnode = map.getNode(nodepos);
  2644. client.removeNode(nodepos);
  2645. if (g_settings->getBool("enable_particles"))
  2646. {
  2647. const ContentFeatures &features =
  2648. client.getNodeDefManager()->get(wasnode);
  2649. addDiggingParticles
  2650. (gamedef, smgr, player, client.getEnv(),
  2651. nodepos, features.tiles);
  2652. }
  2653. dig_time = 0;
  2654. digging = false;
  2655. nodig_delay_timer = dig_time_complete
  2656. / (float)crack_animation_length;
  2657. // We don't want a corresponding delay to
  2658. // very time consuming nodes
  2659. if(nodig_delay_timer > 0.3)
  2660. nodig_delay_timer = 0.3;
  2661. // We want a slight delay to very little
  2662. // time consuming nodes
  2663. float mindelay = 0.15;
  2664. if(nodig_delay_timer < mindelay)
  2665. nodig_delay_timer = mindelay;
  2666. // Send event to trigger sound
  2667. MtEvent *e = new NodeDugEvent(nodepos, wasnode);
  2668. gamedef->event()->put(e);
  2669. }
  2670. if(dig_time_complete < 100000.0)
  2671. dig_time += dtime;
  2672. else {
  2673. dig_time = 0;
  2674. client.setCrack(-1, nodepos);
  2675. }
  2676. camera.setDigging(0); // left click animation
  2677. }
  2678. if((input->getRightClicked() ||
  2679. repeat_rightclick_timer >=
  2680. g_settings->getFloat("repeat_rightclick_time")) &&
  2681. client.checkPrivilege("interact"))
  2682. {
  2683. repeat_rightclick_timer = 0;
  2684. infostream<<"Ground right-clicked"<<std::endl;
  2685. if(meta && meta->getString("formspec") != "" && !random_input
  2686. && !input->isKeyDown(getKeySetting("keymap_sneak")))
  2687. {
  2688. infostream<<"Launching custom inventory view"<<std::endl;
  2689. InventoryLocation inventoryloc;
  2690. inventoryloc.setNodeMeta(nodepos);
  2691. NodeMetadataFormSource* fs_src = new NodeMetadataFormSource(
  2692. &client.getEnv().getClientMap(), nodepos);
  2693. TextDest* txt_dst = new TextDestNodeMetadata(nodepos, &client);
  2694. create_formspec_menu(&current_formspec, &client, gamedef,
  2695. tsrc, device, fs_src, txt_dst);
  2696. current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
  2697. }
  2698. // Otherwise report right click to server
  2699. else
  2700. {
  2701. camera.setDigging(1); // right click animation (always shown for feedback)
  2702. // If the wielded item has node placement prediction,
  2703. // make that happen
  2704. bool placed = nodePlacementPrediction(client,
  2705. playeritem_def,
  2706. nodepos, neighbourpos);
  2707. if(placed) {
  2708. // Report to server
  2709. client.interact(3, pointed);
  2710. // Read the sound
  2711. soundmaker.m_player_rightpunch_sound =
  2712. playeritem_def.sound_place;
  2713. } else {
  2714. soundmaker.m_player_rightpunch_sound =
  2715. SimpleSoundSpec();
  2716. }
  2717. if (playeritem_def.node_placement_prediction == "" ||
  2718. nodedef->get(map.getNode(nodepos)).rightclickable)
  2719. client.interact(3, pointed); // Report to server
  2720. }
  2721. }
  2722. }
  2723. else if(pointed.type == POINTEDTHING_OBJECT)
  2724. {
  2725. infotext = narrow_to_wide(selected_object->infoText());
  2726. if(infotext == L"" && show_debug){
  2727. infotext = narrow_to_wide(selected_object->debugInfoText());
  2728. }
  2729. //if(input->getLeftClicked())
  2730. if(input->getLeftState())
  2731. {
  2732. bool do_punch = false;
  2733. bool do_punch_damage = false;
  2734. if(object_hit_delay_timer <= 0.0){
  2735. do_punch = true;
  2736. do_punch_damage = true;
  2737. object_hit_delay_timer = object_hit_delay;
  2738. }
  2739. if(input->getLeftClicked()){
  2740. do_punch = true;
  2741. }
  2742. if(do_punch){
  2743. infostream<<"Left-clicked object"<<std::endl;
  2744. left_punch = true;
  2745. }
  2746. if(do_punch_damage){
  2747. // Report direct punch
  2748. v3f objpos = selected_object->getPosition();
  2749. v3f dir = (objpos - player_position).normalize();
  2750. bool disable_send = selected_object->directReportPunch(
  2751. dir, &playeritem, time_from_last_punch);
  2752. time_from_last_punch = 0;
  2753. if(!disable_send)
  2754. client.interact(0, pointed);
  2755. }
  2756. }
  2757. else if(input->getRightClicked())
  2758. {
  2759. infostream<<"Right-clicked object"<<std::endl;
  2760. client.interact(3, pointed); // place
  2761. }
  2762. }
  2763. else if(input->getLeftState())
  2764. {
  2765. // When button is held down in air, show continuous animation
  2766. left_punch = true;
  2767. }
  2768. pointed_old = pointed;
  2769. if(left_punch || input->getLeftClicked())
  2770. {
  2771. camera.setDigging(0); // left click animation
  2772. }
  2773. input->resetLeftClicked();
  2774. input->resetRightClicked();
  2775. input->resetLeftReleased();
  2776. input->resetRightReleased();
  2777. /*
  2778. Calculate stuff for drawing
  2779. */
  2780. /*
  2781. Fog range
  2782. */
  2783. if(draw_control.range_all)
  2784. fog_range = 100000*BS;
  2785. else {
  2786. fog_range = draw_control.wanted_range*BS + 0.0*MAP_BLOCKSIZE*BS;
  2787. fog_range = MYMIN(fog_range, (draw_control.farthest_drawn+20)*BS);
  2788. fog_range *= 0.9;
  2789. }
  2790. /*
  2791. Calculate general brightness
  2792. */
  2793. u32 daynight_ratio = client.getEnv().getDayNightRatio();
  2794. float time_brightness = decode_light_f((float)daynight_ratio/1000.0);
  2795. float direct_brightness = 0;
  2796. bool sunlight_seen = false;
  2797. if(g_settings->getBool("free_move")){
  2798. direct_brightness = time_brightness;
  2799. sunlight_seen = true;
  2800. } else {
  2801. ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
  2802. float old_brightness = sky->getBrightness();
  2803. direct_brightness = (float)client.getEnv().getClientMap()
  2804. .getBackgroundBrightness(MYMIN(fog_range*1.2, 60*BS),
  2805. daynight_ratio, (int)(old_brightness*255.5), &sunlight_seen)
  2806. / 255.0;
  2807. }
  2808. time_of_day = client.getEnv().getTimeOfDayF();
  2809. float maxsm = 0.05;
  2810. if(fabs(time_of_day - time_of_day_smooth) > maxsm &&
  2811. fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
  2812. fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
  2813. time_of_day_smooth = time_of_day;
  2814. float todsm = 0.05;
  2815. if(time_of_day_smooth > 0.8 && time_of_day < 0.2)
  2816. time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
  2817. + (time_of_day+1.0) * todsm;
  2818. else
  2819. time_of_day_smooth = time_of_day_smooth * (1.0-todsm)
  2820. + time_of_day * todsm;
  2821. sky->update(time_of_day_smooth, time_brightness, direct_brightness,
  2822. sunlight_seen,camera.getCameraMode(), player->getYaw(),
  2823. player->getPitch());
  2824. video::SColor bgcolor = sky->getBgColor();
  2825. video::SColor skycolor = sky->getSkyColor();
  2826. /*
  2827. Update clouds
  2828. */
  2829. if(clouds){
  2830. if(sky->getCloudsVisible()){
  2831. clouds->setVisible(true);
  2832. clouds->step(dtime);
  2833. clouds->update(v2f(player_position.X, player_position.Z),
  2834. sky->getCloudColor());
  2835. } else{
  2836. clouds->setVisible(false);
  2837. }
  2838. }
  2839. /*
  2840. Update particles
  2841. */
  2842. allparticles_step(dtime);
  2843. allparticlespawners_step(dtime, client.getEnv());
  2844. /*
  2845. Fog
  2846. */
  2847. if(g_settings->getBool("enable_fog") && !force_fog_off)
  2848. {
  2849. driver->setFog(
  2850. bgcolor,
  2851. video::EFT_FOG_LINEAR,
  2852. fog_range*0.4,
  2853. fog_range*1.0,
  2854. 0.01,
  2855. false, // pixel fog
  2856. false // range fog
  2857. );
  2858. }
  2859. else
  2860. {
  2861. driver->setFog(
  2862. bgcolor,
  2863. video::EFT_FOG_LINEAR,
  2864. 100000*BS,
  2865. 110000*BS,
  2866. 0.01,
  2867. false, // pixel fog
  2868. false // range fog
  2869. );
  2870. }
  2871. /*
  2872. Update gui stuff (0ms)
  2873. */
  2874. //TimeTaker guiupdatetimer("Gui updating");
  2875. if(show_debug)
  2876. {
  2877. static float drawtime_avg = 0;
  2878. drawtime_avg = drawtime_avg * 0.95 + (float)drawtime*0.05;
  2879. /*static float beginscenetime_avg = 0;
  2880. beginscenetime_avg = beginscenetime_avg * 0.95 + (float)beginscenetime*0.05;
  2881. static float scenetime_avg = 0;
  2882. scenetime_avg = scenetime_avg * 0.95 + (float)scenetime*0.05;
  2883. static float endscenetime_avg = 0;
  2884. endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/
  2885. u16 fps = (1.0/dtime_avg1);
  2886. std::ostringstream os(std::ios_base::binary);
  2887. os<<std::fixed
  2888. <<"Minetest "<<minetest_version_hash
  2889. <<" FPS = "<<fps
  2890. <<" (R: range_all="<<draw_control.range_all<<")"
  2891. <<std::setprecision(0)
  2892. <<" drawtime = "<<drawtime_avg
  2893. <<std::setprecision(1)
  2894. <<", dtime_jitter = "
  2895. <<(dtime_jitter1_max_fraction * 100.0)<<" %"
  2896. <<std::setprecision(1)
  2897. <<", v_range = "<<draw_control.wanted_range
  2898. <<std::setprecision(3)
  2899. <<", RTT = "<<client.getRTT();
  2900. guitext->setText(narrow_to_wide(os.str()).c_str());
  2901. guitext->setVisible(true);
  2902. }
  2903. else if(show_hud || show_chat)
  2904. {
  2905. std::ostringstream os(std::ios_base::binary);
  2906. os<<"Minetest "<<minetest_version_hash;
  2907. guitext->setText(narrow_to_wide(os.str()).c_str());
  2908. guitext->setVisible(true);
  2909. }
  2910. else
  2911. {
  2912. guitext->setVisible(false);
  2913. }
  2914. if (guitext->isVisible())
  2915. {
  2916. core::rect<s32> rect(
  2917. 5,
  2918. 5,
  2919. screensize.X,
  2920. 5 + text_height
  2921. );
  2922. guitext->setRelativePosition(rect);
  2923. }
  2924. if(show_debug)
  2925. {
  2926. std::ostringstream os(std::ios_base::binary);
  2927. os<<std::setprecision(1)<<std::fixed
  2928. <<"(" <<(player_position.X/BS)
  2929. <<", "<<(player_position.Y/BS)
  2930. <<", "<<(player_position.Z/BS)
  2931. <<") (yaw="<<(wrapDegrees_0_360(camera_yaw))
  2932. <<") (seed = "<<((u64)client.getMapSeed())
  2933. <<")";
  2934. guitext2->setText(narrow_to_wide(os.str()).c_str());
  2935. guitext2->setVisible(true);
  2936. core::rect<s32> rect(
  2937. 5,
  2938. 5 + text_height,
  2939. screensize.X,
  2940. 5 + (text_height * 2)
  2941. );
  2942. guitext2->setRelativePosition(rect);
  2943. }
  2944. else
  2945. {
  2946. guitext2->setVisible(false);
  2947. }
  2948. {
  2949. guitext_info->setText(infotext.c_str());
  2950. guitext_info->setVisible(show_hud && g_menumgr.menuCount() == 0);
  2951. }
  2952. {
  2953. float statustext_time_max = 1.5;
  2954. if(!statustext.empty())
  2955. {
  2956. statustext_time += dtime;
  2957. if(statustext_time >= statustext_time_max)
  2958. {
  2959. statustext = L"";
  2960. statustext_time = 0;
  2961. }
  2962. }
  2963. guitext_status->setText(statustext.c_str());
  2964. guitext_status->setVisible(!statustext.empty());
  2965. if(!statustext.empty())
  2966. {
  2967. s32 status_y = screensize.Y - 130;
  2968. core::rect<s32> rect(
  2969. 10,
  2970. status_y - guitext_status->getTextHeight(),
  2971. 10 + guitext_status->getTextWidth(),
  2972. status_y
  2973. );
  2974. guitext_status->setRelativePosition(rect);
  2975. // Fade out
  2976. video::SColor initial_color(255,0,0,0);
  2977. if(guienv->getSkin())
  2978. initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
  2979. video::SColor final_color = initial_color;
  2980. final_color.setAlpha(0);
  2981. video::SColor fade_color =
  2982. initial_color.getInterpolated_quadratic(
  2983. initial_color,
  2984. final_color,
  2985. pow(statustext_time / (float)statustext_time_max, 2.0f));
  2986. guitext_status->setOverrideColor(fade_color);
  2987. guitext_status->enableOverrideColor(true);
  2988. }
  2989. }
  2990. /*
  2991. Get chat messages from client
  2992. */
  2993. updateChat(client, dtime, show_debug, screensize, show_chat,
  2994. show_profiler, chat_backend, guitext_chat, font);
  2995. /*
  2996. Inventory
  2997. */
  2998. if(client.getPlayerItem() != new_playeritem)
  2999. {
  3000. client.selectPlayerItem(new_playeritem);
  3001. }
  3002. if(client.getLocalInventoryUpdated())
  3003. {
  3004. //infostream<<"Updating local inventory"<<std::endl;
  3005. client.getLocalInventory(local_inventory);
  3006. update_wielded_item_trigger = true;
  3007. }
  3008. if(update_wielded_item_trigger)
  3009. {
  3010. update_wielded_item_trigger = false;
  3011. // Update wielded tool
  3012. InventoryList *mlist = local_inventory.getList("main");
  3013. ItemStack item;
  3014. if((mlist != NULL) && (client.getPlayerItem() < mlist->getSize()))
  3015. item = mlist->getItem(client.getPlayerItem());
  3016. camera.wield(item, client.getPlayerItem());
  3017. }
  3018. /*
  3019. Update block draw list every 200ms or when camera direction has
  3020. changed much
  3021. */
  3022. update_draw_list_timer += dtime;
  3023. if(update_draw_list_timer >= 0.2 ||
  3024. update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2 ||
  3025. camera_offset_changed){
  3026. update_draw_list_timer = 0;
  3027. client.getEnv().getClientMap().updateDrawList(driver);
  3028. update_draw_list_last_cam_dir = camera_direction;
  3029. }
  3030. /*
  3031. make sure menu is on top
  3032. */
  3033. if ((!noMenuActive()) && (current_formspec)) {
  3034. guiroot->bringToFront(current_formspec);
  3035. }
  3036. /*
  3037. Drawing begins
  3038. */
  3039. TimeTaker tt_draw("mainloop: draw");
  3040. {
  3041. TimeTaker timer("beginScene");
  3042. driver->beginScene(true, true, skycolor);
  3043. beginscenetime = timer.stop(true);
  3044. }
  3045. draw_scene(driver, smgr, camera, client, player, hud, guienv,
  3046. hilightboxes, screensize, skycolor, show_hud);
  3047. /*
  3048. Profiler graph
  3049. */
  3050. if(show_profiler_graph)
  3051. {
  3052. graph.draw(10, screensize.Y - 10, driver, font);
  3053. }
  3054. /*
  3055. Damage flash
  3056. */
  3057. if(damage_flash > 0.0)
  3058. {
  3059. video::SColor color(std::min(damage_flash, 180.0f),180,0,0);
  3060. driver->draw2DRectangle(color,
  3061. core::rect<s32>(0,0,screensize.X,screensize.Y),
  3062. NULL);
  3063. damage_flash -= 100.0*dtime;
  3064. }
  3065. /*
  3066. Damage camera tilt
  3067. */
  3068. if(player->hurt_tilt_timer > 0.0)
  3069. {
  3070. player->hurt_tilt_timer -= dtime*5;
  3071. if(player->hurt_tilt_timer < 0)
  3072. player->hurt_tilt_strength = 0;
  3073. }
  3074. /*
  3075. End scene
  3076. */
  3077. {
  3078. TimeTaker timer("endScene");
  3079. driver->endScene();
  3080. endscenetime = timer.stop(true);
  3081. }
  3082. drawtime = tt_draw.stop(true);
  3083. g_profiler->graphAdd("mainloop_draw", (float)drawtime/1000.0f);
  3084. /*
  3085. End of drawing
  3086. */
  3087. /*
  3088. Log times and stuff for visualization
  3089. */
  3090. Profiler::GraphValues values;
  3091. g_profiler->graphGet(values);
  3092. graph.put(values);
  3093. }
  3094. /*
  3095. Drop stuff
  3096. */
  3097. if (clouds)
  3098. clouds->drop();
  3099. if (gui_chat_console)
  3100. gui_chat_console->drop();
  3101. if (sky)
  3102. sky->drop();
  3103. clear_particles();
  3104. /* cleanup menus */
  3105. while (g_menumgr.menuCount() > 0)
  3106. {
  3107. g_menumgr.m_stack.front()->setVisible(false);
  3108. g_menumgr.deletingMenu(g_menumgr.m_stack.front());
  3109. }
  3110. /*
  3111. Draw a "shutting down" screen, which will be shown while the map
  3112. generator and other stuff quits
  3113. */
  3114. {
  3115. wchar_t* text = wgettext("Shutting down stuff...");
  3116. draw_load_screen(text, device, guienv, font, 0, -1, false);
  3117. delete[] text;
  3118. }
  3119. chat_backend.addMessage(L"", L"# Disconnected.");
  3120. chat_backend.addMessage(L"", L"");
  3121. client.Stop();
  3122. //force answer all texture and shader jobs (TODO return empty values)
  3123. while(!client.isShutdown()) {
  3124. tsrc->processQueue();
  3125. shsrc->processQueue();
  3126. sleep_ms(100);
  3127. }
  3128. // Client scope (client is destructed before destructing *def and tsrc)
  3129. }while(0);
  3130. } // try-catch
  3131. catch(SerializationError &e)
  3132. {
  3133. error_message = L"A serialization error occurred:\n"
  3134. + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
  3135. L" running a different version of Minetest.";
  3136. errorstream<<wide_to_narrow(error_message)<<std::endl;
  3137. }
  3138. catch(ServerError &e) {
  3139. error_message = narrow_to_wide(e.what());
  3140. errorstream << "ServerError: " << e.what() << std::endl;
  3141. }
  3142. catch(ModError &e) {
  3143. errorstream << "ModError: " << e.what() << std::endl;
  3144. error_message = narrow_to_wide(e.what()) + wgettext("\nCheck debug.txt for details.");
  3145. }
  3146. if(!sound_is_dummy)
  3147. delete sound;
  3148. //has to be deleted first to stop all server threads
  3149. delete server;
  3150. delete tsrc;
  3151. delete shsrc;
  3152. delete nodedef;
  3153. delete itemdef;
  3154. //extended resource accounting
  3155. infostream << "Irrlicht resources after cleanup:" << std::endl;
  3156. infostream << "\tRemaining meshes : "
  3157. << device->getSceneManager()->getMeshCache()->getMeshCount() << std::endl;
  3158. infostream << "\tRemaining textures : "
  3159. << driver->getTextureCount() << std::endl;
  3160. for (unsigned int i = 0; i < driver->getTextureCount(); i++ ) {
  3161. irr::video::ITexture* texture = driver->getTextureByIndex(i);
  3162. infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str()
  3163. << std::endl;
  3164. }
  3165. clearTextureNameCache();
  3166. infostream << "\tRemaining materials: "
  3167. << driver-> getMaterialRendererCount ()
  3168. << " (note: irrlicht doesn't support removing renderers)"<< std::endl;
  3169. }