1 // Hyperbolic Rogue
2 // This is the main file when the online version of HyperRogue is compiled with Emscripten.
3 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
4 
5 #ifndef ISWEB
6 #define ISWEB 1
7 #define ISMINI 0
8 #define CAP_INV 0
9 #define CAP_URL 1
10 #define GLES_ONLY
11 #define SCU
12 #endif
13 
14 #if CAP_ROGUEVIZ || defined(MAXMDIM)
15 #define MAXMDIM 4
16 #else
17 #define MAXMDIM 3
18 #endif
19 
20 // we want newconformist, but we don't want CAP_GD there
21 #define CAP_NCONF 1
22 #define CAP_GD 0
23 
24 #ifndef EMSCRIPTEN
25 #define EMSCRIPTEN
26 #endif
27 
28 #ifdef FAKEWEB
29 namespace hr { void mainloopiter(); }
emscripten_set_main_loop(A a,B b,C c)30 template<class A, class B, class C> void emscripten_set_main_loop(A a, B b, C c) { while(true) mainloopiter(); }
31 #else
32 #include <emscripten.h>
33 #include <emscripten/html5.h>
34 #endif
35 
36 #include <string>
37 
38 namespace hr {
39   void initweb();
40   void emscripten_get_commandline();
41 
42   void loadCompressedChar(int &otwidth, int &otheight, int *tpix);
43   void get_canvas_size();
44 
45   const char *wheresounds;
46 
47   std::string get_value(std::string name);
48 
49   void offer_download(std::string sfilename, std::string smimetype);
50   }
51 
52 #ifdef SCU
53 #include "hyper.cpp"
54 #else
55 #include "hyper.h"
56 #endif
57 
58 namespace hr {
59 
get_value(string name)60 string get_value(string name) {
61   char *str = (char*)EM_ASM_INT({
62     var name = UTF8ToString($0, $1);
63     var value = document.getElementById(name).value;
64     var lengthBytes = lengthBytesUTF8(value)+1;
65     var stringOnWasmHeap = _malloc(lengthBytes);
66     stringToUTF8(value, stringOnWasmHeap, lengthBytes);
67     return stringOnWasmHeap;
68     }, name.c_str(), int(name.size())
69     );
70   string res = str;
71   free(str);
72   return res;
73   }
74 
offer_download(string sfilename,string smimetype)75 EX void offer_download(string sfilename, string smimetype) {
76 
77   EM_ASM({
78     var name = UTF8ToString($0, $1);
79     var mime = UTF8ToString($2, $3);
80 
81     let content = Module.FS.readFile(name);
82     console.log(`Offering download of "${name}", with ${content.length} bytes...`);
83 
84     var a = document.createElement('a');
85     a.download = name;
86     a.href = URL.createObjectURL(new Blob([content], {type: mime}));
87     a.style.display = 'none';
88 
89     document.body.appendChild(a);
90     a.click();
91     setTimeout(() => {
92       document.body.removeChild(a);
93       URL.revokeObjectURL(a.href);
94     }, 2000);
95     }, sfilename.c_str(), isize(sfilename), smimetype.c_str(), isize(smimetype)
96     );
97 
98   }
99 
__anon0fa804e30102null100 reaction_t on_use_file = [] {};
101 
102 extern "C" {
use_file()103   void use_file() {
104     on_use_file();
105     }
106   }
107 
offer_choose_file(reaction_t r)108 EX void offer_choose_file(reaction_t r) {
109   on_use_file = r;
110   EM_ASM({
111     fileElem.click();
112     });
113   }
114 
115 //    window.open(Pointer_stringify($0));
116 bool demoanim;
117 
toggleanim(bool v)118 void toggleanim(bool v) {
119   demoanim = v;
120   if(v) {
121     sightrange_bonus = -3;
122     vid.wallmode = 5;
123     vid.particles = true;
124     vid.sspeed = -1;
125     vid.mspeed = -1;
126     vid.highdetail = vid.middetail = 5;
127     }
128   else {
129     sightrange_bonus = 0;
130     vid.sspeed = 5;
131     vid.mspeed = 5;
132     vid.particles = false;
133     vid.wallmode = 3;
134     vid.highdetail = vid.middetail = -1;
135     }
136   }
137 
showDemo()138 void showDemo() {
139   gamescreen(2);
140 
141   getcstat = ' ';
142 
143   dialog::init(XLAT("HyperRogue %1: online demo", VER), 0xC00000, 200, 100);
144   dialog::addBreak(50);
145 
146   dialog::addItem(XLAT("play the game"), 'f');
147   dialog::addItem(XLAT("learn about hyperbolic geometry"), 'T');
148   dialog::addHelp();
149   // dialog::addItem(XLAT("toggle high detail"), 'a');
150   dialog::addBreak(100);
151 
152   dialog::addTitle("highlights", 0xC00000, 120);
153   dialog::addItem(XLAT("Temple of Cthulhu"), 't');
154   dialog::addItem(XLAT("Land of Storms"), 'l');
155   dialog::addItem(XLAT("Burial Grounds"), 'b');
156 
157   dialog::display();
158 
159   keyhandler = [] (int sym, int uni) {
160     dialog::handleNavigation(sym, uni);
161     if(sym == SDLK_F1 || uni == 'h') gotoHelp(help);
162     else if(uni == 'a') {
163       toggleanim(!demoanim);
164       popScreen();
165       }
166     else if(uni == 'f') {
167       firstland = laIce;
168       restart_game(tactic::on ? rg::tactic : rg::nothing);
169       }
170 #if CAP_TOUR
171     else if(uni == 'T') {
172       firstland = laIce;
173       if(!tour::on) tour::start();
174       }
175 #endif
176     else if(uni == 't') {
177       firstland = laTemple;
178       restart_game(tactic::on ? rg::tactic : rg::nothing);
179       }
180     else if(uni == 'l') {
181       firstland = laStorms;
182       restart_game(tactic::on ? rg::tactic : rg::nothing);
183       }
184     else if(uni == 'b') {
185       firstland = laBurial;
186       restart_game(tactic::on ? rg::tactic : rg::nothing);
187       items[itOrbSword] = 60;
188       }
189     };
190   }
191 
192 int bak_xres, bak_yres;
193 
fsc_callback(int eventType,const EmscriptenFullscreenChangeEvent * fullscreenChangeEvent,void * userData)194 EM_BOOL fsc_callback(int eventType, const EmscriptenFullscreenChangeEvent *fullscreenChangeEvent, void *userData) {
195   if(fullscreenChangeEvent->isFullscreen) {
196     bak_xres = vid.xres;
197     bak_yres = vid.yres;
198     vid.xres = vid.xscr = fullscreenChangeEvent->screenWidth;
199     vid.yres = vid.yscr = fullscreenChangeEvent->screenHeight;
200     vid.full = true;
201     printf("set to %d x %d\n", vid.xres, vid.yres);
202     setvideomode();
203     }
204   else {
205     vid.xres = vid.xscr = bak_xres;
206     vid.yres = vid.yscr = bak_yres;
207     vid.full = true;
208     printf("reset to %d x %d\n", vid.xres, vid.yres);
209     setvideomode();
210     }
211   return true;
212   }
213 
get_canvas_size()214 EX void get_canvas_size() {
215   vid.xscr = vid.xres = EM_ASM_INT({
216     var d = document.getElementById("this_wide");
217     if(!d) return window.innerWidth;
218     return d.clientWidth;
219     });
220   if(vid.full) vid.yscr = vid.yres = EM_ASM_INT({
221     var d = document.getElementById("this_wide");
222     if(!d) return window.innerWidth;
223     return d.clientHeight;
224     });
225   else {
226     vid.xscr = vid.xres = vid.xres - 32;
227     vid.yscr = vid.yres = EM_ASM_INT({
228       return window.innerHeight;
229       }) - 32;
230     vid.yscr = vid.yres = min(vid.yscr, vid.xscr * 9 / 16);
231     vid.xscr = vid.xres = min(vid.xscr, vid.yscr * 16 / 9);
232     }
233   println(hlog, "X = ", vid.xscr, " Y = ", vid.yscr);
234   }
235 
resize_callback(int eventType,const EmscriptenUiEvent * resizeEvent,void * userData)236 EM_BOOL resize_callback(int eventType, const EmscriptenUiEvent *resizeEvent, void *userData) {
237   if(vid.full) return true;
238   get_canvas_size();
239   setvideomode();
240   return true;
241   }
242 
initweb()243 EX void initweb() {
244   // toggleanim(false);
245   emscripten_set_fullscreenchange_callback(0, NULL, false, fsc_callback);
246   emscripten_set_resize_callback(0, NULL, false, resize_callback);
247   printf("showstartmenu = %d\n", showstartmenu);
248   if(showstartmenu) pushScreen(showDemo);
249   }
250 
251 #if CAP_ORIENTATION
getOrientation()252 transmatrix getOrientation() {
253   ld alpha, beta, gamma;
254   alpha = EM_ASM_DOUBLE({ return rotation_alpha; });
255   beta = EM_ASM_DOUBLE({ return rotation_beta; });
256   gamma = EM_ASM_DOUBLE({ return rotation_gamma; });
257   return
258     cspin(0, 1, alpha * degree) *
259     cspin(1, 2, beta * degree) *
260     cspin(0, 2, gamma * degree);
261   }
262 #endif
263 
emscripten_get_commandline()264 EX void emscripten_get_commandline() {
265 #ifdef EMSCRIPTEN_FIXED_ARG
266   string s = EMSCRIPTEN_FIXED_ARG;
267 #else
268   char *str = (char*)EM_ASM_INT({
269     var jsString = document.location.href;
270     if (typeof(default_arg) != 'undefined' && jsString.indexOf('?') == -1)
271       jsString = default_arg;
272     var lengthBytes = lengthBytesUTF8(jsString)+1;
273     var stringOnWasmHeap = _malloc(lengthBytes);
274     stringToUTF8(jsString, stringOnWasmHeap, lengthBytes+1);
275     return stringOnWasmHeap;
276     });
277   string s = str;
278 #endif
279   s += "+xxxxxx";
280   for(int i=0; i<isize(s); i++) if(s[i] == '?') {
281 #ifndef EMSCRIPTEN_FIXED_ARG
282     printf("HREF: %s\n", str);
283 #endif
284     arg::argument.push_back("hyperweb"); arg::lshift();
285     string next = ""; i += 3;
286     for(; i<isize(s); i++)  {
287       if(s[i] == '+') {
288         arg::argument.push_back(next);
289         next = "";
290         }
291       else if(s[i] == '%') {
292         string s2 = "";
293         s2 += s[i+1];
294         s2 += s[i+2];
295         i += 2;
296         next += strtol(s2.c_str(), NULL, 16);
297         }
298       else if(s[i] == '&') {
299         arg::argument.push_back(next); break;
300         }
301       else next += s[i];
302       }
303     printf("Arguments:"); for(string s: arg::argument) printf(" %s", s.c_str()); printf("\n");
304     break;
305     }
306 
307 #ifndef EMSCRIPTEN_FIXED_ARG
308   free(str);
309 #endif
310   }
311 }
312 
313