1 #include "../simsys.h"
2 #include "../simconst.h"
3 #include "../simtypes.h"
4 #include "../simdebug.h"
5
6 #include "../player/simplay.h"
7 #include "../simworld.h"
8 #include "../simmesg.h"
9 #include "../simmem.h"
10 #include "../simmenu.h"
11
12 #include "../dataobj/loadsave.h"
13 #include "../dataobj/translator.h"
14 #include "../dataobj/environment.h"
15 #include "../network/network.h"
16 #include "../network/network_cmd_scenario.h"
17 #include "../dataobj/schedule.h"
18
19 #include "../utils/cbuffer_t.h"
20
21 // error popup
22 #include "../gui/simwin.h"
23 #include "../gui/scenario_info.h"
24
25 // scripting
26 #include "../script/script.h"
27 #include "../script/export_objs.h"
28 #include "../script/api/api.h"
29 #include "../script/api_param.h"
30 #include "../script/api_class.h"
31
32 #include "../tpl/plainstringhashtable_tpl.h"
33
34 #include "scenario.h"
35
36 #include <stdarg.h>
37
38 // cache the scenario text files
39 static plainstringhashtable_tpl<plainstring> cached_text_files;
40
41
scenario_t(karte_t * w)42 scenario_t::scenario_t(karte_t *w) :
43 description_text("get_short_description"),
44 info_text("get_info_text"),
45 goal_text("get_goal_text"),
46 rule_text("get_rule_text"),
47 result_text("get_result_text"),
48 about_text("get_about_text"),
49 debug_text("get_debug_text")
50 {
51 welt = w;
52 what_scenario = 0;
53
54 script = NULL;
55 won = false;
56 lost = false;
57 rdwr_error = false;
58 need_toolbar_update = false;
59
60 cached_text_files.clear();
61 }
62
63
~scenario_t()64 scenario_t::~scenario_t()
65 {
66 delete script;
67 clear_ptr_vector(forbidden_tools);
68 cached_text_files.clear();
69 }
70
71
init(const char * scenario_base,const char * scenario_name_,karte_t * welt)72 const char* scenario_t::init( const char *scenario_base, const char *scenario_name_, karte_t *welt )
73 {
74 this->welt = welt;
75 scenario_name = scenario_name_;
76
77 // path to scenario files
78 cbuffer_t buf;
79 buf.printf("%s%s/", scenario_base, scenario_name_);
80 scenario_path = buf;
81
82 // scenario script file
83 buf.append("scenario.nut");
84 if (!load_script( buf )) {
85 dbg->warning("scenario_t::init", "could not load script file %s", (const char*)buf);
86 return "Loading scenario script failed";
87 }
88
89 const char *err = NULL;
90 plainstring mapfile;
91
92 // load savegame
93 if ((err = script->call_function(script_vm_t::FORCE, "get_map_file", mapfile))) {
94 dbg->warning("scenario_t::init", "error [%s] calling get_map_file", err);
95 return "No scenario map specified";
96 }
97
98 // if savegame-string == "<attach>" then do not load a savegame, just attach to running game.
99 if ( strcmp(mapfile, "<attach>") ) {
100 // savegame location
101 buf.clear();
102 buf.printf("%s%s/%s", scenario_base, scenario_name_, mapfile.c_str());
103 if (!welt->load( buf )) {
104 dbg->warning("scenario_t::init", "error loading savegame %s", err, (const char*)buf);
105 return "Could not load scenario map!";
106 }
107 // set savegame name
108 buf.clear();
109 buf.printf("%s.sve", scenario_name.c_str());
110 welt->get_settings().set_filename( strdup(buf) );
111 // re-initialize coordinate and rotation handling
112 script_api::coordinate_transform_t::initialize();
113 }
114
115 load_compatibility_script();
116
117 // load translations
118 translator::load_files_from_folder( scenario_path.c_str(), "scenario" );
119 cached_text_files.clear();
120
121 what_scenario = SCRIPTED;
122
123 // callback
124 script->register_callback(&scenario_t::set_completion, "scenario_t_set_completed");
125
126 // register ourselves
127 welt->set_scenario(this);
128 welt->get_message()->clear();
129
130 // set start time
131 sint32 const time = welt->get_current_month();
132 welt->get_settings().set_starting_year( time / 12);
133 welt->get_settings().set_starting_month( time % 12);
134
135 // set my player number to PLAYER_UNOWNED
136 script->set_my_player(PLAYER_UNOWNED);
137
138 // now call startup function
139 if ((err = script->call_function(script_vm_t::QUEUE, "start"))) {
140 dbg->warning("scenario_t::init", "error [%s] calling start", err);
141 }
142
143 return NULL;
144 }
145
146
load_base_script(script_vm_t * script,const char * base)147 bool load_base_script(script_vm_t *script, const char* base)
148 {
149 cbuffer_t buf;
150 buf.printf("%sscript/%s", env_t::program_dir, base);
151 if (const char* err = script->call_script(buf)) {
152 // should not happen
153 dbg->error("load_base_script", "error [%s] calling %s", err, (const char*)buf);
154 return false;
155 }
156 return true;
157 }
158
159
load_script(const char * filename)160 bool scenario_t::load_script(const char* filename)
161 {
162 script = new script_vm_t(scenario_path.c_str(), "script-scenario.log");
163 // load global stuff
164 // constants must be known compile time
165 export_global_constants(script->get_vm());
166 // load scripting base definitions
167 if (!load_base_script(script, "script_base.nut")) {
168 return false;
169 }
170 // load scenario base definitions
171 if (!load_base_script(script, "scenario_base.nut")) {
172 return false;
173 }
174
175 // register api functions
176 register_export_function(script->get_vm(), true);
177 if (script->get_error()) {
178 dbg->error("scenario_t::load_script", "error [%s] calling register_export_function", script->get_error());
179 return false;
180 }
181
182 // init strings
183 dynamic_string::init(script);
184 // register callback
185 if (env_t::server) {
186 nwc_scenario_t::init(script);
187 }
188
189 // load scenario definition
190 if (const char* err = script->call_script(filename)) {
191 dbg->error("scenario_t::load_script", "error [%s] calling %s", err, filename);
192 return false;
193 }
194 return true;
195 }
196
197
load_compatibility_script()198 void scenario_t::load_compatibility_script()
199 {
200 // check api version
201 plainstring api_version;
202 if (const char* err = script->call_function(script_vm_t::FORCE, "get_api_version", api_version)) {
203 dbg->warning("scenario_t::init", "error [%s] calling get_api_version", err);
204 api_version = "120.1";
205 }
206 if (api_version != "*") {
207 // load scenario compatibility script
208 if (load_base_script(script, "script_compat.nut")) {
209 plainstring dummy;
210 // call compatibility function
211 if (const char* err = script->call_function(script_vm_t::FORCE, "compat", dummy, api_version) ) {
212 dbg->warning("scenario_t::init", "error [%s] calling compat", err);
213 }
214 }
215 }
216 }
217
218
koord_sq2w(koord & k) const219 void scenario_t::koord_sq2w(koord &k) const
220 {
221 script_api::coordinate_transform_t::koord_sq2w(k);
222 }
223
224
get_forbidden_text()225 const char* scenario_t::get_forbidden_text()
226 {
227 static cbuffer_t buf;
228 buf.clear();
229 buf.append("<h1>Forbidden stuff:</h1><br>");
230 for(uint32 i=0; i<forbidden_tools.get_count(); i++) {
231 scenario_t::forbidden_t &f = *forbidden_tools[i];
232 buf.printf("[%d] Player = %d, Tool = %d", i, f.player_nr, f.toolnr);
233 if (f.waytype!=invalid_wt) {
234 buf.printf(", Waytype = %d", f.waytype);
235 }
236 if (f.type == forbidden_t::forbid_tool_rect) {
237 if (-128<f.hmin || f.hmax<127) {
238 buf.printf(", Cube = (%s,%d) x ", f.pos_nw.get_str(), f.hmin);
239 buf.printf("(%s,%d)", f.pos_se.get_str(), f.hmax);
240 }
241 else {
242 buf.printf(", Rect = (%s) x ", f.pos_nw.get_str());
243 buf.printf("(%s)", f.pos_se.get_str());
244 }
245 }
246 buf.printf("<br>");
247 }
248 return buf;
249 }
250
251
operator <(const forbidden_t & other) const252 bool scenario_t::forbidden_t::operator <(const forbidden_t &other) const
253 {
254 bool lt = type < other.type;
255 if (!lt && type == other.type) {
256 sint32 diff = (sint32)player_nr - (sint32)other.player_nr;
257 if (diff == 0) {
258 diff = (sint32)toolnr - (sint32)other.toolnr;
259 }
260 if (diff == 0) {
261 diff = (sint32)waytype - (sint32)other.waytype;
262 }
263 lt = diff < 0;
264 }
265 return lt;
266 }
267
268
operator ==(const forbidden_t & other) const269 bool scenario_t::forbidden_t::operator ==(const forbidden_t &other) const
270 {
271 bool eq = (type == other.type) && (player_nr == other.player_nr) && (waytype == other.waytype);
272 if (eq) {
273 switch(type) {
274 case forbid_tool_rect:
275 eq = eq && (hmin == other.hmin) && (hmax == other.hmax);
276 eq = eq && (pos_nw == other.pos_nw);
277 eq = eq && (pos_se == other.pos_se);
278 /* FALLTHROUGH */
279 case forbid_tool:
280 eq = eq && (toolnr == other.toolnr);
281 break;
282 }
283 }
284 return eq;
285 }
286
287
forbidden_t(const forbidden_t & other)288 scenario_t::forbidden_t::forbidden_t(const forbidden_t& other) :
289 type(other.type), player_nr(other.player_nr), toolnr(other.player_nr),
290 waytype(other.waytype), pos_nw(other.pos_nw), pos_se(other.pos_se),
291 hmin(other.hmin), hmax(other.hmax), error(other.error)
292 {
293 }
294
295
rotate90(const sint16 y_size)296 void scenario_t::forbidden_t::rotate90(const sint16 y_size)
297 {
298 switch(type) {
299 case forbid_tool_rect: {
300 pos_nw.rotate90(y_size);
301 pos_se.rotate90(y_size);
302 sint16 x = pos_nw.x; pos_nw.x = pos_se.x; pos_se.x = x;
303 }
304 default: ;
305 }
306 }
307
308
find_first(const forbidden_t & other) const309 uint32 scenario_t::find_first(const forbidden_t &other) const
310 {
311 if (forbidden_tools.empty() || *forbidden_tools.back() < other) {
312 // empty vector, or everything is smaller
313 return forbidden_tools.get_count();
314 }
315 if (other < (*forbidden_tools[0])) {
316 // everything is larger
317 return forbidden_tools.get_count();
318 }
319 else if ( other <= *forbidden_tools[0] ) {
320 return 0;
321 }
322 // now: low < other <= high
323 uint32 low = 0, high = forbidden_tools.get_count()-1;
324 while(low+1 < high) {
325 uint32 mid = (low+high) / 2;
326 if (*forbidden_tools[mid] < other) {
327 low = mid;
328 // now low < other
329 }
330 else {
331 high = mid;
332 // now other <= high
333 }
334 };
335 // still: low < other <= high
336 bool notok = other < *forbidden_tools[high];
337 return notok ? forbidden_tools.get_count() : high;
338 }
339
340
intern_forbid(forbidden_t * test,bool forbid)341 void scenario_t::intern_forbid(forbidden_t *test, bool forbid)
342 {
343 bool changed = false;
344 forbidden_t::forbid_type type = test->type;
345
346 for(uint32 i = find_first(*test); i < forbidden_tools.get_count() && *forbidden_tools[i] <= *test; i++) {
347 if (*test == *forbidden_tools[i]) {
348 // entry exists already
349 delete test;
350 if (!forbid) {
351 delete forbidden_tools[i];
352 forbidden_tools.remove_at(i);
353 changed = true;
354 }
355 goto end;
356 }
357 }
358 // entry does not exist
359 if (forbid) {
360 forbidden_tools.insert_ordered(test, scenario_t::forbidden_t::compare);
361 changed = true;
362 }
363 end:
364 if (changed && type==forbidden_t::forbid_tool) {
365 need_toolbar_update = true;
366 }
367 }
368
call_forbid_tool(forbidden_t * test,bool forbid)369 void scenario_t::call_forbid_tool(forbidden_t *test, bool forbid)
370 {
371 if (env_t::server) {
372 // send information over network
373 nwc_scenario_rules_t *nws = new nwc_scenario_rules_t(welt->get_sync_steps() + 1, welt->get_map_counter());
374 nws->rule = test;
375 nws->forbid = forbid;
376 network_send_all(nws, false);
377 }
378 else {
379 // directly apply
380 intern_forbid(test, forbid);
381 }
382 }
383
forbid_tool(uint8 player_nr,uint16 tool_id)384 void scenario_t::forbid_tool(uint8 player_nr, uint16 tool_id)
385 {
386 forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, player_nr, tool_id, invalid_wt);
387 call_forbid_tool(test, true);
388 }
389
390
allow_tool(uint8 player_nr,uint16 tool_id)391 void scenario_t::allow_tool(uint8 player_nr, uint16 tool_id)
392 {
393 forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, player_nr, tool_id, invalid_wt);
394 call_forbid_tool(test, false);
395 }
396
397
forbid_way_tool(uint8 player_nr,uint16 tool_id,waytype_t wt)398 void scenario_t::forbid_way_tool(uint8 player_nr, uint16 tool_id, waytype_t wt)
399 {
400 forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, player_nr, tool_id, wt);
401 call_forbid_tool(test, true);
402 }
403
404
allow_way_tool(uint8 player_nr,uint16 tool_id,waytype_t wt)405 void scenario_t::allow_way_tool(uint8 player_nr, uint16 tool_id, waytype_t wt)
406 {
407 forbidden_t *test = new forbidden_t(forbidden_t::forbid_tool, player_nr, tool_id, wt);
408 call_forbid_tool(test, false);
409 }
410
411
forbid_way_tool_rect(uint8 player_nr,uint16 tool_id,waytype_t wt,koord pos_nw,koord pos_se,plainstring err)412 void scenario_t::forbid_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, koord pos_nw, koord pos_se, plainstring err)
413 {
414 forbid_way_tool_cube(player_nr, tool_id, wt, koord3d(pos_nw, -128), koord3d(pos_se, 127), err);
415 }
416
417
allow_way_tool_rect(uint8 player_nr,uint16 tool_id,waytype_t wt,koord pos_nw,koord pos_se)418 void scenario_t::allow_way_tool_rect(uint8 player_nr, uint16 tool_id, waytype_t wt, koord pos_nw, koord pos_se)
419 {
420 allow_way_tool_cube(player_nr, tool_id, wt, koord3d(pos_nw, -128), koord3d(pos_se, 127));
421 }
422
423
forbid_way_tool_cube(uint8 player_nr,uint16 tool_id,waytype_t wt,koord3d pos_nw_0,koord3d pos_se_0,plainstring err)424 void scenario_t::forbid_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, koord3d pos_nw_0, koord3d pos_se_0, plainstring err)
425 {
426 koord pos_nw( min(pos_nw_0.x, pos_se_0.x), min(pos_nw_0.y, pos_se_0.y));
427 koord pos_se( max(pos_nw_0.x, pos_se_0.x), max(pos_nw_0.y, pos_se_0.y));
428 sint8 hmin( min(pos_nw_0.z, pos_se_0.z) );
429 sint8 hmax( max(pos_nw_0.z, pos_se_0.z) );
430
431 forbidden_t *test = new forbidden_t(player_nr, tool_id, wt, pos_nw, pos_se, hmin, hmax);
432 test->error = err;
433 call_forbid_tool(test, true);
434 }
435
436
allow_way_tool_cube(uint8 player_nr,uint16 tool_id,waytype_t wt,koord3d pos_nw_0,koord3d pos_se_0)437 void scenario_t::allow_way_tool_cube(uint8 player_nr, uint16 tool_id, waytype_t wt, koord3d pos_nw_0, koord3d pos_se_0)
438 {
439 koord pos_nw( min(pos_nw_0.x, pos_se_0.x), min(pos_nw_0.y, pos_se_0.y));
440 koord pos_se( max(pos_nw_0.x, pos_se_0.x), max(pos_nw_0.y, pos_se_0.y));
441 sint8 hmin( min(pos_nw_0.z, pos_se_0.z) );
442 sint8 hmax( max(pos_nw_0.z, pos_se_0.z) );
443
444 forbidden_t *test = new forbidden_t(player_nr, tool_id, wt, pos_nw, pos_se, hmin, hmax);
445 call_forbid_tool(test, false);
446 }
447
448
clear_rules()449 void scenario_t::clear_rules()
450 {
451 clear_ptr_vector(forbidden_tools);
452 need_toolbar_update = true;
453 }
454
455
is_tool_allowed(const player_t * player,uint16 tool_id,sint16 wt)456 bool scenario_t::is_tool_allowed(const player_t* player, uint16 tool_id, sint16 wt)
457 {
458 if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) {
459 return true;
460 }
461 // first test the list
462 if (!forbidden_tools.empty()) {
463 forbidden_t test(forbidden_t::forbid_tool, PLAYER_UNOWNED, tool_id, invalid_wt);
464 uint8 player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED;
465
466 // first test waytype invalid_wt, then wt
467 // .. and all players then specific player
468 for(uint32 wti = 0; wti<4; wti++) {
469 if (find_first(test) < forbidden_tools.get_count()) {
470 // there is something, hence forbidden
471 return false;
472 }
473 // logic to test all possible four cases
474 sim::swap<sint16>( wt, test.waytype );
475 if (test.waytype == invalid_wt) {
476 sim::swap<uint8>( player_nr, test.player_nr );
477 if (test.player_nr == PLAYER_UNOWNED) {
478 break;
479 }
480 }
481 }
482 }
483 // then call script if available
484 if (what_scenario == SCRIPTED) {
485 bool ok = true;
486 const char* err = script->call_function(script_vm_t::FORCE, "is_tool_allowed", ok, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), tool_id, wt);
487 return err != NULL || ok;
488 }
489
490 return true;
491 }
492
is_work_allowed_here(const player_t * player,uint16 tool_id,sint16 wt,koord3d pos)493 const char* scenario_t::is_work_allowed_here(const player_t* player, uint16 tool_id, sint16 wt, koord3d pos)
494 {
495 if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) {
496 return NULL;
497 }
498
499 // first test the list
500 if (!forbidden_tools.empty()) {
501 forbidden_t test(forbidden_t::forbid_tool_rect, PLAYER_UNOWNED, tool_id, invalid_wt);
502 uint8 player_nr = player ? player->get_player_nr() : PLAYER_UNOWNED;
503
504 // first test waytype invalid_wt, then wt
505 // .. and all players then specific player
506 for(uint32 wti = 0; wti<4; wti++) {
507 for(uint32 i = find_first(test); i < forbidden_tools.get_count() && *forbidden_tools[i] <= test; i++) {
508 forbidden_t const& f = *forbidden_tools[i];
509 // check rectangle
510 if (f.pos_nw.x <= pos.x && f.pos_nw.y <= pos.y && pos.x <= f.pos_se.x && pos.y <= f.pos_se.y) {
511 // check height
512 if (f.hmin <= pos.z && pos.z <= f.hmax) {
513 const char* err = f.error.c_str();
514 if (err == NULL) {
515 err = "";
516 }
517 return err;
518 }
519 }
520 }
521 // logic to test all possible four cases
522 sim::swap<sint16>( wt, test.waytype );
523 if (test.waytype == invalid_wt) {
524 sim::swap<uint8>( player_nr, test.player_nr );
525 if (test.player_nr == PLAYER_UNOWNED) {
526 break;
527 }
528 }
529 }
530 }
531
532 // then call the script
533 // cannot be done for two_click_tool_t's as they depend on routefinding,
534 // which is done per client
535 if (what_scenario == SCRIPTED) {
536 static plainstring msg;
537 const char *err = script->call_function(script_vm_t::FORCE, "is_work_allowed_here", msg, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), tool_id, pos);
538
539 return err == NULL ? msg.c_str() : NULL;
540 }
541 return NULL;
542 }
543
544
is_schedule_allowed(const player_t * player,const schedule_t * schedule)545 const char* scenario_t::is_schedule_allowed(const player_t* player, const schedule_t* schedule)
546 {
547 // sanity checks
548 if (schedule == NULL) {
549 return "";
550 }
551 if (env_t::server) {
552 // networkgame: allowed
553 return NULL;
554 }
555 // call script
556 if (what_scenario == SCRIPTED) {
557 static plainstring msg;
558 const char *err = script->call_function(script_vm_t::FORCE, "is_schedule_allowed", msg, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), schedule);
559
560 return err == NULL ? msg.c_str() : NULL;
561 }
562 return NULL;
563 }
564
565
is_convoy_allowed(const player_t * player,convoihandle_t cnv,depot_t * depot)566 const char* scenario_t::is_convoy_allowed(const player_t* player, convoihandle_t cnv, depot_t* depot)
567 {
568 // sanity checks
569 if (!cnv.is_bound() || depot == NULL) {
570 return "";
571 }
572 if (env_t::server) {
573 // networkgame: allowed
574 return NULL;
575 }
576 // call script
577 if (what_scenario == SCRIPTED) {
578 static plainstring msg;
579 const char *err = script->call_function(script_vm_t::FORCE, "is_convoy_allowed", msg, (uint8)(player ? player->get_player_nr() : PLAYER_UNOWNED), cnv, (obj_t*)depot);
580
581 return err == NULL ? msg.c_str() : NULL;
582 }
583 return NULL;
584
585 }
586
587
get_error_text()588 const char* scenario_t::get_error_text()
589 {
590 if (script) {
591 return script->get_error();
592 }
593 return NULL;
594 }
595
596
step()597 void scenario_t::step()
598 {
599 if (!script) {
600 // update texts at clients if info window open
601 if (env_t::networkmode && !env_t::server && win_get_magic(magic_scenario_info)) {
602 update_scenario_texts();
603 }
604 return;
605 }
606
607 uint16 new_won = 0;
608 uint16 new_lost = 0;
609
610 // first check, whether win/loss state of any player changed
611 for(uint32 i=0; i<PLAYER_UNOWNED; i++) {
612 player_t *player = welt->get_player(i);
613 uint16 mask = 1 << i;
614 // player exists and has not won/lost yet
615 if (player && (((won | lost) & mask)==0)) {
616 sint32 percentage = 0;
617 // callback
618 script->prepare_callback("scenario_t_set_completed", 2, i, percentage );
619 // call script
620 const char *err = script->call_function(script_vm_t::QUEUE, "is_scenario_completed", percentage, i);
621 // clear callback
622 script->clear_pending_callback();
623
624 // script might have deleted the player
625 player = welt->get_player(i);
626 if (player == NULL) {
627 continue;
628 }
629 // call completed?
630 if (script_vm_t::is_call_suspended(err)) {
631 continue;
632 }
633 player->set_scenario_completion(percentage);
634 // won ?
635 if (percentage >= 100) {
636 new_won |= mask;
637 }
638 // lost ?
639 else if (percentage < 0) {
640 new_lost |= mask;
641 }
642 }
643 }
644
645 update_won_lost(new_won, new_lost);
646
647 // update texts
648 if (win_get_magic(magic_scenario_info) ) {
649 update_scenario_texts();
650 }
651
652 // update toolbars if necessary
653 if (need_toolbar_update) {
654 tool_t::update_toolbars();
655 need_toolbar_update = false;
656 }
657 }
658
659
new_month()660 void scenario_t::new_month()
661 {
662 if (script) {
663 script->call_function(script_vm_t::QUEUE, "new_month");
664 }
665 }
666
new_year()667 void scenario_t::new_year()
668 {
669 if (script) {
670 script->call_function(script_vm_t::QUEUE, "new_year");
671 }
672 }
673
674
update_won_lost(uint16 new_won,uint16 new_lost)675 void scenario_t::update_won_lost(uint16 new_won, uint16 new_lost)
676 {
677 // server sends the new state to the clients
678 if (env_t::server && (new_won | new_lost)) {
679 nwc_scenario_t *nwc = new nwc_scenario_t();
680 nwc->won = new_won;
681 nwc->lost = new_lost;
682 nwc->what = nwc_scenario_t::UPDATE_WON_LOST;
683 network_send_all(nwc, true);
684 }
685
686 // we are the champions
687 if (new_won) {
688 won |= new_won;
689 // those are the losers
690 new_lost = ~new_won;
691 }
692 if (new_lost) {
693 lost |= new_lost;
694 }
695
696 // notify active player
697 if ( (new_won|new_lost) & (1<<welt->get_active_player_nr()) ) {
698 // most likely result text has changed, force update
699 result_text.update(script, welt->get_active_player(), true);
700
701 open_info_win();
702 }
703 }
704
705
update_scenario_texts()706 void scenario_t::update_scenario_texts()
707 {
708 player_t *player = welt->get_active_player();
709 info_text.update(script, player);
710 goal_text.update(script, player);
711 rule_text.update(script, player);
712 result_text.update(script, player);
713 about_text.update(script, player);
714 debug_text.update(script, player);
715 description_text.update(script, player);
716 }
717
718
load_language_file(const char * filename)719 plainstring scenario_t::load_language_file(const char* filename)
720 {
721 if (filename == NULL) {
722 return "(null)";
723 }
724 std::string path = scenario_path.c_str();
725 // try user language
726 std::string wanted_file = path + translator::get_lang()->iso + PATH_SEPARATOR + filename;
727
728 const plainstring& cached = cached_text_files.get(wanted_file.c_str());
729 if (cached != NULL) {
730 // file already cached
731 return cached;
732 }
733 // not cached: try to read file
734 FILE* file = dr_fopen(wanted_file.c_str(), "rb");
735 if (file == NULL) {
736 // try English
737 file = dr_fopen((path + "en" + PATH_SEPARATOR + filename).c_str(), "rb");
738 }
739 if (file == NULL) {
740 // try scenario directory
741 file = dr_fopen((path + filename).c_str(), "rb");
742 }
743
744 plainstring text = "";
745 if (file) {
746 fseek(file,0,SEEK_END);
747 long len = ftell(file);
748 if(len>0) {
749 char* const buf = MALLOCN(char, len + 1);
750 fseek(file,0,SEEK_SET);
751 fread(buf, 1, len, file);
752 buf[len] = '\0';
753 text = buf;
754 free(buf);
755 }
756 fclose(file);
757 }
758 // store text to cache
759 cached_text_files.put(wanted_file.c_str(), text);
760
761 return text;
762 }
763
open_info_win(const char * tab) const764 bool scenario_t::open_info_win(const char* tab) const
765 {
766 // pop up for the win
767 scenario_info_t *si = (scenario_info_t*)win_get_magic(magic_scenario_info);
768 if (si == NULL) {
769 si = new scenario_info_t();
770 if (create_win(si, w_info, magic_scenario_info) < 0) {
771 // failed
772 return false;
773 }
774 }
775 si->open_tab(tab);
776 return true; // dummy return value
777 }
778
779
rdwr(loadsave_t * file)780 void scenario_t::rdwr(loadsave_t *file)
781 {
782 file->rdwr_short(what_scenario);
783 if (file->is_version_less(111, 5)) {
784 uint32 city_nr = 0;
785 file->rdwr_long(city_nr);
786 sint64 factor = 0;
787 file->rdwr_longlong(factor);
788 koord k(0,0);
789 k.rdwr( file );
790 }
791
792 if (what_scenario != SCRIPTED && what_scenario != SCRIPTED_NETWORK) {
793 if (file->is_loading()) {
794 what_scenario = 0;
795 }
796 return;
797 }
798
799 script_api::coordinate_transform_t::rdwr(file);
800 file->rdwr_short(won);
801 file->rdwr_short(lost);
802 file->rdwr_str(scenario_name);
803
804 // load scripts and scenario files
805 if (what_scenario == SCRIPTED) {
806 if (file->is_loading()) {
807 // load persistent scenario data
808 plainstring str;
809 file->rdwr_str(str);
810 dbg->warning("scenario_t::rdwr", "loaded persistent scenario data: %s", str.c_str());
811 if (env_t::networkmode && !env_t::server) {
812 // client playing network scenario game:
813 // script files are not available
814 what_scenario = SCRIPTED_NETWORK;
815 script = NULL;
816 }
817 else {
818 // load script
819 cbuffer_t script_filename;
820 // assume error
821 rdwr_error = true;
822 // try addon directory first
823 if (env_t::default_settings.get_with_private_paks()) {
824 scenario_path = ( std::string("addons/") + env_t::objfilename + "scenario/" + scenario_name.c_str() + "/").c_str();
825 script_filename.printf("%sscenario.nut", scenario_path.c_str());
826 rdwr_error = !load_script(script_filename);
827 }
828
829 // failed, try scenario from pakset directory
830 if (rdwr_error) {
831 scenario_path = (env_t::program_dir + env_t::objfilename + "scenario/" + scenario_name.c_str() + "/").c_str();
832 script_filename.clear();
833 script_filename.printf("%sscenario.nut", scenario_path.c_str());
834 rdwr_error = !load_script(script_filename);
835 }
836
837 if (!rdwr_error) {
838 load_compatibility_script();
839 // restore persistent data
840 const char* err = script->eval_string(str);
841 if (err) {
842 dbg->warning("scenario_t::rdwr", "error [%s] evaluating persistent scenario data", err);
843 rdwr_error = true;
844 }
845 // load translations
846 translator::load_files_from_folder( scenario_path.c_str(), "scenario" );
847 // callback
848 script->register_callback(&scenario_t::set_completion, "scenario_t_set_completed");
849 }
850 else {
851 dbg->warning("scenario_t::rdwr", "could not load script file %s", (const char*)script_filename);
852 }
853 }
854 }
855 else {
856 plainstring str;
857 script->call_function(script_vm_t::FORCEX, "save", str);
858 dbg->warning("scenario_t::rdwr", "write persistent scenario data: %s", str.c_str());
859 file->rdwr_str(str);
860 }
861 }
862
863 // load forbidden tools
864 if (file->is_loading()) {
865 clear_ptr_vector(forbidden_tools);
866 }
867 uint32 count = forbidden_tools.get_count();
868 file->rdwr_long(count);
869
870 for(uint32 i=0; i<count; i++) {
871 if (file->is_loading()) {
872 forbidden_tools.append(new forbidden_t());
873 }
874 forbidden_tools[i]->rdwr(file);
875 }
876
877 // cached strings
878 if (file->is_version_atleast(120, 3)) {
879 dynamic_string::rdwr_cache(file);
880 }
881
882 if (what_scenario == SCRIPTED && file->is_loading() && !rdwr_error) {
883 const char* err = script->call_function(script_vm_t::FORCEX, "resume_game");
884 if (err) {
885 dbg->warning("scenario_t::rdwr", "error [%s] calling resume_game", err);
886 rdwr_error = true;
887 }
888 }
889 }
890
rotate90(const sint16 y_size)891 void scenario_t::rotate90(const sint16 y_size)
892 {
893 for(uint32 i=0; i<forbidden_tools.get_count(); i++) {
894 forbidden_tools[i]->rotate90(y_size);
895 }
896 }
897
898
899 // return percentage completed
get_completion(int player_nr)900 sint32 scenario_t::get_completion(int player_nr)
901 {
902 if ( what_scenario == 0 || player_nr < 0 || player_nr >= PLAYER_UNOWNED) {
903 return 0;
904 }
905 // check if won / lost
906 uint32 pl = player_nr;
907 if (won & (1<<player_nr)) {
908 return 100;
909 }
910 else if (lost & (1<<player_nr)) {
911 return -1;
912 }
913
914 sint32 percentage = 0;
915 player_t *player = welt->get_player(player_nr);
916
917 if ( what_scenario == SCRIPTED ) {
918 // take cached value
919 if (player) {
920 percentage = player->get_scenario_completion();
921 }
922 }
923 else if ( what_scenario == SCRIPTED_NETWORK ) {
924 cbuffer_t buf;
925 buf.printf("is_scenario_completed(%d)", pl);
926 const char *ret = dynamic_string::fetch_result((const char*)buf, NULL, NULL);
927 percentage = ret ? atoi(ret) : 0;
928 // cache value
929 if (player) {
930 player->set_scenario_completion(percentage);
931 }
932 }
933 return min( 100, percentage);
934 }
935
936
set_completion(sint32 player_nr,sint32 percentage)937 bool scenario_t::set_completion(sint32 player_nr, sint32 percentage)
938 {
939 if ( player_nr < 0 || player_nr >= PLAYER_UNOWNED) {
940 return false;
941 }
942 player_t *player = welt->get_player(player_nr);
943 if (player == NULL) {
944 return false;
945 }
946 player->set_scenario_completion(percentage - player->get_scenario_completion());
947
948 uint16 mask = 1 << player_nr;
949 // check won/lost
950 if (percentage < 0 && (lost & mask)==0) {
951 update_won_lost(0, mask);
952 }
953 else if (percentage >= 100 && (won & mask)==0) {
954 update_won_lost(mask, 0);
955 }
956 return true;
957 }
958
959
eval_string(const char * squirrel_string) const960 const char* scenario_t::eval_string(const char* squirrel_string) const
961 {
962 if (what_scenario == SCRIPTED) {
963 return script->eval_string(squirrel_string);
964 }
965 return "";
966 }
967