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 
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(strdup("citylife"));
435 }
436 
441  for (auto map : maps) {
442  delete map.second;
443  }
444  maps.clear();
445 }
446 
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:170
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:4286
spawn_zone::sy
int sy
Definition: citylife.cpp:75
citylife_init
void citylife_init(Settings *, ServerSettings *serverSettings)
Definition: citylife.cpp:426
FLAG_STAND_STILL
#define FLAG_STAND_STILL
NPC will not (ever) move.
Definition: define.h:308
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:140
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
maps
static std::unordered_map< std::string, mapzone * > maps
All defined maps, with the path as key.
Definition: citylife.cpp:91
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:86
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
FLAG_UNDEAD
#define FLAG_UNDEAD
Monster is undead.
Definition: define.h:270
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
object::inv
object * inv
Pointer to the first object in the inventory.
Definition: object.h:298
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:249
c
static event_registration c
Definition: citylife.cpp:424
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:173
FLAG_UNIQUE
#define FLAG_UNIQUE
Item is really unique (UNIQUE_ITEMS)
Definition: define.h:287
object::x
int16_t x
Definition: object.h:335
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
mapzone::points
std::vector< spawn_point > points
Points to spawn from when there is a player on the map.
Definition: citylife.cpp:82
get_zone_for_map
static const mapzone * get_zone_for_map(mapstruct *map)
Finds if a map has a zone defined.
Definition: citylife.cpp:102
spawn_point::y
int y
Definition: citylife.cpp:67
object::title
sstring title
Of foo, etc.
Definition: object.h:325
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:355
CITYLIFE_NAME
#define CITYLIFE_NAME
Module name for the event system.
Definition: citylife.cpp:57
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:2842
object::above
object * above
Pointer to the object stacked above this one.
Definition: object.h:296
zones
static const house_zone_struct zones[]
Maps we work on.
Definition: random_house_generator.cpp:51
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:335
m
static event_registration m
Definition: citylife.cpp:424
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
ServerSettings::disabled_plugins
std::vector< char * > disabled_plugins
List of disabled plugins, 'All' means all.
Definition: server.h:16
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:126
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:1545
is_valid_types_gen.line
line
Definition: is_valid_types_gen.py:34
object::subtype
uint8_t subtype
Subtype of object.
Definition: object.h:349
load_citylife
static void load_citylife(BufferReader *reader, const char *filename)
Read a .citylife file.
Definition: citylife.cpp:345
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:552
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:473
add_string
sstring add_string(const char *str)
This will add 'str' to the hash table.
Definition: shstr.cpp:124
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:325
object::below
object * below
Pointer to the object stacked below this one.
Definition: object.h:295
mapzone
Options for a map.
Definition: citylife.cpp:81
mapzone::zones
std::vector< spawn_zone > zones
Zones where to spawn at load time.
Definition: citylife.cpp:85
object::type
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:348
EVENT_CLOCK
#define EVENT_CLOCK
Global time event.
Definition: events.h:53
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:1577
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:188
archetype
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
EVENT_TIME
#define EVENT_TIME
Triggered each time the object can react/move.
Definition: events.h:40
sproto.h
FLAG_NO_DROP
#define FLAG_NO_DROP
Object can't be dropped.
Definition: define.h:288
mapzone::available_archetypes
std::vector< std::string > available_archetypes
What archetypes can we chose from for an NPC?
Definition: citylife.cpp:87
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:154
FLAG_RANDOM_MOVE
#define FLAG_RANDOM_MOVE
NPC will move randomly.
Definition: define.h:309
spawn_point
Point from which a NPC can come when the map is loaded.
Definition: citylife.cpp:65
P_OUT_OF_MAP
#define P_OUT_OF_MAP
This space is outside the map.
Definition: map.h:249
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:638
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:241
llevInfo
@ llevInfo
Information.
Definition: logger.h:12
object::slaying
sstring slaying
Which race to do double damage to.
Definition: object.h:327
citylife_globalEventListener
static int citylife_globalEventListener(int *type,...)
Definition: citylife.cpp:225
object::name
sstring name
The name of the object, obviously...
Definition: object.h:319
event_registration
unsigned long event_registration
Registration identifier type.
Definition: events.h:84
serverSettings
ServerSettings serverSettings
Definition: init.cpp:42
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:206
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:300
mapstruct
This is a game-map.
Definition: map.h:315
ServerSettings
Definition: server.h:15
spawn_zone
Zone in which to add NPCs when the map was just loaded.
Definition: citylife.cpp:74
spawn_zone::ey
int ey
Definition: citylife.cpp:75
object_get_value
sstring object_get_value(const object *op, const char *const key)
Get an extra value by key.
Definition: object.cpp:4331
assets.h
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
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:75
spawn_point::x
int x
Definition: citylife.cpp:66
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:1818
try_find_archetype
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:270
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
code
Crossfire Architecture the general intention is to enhance the enjoyability and playability of CF In this code
Definition: arch-handbook.txt:14
citylife_close
void citylife_close()
Definition: citylife.cpp:437
get_npc
static object * get_npc(const mapzone *zone)
Creates a NPC for the specified zone, and do needed initialization.
Definition: citylife.cpp:114
split
static std::vector< std::string > split(const std::string &field, const std::string &by)
Definition: mapper.cpp:2734
mapzone::mapzone
mapzone()
Definition: citylife.cpp:82
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:4484
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:60
spawn_zone::sx
int sx
Definition: citylife.cpp:75
BufferReader
Definition: bufferreader.cpp:21
object.h
llevDebug
@ llevDebug
Only for debugging purposes.
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)
Return the next line in the buffer, as separated by a newline.
Definition: bufferreader.cpp:102