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 extern "C" {
53 #include "global.h"
54 #include "object.h"
55 #include "sproto.h"
56 }
57 
59 #define CITYLIFE_NAME "citylife"
60 
62 #define FIRST_MOVE_KEY "citylife_first_move"
63 
67 typedef struct {
68  int x;
69  int y;
70 } spawn_point;
71 
76 typedef struct {
77  int sx, sy, ex, ey;
78 } spawn_zone;
79 
83 struct mapzone {
84  mapzone() : population(0) { };
85 
86  std::vector<spawn_point> points;
87  std::vector<spawn_zone> zones;
88  int population;
89  std::vector<std::string> available_archetypes;
90 };
91 
93 static std::unordered_map<std::string, mapzone *> maps;
94 
95 
105  auto find = maps.find(map->path);
106  return find == maps.end() ? nullptr : find->second;
107 }
108 
116 static object *get_npc(const mapzone *zone) {
117  int idx = RANDOM()%zone->available_archetypes.size();
118  archetype *arch = try_find_archetype(zone->available_archetypes[idx].c_str());
119 
120  if (!arch) {
121  LOG(llevError, CITYLIFE_NAME ": get_npc() invalid archetype %s!\n", zone->available_archetypes[idx].c_str());
122  return NULL;
123  }
124 
125  object *npc = arch_to_object(arch);
126  object *evt;
127 
128 
130  /* Prevent disease spreading in cities, mostly rabies. */
132  /* add a key so NPC will not disappear in the house it came from */
134 
135  evt = create_archetype("event_time");
136  evt->slaying = add_string(CITYLIFE_NAME);
137  evt->title = add_string(CITYLIFE_NAME);
139 
140  evt = create_archetype("event_attack");
141  evt->slaying = add_string(CITYLIFE_NAME);
142  evt->title = add_string(CITYLIFE_NAME);
145 
146  return npc;
147 }
148 
156 static void add_npc_to_zone(const mapzone *zone, mapstruct *map) {
157  int which;
158  object *npc = get_npc(zone);
159 
160  if (!npc)
161  return;
162  which = RANDOM() % zone->zones.size();
163  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))) {
165  }
166 }
167 
175 static void add_npc_to_point(const mapzone *zone, mapstruct *map) {
176  int which;
177  object *npc = get_npc(zone);
178 
179  which = RANDOM() % zone->points.size();
180  if (!object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
182  }
183 }
184 
191  int add;
192  const mapzone *zone = get_zone_for_map(map);
193 
194  if (!zone)
195  return;
196 
197  add = 1+RANDOM()%zone->population;
198  LOG(llevDebug, CITYLIFE_NAME ": adding %d NPC to map %s.\n", add, map->path);
199 
200  while (--add >= 0) {
201  add_npc_to_zone(zone, map);
202  }
203 }
204 
208 static void add_npc_to_random_map(void) {
209  int count;
210  mapstruct *list[50];
211  mapzone *zones[50];
212  count = 0;
213 
214  for (auto map = maps.cbegin(); map != maps.cend() && count < 50; map++) {
215  if ((list[count] = has_been_loaded(map->first.c_str())) && (list[count]->in_memory == MAP_IN_MEMORY)) {
216  zones[count] = map->second;
217  count++;
218  }
219  }
220  if (!count)
221  return;
222 
223  int selected = RANDOM() % count;
224  add_npc_to_point(zones[selected], list[selected]);
225 }
226 
227 static int citylife_globalEventListener(int *type, ...) {
228  va_list args;
229  int rv = 0;
230  mapstruct *map;
231  int code;
232 
233  va_start(args, type);
234  code = va_arg(args, int);
235 
236  switch (code) {
237  case EVENT_MAPLOAD:
238  map = va_arg(args, mapstruct *);
240  break;
241 
242  case EVENT_CLOCK:
243  if (RANDOM()%40 == 0)
245  }
246  va_end(args);
247 
248  return rv;
249 }
250 
251 static int eventListener(int *type, ...) {
252  va_list args;
253  object *ground, *who, *event;
254  const char *value;
255 
256  va_start(args, type);
257 
258  who = va_arg(args, object *);
259  va_arg(args, object *);
260  va_arg(args, object *);
261  va_arg(args, char *);
262  va_arg(args, int);
263  event = va_arg(args, object *);
264  va_arg(args, talk_info *);
265  va_end(args);
266 
267  if (event->subtype == EVENT_ATTACKED) {
268  LOG(llevInfo, "citylife: %s attacked, reverting to default behaviour\n", who->name);
270  if (th) { // Should not be NULL, but play it safe
271  object_remove(th);
272  object_free(th, 0);
273  } // Attacked hook is unique thus removed by the event handler
276  return 0; // Let default behaviour apply, NPC becomes aggressive
277  }
278 
280  if (!value) {
286  if (event) {
287  LOG(llevInfo, "citylife: removing event from object %s which we didn't generate\n", who->name);
289  }
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:%ld\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:%ld\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:%ld\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:%ld\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:%ld\n", line, filename, bufferreader_current_line(reader));
418  }
419 
420  if (zone) {
421  check_zone(zone, path.c_str());
422  }
423 }
424 
426 
427 extern "C"
432 
433  settings->hooks_filename[settings->hooks_count] = ".citylife";
436 
437  /* Disable the plugin in case it's still there */
438  linked_char *disable = static_cast<linked_char *>(calloc(1, sizeof(linked_char)));
439  disable->next = settings->disabled_plugins;
440  disable->name = strdup("citylife");
441  settings->disabled_plugins = disable;
442 }
443 
444 extern "C"
449  for (auto map : maps) {
450  delete map.second;
451  }
452  maps.clear();
453 }
454 
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
global.h
Settings::hooks_count
uint8_t hooks_count
Definition: global.h:328
spawn_zone::sy
int sy
Definition: citylife.cpp:77
object_free
void object_free(object *ob, int flags)
Definition: object.c:1565
add_string
sstring add_string(const char *str)
Definition: shstr.c:124
object_remove
void object_remove(object *op)
Definition: object.c:1806
FLAG_STAND_STILL
#define FLAG_STAND_STILL
Definition: define.h:308
llevError
@ llevError
Definition: logger.h:11
maps
static std::unordered_map< std::string, mapzone * > maps
Definition: citylife.cpp:93
EVENT_CONNECTOR
@ EVENT_CONNECTOR
Definition: object.h:227
mapzone::population
int population
Definition: citylife.cpp:88
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
eventListener
static int eventListener(int *type,...)
Definition: citylife.cpp:251
c
static event_registration c
Definition: citylife.cpp:425
events_unregister_object_handler
void events_unregister_object_handler(const char *id)
Definition: events.cpp:254
add_npc_to_point
static void add_npc_to_point(const mapzone *zone, mapstruct *map)
Definition: citylife.cpp:175
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:84
python_event.path
path
Definition: python_event.py:11
commongive.inv
inv
Definition: commongive.py:28
EVENT_MAPLOAD
#define EVENT_MAPLOAD
Definition: events.h:47
inja::string_view::split
std::pair< nonstd::string_view, nonstd::string_view > split(nonstd::string_view view, char Separator)
Definition: inja.hpp:1886
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Definition: bufferreader.c:102
get_zone_for_map
static const mapzone * get_zone_for_map(mapstruct *map)
Definition: citylife.cpp:104
bufferreader_current_line
size_t bufferreader_current_line(BufferReader *br)
Definition: bufferreader.c:140
spawn_point::y
int y
Definition: citylife.cpp:69
npc_dialog.filename
filename
Definition: npc_dialog.py:99
CITYLIFE_NAME
#define CITYLIFE_NAME
Definition: citylife.cpp:59
Moving_Fog.z
z
Definition: Moving_Fog.py:17
linked_char
Definition: global.h:86
zones
static const house_zone_struct zones[]
Definition: random_house_generator.c:50
events_register_global_handler
event_registration events_register_global_handler(int eventcode, f_plug_event hook)
Definition: events.cpp:16
archt
Definition: object.h:468
settings
struct Settings settings
Definition: init.c:39
object_get_value
const char * object_get_value(const object *op, const char *const key)
Definition: object.c:4354
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:249
autojail.who
who
Definition: autojail.py:3
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:131
object_find_by_type_subtype
object * object_find_by_type_subtype(const object *who, int type, int subtype)
Definition: object.c:4309
is_valid_types_gen.line
line
Definition: is_valid_types_gen.py:34
disinfect.map
map
Definition: disinfect.py:4
linked_char::name
const char * name
Definition: global.h:87
citylife_init
void citylife_init(Settings *settings)
Definition: citylife.cpp:428
EVENT_CLOCK
#define EVENT_CLOCK
Definition: events.h:39
make_face_from_files.args
args
Definition: make_face_from_files.py:30
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.c:58
events_unregister_global_handler
void events_unregister_global_handler(int eventcode, event_registration id)
Definition: events.cpp:23
check_zone
static void check_zone(const mapzone *zone, const char *path)
Definition: citylife.cpp:326
mapzone
Definition: citylife.cpp:83
mapzone::zones
std::vector< spawn_zone > zones
Definition: citylife.cpp:87
linked_char::next
struct linked_char * next
Definition: global.h:88
add_npcs_to_map
static void add_npcs_to_map(mapstruct *map)
Definition: citylife.cpp:190
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Definition: object.c:4489
disinfect.count
int count
Definition: disinfect.py:7
Settings::hooks_filename
const char * hooks_filename[20]
Definition: global.h:329
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:89
mapdef
Definition: map.h:324
add_npc_to_zone
static void add_npc_to_zone(const mapzone *zone, mapstruct *map)
Definition: citylife.cpp:156
FLAG_RANDOM_MOVE
#define FLAG_RANDOM_MOVE
Definition: define.h:309
spawn_point
Definition: citylife.cpp:67
P_OUT_OF_MAP
#define P_OUT_OF_MAP
Definition: map.h:250
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.c:500
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:281
RANDOM
#define RANDOM()
Definition: define.h:642
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
EXIT
@ EXIT
Definition: object.h:181
Settings
Definition: global.h:237
Settings::disabled_plugins
linked_char * disabled_plugins
Definition: global.h:322
llevInfo
@ llevInfo
Definition: logger.h:12
obj::type
uint8_t type
Definition: object.h:341
citylife_globalEventListener
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:227
event_registration
unsigned long event_registration
Definition: events.h:62
add_npc_to_random_map
static void add_npc_to_random_map(void)
Definition: citylife.cpp:208
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
autojail.value
value
Definition: autojail.py:6
EVENT_TIME
#define EVENT_TIME
Definition: events.h:31
spawn_zone
Definition: citylife.cpp:76
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
talk_info
Definition: dialog.h:51
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2820
Settings::hooks
collectorHook hooks[20]
Definition: global.h:330
arch_to_object
object * arch_to_object(archetype *at)
Definition: arch.cpp:232
npc_dialog.npc
npc
Definition: npc_dialog.py:95
spawn_point::x
int x
Definition: citylife.cpp:68
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.c:1533
EVENT_ATTACKED
#define EVENT_ATTACKED
Definition: events.h:20
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:282
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.c:583
citylife_close
void citylife_close()
Definition: citylife.cpp:445
get_npc
static object * get_npc(const mapzone *zone)
Definition: citylife.cpp:116
mapzone::mapzone
mapzone()
Definition: citylife.cpp:84
obj::above
struct obj * above
Definition: object.h:289
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.c:311
FIRST_MOVE_KEY
#define FIRST_MOVE_KEY
Definition: citylife.cpp:62
roll-o-matic.evt
evt
Definition: roll-o-matic.py:120
BufferReader
Definition: bufferreader.c:21
object.h
llevDebug
@ llevDebug
Definition: logger.h:13
is_valid_types_gen.type
list type
Definition: is_valid_types_gen.py:25
has_been_loaded
mapstruct * has_been_loaded(const char *name)
Definition: map.c:88