OpenClonk
mapgen-handle.cpp
Go to the documentation of this file.
1 /*
2  * mape - C4 Landscape.txt editor
3  *
4  * Copyright (c) 2005-2009, Armin Burgmeier
5  *
6  * Distributed under the terms of the ISC license; see accompanying file
7  * "COPYING" for details.
8  *
9  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
10  * See accompanying file "TRADEMARK" for details.
11  *
12  * To redistribute this file separately, substitute the full license texts
13  * for the above references.
14  */
15 
16 #include "C4Include.h"
17 #include "landscape/C4MapScript.h"
19 #include "script/C4ScriptHost.h"
20 #include "object/C4DefList.h"
21 #include "object/C4Def.h"
22 #include "script/C4Aul.h"
23 #include "lib/StdMeshLoader.h"
24 
29 
30 #define HANDLE_TO_MATERIAL_MAP(handle) (reinterpret_cast<C4MaterialMap*>(handle))
31 #define HANDLE_TO_TEXTURE_MAP(handle) (reinterpret_cast<C4TextureMap*>(handle))
32 #define HANDLE_TO_GROUP(handle) (reinterpret_cast<C4Group*>(handle))
33 
34 namespace
35 {
36 
37 bool HasAlgoScript(C4MCNode* node)
38 {
39  if(node->Type() == MCN_Overlay && static_cast<C4MCOverlay*>(node)->Algorithm == static_cast<C4MCOverlay*>(node)->GetAlgo("script"))
40  return true;
41 
42  if(node->Child0) return HasAlgoScript(node->Child0);
43  if(node->Next) return HasAlgoScript(node->Next);
44  return false;
45 }
46 
47 class FakeSkeletonLoader: public StdMeshSkeletonLoader
48 {
49 public:
50  virtual StdMeshSkeleton* GetSkeletonByDefinition(const char* definition) const { return nullptr; }
51 };
52 
53 }
54 
55 extern "C" {
56 
58  unsigned int width;
59  unsigned int height;
60  unsigned int rowstride;
63 };
64 
66 {
69 }
70 
72 {
73  MapScript.Clear();
74  GameScript.Clear();
76 }
77 
79 {
81 
82  C4Def* libmap = new C4Def;
83  libmap->id = C4ID(std::string("Library_Map"));
84  libmap->SetName(libmap->id.ToString());
85  libmap->Category = C4D_StaticBack;
86  FakeSkeletonLoader loader;
87  if(!libmap->Load(*HANDLE_TO_GROUP(group_handle), loader, C4D_Load_Script, nullptr, nullptr))
88  {
89  fprintf(stderr, "Failed to load Library_Map script\n");
90  delete libmap;
91  }
92  else
93  {
94  ::Definitions.Add(libmap, false);
95  }
96 }
97 
98 C4MapgenHandle* c4_mapgen_handle_new_script(const char* filename, const char* source, C4MaterialMapHandle* material_map, C4TextureMapHandle* texture_map, unsigned int map_width, unsigned int map_height)
99 {
100  // Re-initialize script engine. Otherwise, we get a warning when the user
101  // changes the value of a constant, since it is defined already from the
102  // previous map rendering. Note that we do not need to re-load the map library.
105 
106  try
107  {
108  // TODO: Could also re-use an existing CSurface8,
109  // saving unnecessary malloc/free between map renderings
110  C4SLandscape landscape;
111  landscape.Default();
112 
113  landscape.MapWdt.Set(map_width, 0, map_width, map_width);
114  landscape.MapHgt.Set(map_height, 0, map_height, map_height);
115  landscape.MapPlayerExtend = 0;
116 
118  ::MapScript.LoadData(filename, source, nullptr);
119  // If InitializeMap() returns false, the map creator wants to
120  // call a fallback in the scenario script. This crashes if no
121  // scenario script is loaded, so simply load an empty script
122  // here:
123  ::GameScript.LoadData("Script.c", "", nullptr);
124 
125  const char* parse_error = c4_log_handle_get_first_log_message();
126  if(parse_error)
127  throw std::runtime_error(parse_error);
128 
129  // Link script engine (resolve includes/appends, generate code)
133  throw std::runtime_error(c4_log_handle_get_first_log_message());
134 
135  // Generate map, fail if return error occurs
137  std::unique_ptr<CSurface8> out_ptr_fg, out_ptr_bg;
138  const bool result = ::MapScript.InitializeMap(
139  &landscape,
140  HANDLE_TO_TEXTURE_MAP(texture_map),
141  HANDLE_TO_MATERIAL_MAP(material_map),
142  1,
143  &out_ptr_fg, &out_ptr_bg);
144 
145  // Don't show any map if there was a script runtime error
146  const char* runtime_error = c4_log_handle_get_first_log_message();
147  if(runtime_error)
148  throw std::runtime_error(runtime_error);
149 
150  if(!result)
151  throw std::runtime_error("No InitializeMap() function present in the script, or it returns false");
152 
153  C4MapgenHandle* handle = new C4MapgenHandle;
154  handle->width = out_ptr_fg->Wdt;
155  handle->height = out_ptr_fg->Hgt;
156  handle->rowstride = out_ptr_fg->Wdt;
157  handle->error_message = nullptr;
158  handle->data = out_ptr_fg->Bits;
159  out_ptr_fg->ReleaseBuffer();
160 
161  return handle;
162  }
163  catch(const std::exception& ex)
164  {
165  C4MapgenHandle* handle = new C4MapgenHandle;
166  handle->width = 0;
167  handle->height = 0;
168  handle->error_message.Copy(ex.what());
169  handle->data = nullptr;
170  return handle;
171  }
172 }
173 
174 C4MapgenHandle* c4_mapgen_handle_new(const char* filename, const char* source, const char* script_path, C4MaterialMapHandle* material_map, C4TextureMapHandle* texture_map, unsigned int map_width, unsigned int map_height)
175 {
176  try
177  {
178  C4SLandscape landscape;
179  landscape.Default();
180 
181  landscape.MapWdt.Set(map_width, 0, map_width, map_width);
182  landscape.MapHgt.Set(map_height, 0, map_height, map_height);
183  landscape.MapPlayerExtend = 0;
184 
185  C4MapCreatorS2 mapgen(
186  &landscape,
187  HANDLE_TO_TEXTURE_MAP(texture_map),
188  HANDLE_TO_MATERIAL_MAP(material_map),
189  1
190  );
191 
192  C4MCParser parser(&mapgen);
193  parser.ParseMemFile(source, filename);
194 
195  C4MCMap* map = mapgen.GetMap(nullptr);
196  if(!map) throw std::runtime_error("No map definition in source file");
197 
198  // Setup the script engine if there is an algo=script overlay in the
199  // Landscape.txt file
200  if(HasAlgoScript(mapgen.GetMap(nullptr)))
201  {
202  // Re-initialize script engine. Otherwise, we get a warning when the user
203  // changes the value of a constant, since it is defined already from the
204  // previous map rendering. Note that we do not need to re-load the map library.
207 
208  if(script_path == nullptr)
209  throw std::runtime_error("For algo=script overlays to work, save the file first at the location of the Script.c file");
210 
211  gchar* dirname = g_path_get_dirname(script_path);
212  gchar* basename = g_path_get_basename(script_path);
213 
214  C4Group File;
215  if(!File.Open(dirname))
216  {
217  StdStrBuf error_msg = FormatString("Failed to open directory '%s': %s", dirname, File.GetError());
218  g_free(dirname);
219  g_free(basename);
220  throw std::runtime_error(error_msg.getData());
221  }
222 
223  // get scripts
224  File.ResetSearch();
225  if(!File.FindNextEntry(basename, (char*)nullptr))
226  {
227  g_free(dirname);
228  g_free(basename);
229  StdStrBuf error_msg = FormatString("Failed to load '%s': No such file", script_path);
230  throw std::runtime_error(error_msg.getData());
231  }
232 
234  GameScript.Load(File, basename, nullptr, nullptr);
235  g_free(dirname);
236  g_free(basename);
237 
238  const char* parse_error = c4_log_handle_get_first_log_message();
239  if(parse_error)
240  throw std::runtime_error(parse_error);
241 
242  // Link script engine (resolve includes/appends, generate code)
246  throw std::runtime_error(c4_log_handle_get_first_log_message());
247  }
248 
250  int32_t out_width, out_height;
251  BYTE* array = mapgen.RenderBuf(nullptr, out_width, out_height);
252 
253  // Don't show any map if there was a script runtime error
254  const char* runtime_error = c4_log_handle_get_first_log_message();
255  if(runtime_error)
256  {
257  delete[] array;
258  throw std::runtime_error(runtime_error);
259  }
260 
261  C4MapgenHandle* handle = new C4MapgenHandle;
262  handle->width = map_width;
263  handle->height = map_height;
264  handle->rowstride = out_width;
265  handle->error_message = nullptr;
266  handle->data = array;
267  return handle;
268  }
269  catch(const C4MCParserErr& err)
270  {
271  C4MapgenHandle* handle = new C4MapgenHandle;
272  handle->width = 0;
273  handle->height = 0;
274  handle->error_message.Copy(err.Msg);
275  handle->data = nullptr;
276  return handle;
277  }
278  catch(const std::exception& ex)
279  {
280  C4MapgenHandle* handle = new C4MapgenHandle;
281  handle->width = 0;
282  handle->height = 0;
283  handle->error_message.Copy(ex.what());
284  handle->data = nullptr;
285  return handle;
286  }
287 }
288 
290 {
291  delete[] mapgen->data;
292  delete mapgen;
293 }
294 
295 const unsigned char* c4_mapgen_handle_get_map(C4MapgenHandle* mapgen)
296 {
297  return reinterpret_cast<unsigned char*>(mapgen->data);
298 }
299 
301 {
302  assert(mapgen->data != nullptr);
303  return mapgen->width;
304 }
305 
307 {
308  assert(mapgen->data != nullptr);
309  return mapgen->height;
310 }
311 
313 {
314  assert(mapgen->data != nullptr);
315  return mapgen->rowstride;
316 }
317 
319 {
320  if(mapgen->data != nullptr)
321  return nullptr;
322  return mapgen->error_message.getData();
323 }
324 
325 } // extern "C"
void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
Definition: C4Script.cpp:1128
const DWORD C4D_Load_Script
Definition: C4Def.h:83
const int32_t C4D_StaticBack
Definition: C4Def.h:40
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
C4DefList Definitions
Definition: C4Globals.cpp:49
@ MCN_Overlay
C4MapScriptHost MapScript
C4GameScriptHost GameScript
uint8_t BYTE
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:270
void Clear()
Definition: C4Aul.cpp:85
void Link(C4DefList *rDefs)
Definition: C4AulLink.cpp:165
Definition: C4Def.h:99
bool Load(C4Group &hGroup, StdMeshSkeletonLoader &loader, DWORD dwLoadWhat, const char *szLanguage, class C4SoundSystem *pSoundSystem=nullptr, C4DefGraphicsPtrBackup *gfx_backup=nullptr)
C4ID id
Definition: C4Def.h:101
int32_t Category
Definition: C4Def.h:117
void Clear()
bool Add(C4Def *ndef, bool fOverload)
bool Load(C4Group &, const char *, const char *, C4LangStringTable *) override
bool LoadData(const char *, const char *, C4LangStringTable *) override
bool FindNextEntry(const char *wildcard, StdStrBuf *filename=nullptr, size_t *size=nullptr, bool start_at_filename=false)
Definition: C4Group.cpp:2217
const char * GetError()
Definition: C4Group.cpp:650
void ResetSearch(bool reload_contents=false)
Definition: C4Group.cpp:1316
bool Open(const char *group_name, bool do_create=false)
Definition: C4Group.cpp:660
Definition: C4Id.h:26
const char * ToString() const
Definition: C4Id.h:56
C4MCNode * Next
C4MCNode * Child0
virtual C4MCNodeType Type()
C4MCAlgorithm * GetAlgo(const char *szName)
C4MCAlgorithm * Algorithm
char Msg[C4MaxMessage]
void ParseMemFile(const char *szScript, const char *szFilename)
BYTE * RenderBuf(const char *szMapName, int32_t &sfcWdt, int32_t &sfcHgt)
C4MCMap * GetMap(const char *szMapName)
bool InitializeMap(C4SLandscape *pLandscape, C4TextureMap *pTexMap, C4MaterialMap *pMatMap, uint32_t iPlayerCount, std::unique_ptr< CSurface8 > *pmap_fg_surface, std::unique_ptr< CSurface8 > *pmap_bg_surface)
bool LoadData(const char *, const char *, C4LangStringTable *) override
void InitFunctionMap(C4AulScriptEngine *pEngine)
virtual void SetName(const char *NewName=nullptr)
Definition: C4PropList.cpp:625
C4SVal MapWdt
Definition: C4Scenario.h:178
C4SVal MapHgt
Definition: C4Scenario.h:178
int32_t MapPlayerExtend
Definition: C4Scenario.h:181
void Default()
Definition: C4Scenario.cpp:286
void Set(int32_t std=0, int32_t rnd=0, int32_t min=0, int32_t max=100)
Definition: C4Scenario.cpp:36
virtual StdMeshSkeleton * GetSkeletonByDefinition(const char *definition) const =0
const char * getData() const
Definition: StdBuf.h:442
typedefG_BEGIN_DECLS struct _C4GroupHandle C4GroupHandle
Definition: group-handle.h:23
unsigned int c4_log_handle_get_n_log_messages()
Definition: log-handle.cpp:67
const char * c4_log_handle_get_first_log_message()
Definition: log-handle.cpp:61
void c4_log_handle_clear()
Definition: log-handle.cpp:55
typedefG_BEGIN_DECLS struct _C4MapgenHandle C4MapgenHandle
Definition: log-handle.h:23
unsigned int c4_mapgen_handle_get_height(C4MapgenHandle *mapgen)
unsigned int width
void c4_mapgen_handle_init_script_engine()
const char * c4_mapgen_handle_get_error(C4MapgenHandle *mapgen)
const unsigned char * c4_mapgen_handle_get_map(C4MapgenHandle *mapgen)
#define HANDLE_TO_MATERIAL_MAP(handle)
void c4_mapgen_handle_deinit_script_engine()
C4MapgenHandle * c4_mapgen_handle_new_script(const char *filename, const char *source, C4MaterialMapHandle *material_map, C4TextureMapHandle *texture_map, unsigned int map_width, unsigned int map_height)
unsigned int c4_mapgen_handle_get_width(C4MapgenHandle *mapgen)
unsigned int height
void c4_mapgen_handle_free(C4MapgenHandle *mapgen)
StdCopyStrBuf error_message
C4MapgenHandle * c4_mapgen_handle_new(const char *filename, const char *source, const char *script_path, C4MaterialMapHandle *material_map, C4TextureMapHandle *texture_map, unsigned int map_width, unsigned int map_height)
#define HANDLE_TO_GROUP(handle)
#define HANDLE_TO_TEXTURE_MAP(handle)
void c4_mapgen_handle_set_map_library(C4GroupHandle *group_handle)
unsigned int rowstride
unsigned int c4_mapgen_handle_get_rowstride(C4MapgenHandle *mapgen)
struct _C4MaterialMapHandle C4MaterialMapHandle
typedefG_BEGIN_DECLS struct _C4TextureMapHandle C4TextureMapHandle