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 #include "server.h"
54 #include "assets.h"
55 #include "modules.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 
103 static const mapzone *get_zone_for_map(mapstruct *map) {
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. */
130  SET_FLAG(npc, FLAG_UNDEAD);
131  /* add a key so NPC will not disappear in the house it came from */
132  object_set_value(npc, FIRST_MOVE_KEY, "1", 1);
133 
134  evt = create_archetype("event_time");
137  object_insert_in_ob(evt, npc);
138 
139  evt = create_archetype("event_attack");
142  SET_FLAG(evt, FLAG_UNIQUE);
143  object_insert_in_ob(evt, npc);
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  if (!npc)
179  return;
180  which = RANDOM() % zone->points.size();
181  if (!object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
183  }
184 }
185 
191 static void add_npcs_to_map(mapstruct *map) {
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 *);
240  add_npcs_to_map(map);
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);
277  } // Attacked hook is unique thus removed by the event handler
280  return 0; // Let default behaviour apply, NPC becomes aggressive
281  }
282 
283  value = object_get_value(who, FIRST_MOVE_KEY);
284  if (!value) {
290  LOG(llevInfo, "citylife: removing event from object %s which we didn't generate\n", who->name);
291  object_remove(event);
292  return 1;
293  }
294  // Set the flag regardless of whether we tried to move through an exit
295  if (strcmp(value, "1") == 0) {
296  object_set_value(who, FIRST_MOVE_KEY, "0", 1);
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)
300  SET_FLAG(inv, FLAG_NO_DROP);
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) {
309  object_remove(who);
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:%zu\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:%zu\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:%zu\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:%zu\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:%zu\n", line, filename, bufferreader_current_line(reader));
420  }
421 
422  if (zone) {
423  check_zone(zone, path.c_str());
424  }
425 }
426 
428 
430  if (stage != STARTUP_STAGE_COLLECT_HOOKS)
431  return;
435 
437 
438  /* Disable the plugin in case it's still there */
439  serverSettings->disabled_plugins.push_back("citylife");
440 }
441 
446  for (const auto& map : maps) {
447  delete map.second;
448  }
449  maps.clear();
450 }
451 
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:175
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:4300
spawn_zone::sy
int sy
Definition: citylife.cpp:76
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:148
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
maps
static std::unordered_map< std::string, mapzone * > maps
All defined maps, with the path as key.
Definition: citylife.cpp:92
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:87
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:369
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:300
ServerSettings::disabled_plugins
std::vector< std::string > disabled_plugins
List of disabled plugins, 'All' means all.
Definition: server.h:16
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:252
c
static event_registration c
Definition: citylife.cpp:427
events_unregister_object_handler
void events_unregister_object_handler(const char *id)
Remove an object event handler.
Definition: events.cpp:304
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:174
object::x
int16_t x
Definition: object.h:337
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:131
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:307
STARTUP_STAGE_COLLECT_HOOKS
@ STARTUP_STAGE_COLLECT_HOOKS
Called when adding collect hooks, before assets loading.
Definition: modules.h:23
mapzone::points
std::vector< spawn_point > points
Points to spawn from when there is a player on the map.
Definition: citylife.cpp:83
citylife_init
void citylife_init(Settings *, ServerSettings *serverSettings, StartupStage stage)
Definition: citylife.cpp:429
get_zone_for_map
static const mapzone * get_zone_for_map(mapstruct *map)
Finds if a map has a zone defined.
Definition: citylife.cpp:103
spawn_point::y
int y
Definition: citylife.cpp:68
object::title
sstring title
Of foo, etc.
Definition: object.h:327
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:360
CITYLIFE_NAME
#define CITYLIFE_NAME
Module name for the event system.
Definition: citylife.cpp:58
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:2856
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:298
zones
static const house_zone_struct zones[]
Maps we work on.
Definition: random_house_generator.cpp:52
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:337
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)
Register an object event handler.
Definition: events.cpp:299
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:1558
is_valid_types_gen.line
line
Definition: is_valid_types_gen.py:34
object::subtype
uint8_t subtype
Subtype of object.
Definition: object.h:351
FLAG_NO_DROP
#define FLAG_NO_DROP
Object can't be dropped.
Definition: define.h:275
load_citylife
static void load_citylife(BufferReader *reader, const char *filename)
Read a .citylife file.
Definition: citylife.cpp:348
move_ob
int move_ob(object *op, int dir, object *originator)
Op is trying to move in direction dir.
Definition: move.cpp:58
assets_add_collector_hook
void assets_add_collector_hook(const char *name, collectorHook hook)
Definition: assets.cpp:556
FREE_OBJ_NO_DESTROY_CALLBACK
#define FREE_OBJ_NO_DESTROY_CALLBACK
Do not run the destroy callback.
Definition: object.h:546
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:479
add_string
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
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:328
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:297
mapzone
Options for a map.
Definition: citylife.cpp:82
mapzone::zones
std::vector< spawn_zone > zones
Zones where to spawn at load time.
Definition: citylife.cpp:86
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:350
EVENT_CLOCK
#define EVENT_CLOCK
Global time event.
Definition: events.h:53
code
Server as a resource for server administrators and developers the server recognizes the following distinct directories The *name *is what it s called in the server code
Definition: server-directories.txt:8
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:1590
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:191
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:485
EVENT_TIME
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: events.h:40
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:254
sproto.h
mapzone::available_archetypes
std::vector< std::string > available_archetypes
What archetypes can we chose from for an NPC?
Definition: citylife.cpp:88
modules.h
FLAG_RANDOM_MOVE
#define FLAG_RANDOM_MOVE
NPC will move randomly.
Definition: define.h:296
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:155
spawn_point
Point from which a NPC can come when the map is loaded.
Definition: citylife.cpp:66
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
RANDOM
#define RANDOM()
Definition: define.h:628
StartupStage
StartupStage
Definition: modules.h:21
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:245
llevInfo
@ llevInfo
Information.
Definition: logger.h:14
object::slaying
sstring slaying
Which race to do double damage to.
Definition: object.h:329
citylife_globalEventListener
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:228
object::name
sstring name
The name of the object, obviously...
Definition: object.h:321
event_registration
unsigned long event_registration
Registration identifier type.
Definition: events.h:84
serverSettings
ServerSettings serverSettings
Definition: init.cpp:46
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:209
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:280
mapstruct
This is a game-map.
Definition: map.h:320
ServerSettings
Definition: server.h:15
FLAG_STAND_STILL
#define FLAG_STAND_STILL
NPC will not (ever) move.
Definition: define.h:295
FLAG_UNDEAD
#define FLAG_UNDEAD
Monster is undead.
Definition: define.h:257
spawn_zone
Zone in which to add NPCs when the map was just loaded.
Definition: citylife.cpp:75
spawn_zone::ey
int ey
Definition: citylife.cpp:76
object_get_value
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4345
assets.h
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:370
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:76
spawn_point::x
int x
Definition: citylife.cpp:67
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:1832
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:274
EVENT_MAPLOAD
#define EVENT_MAPLOAD
A map is loaded (pristine state)
Definition: events.h:61
EVENT_ATTACKED
#define EVENT_ATTACKED
Object attacked, with weapon or spell.
Definition: events.h:29
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
citylife_close
void citylife_close()
Definition: citylife.cpp:442
get_npc
static object * get_npc(const mapzone *zone)
Creates a NPC for the specified zone, and do needed initialization.
Definition: citylife.cpp:115
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2741
mapzone::mapzone
mapzone()
Definition: citylife.cpp:83
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:4498
server.h
FIRST_MOVE_KEY
#define FIRST_MOVE_KEY
Key to contain whether it's the first move of the NPC or not.
Definition: citylife.cpp:61
FLAG_UNIQUE
#define FLAG_UNIQUE
Item is really unique (UNIQUE_ITEMS)
Definition: define.h:274
spawn_zone::sx
int sx
Definition: citylife.cpp:76
BufferReader
Definition: bufferreader.cpp:22
object.h
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:15
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:110