452 lines
14 KiB
C
452 lines
14 KiB
C
/*****************************************************************************/
|
|
/* Newspaper plugin version 1.0 alpha. */
|
|
/* Contact: */
|
|
/*****************************************************************************/
|
|
/* That code is placed under the GNU General Public Licence (GPL) */
|
|
/* (C)2007 by Weeger Nicolas (Feel free to deliver your complaints) */
|
|
/*****************************************************************************/
|
|
/* CrossFire, A Multiplayer game for X-windows */
|
|
/* */
|
|
/* Copyright (C) 2000 Mark Wedel */
|
|
/* Copyright (C) 1992 Frank Tore Johansen */
|
|
/* */
|
|
/* This program is free software; you can redistribute it and/or modify */
|
|
/* it under the terms of the GNU General Public License as published by */
|
|
/* the Free Software Foundation; either version 2 of the License, or */
|
|
/* (at your option) any later version. */
|
|
/* */
|
|
/* This program is distributed in the hope that it will be useful, */
|
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
|
/* GNU General Public License for more details. */
|
|
/* */
|
|
/* You should have received a copy of the GNU General Public License */
|
|
/* along with this program; if not, write to the Free Software */
|
|
/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
/* First let's include the header file needed */
|
|
|
|
#include <cfnewspaper.h>
|
|
#include <stdarg.h>
|
|
#ifndef __CEXTRACT__
|
|
#include <cfnewspaper_proto.h>
|
|
#endif
|
|
#include <sqlite3.h>
|
|
|
|
f_plug_api gethook;
|
|
|
|
f_plug_api registerGlobalEvent;
|
|
|
|
f_plug_api unregisterGlobalEvent;
|
|
|
|
f_plug_api reCmp;
|
|
|
|
static sqlite3 *logger_database;
|
|
|
|
static sqlite3 *newspaper_database;
|
|
|
|
static void do_sql(const char *sql, sqlite3 *base) {
|
|
int err;
|
|
char *msg;
|
|
|
|
if (!base)
|
|
return;
|
|
|
|
err = sqlite3_exec(base, sql, NULL, NULL, &msg);
|
|
if (err != SQLITE_OK) {
|
|
cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
|
|
sqlite3_free(msg);
|
|
}
|
|
}
|
|
|
|
static int get_living_id(object *living) {
|
|
char **line;
|
|
char *sql;
|
|
int nrow, ncolumn, id;
|
|
|
|
if (living->type == PLAYER)
|
|
sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 1", living->name);
|
|
else
|
|
sql = sqlite3_mprintf("select liv_id from living where liv_name='%q' and liv_is_player = 0 and liv_level = %d", living->name, living->level);
|
|
sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
|
|
/* printf("get_table: nrow = %d, ncolumn = %d\n", nrow, ncolumn); */
|
|
if (nrow > 0)
|
|
id = atoi(line[ncolumn]);
|
|
else {
|
|
sqlite3_free(sql);
|
|
sql = sqlite3_mprintf("insert into living(liv_name, liv_is_player, liv_level) values('%q', %d, %d)", living->name, living->type == PLAYER ? 1 : 0, living->level);
|
|
do_sql(sql, logger_database);
|
|
id = sqlite3_last_insert_rowid(logger_database);
|
|
}
|
|
sqlite3_free(sql);
|
|
sqlite3_free_table(line);
|
|
return id;
|
|
}
|
|
|
|
static int get_region_id(region *reg) {
|
|
char **line;
|
|
char *sql;
|
|
int nrow, ncolumn, id;
|
|
|
|
if (!reg)
|
|
return 0;
|
|
|
|
sql = sqlite3_mprintf("select reg_id from region where reg_name='%q'", reg->name);
|
|
sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
|
|
|
|
if (nrow > 0)
|
|
id = atoi(line[ncolumn]);
|
|
else {
|
|
sqlite3_free(sql);
|
|
sql = sqlite3_mprintf("insert into region(reg_name) values( '%q' )", reg->name);
|
|
do_sql(sql, logger_database);
|
|
id = sqlite3_last_insert_rowid(logger_database);
|
|
}
|
|
sqlite3_free(sql);
|
|
sqlite3_free_table(line);
|
|
return id;
|
|
}
|
|
|
|
static void format_time(timeofday_t *tod, char *buffer, int size) {
|
|
snprintf(buffer, size, "%10d-%2d-%2d %2d:%2d", tod->year, tod->month, tod->day, tod->hour, tod->minute);
|
|
}
|
|
|
|
static int get_time_id(timeofday_t *tod, int create) {
|
|
char **line;
|
|
char *sql;
|
|
int nrow, ncolumn, id = 0;
|
|
char date[50];
|
|
|
|
format_time(tod, date, 50);
|
|
|
|
sql = sqlite3_mprintf("select time_id from time where time_time='%q'", date);
|
|
sqlite3_get_table(logger_database, sql, &line, &nrow, &ncolumn, NULL);
|
|
if (nrow > 0)
|
|
id = atoi(line[ncolumn]);
|
|
else if (create) {
|
|
sqlite3_free(sql);
|
|
sql = sqlite3_mprintf("insert into time(time_time) values( '%q' )", date);
|
|
do_sql(sql, logger_database);
|
|
id = sqlite3_last_insert_rowid(logger_database);
|
|
}
|
|
sqlite3_free(sql);
|
|
sqlite3_free_table(line);
|
|
return id;
|
|
}
|
|
|
|
static void read_parameters(void) {
|
|
}
|
|
|
|
CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
|
|
cf_init_plugin(gethooksptr);
|
|
cf_log(llevInfo, "%s init\n", PLUGIN_VERSION);
|
|
return 0;
|
|
}
|
|
|
|
CF_PLUGIN void *getPluginProperty(int *type, ...) {
|
|
va_list args;
|
|
const char *propname;
|
|
int size;
|
|
char *buf;
|
|
|
|
va_start(args, type);
|
|
propname = va_arg(args, const char *);
|
|
|
|
if (!strcmp(propname, "Identification")) {
|
|
buf = va_arg(args, char *);
|
|
size = va_arg(args, int);
|
|
va_end(args);
|
|
snprintf(buf, size, PLUGIN_NAME);
|
|
return NULL;
|
|
}
|
|
|
|
if (!strcmp(propname, "FullName")) {
|
|
buf = va_arg(args, char *);
|
|
size = va_arg(args, int);
|
|
va_end(args);
|
|
snprintf(buf, size, PLUGIN_VERSION);
|
|
return NULL;
|
|
}
|
|
|
|
va_end(args);
|
|
return NULL;
|
|
}
|
|
|
|
CF_PLUGIN int cfnewspaper_runPluginCommand(object *op, char *params) {
|
|
return -1;
|
|
}
|
|
|
|
CF_PLUGIN void *cfnewspaper_globalEventListener(int *type, ...) {
|
|
va_list args;
|
|
static int rv = 0;
|
|
int event_code;
|
|
|
|
va_start(args, type);
|
|
event_code = va_arg(args, int);
|
|
|
|
switch (event_code) {
|
|
}
|
|
va_end(args);
|
|
|
|
return &rv;
|
|
}
|
|
|
|
CF_PLUGIN int postInitPlugin(void) {
|
|
char path[500];
|
|
const char *dir;
|
|
|
|
cf_log(llevInfo, "%s post init\n", PLUGIN_VERSION);
|
|
|
|
dir = cf_get_directory(4);
|
|
snprintf(path, 500, "%s/cflogger.db", dir);
|
|
|
|
if (sqlite3_open(path, &logger_database) != SQLITE_OK) {
|
|
cf_log(llevError, " [%s] couldn't connect to logger database!\n", PLUGIN_NAME);
|
|
sqlite3_close(logger_database);
|
|
logger_database = NULL;
|
|
return 0;
|
|
}
|
|
|
|
snprintf(path, 500, "%s/cfnewspaper.db", dir);
|
|
if (sqlite3_open(path, &newspaper_database) != SQLITE_OK) {
|
|
cf_log(llevError, " [%s] unable to open newspaper database!\n", PLUGIN_NAME);
|
|
sqlite3_close(logger_database);
|
|
sqlite3_close(newspaper_database);
|
|
logger_database = NULL;
|
|
newspaper_database = NULL;
|
|
return 0;
|
|
}
|
|
|
|
read_parameters();
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef struct paper_properties {
|
|
const char *name;
|
|
int info_region;
|
|
int info_world;
|
|
} paper_properties;
|
|
|
|
static paper_properties default_properties = {
|
|
"world newspaper",
|
|
0,
|
|
1
|
|
};
|
|
|
|
typedef struct kill_format {
|
|
const char *no_player_death;
|
|
const char *one_player_death;
|
|
const char *many_player_death;
|
|
const char *no_monster_death;
|
|
const char *one_monster_death;
|
|
const char *many_monster_death;
|
|
} kill_format;
|
|
|
|
static paper_properties *get_newspaper(const char *name) {
|
|
return &default_properties;
|
|
}
|
|
|
|
static void news_cat(char *buffer, int size, const char *format, ...) {
|
|
va_list args;
|
|
|
|
size -= strlen(buffer)-1;
|
|
buffer += strlen(buffer);
|
|
|
|
va_start(args, format);
|
|
vsprintf(buffer, format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
static void do_kills(char *buffer, int size, time_t start, time_t end, const char *reg, kill_format *format) {
|
|
char *sql;
|
|
char **results;
|
|
int deaths = 0;
|
|
int nrow, ncolumn;
|
|
int err;
|
|
char *msg;
|
|
const char *raw = "select sum(1) as deaths from kill_event inner join living on liv_id = ke_victim_id where liv_is_player = %d and ke_time >= %d and ke_time < %d %s";
|
|
|
|
sql = sqlite3_mprintf(raw, 1, start, end, reg);
|
|
err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
|
|
if (err != SQLITE_OK) {
|
|
cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
|
|
sqlite3_free(msg);
|
|
}
|
|
if (nrow > 0 && results[ncolumn] != NULL)
|
|
deaths = atoi(results[ncolumn]);
|
|
sqlite3_free_table(results);
|
|
|
|
if (deaths == 0)
|
|
news_cat(buffer, size, format->no_player_death);
|
|
else if (deaths == 1)
|
|
news_cat(buffer, size, format->one_player_death);
|
|
else
|
|
news_cat(buffer, size, format->many_player_death, deaths);
|
|
news_cat(buffer, size, "\n");
|
|
|
|
sql = sqlite3_mprintf(raw, 0, start, end);
|
|
err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
|
|
if (err != SQLITE_OK) {
|
|
cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
|
|
sqlite3_free(msg);
|
|
}
|
|
if (nrow > 0 && results[ncolumn] != NULL)
|
|
deaths = atoi(results[ncolumn]);
|
|
sqlite3_free_table(results);
|
|
|
|
if (deaths == 0)
|
|
news_cat(buffer, size, format->no_monster_death);
|
|
else if (deaths == 1)
|
|
news_cat(buffer, size, format->one_monster_death);
|
|
else
|
|
news_cat(buffer, size, format->many_monster_death, deaths);
|
|
news_cat(buffer, size, "\n");
|
|
}
|
|
|
|
static void do_region_kills(region *reg, char *buffer, int size, time_t start, time_t end) {
|
|
kill_format f;
|
|
char where[50];
|
|
int region_id;
|
|
|
|
f.no_player_death = "No player died.";
|
|
f.one_player_death = "Only one player died, May Fido(tm) Have Mercy.";
|
|
f.many_player_death = "Monsters were busy, %d players died.";
|
|
f.no_monster_death = "No monster was killed, players were lazy around here.";
|
|
f.one_monster_death = "One poor monster was killed.";
|
|
f.many_monster_death = "Players tried hard to kill monsters, with %d victims.";
|
|
|
|
region_id = get_region_id(reg);
|
|
snprintf(where, 50, "and map_reg_id = %d", region_id);
|
|
|
|
do_kills(buffer, size, start, end, where, &f);
|
|
}
|
|
|
|
static void do_region(region *reg, char *buffer, int size, time_t start, time_t end) {
|
|
news_cat(buffer, size, "--- local %s news ---\n", reg->name);
|
|
do_region_kills(reg, buffer, size, start, end);
|
|
news_cat(buffer, size, "\n\n");
|
|
}
|
|
|
|
static void do_world_kills(char *buffer, int size, time_t start, time_t end) {
|
|
kill_format f;
|
|
|
|
f.no_player_death = "No player died at all.";
|
|
f.one_player_death = "Only one player died in the whole world, May Fido(tm) Have Mercy.";
|
|
f.many_player_death = "Monsters all around the world were busy, %d players died.";
|
|
f.no_monster_death = "No monster was killed at all, players must be tired!";
|
|
f.one_monster_death = "One poor monster was killed in the whole, too bad for it.";
|
|
f.many_monster_death = "Bad day for monsters, with %d dead in their ranks.";
|
|
do_kills(buffer, size, start, end, "", &f);
|
|
}
|
|
|
|
static void do_world(char *buffer, int size, time_t start, time_t end) {
|
|
news_cat(buffer, size, "--- worldnews section ---\n");
|
|
do_world_kills(buffer, size, start, end);
|
|
news_cat(buffer, size, "\n\n");
|
|
}
|
|
|
|
static void get_newspaper_content(object *paper, paper_properties *properties, region *reg) {
|
|
char contents[5000];
|
|
char *sql;
|
|
char **results;
|
|
char date[50];
|
|
int nrow, ncolumn;
|
|
time_t start, end;
|
|
timeofday_t tod;
|
|
int err;
|
|
char *msg;
|
|
|
|
start = 0;
|
|
time(&end);
|
|
|
|
cf_get_time(&tod);
|
|
format_time(&tod, date, 50);
|
|
|
|
sql = sqlite3_mprintf("select * from time where time_ingame < '%q' order by time_ingame desc", date);
|
|
err = sqlite3_get_table(logger_database, sql, &results, &nrow, &ncolumn, &msg);
|
|
if (err != SQLITE_OK) {
|
|
cf_log(llevError, " [%s] error: %d [%s] for sql = %s\n", PLUGIN_NAME, err, msg, sql);
|
|
sqlite3_free(msg);
|
|
}
|
|
if (nrow > 1 && results[ncolumn+1] != NULL) {
|
|
end = atol(results[ncolumn+1]);
|
|
if (nrow > 1 && results[ncolumn+2] != NULL)
|
|
start = atol(results[ncolumn+2]);
|
|
}
|
|
|
|
contents[0] = '\0';
|
|
|
|
if (properties->info_region)
|
|
do_region(reg, contents, 5000, start, end);
|
|
|
|
if (properties->info_world)
|
|
do_world(contents, 5000, start, end);
|
|
|
|
cf_object_set_string_property(paper, CFAPI_OBJECT_PROP_MESSAGE, contents);
|
|
}
|
|
|
|
CF_PLUGIN void *eventListener(int *type, ...) {
|
|
static int rv = 0;
|
|
va_list args;
|
|
object *who;
|
|
int event_code;
|
|
object *activator;
|
|
object *third;
|
|
object *event;
|
|
char *buf;
|
|
int fix;
|
|
object *newspaper;
|
|
paper_properties *paper;
|
|
region *reg;
|
|
|
|
va_start(args, type);
|
|
who = va_arg(args, object *);
|
|
/*event_code = va_arg(args, int);*/
|
|
activator = va_arg(args, object *);
|
|
third = va_arg(args, object *);
|
|
buf = va_arg(args, char *);
|
|
fix = va_arg(args, int);
|
|
/*buf = va_arg(args, char *);*/
|
|
event = va_arg(args, object *);
|
|
event_code = event->subtype;
|
|
|
|
va_end(args);
|
|
|
|
if (event_code != EVENT_APPLY)
|
|
return &rv;
|
|
|
|
paper = get_newspaper(event->slaying);
|
|
|
|
newspaper = cf_create_object_by_name("scroll");
|
|
|
|
cf_object_set_string_property(newspaper, CFAPI_OBJECT_PROP_NAME, paper->name);
|
|
cf_object_set_string_property(newspaper, CFAPI_OBJECT_PROP_NAME_PLURAL, paper->name);
|
|
|
|
if (activator->map)
|
|
reg = cf_map_get_region_property(activator->map, CFAPI_MAP_PROP_REGION);
|
|
else
|
|
reg = NULL;
|
|
|
|
get_newspaper_content(newspaper, paper, reg);
|
|
|
|
cf_object_insert_object(newspaper, who);
|
|
|
|
return &rv;
|
|
}
|
|
|
|
CF_PLUGIN int closePlugin(void) {
|
|
cf_log(llevInfo, "%s closing.\n", PLUGIN_VERSION);
|
|
if (logger_database) {
|
|
sqlite3_close(logger_database);
|
|
logger_database = NULL;
|
|
}
|
|
if (newspaper_database) {
|
|
sqlite3_close(newspaper_database);
|
|
newspaper_database = NULL;
|
|
}
|
|
return 0;
|
|
}
|