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 <assert.h>
54 #include "global.h"
55 #include "object.h"
56 #include "sproto.h"
57 }
58 
60 #define CITYLIFE_NAME "citylife"
61 
63 #define FIRST_MOVE_KEY "citylife_first_move"
64 
68 typedef struct {
69  int x;
70  int y;
71 } spawn_point;
72 
77 typedef struct {
78  int sx, sy, ex, ey;
79 } spawn_zone;
80 
84 struct mapzone {
85  mapzone() : population(0) { };
86 
87  std::vector<spawn_point> points;
88  std::vector<spawn_zone> zones;
89  int population;
90  std::vector<std::string> available_archetypes;
91 };
92 
94 static std::unordered_map<std::string, mapzone *> maps;
95 
96 
106  auto find = maps.find(map->path);
107  return find == maps.end() ? nullptr : find->second;
108 }
109 
117 static object *get_npc(const mapzone *zone) {
118  int idx = RANDOM()%zone->available_archetypes.size();
119  archetype *arch = try_find_archetype(zone->available_archetypes[idx].c_str());
120 
121  if (!arch) {
122  LOG(llevError, CITYLIFE_NAME ": get_npc() invalid archetype %s!\n", zone->available_archetypes[idx].c_str());
123  return NULL;
124  }
125 
126  object *npc = arch_to_object(arch);
127  object *evt;
128 
129 
131  /* Prevent disease spreading in cities, mostly rabies. */
133  /* add a key so NPC will not disappear in the house it came from */
135 
136  evt = create_archetype("event_time");
137  evt->slaying = add_string(CITYLIFE_NAME);
138  evt->title = add_string(CITYLIFE_NAME);
140 
141  evt = create_archetype("event_attack");
142  evt->slaying = add_string(CITYLIFE_NAME);
143  evt->title = add_string(CITYLIFE_NAME);
146 
147  return npc;
148 }
149 
157 static void add_npc_to_zone(const mapzone *zone, mapstruct *map) {
158  int which;
159  object *npc = get_npc(zone);
160 
161  if (!npc)
162  return;
163  which = RANDOM() % zone->zones.size();
164  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))) {
166  }
167 }
168 
176 static void add_npc_to_point(const mapzone *zone, mapstruct *map) {
177  int which;
178  object *npc = get_npc(zone);
179 
180  which = RANDOM() % zone->points.size();
181  if (!object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
183  }
184 }
185 
192  int add;
193  const mapzone *zone = get_zone_for_map(map);
194 
195  if (!zone)
196  return;
197 
198  add = 1+RANDOM()%zone->population;
199  LOG(llevDebug, CITYLIFE_NAME ": adding %d NPC to map %s.\n", add, map->path);
200 
201  while (--add >= 0) {
202  add_npc_to_zone(zone, map);
203  }
204 }
205 
209 static void add_npc_to_random_map(void) {
210  int count;
211  mapstruct *list[50];
212  mapzone *zones[50];
213  count = 0;
214 
215  for (auto map = maps.cbegin(); map != maps.cend() && count < 50; map++) {
216  if ((list[count] = has_been_loaded(map->first.c_str())) && (list[count]->in_memory == MAP_IN_MEMORY)) {
217  zones[count] = map->second;
218  count++;
219  }
220  }
221  if (!count)
222  return;
223 
224  int selected = RANDOM() % count;
225  add_npc_to_point(zones[selected], list[selected]);
226 }
227 
228 static int citylife_globalEventListener(int *type, ...) {
229  va_list args;
230  int rv = 0;
231  mapstruct *map;
232  int code;
233 
234  va_start(args, type);
235  code = va_arg(args, int);
236 
237  switch (code) {
238  case EVENT_MAPLOAD:
239  map = va_arg(args, mapstruct *);
241  break;
242 
243  case EVENT_CLOCK:
244  if (RANDOM()%40 == 0)
246  }
247  va_end(args);
248 
249  return rv;
250 }
251 
252 static int eventListener(int *type, ...) {
253  va_list args;
254  object *ground, *who, *event;
255  const char *value;
256 
257  va_start(args, type);
258 
259  who = va_arg(args, object *);
260  va_arg(args, object *);
261  va_arg(args, object *);
262  va_arg(args, char *);
263  va_arg(args, int);
264  event = va_arg(args, object *);
265  va_arg(args, talk_info *);
266  va_end(args);
267 
268  assert(who);
269  assert(event);
270 
271  if (event->subtype == EVENT_ATTACKED) {
272  LOG(llevInfo, "citylife: %s attacked, reverting to default behaviour\n", who->name);
274  if (th) { // Should not be NULL, but play it safe
275  object_remove(th);
276  object_free(th, 0);
277  } // Attacked hook is unique thus removed by the event handler
280  return 0; // Let default behaviour apply, NPC becomes aggressive
281  }
282 
284  if (!value) {
290  LOG(llevInfo, "citylife: removing event from object %s which we didn't generate\n", who->name);
292  return 1;
293  }
294  // Set the flag regardless of whether we tried to move through an exit
295  if (strcmp(value, "1") == 0) {
297 
298  /* must set inventory as no drop, else it'll just drop on the ground */
299  for (object *inv = who->inv; inv; inv = inv->below)
301  }
302  /* should our npc disappear? -- Only attempt this if not first move */
303  else if (RANDOM()%100 < 30) {
304  int16_t sx = who->x, sy = who->y;
305  mapstruct *map = who->map;
306  if (!(get_map_flags(who->map, &map, who->x, who->y, &sx, &sy) & P_OUT_OF_MAP)) {
307  for (ground = GET_MAP_OB(map, sx, sy); ground; ground = ground->above) {
308  if (ground->type == EXIT) {
311  return 1;
312  }
313  }
314  }
315  }
316 
317  /* We have to move manually, because during the night NPCs don't move. */
318  move_ob(who, 1 + RANDOM() % 8, NULL);
319 
320  return 1;
321 }
322 
328 static void check_zone(const mapzone *zone, const char *path) {
329  if (zone->population == 0) {
330  LOG(llevError, "zone for %s has population of 0!\n", path);
331  }
332  if (zone->available_archetypes.empty()) {
333  LOG(llevError, "zone for %s has no archetype!\n", path);
334  }
335  if (zone->zones.empty()) {
336  LOG(llevError, "zone for %s has no zone!\n", path);
337  }
338  if (zone->points.empty()) {
339  LOG(llevError, "zone for %s has no spawn point!\n", path);
340  }
341 }
342 
348 static void load_citylife(BufferReader *reader, const char *filename) {
349  char *line;
350  mapzone *zone = nullptr;
351  char *split[4];
352  std::string path;
353 
354  while ((line = bufferreader_next_line(reader)) != NULL) {
355  if (line[0] == '\0' || line[0] == '#') {
356  continue;
357  }
358 
359  char *space = strchr(line, ' ');
360  if (!space) {
361  LOG(llevError, "citylife: invalid line in file %s:%ld\n", filename, bufferreader_current_line(reader));
362  continue;
363  }
364  *space = '\0';
365  space++;
366 
367  if (strcmp(line, "map") == 0) {
368  if (zone) {
369  check_zone(zone, path.c_str());
370  }
371  path = space;
372  auto existing = maps.find(path);
373  if (existing == maps.end()) {
374  zone = new mapzone();
375  maps[path] = zone;
376  } else {
377  zone = existing->second;
378  }
379  continue;
380  }
381  if (!zone) {
382  LOG(llevError, "citylife: error, missing 'map' in file %s:%ld\n", filename, bufferreader_current_line(reader));
383  continue;
384  }
385  if (strcmp(line, "population") == 0) {
386  zone->population = atoi(space);
387  continue;
388  }
389  if (strcmp(line, "zone") == 0) {
390  size_t found = split_string(space, split, 4, ' ');
391  if (found != 4) {
392  LOG(llevError, "citylife: 'zone' should have 4 values in file %s:%ld\n", filename, bufferreader_current_line(reader));
393  } else {
394  spawn_zone z;
395  z.sx = atoi(split[0]);
396  z.sy = atoi(split[1]);
397  z.ex = atoi(split[2]);
398  z.ey = atoi(split[3]);
399  zone->zones.push_back(z);
400  }
401  continue;
402  }
403  if (strcmp(line, "point") == 0) {
404  size_t found = split_string(space, split, 2, ' ');
405  if (found != 2) {
406  LOG(llevError, "citylife: 'point' should have 2 values in file %s:%ld\n", filename, bufferreader_current_line(reader));
407  } else {
408  spawn_point p;
409  p.x = atoi(split[0]);
410  p.y = atoi(split[1]);
411  zone->points.push_back(p);
412  }
413  continue;
414  }
415  if (strcmp(line, "arch") == 0) {
416  zone->available_archetypes.push_back(space);
417  continue;
418  }
419  LOG(llevError, "citylife: unknown line %s in file %s:%ld\n", line, filename, bufferreader_current_line(reader));
420  }
421 
422  if (zone) {
423  check_zone(zone, path.c_str());
424  }
425 }
426 
428 
429 extern "C"
434 
435  settings->hooks_filename[settings->hooks_count] = ".citylife";
438 
439  /* Disable the plugin in case it's still there */
440  linked_char *disable = static_cast<linked_char *>(calloc(1, sizeof(linked_char)));
441  disable->next = settings->disabled_plugins;
442  disable->name = strdup("citylife");
443  settings->disabled_plugins = disable;
444 }
445 
446 extern "C"
451  for (auto map : maps) {
452  delete map.second;
453  }
454  maps.clear();
455 }
456 
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:327
spawn_zone::sy
int sy
Definition: citylife.cpp:78
object_free
void object_free(object *ob, int flags)
Definition: object.c:1578
add_string
sstring add_string(const char *str)
Definition: shstr.c:124
object_remove
void object_remove(object *op)
Definition: object.c:1819
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:94
EVENT_CONNECTOR
@ EVENT_CONNECTOR
Definition: object.h:227
mapzone::population
int population
Definition: citylife.cpp:89
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:252
c
static event_registration c
Definition: citylife.cpp:427
events_unregister_object_handler
void events_unregister_object_handler(const char *id)
Definition: events.cpp:269
add_npc_to_point
static void add_npc_to_point(const mapzone *zone, mapstruct *map)
Definition: citylife.cpp:176
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:85
commongive.inv
inv
Definition: commongive.py:28
EVENT_MAPLOAD
#define EVENT_MAPLOAD
Definition: events.h:47
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:105
bufferreader_current_line
size_t bufferreader_current_line(BufferReader *br)
Definition: bufferreader.c:140
spawn_point::y
int y
Definition: citylife.cpp:70
npc_dialog.filename
filename
Definition: npc_dialog.py:99
CITYLIFE_NAME
#define CITYLIFE_NAME
Definition: citylife.cpp:60
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:18
archt
Definition: object.h:469
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:4317
m
static event_registration m
Definition: citylife.cpp:427
events_register_object_handler
void events_register_object_handler(const char *id, f_plug_event handler)
Definition: events.cpp:264
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:4272
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:430
EVENT_CLOCK
#define EVENT_CLOCK
Definition: events.h:39
make_face_from_files.args
args
Definition: make_face_from_files.py:31
load_citylife
static void load_citylife(BufferReader *reader, const char *filename)
Definition: citylife.cpp:348
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:25
check_zone
static void check_zone(const mapzone *zone, const char *path)
Definition: citylife.cpp:328
python_init.path
path
Definition: python_init.py:8
mapzone
Definition: citylife.cpp:84
mapzone::zones
std::vector< spawn_zone > zones
Definition: citylife.cpp:88
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:191
object_set_value
int object_set_value(object *op, const char *key, const char *value, int add_key)
Definition: object.c:4470
disinfect.count
int count
Definition: disinfect.py:7
Settings::hooks_filename
const char * hooks_filename[20]
Definition: global.h:328
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:90
mapdef
Definition: map.h:317
add_npc_to_zone
static void add_npc_to_zone(const mapzone *zone, mapstruct *map)
Definition: citylife.cpp:157
FLAG_RANDOM_MOVE
#define FLAG_RANDOM_MOVE
Definition: define.h:309
spawn_point
Definition: citylife.cpp:68
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:483
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:281
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:181
Settings
Definition: global.h:236
Settings::disabled_plugins
linked_char * disabled_plugins
Definition: global.h:321
llevInfo
@ llevInfo
Definition: logger.h:12
obj::type
uint8_t type
Definition: object.h:343
citylife_globalEventListener
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:228
event_registration
unsigned long event_registration
Definition: events.h:63
add_npc_to_random_map
static void add_npc_to_random_map(void)
Definition: citylife.cpp:209
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:77
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:2833
Settings::hooks
collectorHook hooks[20]
Definition: global.h:329
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:69
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.c:1546
EVENT_ATTACKED
#define EVENT_ATTACKED
Definition: events.h:20
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:288
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:597
citylife_close
void citylife_close()
Definition: citylife.cpp:447
get_npc
static object * get_npc(const mapzone *zone)
Definition: citylife.cpp:117
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2655
mapzone::mapzone
mapzone()
Definition: citylife.cpp:85
obj::above
struct obj * above
Definition: object.h:291
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:301
FIRST_MOVE_KEY
#define FIRST_MOVE_KEY
Definition: citylife.cpp:63
roll-o-matic.evt
evt
Definition: roll-o-matic.py:190
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:78