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:171
global.h
object_find_by_type_subtype
object * object_find_by_type_subtype(const object *who, int type, int subtype)
Definition: object.cpp:4292
line
Install Bug reporting Credits so make sure you have version or later There are files involved in the automatic convert convertall and filelist py GuildList has the list of guilds for the server GuildLocations is what is used by the install script for setting up the maps It has columns in the first is the name of the no spaces The second is the region of the the third is the destination folder for the the fourth is the exit the fifth and sixth are the x and y coords within the exit the seventh eighth and ninth are the exit location for the storage hall If field seven is then it uses the same exit map as for the guild hall itself filelist py has a list of which files to process for each guild hall convert py takes all the files in filelist py and customises them to the specific guild then outputs them into a in the same order that they are listed in GuildLocations convertall py reads the lines from GuildLocations and runs line by line
Definition: README.txt:12
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:51
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:78
Settings
Definition: global.h:240
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:287
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:4337
CITYLIFE_NAME
#define CITYLIFE_NAME
Definition: citylife.cpp:58
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2848
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:16
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:282
autojail.who
who
Definition: autojail.py:3
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:127
space
Crossfire Protocol most of the time after the actual code was already omit certain important and possibly make life miserable any new developer or curious player should be able to find most of the relevant information here If inconsistencies are found or this documentation proves to be consider the latest server side protocol code in the public source code repository as the authoritative reference Introduction If you were ever curious enough to telnet or netcat to a Crossfire chances are you were sorely disappointed While the protocol may seem to use plain text at it actually uses a mix of ASCII and binary data This handbook attempts to document various aspects of the Crossfire protocol As consult the README file to find out how to get in touch with helpful people via mailing and more History the communications plan was set to be a text based system It was up to the server and client to parse these messages and determine what to do These messages were assumed to be line per message At a reasonably early stage of Eric Anderson wrote a then the data itself you could send many data and after the other end could decode these commands This works fairly but I think the creation of numerous sub packets has some performance hit the eutl was not especially well so writing a client for a different platform became more Eric left to work on other products shortly after writing his which didn t really leave anyone with a full understanding of the socket code I have decided to remove the eutl dependency At least one advantage is that having this network related code directly in the client and server makes error handling a bit easier cleaner Packet Format the outside packet method the byte size for the size information is not included here Eutl originally used bytes for the size to bytes seems it makes a least some sense The actual data is something of the nature of the commands listed below It is a text followed by possible other data The remaining data can be binary it is up to the client and server to decode what it sent The commands as described below is just the data portion of the packet If writing a new remember that you must take into account the size of the packet There is no termination of other than knowing how long it should be For most everything that is sent is text This is more or less how things worked under except it packed the ints into bytes in a known order In some we handle ints as in they are sent as binary information How any command handles it is detailed below in the command description The S and C represent the direction of the we use MSB as well as any ints or shorts that get sent inside the packets All packets are defined to have at least one word of followed by a space
Definition: protocol.txt:85
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1555
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:23
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
mapzone
Definition: citylife.cpp:82
mapzone::zones
std::vector< spawn_zone > zones
Definition: citylife.cpp:86
Settings::add_hook
void add_hook(const char *name, collectorHook hook)
Definition: global.h:339
object::type
uint8_t type
Definition: object.h:348
object_free
void object_free(object *ob, int flags)
Definition: object.cpp:1587
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:474
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:248
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
path
pluglist shows those as well as a short text describing each the list will simply appear empty The keyword for the Python plugin is Python plugout< keyword > Unloads a given identified by its _keyword_ So if you want to unload the Python you need to do plugout Python plugin< libname > Loads a given whose _filename_ is libname So in the case of you d have to do a plugin cfpython so Note that all filenames are relative to the default plugin path(SHARE/plugins). Console messages. ----------------- When Crossfire starts
RANDOM
#define RANDOM()
Definition: define.h:644
Settings::disabled_plugins
std::vector< char * > disabled_plugins
Definition: global.h:326
is_valid_types_gen.found
found
Definition: is_valid_types_gen.py:39
EXIT
@ EXIT
Definition: object.h:186
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
Moving_Fog.z
z
Definition: Moving_Fog.py:17
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:299
mapstruct
Definition: map.h:314
BufferReader
Definition: bufferreader.cpp:21
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
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:473
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:1828
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
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: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:2608
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:4490
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
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