Simutrans-Squirrel-API  r11919
api_command.cc
Go to the documentation of this file.
1 /*
2  * This file is part of the Simutrans project under the Artistic License.
3  * (see LICENSE.txt)
4  */
5 
6 #include "api.h"
7 
10 #include "api_command.h"
11 #include "api_obj_desc_base.h"
12 #include "api_simple.h"
13 #include "../api_param.h"
14 #include "../api_class.h"
15 #include "../api_function.h"
16 #include "../../builder/brueckenbauer.h"
17 #include "../../ground/grund.h"
18 #include "../../tool/simtool.h"
19 #include "../../world/simworld.h"
20 #include "../../dataobj/environment.h"
21 #include "../../script/script.h"
22 #include "../../descriptor/bridge_desc.h"
23 #include "../../descriptor/building_desc.h"
24 #include "../../descriptor/roadsign_desc.h"
25 #include "../../descriptor/way_obj_desc.h"
26 
27 #include <memory> // auto_ptr
28 
29 using namespace script_api;
30 
31 
32 SQInteger command_constructor(HSQUIRRELVM vm)
33 {
34  // create tool
35  uint16 id = param<uint16>::get(vm, 2);
36 
37  if (id & GENERAL_TOOL) {
38  // we do not want scripts to open dialogues or quitting the game etc
39 
40  if (id == (TOOL_EXEC_TWO_CLICK_SCRIPT | GENERAL_TOOL)) {
41  // do not create & attach instance, will be done separately
42  return 0;
43  }
44  else {
45  if (tool_t *tool = create_tool(id)) {
46  my_tool_t* mtool = new my_tool_t(tool);
47  attach_instance(vm, 1, mtool);
48  return 0;
49  }
50  }
51  }
52  return sq_raise_error(vm, "Invalid tool called (%d / 0x%x)", id & 0xfff, id);
53 }
54 
55 
56 SQInteger param<call_tool_init>::push(HSQUIRRELVM vm, call_tool_init v)
57 {
58  if (v.error) {
59  return sq_raise_error(vm, *v.error ? v.error : "Strange error occurred");
60  }
61  // create tool, if necessary, delete on exit
62  std::unique_ptr<tool_t> our_tool;
63  tool_t *tool = v.tool;
64  if (tool == NULL) {
65  our_tool.reset(v.create_tool());
66  tool = our_tool.get();
67  }
68  if (tool == NULL) {
69  return sq_raise_error(vm, "Called null tool");
70  }
71  // set correct flags
72  tool->flags &= (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL);
73  tool->flags |= tool_t::WFL_SCRIPT;
74  // get player parameter
75  player_t *player = get_my_player(vm);
76  if (player == NULL) {
77  player = v.player;
78  // must be scenario - set flag
79  tool->flags |= tool_t::WFL_NO_CHK;
80  }
81 #if 0
82  // sanity check
83  if (player == NULL) {
84  return sq_raise_error(vm, "Called tool with player == null");
85  }
86 #endif
87  // check if calling suspendable tools is blocked
88  if (!v.no_block) {
89  if (const char* blocker = env_t::networkmode ? sq_get_suspend_blocker(vm) : NULL) {
90  return sq_raise_error(vm, "Cannot call this tool from within `%s'.", blocker);
91  }
92  }
93  // register this tool call for callback with this id
94  uint32 callback_id = suspended_scripts_t::get_unique_key(tool);
95  tool->callback_id = callback_id;
96 
97  // HACK call karte_t::set_tool
98  bool suspended;
99  welt->set_tool_api(tool, player, suspended);
100  // in networkmode, call is suspended
101 
102  if (suspended && !v.no_block) {
103  // register for wakeup
104  suspended_scripts_t::register_suspended_script(callback_id, vm);
105  // suspend vm for now, after wakeup it returns to the script
106  return sq_suspendvm(vm);
107  }
108  else {
109  bool res = true;
110  return param<bool>::push(vm, res);
111  }
112 }
113 
114 
115 call_tool_init script_api::command_rename(player_t *owner, char what, koord3d pos, const char* name)
116 {
117  // build param string (see tool_rename_t::init)
118  cbuffer_t buf;
119  buf.printf( "%c%s,%s", what, pos.get_str(), name);
120 
121  // empty name? trigger error by setting id to zero
122  uint16 tool_id = name ? TOOL_RENAME | SIMPLE_TOOL : 0;
123 
124  return call_tool_init(tool_id, (const char*)buf, 0, owner);
125 }
126 
127 call_tool_init script_api::command_rename(player_t *owner, char what, uint32 id, const char* name)
128 {
129  // build param string (see tool_rename_t::init)
130  cbuffer_t buf;
131  buf.printf( "%c%u,%s", what, id, name);
132 
133  // empty name? trigger error by setting id to zero
134  uint16 tool_id = name ? TOOL_RENAME | SIMPLE_TOOL : 0;
135 
136  return call_tool_init(tool_id, (const char*)buf, 0, owner);
137 }
138 
139 
140 SQInteger command_work(HSQUIRRELVM vm)
141 {
142  tool_t *tool = param<tool_t*>::get(vm, 1);
143 
144  player_t *player = param<player_t*>::get(vm, 2);
145 
146  koord3d pos1 = param<koord3d>::get(vm, 3);
147 
148  SQInteger top = sq_gettop(vm); // top >=3
149  /* three possible calling conventions:
150  * (1) tool.work(pl, pos)
151  * (2) tool.work(pl, pos, param)
152  * (3) tool.work(pl, pos, pos2, param)
153  */
154  const char* default_param = top>3 ? param<const char*>::get(vm, top) : NULL;
155  koord3d pos2 = top>4 ? param<koord3d>::get(vm, 4) : koord3d::invalid;
156 
157  bool twoclick = top>4;
158 
159  // save & set default_param
160  my_tool_t *mtool = get_attached_instance<my_tool_t>(vm, 1, param<tool_t*>::tag());
161  if (mtool == NULL) {
162  return sq_raise_error(vm, "Called from an instance different to tool_x");
163  }
164  plainstring &pdefault_param = mtool->default_param;
165  pdefault_param = default_param; // copy
166  tool->set_default_param( (const char*)pdefault_param ); // .. and set param
167 
168  if (!twoclick) {
169  return param<call_tool_work>::push(vm, call_tool_work(tool, player, pos1));
170  }
171  else {
172  return param<call_tool_work>::push(vm, call_tool_work(tool, player, pos1, pos2));
173  }
174 }
175 
176 tool_t * call_tool_base_t::create_tool()
177 {
178  tool_t *tool = ::create_tool(tool_id);
179  if (tool) {
180  tool->set_default_param(default_param);
181  tool->flags = flags;
182  }
183  return tool;
184 }
185 
186 
187 SQInteger param<call_tool_work>::push(HSQUIRRELVM vm, call_tool_work v)
188 {
189  if (v.error) {
190  if (v.tool_id==0) {
191  // return error as if the tool failed normally
192  return param<const char*>::push(vm, v.error);
193  }
194  else {
195  return sq_raise_error(vm, *v.error ? v.error : "Strange error occurred");
196  }
197  }
198  // create tool, if necessary, delete on exit
199  std::unique_ptr<tool_t> our_tool;
200  tool_t *tool = v.tool;
201  if (tool == NULL) {
202  our_tool.reset(v.create_tool());
203  tool = our_tool.get();
204  }
205  if (tool == NULL) {
206  return sq_raise_error(vm, "Called null tool");
207  }
208  // set correct flags
209  tool->flags &= (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL);
210  tool->flags |= tool_t::WFL_SCRIPT;
211  // get player parameter
212  player_t *player = get_my_player(vm);
213  if (player == NULL) {
214  player = v.player;
215  // must be scenario - set flag
216  tool->flags |= tool_t::WFL_NO_CHK;
217  }
218 #if 0
219  // sanity check
220  if (player == NULL) {
221  return sq_raise_error(vm, "Called tool with player == null");
222  }
223 #endif
224  uint8 flags = tool->flags; // might be reset by init()
225 
226  // call init before work (but check network safety)
227  if (!tool->is_init_keeps_game_state()) {
228  return sq_raise_error(vm, "Initializing tool has side effects");
229  }
230  if (!tool->init(player)) {
231  return sq_raise_error(vm, "Error during initializing tool");
232  }
233  // set flags
234  tool->flags = flags;
235  // test work
236  if (tool->is_work_keeps_game_state() || (!v.twoclick && tool->is_work_here_keeps_game_state(player, v.start))) {
237  return sq_raise_error(vm, "Tool has no effects");
238  }
239  // two-click tool
240  if (v.twoclick) {
241  if (dynamic_cast<two_click_tool_t*>(tool)==NULL) {
242  return sq_raise_error(vm, "Cannot call this tool with two coordinates");
243  }
244  if (!tool->is_work_here_keeps_game_state(player, v.start)) {
245  return sq_raise_error(vm, "First click has side effects");
246  }
247  }
248  // check if calling suspendable tools is blocked
249  if (!v.no_block) {
250  if (const char* blocker = env_t::networkmode ? sq_get_suspend_blocker(vm) : NULL) {
251  return sq_raise_error(vm, "Cannot call this tool from within `%s'.", blocker);
252  }
253  }
254 
255  bool suspended = false;
256  const char* err = NULL;
257 
258  // first click of two_click_tool_t
259  if (v.twoclick) {
260  err = welt->call_work_api(tool, player, v.start, suspended);
261  assert(!suspended);
262  }
263  // call the work that has effects
264  if (err == NULL) {
265  // register this tool call for callback with this id
266  uint32 callback_id = suspended_scripts_t::get_unique_key(tool);
267  tool->callback_id = callback_id;
268  err = welt->call_work_api(tool, player, v.twoclick ? v.end : v.start, suspended);
269 
270  if (suspended && !v.no_block) {
271  // register for wakeup
272  suspended_scripts_t::register_suspended_script(callback_id, vm);
273  // suspend vm for now, after wakeup it returns to the script
274  return sq_suspendvm(vm);
275  }
276  }
277  // return error message or NULL if tool was called
278  return param<const char*>::push(vm, err);
279 }
280 
281 
282 uint8 get_flags(tool_t *tool)
283 {
284  return tool->flags & (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL);
285 }
286 
287 
288 void set_flags(tool_t *tool, uint8 flags)
289 {
290  tool->flags = flags & (tool_t::WFL_SHIFT | tool_t:: WFL_CTRL);
291 }
292 
293 
294 void* script_api::param<tool_t*>::tag()
295 {
296  return (void*)param<tool_t*>::get;
297 }
298 
299 
300 const char* is_available(const obj_desc_timelined_t* desc)
301 {
302  return desc->is_available(welt->get_timeline_year_month()) ? NULL : "Object not available (retired or future).";
303 }
304 
305 
306 call_tool_work build_way(player_t* pl, koord3d start, koord3d end, const way_desc_t* way, bool straight, bool keep_city_roads)
307 {
308  if (way == NULL) {
309  return call_tool_work("No way provided");
310  }
311  if (const char* err = is_available(way)) {
312  return call_tool_work(err);
313  }
314  return call_tool_work(TOOL_BUILD_WAY | GENERAL_TOOL, way->get_name(), (straight ? 2 : 0) + (keep_city_roads ? 1 : 0), pl, start, end);
315 }
316 
317 
318 call_tool_work build_wayobj(player_t* pl, koord3d start, koord3d end, const way_obj_desc_t* wayobj)
319 {
320  if (wayobj == NULL) {
321  return call_tool_work("No wayobj provided");
322  }
323  if (const char* err = is_available(wayobj)) {
324  return call_tool_work(err);
325  }
326  return call_tool_work(TOOL_BUILD_WAYOBJ | GENERAL_TOOL, wayobj->get_name(), 0, pl, start, end);
327 }
328 
329 
330 typedef call_tool_work(*bsr_type)(player_t*, koord3d, const building_desc_t*, sint16);
331 
332 call_tool_work build_station_rotation(player_t* pl, koord3d pos, const building_desc_t* building, sint16 layout)
333 {
334  // rotation: SENW -> 0123, see station_building_select_t
335  if (building == NULL || !building->is_transport_building()) {
336  return call_tool_work("No building provided");
337  }
338  if (const char* err = is_available(building)) {
339  return call_tool_work(err);
340  }
341  static cbuffer_t buf;
342  buf.clear();
343  if (layout >= 0) {
344  uint8 rotation = welt->get_settings().get_rotation();
345  // we need to rotate the station back according to the map
346  if (rotation) {
347  if (building->get_all_layouts() <= 4) {
348  layout = (layout + 4 - rotation) & (building->get_all_layouts() - 1);
349  }
350  else for (uint8 i = 0; i < rotation; i++) {
351  // this is different for station building that houses ...
352  static uint8 layout_rotate[16] = { 1, 8, 5, 10, 3, 12, 7, 14, 9, 0, 13, 2, 11, 4, 15, 6 };
353  // 8 & 16 tile lyoutout for stations
354  layout = layout_rotate[layout] & (building->get_all_layouts()-1);
355  }
356  }
357  buf.printf("%s,%i", building->get_name(), layout);
358  }
359  else {
360  buf.printf("%s", building->get_name());
361  }
362  return call_tool_work(TOOL_BUILD_STATION | GENERAL_TOOL, buf, 0, pl, pos);
363 }
364 
365 SQInteger command_build_station(HSQUIRRELVM vm)
366 {
367  /* possible calling conventions:
368  *
369  * build_station(player, pos, desc) - top == 4
370  * build_station(player, pos, desc, layout ) - top == 5
371  */
372  if (sq_gettop(vm) == 4) {
373  // layout parameter missing, push default value
374  sq_pushinteger(vm, -1);
375  }
376  return embed_call_t<bsr_type>::call_function(vm, build_station_rotation, false);
377 }
378 
379 
380 call_tool_work build_depot(player_t* pl, koord3d pos, const building_desc_t* building)
381 {
382  if (building == NULL || !building->is_depot()) {
383  return call_tool_work("No depot provided");
384  }
385  if (const char* err = is_available(building)) {
386  return call_tool_work(err);
387  }
388  return call_tool_work(TOOL_BUILD_DEPOT | GENERAL_TOOL, building->get_name(), 0, pl, pos);
389 }
390 
391 call_tool_work build_bridge(player_t* pl, koord3d start, koord3d end, const bridge_desc_t* bridge)
392 {
393  if (bridge == NULL) {
394  return call_tool_work("No bridge provided");
395  }
396  if (const char* err = is_available(bridge)) {
397  return call_tool_work(err);
398  }
399  return call_tool_work(TOOL_BUILD_BRIDGE | GENERAL_TOOL, bridge->get_name(), 2, pl, start, end);
400 }
401 
402 call_tool_work build_bridge_at(player_t* pl, koord3d start, const bridge_desc_t* bridge)
403 {
404  if (bridge == NULL) {
405  return call_tool_work("No bridge provided");
406  }
407  if (const char* err = is_available(bridge)) {
408  return call_tool_work(err);
409  }
410  if (grund_t *gr = world()->lookup(start)) {
411  sint8 height;
412  koord3d end = start;
413  if (const char *err = bridge_builder_t::find_end_pos(pl, start, -koord(gr->get_weg_hang()), height, bridge, 1, 10, false)) {
414  return call_tool_work(err); // to keep compatibility with old error message
415  }
416  return call_tool_work(TOOL_BUILD_BRIDGE | GENERAL_TOOL, bridge->get_name(), 0, pl, start, end);
417  }
418  return call_tool_work("Bridge is too long for this type!\n"); // to keep compatibility with old error message
419 }
420 
421 call_tool_work set_slope(player_t* pl, koord3d start, my_slope_t slope)
422 {
423  // communicate per default_param
424  static char buf[8];
425  sprintf(buf, "%2d", (uint8)slope);
426  static tool_setslope_t tool;
427  // we do not want our slopes translated to double-height (even for single-height paksets), they are already in the double-height system
428  tool.old_slope_compatibility_mode = false;
429  tool.set_default_param(buf);
430  return call_tool_work(&tool, pl, start);
431 }
432 
433 call_tool_work restore_slope(player_t* pl, koord3d start)
434 {
435  return call_tool_work(TOOL_RESTORESLOPE | GENERAL_TOOL, "", 0, pl, start);
436 }
437 
438 const char* can_set_slope(player_t* pl, koord3d pos, my_slope_t slope)
439 {
440  return tool_setslope_t::tool_set_slope_work(pl, pos, slope, false /* compatibility */, true /* check */);
441 }
442 
443 sint64 set_slope_get_price(my_slope_t slope)
444 {
445  return slope == RESTORE_SLOPE ? -welt->get_settings().cst_alter_land : -welt->get_settings().cst_set_slope;
446 }
447 
448 call_tool_work build_sign_at(player_t* pl, koord3d start, const roadsign_desc_t* sign)
449 {
450  if (sign == NULL) {
451  return call_tool_work("No sign provided");
452  }
453  if (const char* err = is_available(sign)) {
454  return call_tool_work(err);
455  }
456  return call_tool_work(TOOL_BUILD_ROADSIGN | GENERAL_TOOL, sign->get_name(), 0, pl, start);
457 }
458 
459 call_tool_work change_climate_at(player_t* pl, koord3d start, int climate)
460 {
461  if (climate < water_climate || climate >= MAX_CLIMATES) {
462  return call_tool_work("Invalid climate number provided");
463  }
464  // communicate per default_param
465  static cbuffer_t param;
466  param.clear();
467  param.printf("%d", climate);
468  return call_tool_work(TOOL_SET_CLIMATE | GENERAL_TOOL, param, 0, pl, start, start);
469 }
470 
471 
472 call_tool_work lower_raise(player_t* pl, koord3d pos, bool lower)
473 {
474  // need to transform coordinate to grid coordinates
475  switch(coordinate_transform_t::get_rotation()) {
476  case 1: pos += koord(1,0); break;
477  case 2: pos += koord(1,1); break;
478  case 3: pos += koord(0,1); break;
479  default:;
480  }
481  return call_tool_work((lower ? TOOL_LOWER_LAND : TOOL_RAISE_LAND) | GENERAL_TOOL, NULL, 0, pl, pos);
482 }
483 
484 void export_commands(HSQUIRRELVM vm)
485 {
492  create_class(vm, "command_x");
493 
499  register_function(vm, command_constructor, "constructor", 2, "xi");
500  // set type tag to custom getter
501  sq_settypetag(vm, -1, param<tool_t*>::tag());
502 
506  register_method(vm, &get_flags, "get_flags", true);
512  register_method(vm, &set_flags, "set_flags", true);
521  register_function(vm, command_work, "work", -3, "x t|x|y t|x|y");
522 
523 #ifdef DOXYGEN
524  // command_work works with different numbers of parameters.
525  // Their documentation is included here.
526 
536  register_function(vm,, "work");
537  const char* work(player_x player, coord3d pos, string param);
549  register_function(vm,, "work");
550 #endif
551 
559  STATIC register_method_fv(vm, build_way, "build_way", freevariable<bool>(false), false, true);
569  STATIC register_method(vm, build_way, "build_road", false, true);
576  STATIC register_method(vm, build_depot, "build_depot", false, true);
584  STATIC register_function(vm, command_build_station, "build_station", -4 /* at least 4 parameters */,
585  func_signature_t<bsr_type>::get_typemask(false).c_str(), false /* static */);
586 
587  log_squirrel_type(func_signature_t<bsr_type>::get_squirrel_class(false), "build_station", func_signature_t<bsr_type>::get_squirrel_type(false, 0));
596  STATIC register_method(vm, build_bridge, "build_bridge", false, true);
604  STATIC register_method(vm, build_bridge_at, "build_bridge_at", false, true);
611  STATIC register_method(vm, set_slope, "set_slope", false, true);
617  STATIC register_method(vm, restore_slope, "restore_slope", false, true);
625  STATIC register_method(vm, can_set_slope, "can_set_slope", false, true);
630  STATIC register_method(vm, set_slope_get_price, "slope_get_price", false, true);
637  STATIC register_method(vm, build_sign_at, "build_sign_at", false, true);
645  STATIC register_method(vm, build_wayobj, "build_wayobj", false, true);
652  STATIC register_method(vm, change_climate_at, "change_climate_at", false, true);
653 
659  STATIC register_method_fv(vm, lower_raise, "grid_lower", freevariable<bool>(true), false, true);
665  STATIC register_method_fv(vm, lower_raise, "grid_raise", freevariable<bool>(false), false, true);
666 
667  end_class(vm);
668 }
string work(player_x pl, coord3d pos, int keys)
void start()