Crossfire Server, Trunk
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 <stdarg.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unordered_map>
49 #include <vector>
50 #include <string>
51 
52 #include <assert.h>
53 #include "global.h"
54 #include "object.h"
55 #include "sproto.h"
56 
58 #define CITYLIFE_NAME "citylife"
59 
61 #define FIRST_MOVE_KEY "citylife_first_move"
62 
66 struct spawn_point {
67  int x;
68  int y;
69 };
70 
75 struct spawn_zone {
76  int sx, sy, ex, ey;
77 };
78 
82 struct mapzone {
83  mapzone() : population(0) { };
84 
85  std::vector<spawn_point> points;
86  std::vector<spawn_zone> zones;
87  int population;
88  std::vector<std::string> available_archetypes;
89 };
90 
92 static std::unordered_map<std::string, mapzone *> maps;
93 
94 
104  auto find = maps.find(map->path);
105  return find == maps.end() ? nullptr : find->second;
106 }
107 
115 static object *get_npc(const mapzone *zone) {
116  int idx = RANDOM()%zone->available_archetypes.size();
117  archetype *arch = try_find_archetype(zone->available_archetypes[idx].c_str());
118 
119  if (!arch) {
120  LOG(llevError, CITYLIFE_NAME ": get_npc() invalid archetype %s!\n", zone->available_archetypes[idx].c_str());
121  return NULL;
122  }
123 
124  object *npc = arch_to_object(arch);
125  object *evt;
126 
127 
129  /* Prevent disease spreading in cities, mostly rabies. */
131  /* add a key so NPC will not disappear in the house it came from */
133 
134  evt = create_archetype("event_time");
135  evt->slaying = add_string(CITYLIFE_NAME);
136  evt->title = add_string(CITYLIFE_NAME);
138 
139  evt = create_archetype("event_attack");
140  evt->slaying = add_string(CITYLIFE_NAME);
141  evt->title = add_string(CITYLIFE_NAME);
144 
145  return npc;
146 }
147 
155 static void add_npc_to_zone(const mapzone *zone, mapstruct *map) {
156  int which;
157  object *npc = get_npc(zone);
158 
159  if (!npc)
160  return;
161  which = RANDOM() % zone->zones.size();
162  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))) {
164  }
165 }
166 
174 static void add_npc_to_point(const mapzone *zone, mapstruct *map) {
175  int which;
176  object *npc = get_npc(zone);
177 
178  which = RANDOM() % zone->points.size();
179  if (!object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
181  }
182 }
183 
190  int add;
191  const mapzone *zone = get_zone_for_map(map);
192 
193  if (!zone)
194  return;
195 
196  add = 1+RANDOM()%zone->population;
197  LOG(llevDebug, CITYLIFE_NAME ": adding %d NPC to map %s.\n", add, map->path);
198 
199  while (--add >= 0) {
200  add_npc_to_zone(zone, map);
201  }
202 }
203 
207 static void add_npc_to_random_map(void) {
208  int count;
209  mapstruct *list[50];
210  mapzone *zones[50];
211  count = 0;
212 
213  for (auto map = maps.cbegin(); map != maps.cend() && count < 50; map++) {
214  if ((list[count] = has_been_loaded(map->first.c_str())) && (list[count]->in_memory == MAP_IN_MEMORY)) {
215  zones[count] = map->second;
216  count++;
217  }
218  }
219  if (!count)
220  return;
221 
222  int selected = RANDOM() % count;
223  add_npc_to_point(zones[selected], list[selected]);
224 }
225 
226 static int citylife_globalEventListener(int *type, ...) {
227  va_list args;
228  int rv = 0;
229  mapstruct *map;
230  int code;
231 
232  va_start(args, type);
233  code = va_arg(args, int);
234 
235  switch (code) {
236  case EVENT_MAPLOAD:
237  map = va_arg(args, mapstruct *);
239  break;
240 
241  case EVENT_CLOCK:
242  if (RANDOM()%40 == 0)
244  }
245  va_end(args);
246 
247  return rv;
248 }
249 
250 static int eventListener(int *type, ...) {
251  va_list args;
252  object *ground, *who, *event;
253  const char *value;
254 
255  va_start(args, type);
256 
257  who = va_arg(args, object *);
258  va_arg(args, object *);
259  va_arg(args, object *);
260  va_arg(args, char *);
261  va_arg(args, int);
262  event = va_arg(args, object *);
263  va_arg(args, talk_info *);
264  va_end(args);
265 
266  assert(who);
267  assert(event);
268 
269  if (event->subtype == EVENT_ATTACKED) {
270  LOG(llevInfo, "citylife: %s attacked, reverting to default behaviour\n", who->name);
272  if (th) { // Should not be NULL, but play it safe
273  object_remove(th);
274  object_free(th, 0);
275  } // Attacked hook is unique thus removed by the event handler
278  return 0; // Let default behaviour apply, NPC becomes aggressive
279  }
280 
282  if (!value) {
288  LOG(llevInfo, "citylife: removing event from object %s which we didn't generate\n", who->name);
290  return 1;
291  }
292  // Set the flag regardless of whether we tried to move through an exit
293  if (strcmp(value, "1") == 0) {
295 
296  /* must set inventory as no drop, else it'll just drop on the ground */
297  for (object *inv = who->inv; inv; inv = inv->below)
299  }
300  /* should our npc disappear? -- Only attempt this if not first move */
301  else if (RANDOM()%100 < 30) {
302  int16_t sx = who->x, sy = who->y;
303  mapstruct *map = who->map;
304  if (!(get_map_flags(who->map, &map, who->x, who->y, &sx, &sy) & P_OUT_OF_MAP)) {
305  for (ground = GET_MAP_OB(map, sx, sy); ground; ground = ground->above) {
306  if (ground->type == EXIT) {
309  return 1;
310  }
311  }
312  }
313  }
314 
315  /* We have to move manually, because during the night NPCs don't move. */
316  move_ob(who, 1 + RANDOM() % 8, NULL);
317 
318  return 1;
319 }
320 
326 static void check_zone(const mapzone *zone, const char *path) {
327  if (zone->population == 0) {
328  LOG(llevError, "zone for %s has population of 0!\n", path);
329  }
330  if (zone->available_archetypes.empty()) {
331  LOG(llevError, "zone for %s has no archetype!\n", path);
332  }
333  if (zone->zones.empty()) {
334  LOG(llevError, "zone for %s has no zone!\n", path);
335  }
336  if (zone->points.empty()) {
337  LOG(llevError, "zone for %s has no spawn point!\n", path);
338  }
339 }
340 
346 static void load_citylife(BufferReader *reader, const char *filename) {
347  char *line;
348  mapzone *zone = nullptr;
349  char *split[4];
350  std::string path;
351 
352  while ((line = bufferreader_next_line(reader)) != NULL) {
353  if (line[0] == '\0' || line[0] == '#') {
354  continue;
355  }
356 
357  char *space = strchr(line, ' ');
358  if (!space) {
359  LOG(llevError, "citylife: invalid line in file %s:%zu\n", filename, bufferreader_current_line(reader));
360  continue;
361  }
362  *space = '\0';
363  space++;
364 
365  if (strcmp(line, "map") == 0) {
366  if (zone) {
367  check_zone(zone, path.c_str());
368  }
369  path = space;
370  auto existing = maps.find(path);
371  if (existing == maps.end()) {
372  zone = new mapzone();
373  maps[path] = zone;
374  } else {
375  zone = existing->second;
376  }
377  continue;
378  }
379  if (!zone) {
380  LOG(llevError, "citylife: error, missing 'map' in file %s:%zu\n", filename, bufferreader_current_line(reader));
381  continue;
382  }
383  if (strcmp(line, "population") == 0) {
384  zone->population = atoi(space);
385  continue;
386  }
387  if (strcmp(line, "zone") == 0) {
388  size_t found = split_string(space, split, 4, ' ');
389  if (found != 4) {
390  LOG(llevError, "citylife: 'zone' should have 4 values in file %s:%zu\n", filename, bufferreader_current_line(reader));
391  } else {
392  spawn_zone z;
393  z.sx = atoi(split[0]);
394  z.sy = atoi(split[1]);
395  z.ex = atoi(split[2]);
396  z.ey = atoi(split[3]);
397  zone->zones.push_back(z);
398  }
399  continue;
400  }
401  if (strcmp(line, "point") == 0) {
402  size_t found = split_string(space, split, 2, ' ');
403  if (found != 2) {
404  LOG(llevError, "citylife: 'point' should have 2 values in file %s:%zu\n", filename, bufferreader_current_line(reader));
405  } else {
406  spawn_point p;
407  p.x = atoi(split[0]);
408  p.y = atoi(split[1]);
409  zone->points.push_back(p);
410  }
411  continue;
412  }
413  if (strcmp(line, "arch") == 0) {
414  zone->available_archetypes.push_back(space);
415  continue;
416  }
417  LOG(llevError, "citylife: unknown line %s in file %s:%zu\n", line, filename, bufferreader_current_line(reader));
418  }
419 
420  if (zone) {
421  check_zone(zone, path.c_str());
422  }
423 }
424 
426 
431 
432  settings->add_hook(".citylife", load_citylife);
433 
434  /* Disable the plugin in case it's still there */
435  settings->disabled_plugins.push_back(strdup("citylife"));
436 }
437 
442  for (auto map : maps) {
443  delete map.second;
444  }
445  maps.clear();
446 }
447 
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:170
global.h
object_find_by_type_subtype
object * object_find_by_type_subtype(const object *who, int type, int subtype)
Definition: object.cpp:4301
settings
struct Settings settings
Definition: init.cpp:139
spawn_zone::sy
int sy
Definition: citylife.cpp:76
FLAG_STAND_STILL
#define FLAG_STAND_STILL
Definition: define.h:308
bufferreader_current_line
size_t bufferreader_current_line(BufferReader *br)
Definition: bufferreader.cpp:140
llevError
@ llevError
Definition: logger.h:11
maps
static std::unordered_map< std::string, mapzone * > maps
Definition: citylife.cpp:92
EVENT_CONNECTOR
@ EVENT_CONNECTOR
Definition: object.h:232
mapzone::population
int population
Definition: citylife.cpp:87
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
FLAG_UNDEAD
#define FLAG_UNDEAD
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
archininventory.arch
arch
DIALOGCHECK MINARGS 1 MAXARGS 1
Definition: archininventory.py:16
has_been_loaded
mapstruct * has_been_loaded(const char *name)
Definition: map.cpp:79
eventListener
static int eventListener(int *type,...)
Definition: citylife.cpp:250
c
static event_registration c
Definition: citylife.cpp:425
events_unregister_object_handler
void events_unregister_object_handler(const char *id)
Definition: events.cpp:294
add_npc_to_point
static void add_npc_to_point(const mapzone *zone, mapstruct *map)
Definition: citylife.cpp:174
FLAG_UNIQUE
#define FLAG_UNIQUE
Definition: define.h:287
guildoracle.list
list
Definition: guildoracle.py:87
mapzone::points
std::vector< spawn_point > points
Definition: citylife.cpp:83
commongive.inv
inv
Definition: commongive.py:29
EVENT_MAPLOAD
#define EVENT_MAPLOAD
Definition: events.h:48
get_zone_for_map
static const mapzone * get_zone_for_map(mapstruct *map)
Definition: citylife.cpp:103
spawn_point::y
int y
Definition: citylife.cpp:68
npc_dialog.filename
filename
Definition: npc_dialog.py:99
object_get_value
const char * object_get_value(const object *op, const char *const key)
Definition: object.cpp:4346
CITYLIFE_NAME
#define CITYLIFE_NAME
Definition: citylife.cpp:58
Moving_Fog.z
z
Definition: Moving_Fog.py:17
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2857
object::above
object * above
Definition: object.h:296
zones
static const house_zone_struct zones[]
Definition: random_house_generator.cpp:50
events_register_global_handler
event_registration events_register_global_handler(int eventcode, f_plug_event hook)
Definition: events.cpp:21
m
static event_registration m
Definition: citylife.cpp:425
events_register_object_handler
void events_register_object_handler(const char *id, f_plug_event handler)
Definition: events.cpp:289
autojail.who
who
Definition: autojail.py:3
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:126
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
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
disinfect.map
map
Definition: disinfect.py:4
citylife_init
void citylife_init(Settings *settings)
Definition: citylife.cpp:427
EVENT_CLOCK
#define EVENT_CLOCK
Definition: events.h:40
make_face_from_files.args
args
Definition: make_face_from_files.py:37
load_citylife
static void load_citylife(BufferReader *reader, const char *filename)
Definition: citylife.cpp:346
move_ob
int move_ob(object *op, int dir, object *originator)
Definition: move.cpp:58
events_unregister_global_handler
void events_unregister_global_handler(int eventcode, event_registration id)
Definition: events.cpp:28
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:473
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
check_zone
static void check_zone(const mapzone *zone, const char *path)
Definition: citylife.cpp:326
python_init.path
path
Definition: python_init.py:8
mapzone
Definition: citylife.cpp:82
mapzone::zones
std::vector< spawn_zone > zones
Definition: citylife.cpp:86
object::type
uint8_t type
Definition: object.h:348
Settings::disabled_plugins
std::vector< char * > disabled_plugins
Definition: global.h:326
object_free
void object_free(object *ob, int flags)
Definition: object.cpp:1592
add_npcs_to_map
static void add_npcs_to_map(mapstruct *map)
Definition: citylife.cpp:189
disinfect.count
int count
Definition: disinfect.py:7
archetype
Definition: object.h:483
sproto.h
FLAG_NO_DROP
#define FLAG_NO_DROP
Definition: define.h:288
mapzone::available_archetypes
std::vector< std::string > available_archetypes
Definition: citylife.cpp:88
add_npc_to_zone
static void add_npc_to_zone(const mapzone *zone, mapstruct *map)
Definition: citylife.cpp:155
FLAG_RANDOM_MOVE
#define FLAG_RANDOM_MOVE
Definition: define.h:309
spawn_point
Definition: citylife.cpp:66
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:247
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
RANDOM
#define RANDOM()
Definition: define.h:644
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
EXIT
@ EXIT
Definition: object.h:186
Settings
Definition: global.h:240
llevInfo
@ llevInfo
Definition: logger.h:12
citylife_globalEventListener
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:226
event_registration
unsigned long event_registration
Definition: events.h:69
add_npc_to_random_map
static void add_npc_to_random_map(void)
Definition: citylife.cpp:207
get_map_flags
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, int16_t x, int16_t y, int16_t *nx, int16_t *ny)
Definition: map.cpp:300
mapstruct
Definition: map.h:313
autojail.value
value
Definition: autojail.py:6
EVENT_TIME
#define EVENT_TIME
Definition: events.h:32
spawn_zone
Definition: citylife.cpp:75
spawn_zone::ey
int ey
Definition: citylife.cpp:76
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
talk_info
Definition: dialog.h:50
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:229
npc_dialog.npc
npc
Definition: npc_dialog.py:95
spawn_zone::ex
int ex
Definition: citylife.cpp:76
spawn_point::x
int x
Definition: citylife.cpp:67
object_remove
void object_remove(object *op)
Definition: object.cpp:1833
EVENT_ATTACKED
#define EVENT_ATTACKED
Definition: events.h:21
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:270
animate.event
event
DIALOGCHECK MINARGS 1 MAXARGS 2
Definition: animate.py:17
object_teleport
int object_teleport(object *op, mapstruct *map, int x, int y)
Definition: move.cpp:597
citylife_close
void citylife_close()
Definition: citylife.cpp:438
get_npc
static object * get_npc(const mapzone *zone)
Definition: citylife.cpp:115
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2606
mapzone::mapzone
mapzone()
Definition: citylife.cpp:83
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Definition: object.cpp:4499
FIRST_MOVE_KEY
#define FIRST_MOVE_KEY
Definition: citylife.cpp:61
spawn_zone::sx
int sx
Definition: citylife.cpp:76
roll-o-matic.evt
evt
Definition: roll-o-matic.py:190
BufferReader
Definition: bufferreader.cpp:21
object.h
llevDebug
@ llevDebug
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)
Definition: bufferreader.cpp:102