Crossfire Server  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 #include "server.h"
54 #include "assets.h"
55 
57 #define CITYLIFE_NAME "citylife"
58 
60 #define FIRST_MOVE_KEY "citylife_first_move"
61 
65 struct spawn_point {
66  int x;
67  int y;
68 };
69 
74 struct spawn_zone {
75  int sx, sy, ex, ey;
76 };
77 
81 struct mapzone {
82  mapzone() : population(0) { };
83 
84  std::vector<spawn_point> points;
85  std::vector<spawn_zone> zones;
86  int population;
87  std::vector<std::string> available_archetypes;
88 };
89 
91 static std::unordered_map<std::string, mapzone *> maps;
92 
93 
102 static const mapzone *get_zone_for_map(mapstruct *map) {
103  auto find = maps.find(map->path);
104  return find == maps.end() ? nullptr : find->second;
105 }
106 
114 static object *get_npc(const mapzone *zone) {
115  int idx = RANDOM()%zone->available_archetypes.size();
116  archetype *arch = try_find_archetype(zone->available_archetypes[idx].c_str());
117 
118  if (!arch) {
119  LOG(llevError, CITYLIFE_NAME ": get_npc() invalid archetype %s!\n", zone->available_archetypes[idx].c_str());
120  return NULL;
121  }
122 
123  object *npc = arch_to_object(arch);
124  object *evt;
125 
126 
128  /* Prevent disease spreading in cities, mostly rabies. */
129  SET_FLAG(npc, FLAG_UNDEAD);
130  /* add a key so NPC will not disappear in the house it came from */
131  object_set_value(npc, FIRST_MOVE_KEY, "1", 1);
132 
133  evt = create_archetype("event_time");
136  object_insert_in_ob(evt, npc);
137 
138  evt = create_archetype("event_attack");
141  SET_FLAG(evt, FLAG_UNIQUE);
142  object_insert_in_ob(evt, npc);
143 
144  return npc;
145 }
146 
154 static void add_npc_to_zone(const mapzone *zone, mapstruct *map) {
155  int which;
156  object *npc = get_npc(zone);
157 
158  if (!npc)
159  return;
160  which = RANDOM() % zone->zones.size();
161  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))) {
163  }
164 }
165 
173 static void add_npc_to_point(const mapzone *zone, mapstruct *map) {
174  int which;
175  object *npc = get_npc(zone);
176 
177  which = RANDOM() % zone->points.size();
178  if (!object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
180  }
181 }
182 
188 static void add_npcs_to_map(mapstruct *map) {
189  int add;
190  const mapzone *zone = get_zone_for_map(map);
191 
192  if (!zone)
193  return;
194 
195  add = 1+RANDOM()%zone->population;
196  LOG(llevDebug, CITYLIFE_NAME ": adding %d NPC to map %s.\n", add, map->path);
197 
198  while (--add >= 0) {
199  add_npc_to_zone(zone, map);
200  }
201 }
202 
206 static void add_npc_to_random_map(void) {
207  int count;
208  mapstruct *list[50];
209  mapzone *zones[50];
210  count = 0;
211 
212  for (auto map = maps.cbegin(); map != maps.cend() && count < 50; map++) {
213  if ((list[count] = has_been_loaded(map->first.c_str())) && (list[count]->in_memory == MAP_IN_MEMORY)) {
214  zones[count] = map->second;
215  count++;
216  }
217  }
218  if (!count)
219  return;
220 
221  int selected = RANDOM() % count;
222  add_npc_to_point(zones[selected], list[selected]);
223 }
224 
225 static int citylife_globalEventListener(int *type, ...) {
226  va_list args;
227  int rv = 0;
228  mapstruct *map;
229  int code;
230 
231  va_start(args, type);
232  code = va_arg(args, int);
233 
234  switch (code) {
235  case EVENT_MAPLOAD:
236  map = va_arg(args, mapstruct *);
237  add_npcs_to_map(map);
238  break;
239 
240  case EVENT_CLOCK:
241  if (RANDOM()%40 == 0)
243  }
244  va_end(args);
245 
246  return rv;
247 }
248 
249 static int eventListener(int *type, ...) {
250  va_list args;
251  object *ground, *who, *event;
252  const char *value;
253 
254  va_start(args, type);
255 
256  who = va_arg(args, object *);
257  va_arg(args, object *);
258  va_arg(args, object *);
259  va_arg(args, char *);
260  va_arg(args, int);
261  event = va_arg(args, object *);
262  va_arg(args, talk_info *);
263  va_end(args);
264 
265  assert(who);
266  assert(event);
267 
268  if (event->subtype == EVENT_ATTACKED) {
269  LOG(llevInfo, "citylife: %s attacked, reverting to default behaviour\n", who->name);
271  if (th) { // Should not be NULL, but play it safe
272  object_remove(th);
273  object_free(th, 0);
274  } // Attacked hook is unique thus removed by the event handler
277  return 0; // Let default behaviour apply, NPC becomes aggressive
278  }
279 
280  value = object_get_value(who, FIRST_MOVE_KEY);
281  if (!value) {
287  LOG(llevInfo, "citylife: removing event from object %s which we didn't generate\n", who->name);
288  object_remove(event);
289  return 1;
290  }
291  // Set the flag regardless of whether we tried to move through an exit
292  if (strcmp(value, "1") == 0) {
293  object_set_value(who, FIRST_MOVE_KEY, "0", 1);
294 
295  /* must set inventory as no drop, else it'll just drop on the ground */
296  for (object *inv = who->inv; inv; inv = inv->below)
297  SET_FLAG(inv, FLAG_NO_DROP);
298  }
299  /* should our npc disappear? -- Only attempt this if not first move */
300  else if (RANDOM()%100 < 30) {
301  int16_t sx = who->x, sy = who->y;
302  mapstruct *map = who->map;
303  if (!(get_map_flags(who->map, &map, who->x, who->y, &sx, &sy) & P_OUT_OF_MAP)) {
304  for (ground = GET_MAP_OB(map, sx, sy); ground; ground = ground->above) {
305  if (ground->type == EXIT) {
306  object_remove(who);
308  return 1;
309  }
310  }
311  }
312  }
313 
314  /* We have to move manually, because during the night NPCs don't move. */
315  move_ob(who, 1 + RANDOM() % 8, NULL);
316 
317  return 1;
318 }
319 
325 static void check_zone(const mapzone *zone, const char *path) {
326  if (zone->population == 0) {
327  LOG(llevError, "zone for %s has population of 0!\n", path);
328  }
329  if (zone->available_archetypes.empty()) {
330  LOG(llevError, "zone for %s has no archetype!\n", path);
331  }
332  if (zone->zones.empty()) {
333  LOG(llevError, "zone for %s has no zone!\n", path);
334  }
335  if (zone->points.empty()) {
336  LOG(llevError, "zone for %s has no spawn point!\n", path);
337  }
338 }
339 
345 static void load_citylife(BufferReader *reader, const char *filename) {
346  char *line;
347  mapzone *zone = nullptr;
348  char *split[4];
349  std::string path;
350 
351  while ((line = bufferreader_next_line(reader)) != NULL) {
352  if (line[0] == '\0' || line[0] == '#') {
353  continue;
354  }
355 
356  char *space = strchr(line, ' ');
357  if (!space) {
358  LOG(llevError, "citylife: invalid line in file %s:%zu\n", filename, bufferreader_current_line(reader));
359  continue;
360  }
361  *space = '\0';
362  space++;
363 
364  if (strcmp(line, "map") == 0) {
365  if (zone) {
366  check_zone(zone, path.c_str());
367  }
368  path = space;
369  auto existing = maps.find(path);
370  if (existing == maps.end()) {
371  zone = new mapzone();
372  maps[path] = zone;
373  } else {
374  zone = existing->second;
375  }
376  continue;
377  }
378  if (!zone) {
379  LOG(llevError, "citylife: error, missing 'map' in file %s:%zu\n", filename, bufferreader_current_line(reader));
380  continue;
381  }
382  if (strcmp(line, "population") == 0) {
383  zone->population = atoi(space);
384  continue;
385  }
386  if (strcmp(line, "zone") == 0) {
387  size_t found = split_string(space, split, 4, ' ');
388  if (found != 4) {
389  LOG(llevError, "citylife: 'zone' should have 4 values in file %s:%zu\n", filename, bufferreader_current_line(reader));
390  } else {
391  spawn_zone z;
392  z.sx = atoi(split[0]);
393  z.sy = atoi(split[1]);
394  z.ex = atoi(split[2]);
395  z.ey = atoi(split[3]);
396  zone->zones.push_back(z);
397  }
398  continue;
399  }
400  if (strcmp(line, "point") == 0) {
401  size_t found = split_string(space, split, 2, ' ');
402  if (found != 2) {
403  LOG(llevError, "citylife: 'point' should have 2 values in file %s:%zu\n", filename, bufferreader_current_line(reader));
404  } else {
405  spawn_point p;
406  p.x = atoi(split[0]);
407  p.y = atoi(split[1]);
408  zone->points.push_back(p);
409  }
410  continue;
411  }
412  if (strcmp(line, "arch") == 0) {
413  zone->available_archetypes.push_back(space);
414  continue;
415  }
416  LOG(llevError, "citylife: unknown line %s in file %s:%zu\n", line, filename, bufferreader_current_line(reader));
417  }
418 
419  if (zone) {
420  check_zone(zone, path.c_str());
421  }
422 }
423 
425 
430 
432 
433  /* Disable the plugin in case it's still there */
434  serverSettings->disabled_plugins.push_back("citylife");
435 }
436 
441  for (auto map : maps) {
442  delete map.second;
443  }
444  maps.clear();
445 }
446 
static void load_citylife(BufferReader *reader, const char *filename)
Read a .citylife file.
Definition: citylife.cpp:345
Error, serious thing.
Definition: logger.h:11
static event_registration c
Definition: citylife.cpp:424
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:225
#define FLAG_NO_DROP
Object can&#39;t be dropped.
Definition: define.h:276
void events_register_object_handler(const char *id, f_plug_event handler)
Register an object event handler.
Definition: events.cpp:299
Information.
Definition: logger.h:12
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
Options for a map.
Definition: citylife.cpp:81
#define FLAG_STAND_STILL
NPC will not (ever) move.
Definition: define.h:296
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2734
void assets_add_collector_hook(const char *name, collectorHook hook)
Definition: assets.cpp:556
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:1818
static const house_zone_struct zones[]
Maps we work on.
object * below
Pointer to the object stacked below this one.
Definition: object.h:295
sstring slaying
Which race to do double damage to.
Definition: object.h:327
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
object * above
Pointer to the object stacked above this one.
Definition: object.h:296
int object_teleport(object *op, mapstruct *map, int x, int y)
Move the specified object in a free spot around the map&#39;s x & y.
Definition: move.cpp:597
size_t bufferreader_current_line(BufferReader *br)
Return the index of the last line returned by bufferreader_next_line().
#define FLAG_RANDOM_MOVE
NPC will move randomly.
Definition: define.h:297
int16_t y
Position in the map for this object.
Definition: object.h:335
int16_t x
Definition: object.h:335
Point from which a NPC can come when the map is loaded.
Definition: citylife.cpp:65
Global type definitions and header inclusions.
#define FIRST_MOVE_KEY
Key to contain whether it&#39;s the first move of the NPC or not.
Definition: citylife.cpp:60
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:274
#define EVENT_ATTACKED
Object attacked, with weapon or spell.
Definition: events.h:29
#define FLAG_UNDEAD
Monster is undead.
Definition: define.h:257
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:129
void object_free(object *ob, int flags)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.cpp:1577
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
object * object_find_by_type_subtype(const object *who, int type, int subtype)
Find object in inventory.
Definition: object.cpp:4286
void citylife_close()
Definition: citylife.cpp:437
unsigned long event_registration
Registration identifier type.
Definition: events.h:84
See Exit.
Definition: object.h:186
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
static void add_npc_to_random_map(void)
Find a suitable map loaded and add an NPC to it.
Definition: citylife.cpp:206
Zone in which to add NPCs when the map was just loaded.
Definition: citylife.cpp:74
object * arch_to_object(archetype *at)
Creates and returns a new object which is a copy of the given archetype.
Definition: arch.cpp:227
Structure used to build up dialog information when a player says something.
Definition: dialog.h:50
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:360
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
#define SET_FLAG(xyz, p)
Definition: define.h:384
std::vector< std::string > disabled_plugins
List of disabled plugins, &#39;All&#39; means all.
Definition: server.h:16
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.cpp:1545
#define EVENT_CLOCK
Global time event.
Definition: events.h:53
This is a game-map.
Definition: map.h:320
ServerSettings serverSettings
Definition: init.cpp:42
static const mapzone * get_zone_for_map(mapstruct *map)
Finds if a map has a zone defined.
Definition: citylife.cpp:102
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4331
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:254
int move_ob(object *op, int dir, object *originator)
Op is trying to move in direction dir.
Definition: move.cpp:58
static void add_npc_to_zone(const mapzone *zone, mapstruct *map)
Add an NPC somewhere in a spawn zone.
Definition: citylife.cpp:154
Server settings.
Definition: global.h:241
static void check_zone(const mapzone *zone, const char *path)
Check if the zone has valid parameters, LOG() when invalid ones.
Definition: citylife.cpp:325
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
uint32_t in_memory
Combination of IN_MEMORY_xxx flags.
Definition: map.h:340
std::vector< spawn_zone > zones
Zones where to spawn at load time.
Definition: citylife.cpp:85
static void add_npc_to_point(const mapzone *zone, mapstruct *map)
Add an NPC somewhere at a spawn point.
Definition: citylife.cpp:173
Object structure, the core of Crossfire.
static event_registration m
Definition: citylife.cpp:424
#define RANDOM()
Definition: define.h:667
sstring name
The name of the object, obviously...
Definition: object.h:319
sstring title
Of foo, etc.
Definition: object.h:325
Only for debugging purposes.
Definition: logger.h:13
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
#define CITYLIFE_NAME
Module name for the event system.
Definition: citylife.cpp:57
#define CLEAR_FLAG(xyz, p)
Definition: define.h:385
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
static int eventListener(int *type,...)
Definition: citylife.cpp:249
void events_unregister_global_handler(int eventcode, event_registration id)
Remove a global event handler.
Definition: events.cpp:26
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:4484
object * inv
Pointer to the first object in the inventory.
Definition: object.h:298
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: events.h:40
Lauwenmark: an invisible object holding a plugin event hook.
Definition: object.h:232
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:173
#define EVENT_MAPLOAD
A map is loaded (pristine state)
Definition: events.h:61
C function wrappers to interact with assets.
void events_unregister_object_handler(const char *id)
Remove an object event handler.
Definition: events.cpp:304
event_registration events_register_global_handler(int eventcode, f_plug_event hook)
Register a global event handler.
Definition: events.cpp:19
mapstruct * has_been_loaded(const char *name)
Checks whether map has been loaded.
Definition: map.cpp:79
static void add_npcs_to_map(mapstruct *map)
Add some NPCs to the map, based on the zone definition.
Definition: citylife.cpp:188
std::vector< spawn_point > points
Points to spawn from when there is a player on the map.
Definition: citylife.cpp:82
char * bufferreader_next_line(BufferReader *br)
Return the next line in the buffer, as separated by a newline.
uint8_t subtype
Subtype of object.
Definition: object.h:349
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:2842
#define FLAG_UNIQUE
Item is really unique (UNIQUE_ITEMS)
Definition: define.h:275
static std::unordered_map< std::string, mapzone * > maps
All defined maps, with the path as key.
Definition: citylife.cpp:91
int population
Maximum of NPCs to add at load time.
Definition: citylife.cpp:86
static object * get_npc(const mapzone *zone)
Creates a NPC for the specified zone, and do needed initialization.
Definition: citylife.cpp:114
std::vector< std::string > available_archetypes
What archetypes can we chose from for an NPC?
Definition: citylife.cpp:87
void citylife_init(Settings *, ServerSettings *serverSettings)
Definition: citylife.cpp:426