Crossfire Server, Trunk  1.75.0
citylife.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2021 The Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unordered_map>
48 
49 #include <assert.h>
50 #include "global.h"
51 #include "object.h"
52 #include "sproto.h"
53 
55 #define CITYLIFE_NAME "citylife"
56 
58 #define FIRST_MOVE_KEY "citylife_first_move"
59 
63 struct spawn_point {
64  int x;
65  int y;
66 };
67 
72 struct spawn_zone {
73  int sx, sy, ex, ey;
74 };
75 
79 struct mapzone {
80  mapzone() : population(0) { };
81 
82  std::vector<spawn_point> points;
83  std::vector<spawn_zone> zones;
84  int population;
85  std::vector<std::string> available_archetypes;
86 };
87 
89 static std::unordered_map<std::string, mapzone *> maps;
90 
91 
100 static const mapzone *get_zone_for_map(mapstruct *map) {
101  auto find = maps.find(map->path);
102  return find == maps.end() ? nullptr : find->second;
103 }
104 
112 static object *get_npc(const mapzone *zone) {
113  int idx = RANDOM()%zone->available_archetypes.size();
114  archetype *arch = try_find_archetype(zone->available_archetypes[idx].c_str());
115 
116  if (!arch) {
117  LOG(llevError, CITYLIFE_NAME ": get_npc() invalid archetype %s!\n", zone->available_archetypes[idx].c_str());
118  return NULL;
119  }
120 
121  object *npc = arch_to_object(arch);
122  object *evt;
123 
124 
126  /* Prevent disease spreading in cities, mostly rabies. */
127  SET_FLAG(npc, FLAG_UNDEAD);
128  /* add a key so NPC will not disappear in the house it came from */
129  object_set_value(npc, FIRST_MOVE_KEY, "1", 1);
130 
131  evt = create_archetype("event_time");
134  object_insert_in_ob(evt, npc);
135 
136  evt = create_archetype("event_attack");
139  SET_FLAG(evt, FLAG_UNIQUE);
140  object_insert_in_ob(evt, npc);
141 
142  return npc;
143 }
144 
152 static void add_npc_to_zone(const mapzone *zone, mapstruct *map) {
153  int which;
154  object *npc = get_npc(zone);
155 
156  if (!npc)
157  return;
158  which = RANDOM() % zone->zones.size();
159  if (!object_teleport(npc, map, zone->zones[which].sx+RANDOM()%(zone->zones[which].ex-zone->zones[which].sx), zone->zones[which].sy+RANDOM()%(zone->zones[which].ey-zone->zones[which].sy))) {
161  }
162 }
163 
171 static void add_npc_to_point(const mapzone *zone, mapstruct *map) {
172  int which;
173  object *npc = get_npc(zone);
174 
175  which = RANDOM() % zone->points.size();
176  if (!object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
178  }
179 }
180 
186 static void add_npcs_to_map(mapstruct *map) {
187  int add;
188  const mapzone *zone = get_zone_for_map(map);
189 
190  if (!zone)
191  return;
192 
193  add = 1+RANDOM()%zone->population;
194  LOG(llevDebug, CITYLIFE_NAME ": adding %d NPC to map %s.\n", add, map->path);
195 
196  while (--add >= 0) {
197  add_npc_to_zone(zone, map);
198  }
199 }
200 
204 static void add_npc_to_random_map(void) {
205  int count;
206  mapstruct *list[50];
207  mapzone *zones[50];
208  count = 0;
209 
210  for (auto map = maps.cbegin(); map != maps.cend() && count < 50; map++) {
211  if ((list[count] = has_been_loaded(map->first.c_str())) && (list[count]->in_memory == MAP_IN_MEMORY)) {
212  zones[count] = map->second;
213  count++;
214  }
215  }
216  if (!count)
217  return;
218 
219  int selected = RANDOM() % count;
220  add_npc_to_point(zones[selected], list[selected]);
221 }
222 
223 static int citylife_globalEventListener(int *type, ...) {
224  va_list args;
225  int rv = 0;
226  mapstruct *map;
227  int code;
228 
229  va_start(args, type);
230  code = va_arg(args, int);
231 
232  switch (code) {
233  case EVENT_MAPLOAD:
234  map = va_arg(args, mapstruct *);
235  add_npcs_to_map(map);
236  break;
237 
238  case EVENT_CLOCK:
239  if (RANDOM()%40 == 0)
241  }
242  va_end(args);
243 
244  return rv;
245 }
246 
247 static int eventListener(int *type, ...) {
248  va_list args;
249  object *ground, *who, *event;
250  const char *value;
251 
252  va_start(args, type);
253 
254  who = va_arg(args, object *);
255  va_arg(args, object *);
256  va_arg(args, object *);
257  va_arg(args, char *);
258  va_arg(args, int);
259  event = va_arg(args, object *);
260  va_arg(args, talk_info *);
261  va_end(args);
262 
263  assert(who);
264  assert(event);
265 
266  if (event->subtype == EVENT_ATTACKED) {
267  LOG(llevInfo, "citylife: %s attacked, reverting to default behaviour\n", who->name);
269  if (th) { // Should not be NULL, but play it safe
270  object_remove(th);
271  object_free(th, 0);
272  } // Attacked hook is unique thus removed by the event handler
275  return 0; // Let default behaviour apply, NPC becomes aggressive
276  }
277 
278  value = object_get_value(who, FIRST_MOVE_KEY);
279  if (!value) {
285  LOG(llevInfo, "citylife: removing event from object %s which we didn't generate\n", who->name);
286  object_remove(event);
287  return 1;
288  }
289  // Set the flag regardless of whether we tried to move through an exit
290  if (strcmp(value, "1") == 0) {
291  object_set_value(who, FIRST_MOVE_KEY, "0", 1);
292 
293  /* must set inventory as no drop, else it'll just drop on the ground */
294  for (object *inv = who->inv; inv; inv = inv->below)
295  SET_FLAG(inv, FLAG_NO_DROP);
296  }
297  /* should our npc disappear? -- Only attempt this if not first move */
298  else if (RANDOM()%100 < 30) {
299  int16_t sx = who->x, sy = who->y;
300  mapstruct *map = who->map;
301  if (!(get_map_flags(who->map, &map, who->x, who->y, &sx, &sy) & P_OUT_OF_MAP)) {
302  for (ground = GET_MAP_OB(map, sx, sy); ground; ground = ground->above) {
303  if (ground->type == EXIT) {
304  object_remove(who);
306  return 1;
307  }
308  }
309  }
310  }
311 
312  /* We have to move manually, because during the night NPCs don't move. */
313  move_ob(who, 1 + RANDOM() % 8, NULL);
314 
315  return 1;
316 }
317 
323 static void check_zone(const mapzone *zone, const char *path) {
324  if (zone->population == 0) {
325  LOG(llevError, "zone for %s has population of 0!\n", path);
326  }
327  if (zone->available_archetypes.empty()) {
328  LOG(llevError, "zone for %s has no archetype!\n", path);
329  }
330  if (zone->zones.empty()) {
331  LOG(llevError, "zone for %s has no zone!\n", path);
332  }
333  if (zone->points.empty()) {
334  LOG(llevError, "zone for %s has no spawn point!\n", path);
335  }
336 }
337 
343 static void load_citylife(BufferReader *reader, const char *filename) {
344  char *line;
345  mapzone *zone = nullptr;
346  char *split[4];
347  std::string path;
348 
349  while ((line = bufferreader_next_line(reader)) != NULL) {
350  if (line[0] == '\0' || line[0] == '#') {
351  continue;
352  }
353 
354  char *space = strchr(line, ' ');
355  if (!space) {
356  LOG(llevError, "citylife: invalid line in file %s:%zu\n", filename, bufferreader_current_line(reader));
357  continue;
358  }
359  *space = '\0';
360  space++;
361 
362  if (strcmp(line, "map") == 0) {
363  if (zone) {
364  check_zone(zone, path.c_str());
365  }
366  path = space;
367  auto existing = maps.find(path);
368  if (existing == maps.end()) {
369  zone = new mapzone();
370  maps[path] = zone;
371  } else {
372  zone = existing->second;
373  }
374  continue;
375  }
376  if (!zone) {
377  LOG(llevError, "citylife: error, missing 'map' in file %s:%zu\n", filename, bufferreader_current_line(reader));
378  continue;
379  }
380  if (strcmp(line, "population") == 0) {
381  zone->population = atoi(space);
382  continue;
383  }
384  if (strcmp(line, "zone") == 0) {
385  size_t found = split_string(space, split, 4, ' ');
386  if (found != 4) {
387  LOG(llevError, "citylife: 'zone' should have 4 values in file %s:%zu\n", filename, bufferreader_current_line(reader));
388  } else {
389  spawn_zone z;
390  z.sx = atoi(split[0]);
391  z.sy = atoi(split[1]);
392  z.ex = atoi(split[2]);
393  z.ey = atoi(split[3]);
394  zone->zones.push_back(z);
395  }
396  continue;
397  }
398  if (strcmp(line, "point") == 0) {
399  size_t found = split_string(space, split, 2, ' ');
400  if (found != 2) {
401  LOG(llevError, "citylife: 'point' should have 2 values in file %s:%zu\n", filename, bufferreader_current_line(reader));
402  } else {
403  spawn_point p;
404  p.x = atoi(split[0]);
405  p.y = atoi(split[1]);
406  zone->points.push_back(p);
407  }
408  continue;
409  }
410  if (strcmp(line, "arch") == 0) {
411  zone->available_archetypes.push_back(space);
412  continue;
413  }
414  LOG(llevError, "citylife: unknown line %s in file %s:%zu\n", line, filename, bufferreader_current_line(reader));
415  }
416 
417  if (zone) {
418  check_zone(zone, path.c_str());
419  }
420 }
421 
423 
428 
429  settings->add_hook(".citylife", load_citylife);
430 
431  /* Disable the plugin in case it's still there */
432  settings->disabled_plugins.push_back(strdup("citylife"));
433 }
434 
439  for (auto map : maps) {
440  delete map.second;
441  }
442  maps.clear();
443 }
444 
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:170
global.h
object_find_by_type_subtype
object * object_find_by_type_subtype(const object *who, int type, int subtype)
Find object in inventory.
Definition: object.cpp:4301
settings
struct Settings settings
Server settings.
Definition: init.cpp:139
spawn_zone::sy
int sy
Definition: citylife.cpp:73
FLAG_STAND_STILL
#define FLAG_STAND_STILL
NPC will not (ever) move.
Definition: define.h:308
bufferreader_current_line
size_t bufferreader_current_line(BufferReader *br)
Return the index of the last line returned by bufferreader_next_line().
Definition: bufferreader.cpp:140
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
maps
static std::unordered_map< std::string, mapzone * > maps
All defined maps, with the path as key.
Definition: citylife.cpp:89
EVENT_CONNECTOR
@ EVENT_CONNECTOR
Lauwenmark: an invisible object holding a plugin event hook.
Definition: object.h:232
mapzone::population
int population
Maximum of NPCs to add at load time.
Definition: citylife.cpp:84
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
FLAG_UNDEAD
#define FLAG_UNDEAD
Monster is undead.
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:298
has_been_loaded
mapstruct * has_been_loaded(const char *name)
Checks whether map has been loaded.
Definition: map.cpp:79
eventListener
static int eventListener(int *type,...)
Definition: citylife.cpp:247
c
static event_registration c
Definition: citylife.cpp:422
events_unregister_object_handler
void events_unregister_object_handler(const char *id)
Remove an object event handler.
Definition: events.cpp:303
add_npc_to_point
static void add_npc_to_point(const mapzone *zone, mapstruct *map)
Add an NPC somewhere at a spawn point.
Definition: citylife.cpp:171
FLAG_UNIQUE
#define FLAG_UNIQUE
Item is really unique (UNIQUE_ITEMS)
Definition: define.h:287
object::x
int16_t x
Definition: object.h:335
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
mapzone::points
std::vector< spawn_point > points
Points to spawn from when there is a player on the map.
Definition: citylife.cpp:80
EVENT_MAPLOAD
#define EVENT_MAPLOAD
A map is loaded (pristine state)
Definition: events.h:48
get_zone_for_map
static const mapzone * get_zone_for_map(mapstruct *map)
Finds if a map has a zone defined.
Definition: citylife.cpp:100
spawn_point::y
int y
Definition: citylife.cpp:65
object::title
sstring title
Of foo, etc.
Definition: object.h:325
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:355
object_get_value
const char * object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4346
CITYLIFE_NAME
#define CITYLIFE_NAME
Module name for the event system.
Definition: citylife.cpp:55
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.cpp:2857
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:296
zones
static const house_zone_struct zones[]
Maps we work on.
Definition: random_house_generator.cpp:50
events_register_global_handler
event_registration events_register_global_handler(int eventcode, f_plug_event hook)
Register a global event handler.
Definition: events.cpp:19
object::y
int16_t y
Position in the map for this object.
Definition: object.h:335
m
static event_registration m
Definition: citylife.cpp:422
events_register_object_handler
void events_register_object_handler(const char *id, f_plug_event handler)
Register an object event handler.
Definition: events.cpp:298
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:126
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects,...
Definition: object.cpp:1560
Settings::add_hook
void add_hook(const char *name, collectorHook hook)
Definition: global.h:340
is_valid_types_gen.line
line
Definition: is_valid_types_gen.py:34
object::subtype
uint8_t subtype
Subtype of object.
Definition: object.h:349
citylife_init
void citylife_init(Settings *settings)
Definition: citylife.cpp:424
EVENT_CLOCK
#define EVENT_CLOCK
Global time event.
Definition: events.h:40
load_citylife
static void load_citylife(BufferReader *reader, const char *filename)
Read a .citylife file.
Definition: citylife.cpp:343
move_ob
int move_ob(object *op, int dir, object *originator)
Op is trying to move in direction dir.
Definition: move.cpp:58
events_unregister_global_handler
void events_unregister_global_handler(int eventcode, event_registration id)
Remove a global event handler.
Definition: events.cpp:26
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings.
Definition: utils.cpp:473
add_string
sstring add_string(const char *str)
This will add 'str' to the hash table.
Definition: shstr.cpp:124
check_zone
static void check_zone(const mapzone *zone, const char *path)
Check if the zone has valid parameters, LOG() when invalid ones.
Definition: citylife.cpp:323
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:295
mapzone
Options for a map.
Definition: citylife.cpp:79
mapzone::zones
std::vector< spawn_zone > zones
Zones where to spawn at load time.
Definition: citylife.cpp:83
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
Settings::disabled_plugins
std::vector< char * > disabled_plugins
List of disabled plugins, 'All' means all.
Definition: global.h:328
object_free
void object_free(object *ob, int flags)
Frees everything allocated by an object, removes it from the list of used objects,...
Definition: object.cpp:1592
add_npcs_to_map
static void add_npcs_to_map(mapstruct *map)
Add some NPCs to the map, based on the zone definition.
Definition: citylife.cpp:186
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
sproto.h
FLAG_NO_DROP
#define FLAG_NO_DROP
Object can't be dropped.
Definition: define.h:288
mapzone::available_archetypes
std::vector< std::string > available_archetypes
What archetypes can we chose from for an NPC?
Definition: citylife.cpp:85
add_npc_to_zone
static void add_npc_to_zone(const mapzone *zone, mapstruct *map)
Add an NPC somewhere in a spawn zone.
Definition: citylife.cpp:152
FLAG_RANDOM_MOVE
#define FLAG_RANDOM_MOVE
NPC will move randomly.
Definition: define.h:309
spawn_point
Point from which a NPC can come when the map is loaded.
Definition: citylife.cpp:63
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:249
create_archetype
object * create_archetype(const char *name)
Finds which archetype matches the given name, and returns a new object containing a copy of the arche...
Definition: arch.cpp:276
path
pluglist shows those as well as a short text describing each the list will simply appear empty The keyword for the Python plugin is Python plugout< keyword > Unloads a given identified by its _keyword_ So if you want to unload the Python you need to do plugout Python plugin< libname > Loads a given whose _filename_ is libname So in the case of you d have to do a plugin cfpython so Note that all filenames are relative to the default plugin path(SHARE/plugins). Console messages. ----------------- When Crossfire starts
RANDOM
#define RANDOM()
Definition: define.h:638
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
EXIT
@ EXIT
Definition: object.h:186
Settings
Server settings.
Definition: global.h:242
llevInfo
@ llevInfo
Information.
Definition: logger.h:12
object::slaying
sstring slaying
Which race to do double damage to.
Definition: object.h:327
citylife_globalEventListener
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:223
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
event_registration
unsigned long event_registration
Registration identifier type.
Definition: events.h:71
add_npc_to_random_map
static void add_npc_to_random_map(void)
Find a suitable map loaded and add an NPC to it.
Definition: citylife.cpp:204
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
This rolls up wall, blocks_magic, blocks_view, etc, all into one function that just returns a P_.
Definition: map.cpp:300
mapstruct
This is a game-map.
Definition: map.h:315
EVENT_TIME
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: events.h:32
spawn_zone
Zone in which to add NPCs when the map was just loaded.
Definition: citylife.cpp:72
spawn_zone::ey
int ey
Definition: citylife.cpp:73
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
talk_info
Structure used to build up dialog information when a player says something.
Definition: dialog.h:50
arch_to_object
object * arch_to_object(archetype *at)
Creates and returns a new object which is a copy of the given archetype.
Definition: arch.cpp:227
spawn_zone::ex
int ex
Definition: citylife.cpp:73
spawn_point::x
int x
Definition: citylife.cpp:64
object_remove
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to.
Definition: object.cpp:1833
EVENT_ATTACKED
#define EVENT_ATTACKED
Object attacked, with weapon or spell.
Definition: events.h:21
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:269
object_teleport
int object_teleport(object *op, mapstruct *map, int x, int y)
Move the specified object in a free spot around the map's x & y.
Definition: move.cpp:597
code
Crossfire Architecture the general intention is to enhance the enjoyability and playability of CF In this code
Definition: arch-handbook.txt:14
citylife_close
void citylife_close()
Definition: citylife.cpp:435
get_npc
static object * get_npc(const mapzone *zone)
Creates a NPC for the specified zone, and do needed initialization.
Definition: citylife.cpp:112
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2734
mapzone::mapzone
mapzone()
Definition: citylife.cpp:80
list
How to Install a Crossfire Server on you must install a python script engine on your computer Python is the default script engine of Crossfire You can find the python engine you have only to install them The VisualC Crossfire settings are for but you habe then to change the pathes in the VC settings Go in Settings C and Settings Link and change the optional include and libs path to the new python installation path o except the maps ! You must download a map package and install them the share folder Its must look like doubleclick on crossfire32 dsw There are projects in your libcross lib and plugin_python You need to compile all Easiest way is to select the plugin_python ReleaseLog as active this will compile all others too Then in Visual C press< F7 > to compile If you don t have an appropriate compiler you can try to get the the VC copies the crossfire32 exe in the crossfire folder and the plugin_python dll in the crossfire share plugins folder we will remove it when we get time for it o Last showing lots of weird write to the Crossfire mailing list
Definition: INSTALL_WIN32.txt:50
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Updates the key in op to value.
Definition: object.cpp:4499
FIRST_MOVE_KEY
#define FIRST_MOVE_KEY
Key to contain whether it's the first move of the NPC or not.
Definition: citylife.cpp:58
spawn_zone::sx
int sx
Definition: citylife.cpp:73
BufferReader
Definition: bufferreader.cpp:21
object.h
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Return the next line in the buffer, as separated by a newline.
Definition: bufferreader.cpp:102