summaryrefslogtreecommitdiff
path: root/src/map/npc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/map/npc.c')
-rw-r--r--src/map/npc.c1637
1 files changed, 1246 insertions, 391 deletions
diff --git a/src/map/npc.c b/src/map/npc.c
index 1b784b0c8..40ec380ee 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -2,8 +2,8 @@
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
- * Copyright (C) 2012-2015 Hercules Dev Team
- * Copyright (C) Athena Dev Teams
+ * Copyright (C) 2012-2020 Hercules Dev Team
+ * Copyright (C) Athena Dev Teams
*
* Hercules is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,6 +25,7 @@
#include "map/battle.h"
#include "map/chat.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/guild.h"
#include "map/instance.h"
@@ -35,10 +36,12 @@
#include "map/mob.h"
#include "map/pc.h"
#include "map/pet.h"
+#include "map/quest.h"
#include "map/script.h"
#include "map/skill.h"
#include "map/status.h"
#include "map/unit.h"
+#include "map/achievement.h"
#include "common/HPM.h"
#include "common/cbasetypes.h"
#include "common/db.h"
@@ -59,27 +62,16 @@
#include <string.h>
#include <time.h>
-struct npc_interface npc_s;
+static struct npc_interface npc_s;
struct npc_interface *npc;
-static int npc_id=START_NPC_NUM;
-static int npc_warp=0;
-static int npc_shop=0;
-static int npc_script=0;
-static int npc_mob=0;
-static int npc_delay_mob=0;
-static int npc_cache_mob=0;
-
-static const char *npc_last_path;
-static const char *npc_last_ref;
-struct npc_path_data *npc_last_npd;
//For holding the view data of npc classes. [Skotlex]
static struct view_data npc_viewdb[MAX_NPC_CLASS];
static struct view_data npc_viewdb2[MAX_NPC_CLASS2_END-MAX_NPC_CLASS2_START];
/* for speedup */
-unsigned int npc_market_qty[MAX_INVENTORY];
+static unsigned int npc_market_qty[MAX_INVENTORY];
static struct script_event_s {
//Holds pointers to the commonly executed scripts for speedup. [Skotlex]
@@ -93,7 +85,7 @@ static struct script_event_s {
* @param class_ The NPC class ID.
* @return The viewdata, or NULL if the ID is invalid.
*/
-struct view_data *npc_get_viewdata(int class_)
+static struct view_data *npc_get_viewdata(int class_)
{
if (class_ == INVISIBLE_CLASS)
return &npc_viewdb[0];
@@ -115,7 +107,7 @@ struct view_data *npc_get_viewdata(int class_)
* @param id The NPC ID to validate.
* @return Whether the value is a valid ID.
*/
-bool npc_db_checkid(int id)
+static bool npc_db_checkid(int id)
{
if (id >= WARP_CLASS && id <= 125) // First subrange
return true;
@@ -125,22 +117,25 @@ bool npc_db_checkid(int id)
return true;
if (id >= MAX_NPC_CLASS2_START && id < MAX_NPC_CLASS2_END) // Second range
return true;
+ if (pc->db_checkid(id))
+ return true;
// Anything else is invalid
return false;
}
/// Returns a new npc id that isn't being used in id_db.
/// Fatal error if nothing is available.
-int npc_get_new_npc_id(void) {
- if( npc_id >= START_NPC_NUM && !map->blid_exists(npc_id) )
- return npc_id++;// available
+static int npc_get_new_npc_id(void)
+{
+ if (npc->npc_id >= START_NPC_NUM && !map->blid_exists(npc->npc_id))
+ return npc->npc_id++;// available
else {// find next id
- int base_id = npc_id;
- while( base_id != ++npc_id ) {
- if( npc_id < START_NPC_NUM )
- npc_id = START_NPC_NUM;
- if( !map->blid_exists(npc_id) )
- return npc_id++;// available
+ int base_id = npc->npc_id;
+ while (base_id != ++npc->npc_id) {
+ if (npc->npc_id < START_NPC_NUM)
+ npc->npc_id = START_NPC_NUM;
+ if (!map->blid_exists(npc->npc_id))
+ return npc->npc_id++;// available
}
// full loop, nothing available
ShowFatalError("npc_get_new_npc_id: All ids are taken. Exiting...");
@@ -148,7 +143,7 @@ int npc_get_new_npc_id(void) {
}
}
-int npc_isnear_sub(struct block_list *bl, va_list args)
+static int npc_isnear_sub(struct block_list *bl, va_list args)
{
const struct npc_data *nd = NULL;
@@ -165,7 +160,8 @@ int npc_isnear_sub(struct block_list *bl, va_list args)
return 1;
}
-bool npc_isnear(struct block_list * bl) {
+static bool npc_isnear(struct block_list *bl)
+{
if( battle_config.min_npc_vendchat_distance > 0
&& map->foreachinrange(npc->isnear_sub,bl, battle_config.min_npc_vendchat_distance, BL_NPC) )
return true;
@@ -173,7 +169,7 @@ bool npc_isnear(struct block_list * bl) {
return false;
}
-int npc_ontouch_event(struct map_session_data *sd, struct npc_data *nd)
+static int npc_ontouch_event(struct map_session_data *sd, struct npc_data *nd)
{
char name[EVENT_NAME_LENGTH];
@@ -188,7 +184,7 @@ int npc_ontouch_event(struct map_session_data *sd, struct npc_data *nd)
return npc->event(sd,name,1);
}
-int npc_ontouch2_event(struct map_session_data *sd, struct npc_data *nd)
+static int npc_ontouch2_event(struct map_session_data *sd, struct npc_data *nd)
{
char name[EVENT_NAME_LENGTH];
@@ -201,7 +197,7 @@ int npc_ontouch2_event(struct map_session_data *sd, struct npc_data *nd)
return npc->event(sd, name, 2);
}
-int npc_onuntouch_event(struct map_session_data *sd, struct npc_data *nd)
+static int npc_onuntouch_event(struct map_session_data *sd, struct npc_data *nd)
{
char name[EVENT_NAME_LENGTH];
@@ -217,7 +213,7 @@ int npc_onuntouch_event(struct map_session_data *sd, struct npc_data *nd)
/*==========================================
* Sub-function of npc_enable, runs OnTouch event when enabled
*------------------------------------------*/
-int npc_enable_sub(struct block_list *bl, va_list ap)
+static int npc_enable_sub(struct block_list *bl, va_list ap)
{
struct npc_data *nd;
@@ -245,7 +241,7 @@ int npc_enable_sub(struct block_list *bl, va_list ap)
/*==========================================
* Disable / Enable NPC
*------------------------------------------*/
-int npc_enable(const char* name, int flag)
+static int npc_enable(const char *name, int flag)
{
struct npc_data* nd = npc->name2id(name);
@@ -283,7 +279,7 @@ int npc_enable(const char* name, int flag)
/*==========================================
* NPC lookup (get npc_data through npcname)
*------------------------------------------*/
-struct npc_data *npc_name2id(const char *name)
+static struct npc_data *npc_name2id(const char *name)
{
return strdb_get(npc->name_db, name);
}
@@ -293,7 +289,8 @@ struct npc_data *npc_name2id(const char *name)
/**
* Timer to check for idle time and timeout the dialog if necessary
**/
-int npc_rr_secure_timeout_timer(int tid, int64 tick, int id, intptr_t data) {
+static int npc_rr_secure_timeout_timer(int tid, int64 tick, int id, intptr_t data)
+{
#ifdef SECURE_NPCTIMEOUT
struct map_session_data* sd = NULL;
unsigned int timeout = NPC_SECURE_TIMEOUT_NEXT;
@@ -340,7 +337,7 @@ int npc_rr_secure_timeout_timer(int tid, int64 tick, int id, intptr_t data) {
/*==========================================
* Dequeue event and add timer for execution (100ms)
*------------------------------------------*/
-int npc_event_dequeue(struct map_session_data* sd)
+static int npc_event_dequeue(struct map_session_data *sd)
{
nullpo_ret(sd);
@@ -372,7 +369,7 @@ int npc_event_dequeue(struct map_session_data* sd)
/**
* @see DBCreateData
*/
-struct DBData npc_event_export_create(union DBKey key, va_list args)
+static struct DBData npc_event_export_create(union DBKey key, va_list args)
{
struct linkdb_node** head_ptr;
CREATE(head_ptr, struct linkdb_node*, 1);
@@ -384,7 +381,7 @@ struct DBData npc_event_export_create(union DBKey key, va_list args)
* exports a npc event label
* called from npc_parse_script
*------------------------------------------*/
-int npc_event_export(struct npc_data *nd, int i)
+static int npc_event_export(struct npc_data *nd, int i)
{
char* lname;
int pos;
@@ -410,13 +407,13 @@ int npc_event_export(struct npc_data *nd, int i)
return 0;
}
-int npc_event_sub(struct map_session_data* sd, struct event_data* ev, const char* eventname); //[Lance]
+static int npc_event_sub(struct map_session_data *sd, struct event_data *ev, const char *eventname); //[Lance]
/**
* Exec name (NPC events) on player or global
* Do on all NPC when called with foreach
*/
-void npc_event_doall_sub(void *key, void *data, va_list ap)
+static void npc_event_doall_sub(void *key, void *data, va_list ap)
{
struct event_data* ev = data;
int* c;
@@ -442,7 +439,7 @@ void npc_event_doall_sub(void *key, void *data, va_list ap)
}
// runs the specified event (supports both single-npc and global events)
-int npc_event_do(const char* name)
+static int npc_event_do(const char *name)
{
nullpo_ret(name);
if( name[0] == ':' && name[1] == ':' ) {
@@ -459,7 +456,7 @@ int npc_event_do(const char* name)
}
// runs the specified event, with a RID attached (global only)
-int npc_event_doall_id(const char* name, int rid)
+static int npc_event_doall_id(const char *name, int rid)
{
int c = 0;
struct linkdb_node **label_linkdb = strdb_get(npc->ev_label_db, name);
@@ -472,7 +469,7 @@ int npc_event_doall_id(const char* name, int rid)
}
// runs the specified event (global only)
-int npc_event_doall(const char* name)
+static int npc_event_doall(const char *name)
{
return npc->event_doall_id(name, 0);
}
@@ -481,7 +478,8 @@ int npc_event_doall(const char* name)
* Clock event execution
* OnMinute/OnClock/OnHour/OnDay/OnDDHHMM
*------------------------------------------*/
-int npc_event_do_clock(int tid, int64 tick, int id, intptr_t data) {
+static int npc_event_do_clock(int tid, int64 tick, int id, intptr_t data)
+{
static struct tm ev_tm_b; // tracks previous execution time
time_t clock;
struct tm* t;
@@ -533,7 +531,7 @@ int npc_event_do_clock(int tid, int64 tick, int id, intptr_t data) {
* OnInit event execution (the start of the event and watch)
* @param reload Is the server reloading?
**/
-void npc_event_do_oninit( bool reload )
+static void npc_event_do_oninit(bool reload)
{
ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs."CL_CLL"\n", npc->event_doall("OnInit"));
@@ -546,7 +544,7 @@ void npc_event_do_oninit( bool reload )
* Incorporation of the label for the timer event
* called from npc_parse_script
*------------------------------------------*/
-int npc_timerevent_export(struct npc_data *nd, int i)
+static int npc_timerevent_export(struct npc_data *nd, int i)
{
int t = 0, len = 0;
char *lname;
@@ -585,7 +583,7 @@ struct timer_event_data {
/*==========================================
* triger 'OnTimerXXXX' events
*------------------------------------------*/
-int npc_timerevent(int tid, int64 tick, int id, intptr_t data)
+static int npc_timerevent(int tid, int64 tick, int id, intptr_t data)
{
int old_rid, old_timer;
int64 old_tick;
@@ -656,7 +654,8 @@ int npc_timerevent(int tid, int64 tick, int id, intptr_t data)
/*==========================================
* Start/Resume NPC timer
*------------------------------------------*/
-int npc_timerevent_start(struct npc_data* nd, int rid) {
+static int npc_timerevent_start(struct npc_data *nd, int rid)
+{
int j;
int64 tick = timer->gettick();
struct map_session_data *sd = NULL; //Player to whom script is attached.
@@ -708,7 +707,7 @@ int npc_timerevent_start(struct npc_data* nd, int rid) {
/*==========================================
* Stop NPC timer
*------------------------------------------*/
-int npc_timerevent_stop(struct npc_data* nd)
+static int npc_timerevent_stop(struct npc_data *nd)
{
struct map_session_data *sd = NULL;
int *tid;
@@ -742,7 +741,7 @@ int npc_timerevent_stop(struct npc_data* nd)
/*==========================================
* Aborts a running NPC timer that is attached to a player.
*------------------------------------------*/
-void npc_timerevent_quit(struct map_session_data* sd)
+static void npc_timerevent_quit(struct map_session_data *sd)
{
const struct TimerData *td;
struct npc_data* nd;
@@ -807,7 +806,8 @@ void npc_timerevent_quit(struct map_session_data* sd)
* Get the tick value of an NPC timer
* If it's stopped, return stopped time
*------------------------------------------*/
-int64 npc_gettimerevent_tick(struct npc_data* nd) {
+static int64 npc_gettimerevent_tick(struct npc_data *nd)
+{
int64 tick;
nullpo_ret(nd);
@@ -823,7 +823,7 @@ int64 npc_gettimerevent_tick(struct npc_data* nd) {
/*==========================================
* Set tick for running and stopped timer
*------------------------------------------*/
-int npc_settimerevent_tick(struct npc_data* nd, int newtimer)
+static int npc_settimerevent_tick(struct npc_data *nd, int newtimer)
{
bool flag;
int old_rid;
@@ -847,7 +847,7 @@ int npc_settimerevent_tick(struct npc_data* nd, int newtimer)
return 0;
}
-int npc_event_sub(struct map_session_data* sd, struct event_data* ev, const char* eventname)
+static int npc_event_sub(struct map_session_data *sd, struct event_data *ev, const char *eventname)
{
nullpo_retr(2, sd);
nullpo_retr(2, eventname);
@@ -878,7 +878,7 @@ int npc_event_sub(struct map_session_data* sd, struct event_data* ev, const char
/*==========================================
* NPC processing event type
*------------------------------------------*/
-int npc_event(struct map_session_data* sd, const char* eventname, int ontouch)
+static int npc_event(struct map_session_data *sd, const char *eventname, int ontouch)
{
struct event_data* ev = (struct event_data*)strdb_get(npc->ev_db, eventname);
struct npc_data *nd;
@@ -907,7 +907,8 @@ int npc_event(struct map_session_data* sd, const char* eventname, int ontouch)
/*==========================================
* Sub chk then execute area event type
*------------------------------------------*/
-int npc_touch_areanpc_sub(struct block_list *bl, va_list ap) {
+static int npc_touch_areanpc_sub(struct block_list *bl, va_list ap)
+{
struct map_session_data *sd;
int pc_id;
char *name;
@@ -934,7 +935,8 @@ int npc_touch_areanpc_sub(struct block_list *bl, va_list ap) {
* Chk if sd is still touching his assigned npc.
* If not, it unsets it and searches for another player in range.
*------------------------------------------*/
-int npc_touchnext_areanpc(struct map_session_data* sd, bool leavemap) {
+static int npc_touchnext_areanpc(struct map_session_data *sd, bool leavemap)
+{
struct npc_data *nd;
short xs, ys;
@@ -963,7 +965,7 @@ int npc_touchnext_areanpc(struct map_session_data* sd, bool leavemap) {
/*==========================================
* Exec OnTouch for player if in range of area event
*------------------------------------------*/
-int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y)
+static int npc_touch_areanpc(struct map_session_data *sd, int16 m, int16 x, int16 y)
{
int xs,ys;
int f = 1;
@@ -1052,7 +1054,7 @@ int npc_touch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y)
/*==========================================
* Exec OnUnTouch for player if out range of area event
*------------------------------------------*/
-int npc_untouch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y)
+static int npc_untouch_areanpc(struct map_session_data *sd, int16 m, int16 x, int16 y)
{
struct npc_data *nd = NULL;
nullpo_retr(1, sd);
@@ -1074,7 +1076,7 @@ int npc_untouch_areanpc(struct map_session_data* sd, int16 m, int16 x, int16 y)
// OnTouch NPC or Warp for Mobs
// Return 1 if Warped
-int npc_touch_areanpc2(struct mob_data *md)
+static int npc_touch_areanpc2(struct mob_data *md)
{
int i, m, x, y, id;
char eventname[EVENT_NAME_LENGTH];
@@ -1139,7 +1141,8 @@ int npc_touch_areanpc2(struct mob_data *md)
//Flag determines the type of object to check for:
//&1: NPC Warps
//&2: NPCs with on-touch events.
-int npc_check_areanpc(int flag, int16 m, int16 x, int16 y, int16 range) {
+static int npc_check_areanpc(int flag, int16 m, int16 x, int16 y, int16 range)
+{
int i;
int x0,y0,x1,y1;
int xs,ys;
@@ -1198,7 +1201,7 @@ int npc_check_areanpc(int flag, int16 m, int16 x, int16 y, int16 range) {
* Chk if player not too far to access the npc.
* Returns npc_data (success) or NULL (fail).
*------------------------------------------*/
-struct npc_data* npc_checknear(struct map_session_data* sd, struct block_list* bl)
+static struct npc_data *npc_checknear(struct map_session_data *sd, struct block_list *bl)
{
struct npc_data *nd = BL_CAST(BL_NPC, bl);
int distance = AREA_SIZE + 1;
@@ -1231,7 +1234,7 @@ struct npc_data* npc_checknear(struct map_session_data* sd, struct block_list* b
/*==========================================
* Make NPC talk in global chat (like npctalk)
*------------------------------------------*/
-int npc_globalmessage(const char* name, const char* mes)
+static int npc_globalmessage(const char *name, const char *mes)
{
struct npc_data* nd = npc->name2id(name);
char temp[100];
@@ -1249,11 +1252,15 @@ int npc_globalmessage(const char* name, const char* mes)
}
// MvP tomb [GreenBox]
-void run_tomb(struct map_session_data* sd, struct npc_data* nd) {
+static void run_tomb(struct map_session_data *sd, struct npc_data *nd)
+{
char buffer[200];
char time[10];
nullpo_retv(nd);
+
+ sd->npc_id = nd->bl.id;
+
strftime(time, sizeof(time), "%H:%M", localtime(&nd->u.tomb.kill_time));
// TODO: Find exact color?
@@ -1277,7 +1284,7 @@ void run_tomb(struct map_session_data* sd, struct npc_data* nd) {
* NPC 1st call when clicking on npc
* Do specific action for NPC type (openshop, run scripts...)
*------------------------------------------*/
-int npc_click(struct map_session_data* sd, struct npc_data* nd)
+static int npc_click(struct map_session_data *sd, struct npc_data *nd)
{
nullpo_retr(1, sd);
@@ -1328,48 +1335,68 @@ int npc_click(struct map_session_data* sd, struct npc_data* nd)
return 0;
}
-/*==========================================
+/**
+ * Validates a character's script related data and (re-)runs the script if validation was successful.
*
- *------------------------------------------*/
-int npc_scriptcont(struct map_session_data* sd, int id, bool closing) {
- struct block_list *target = map->id2bl(id);
+ * Is called when:
+ * - The Next/Close button was clicked.
+ * - A menu option was selected.
+ * - A value was entered by input() script command.
+ * - A progress bar has reached 100%.
+ * - The character timed out because of idling.
+ *
+ * @param sd The character's session data.
+ * @param id The NPC ID.
+ * @param closing Whether the script is closing, or not.
+ * @return 0 on success, otherwise 1.
+ *
+**/
+static int npc_scriptcont(struct map_session_data *sd, int id, bool closing)
+{
nullpo_retr(1, sd);
- if( id != sd->npc_id ){
- struct npc_data *nd_sd = map->id2nd(sd->npc_id);
- struct npc_data *nd = BL_CAST(BL_NPC, target);
- ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n",
- nd_sd?(char*)nd_sd->name:"'Unknown NPC'", (int)sd->npc_id,
- nd?(char*)nd->name:"'Unknown NPC'", (int)id);
- return 1;
+ struct block_list *target = map->id2bl(id);
+
+#ifdef SECURE_NPCTIMEOUT
+ if (sd->npc_idle_timer != INVALID_TIMER) { /// Not yet timed out.
+#endif
+ if (id != sd->npc_id) {
+ struct npc_data *nd_sd = map->id2nd(sd->npc_id);
+ struct npc_data *nd = BL_CAST(BL_NPC, target);
+
+ ShowDebug("npc_scriptcont: %s (sd->npc_id=%d) is not %s (id=%d).\n",
+ (nd_sd != NULL) ? nd_sd->name : "'Unknown NPC'", sd->npc_id,
+ (nd != NULL) ? nd->name : "'Unknown NPC'", id);
+
+ return 1;
+ }
+#ifdef SECURE_NPCTIMEOUT
}
+#endif
- if(id != npc->fake_nd->bl.id) { // Not item script
- if ((npc->checknear(sd,target)) == NULL){
- ShowWarning("npc_scriptcont: failed npc->checknear test.\n");
+ if (id != npc->fake_nd->bl.id) { /// Not an item script.
+ if (sd->state.npc_unloaded != 0) {
+ sd->state.npc_unloaded = 0;
+ } else if (npc->checknear(sd, target) == NULL) {
+ ShowWarning("npc_scriptcont: Failed npc->checknear test.\n");
return 1;
}
}
- /**
- * For the Secure NPC Timeout option (check config/Secure.h) [RR]
- **/
+
#ifdef SECURE_NPCTIMEOUT
- /**
- * Update the last NPC iteration
- **/
- sd->npc_idle_tick = timer->gettick();
+ sd->npc_idle_tick = timer->gettick(); /// Update the last NPC iteration.
#endif
- /**
- * WPE can get to this point with a progressbar; we deny it.
- **/
- if( sd->progressbar.npc_id && DIFF_TICK(sd->progressbar.timeout,timer->gettick()) > 0 )
+ /// WPE can get to this point with a progressbar; we deny it.
+ if (sd->progressbar.npc_id != 0 && DIFF_TICK(sd->progressbar.timeout, timer->gettick()) > 0)
return 1;
- if( !sd->st )
+ if (sd->st == NULL) {
+ sd->npc_id = 0;
return 1;
+ }
- if( closing && sd->st->state == CLOSE )
+ if (closing && sd->st->state == CLOSE)
sd->st->state = END;
script->run_main(sd->st);
@@ -1380,7 +1407,8 @@ int npc_scriptcont(struct map_session_data* sd, int id, bool closing) {
/*==========================================
* Chk if valid call then open buy or selling list
*------------------------------------------*/
-int npc_buysellsel(struct map_session_data* sd, int id, int type) {
+static int npc_buysellsel(struct map_session_data *sd, int id, int type)
+{
struct npc_data *nd;
nullpo_retr(1, sd);
@@ -1419,9 +1447,9 @@ int npc_buysellsel(struct map_session_data* sd, int id, int type) {
}
/*==========================================
-* Cash Shop Buy List
-*------------------------------------------*/
-int npc_cashshop_buylist(struct map_session_data *sd, int points, struct itemlist *item_list)
+ * Cash Shop Buy List
+ *------------------------------------------*/
+static int npc_cashshop_buylist(struct map_session_data *sd, int points, struct itemlist *item_list)
{
int i, j, new_, w, vt;
struct npc_data *nd = NULL;
@@ -1444,11 +1472,16 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, struct itemlis
return ERROR_TYPE_NPC;
if( nd->subtype != CASHSHOP ) {
- if( nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET ) {
+ if (nd->subtype == SCRIPT && nd->u.scr.shop &&
+ nd->u.scr.shop->type != NST_ZENY &&
+ nd->u.scr.shop->type != NST_MARKET &&
+ nd->u.scr.shop->type != NST_BARTER &&
+ nd->u.scr.shop->type != NST_EXPANDED_BARTER) {
shop = nd->u.scr.shop->item;
shop_size = nd->u.scr.shop->items;
- } else
+ } else {
return ERROR_TYPE_NPC;
+ }
} else {
shop = nd->u.shop.shop_item;
shop_size = nd->u.shop.count;
@@ -1522,7 +1555,7 @@ int npc_cashshop_buylist(struct map_session_data *sd, int points, struct itemlis
}
//npc_buylist for script-controlled shops.
-int npc_buylist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd)
+static int npc_buylist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd)
{
char npc_ev[EVENT_NAME_LENGTH];
int i;
@@ -1554,7 +1587,7 @@ int npc_buylist_sub(struct map_session_data *sd, struct itemlist *item_list, str
/**
* Loads persistent NPC Market Data from SQL
**/
-void npc_market_fromsql(void)
+static void npc_market_fromsql(void)
{
struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle);
char name[NAME_LENGTH+1];
@@ -1569,9 +1602,9 @@ void npc_market_fromsql(void)
return;
}
- SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name[0], sizeof(name), NULL, NULL);
- SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &itemid, 0, NULL, NULL);
- SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &amount, 0, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof name, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &itemid, sizeof itemid, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 2, SQLDT_INT, &amount, sizeof amount, NULL, NULL);
while ( SQL_SUCCESS == SQL->StmtNextRow(stmt) ) {
struct npc_data *nd = NULL;
@@ -1579,11 +1612,11 @@ void npc_market_fromsql(void)
if( !(nd = npc->name2id(name)) ) {
ShowError("npc_market_fromsql: NPC '%s' not found! skipping...\n",name);
- npc->market_delfromsql_sub(name, USHRT_MAX);
+ npc->market_delfromsql_sub(name, INT_MAX);
continue;
- } else if ( nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_MARKET ) {
+ } else if (nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_MARKET) {
ShowError("npc_market_fromsql: NPC '%s' is not proper for market, skipping...\n",name);
- npc->market_delfromsql_sub(name, USHRT_MAX);
+ npc->market_delfromsql_sub(name, INT_MAX);
continue;
}
@@ -1605,18 +1638,20 @@ void npc_market_fromsql(void)
/**
* Saves persistent NPC Market Data into SQL
**/
-void npc_market_tosql(struct npc_data *nd, unsigned short index) {
+static void npc_market_tosql(struct npc_data *nd, int index)
+{
nullpo_retv(nd);
- Assert_retv(index < nd->u.scr.shop->items);
- if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s','%d','%u')",
+ Assert_retv(index >= 0 && index < nd->u.scr.shop->items);
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s','%d','%d')",
map->npc_market_data_db, nd->exname, nd->u.scr.shop->item[index].nameid, nd->u.scr.shop->item[index].qty))
Sql_ShowDebug(map->mysql_handle);
}
/**
* Removes persistent NPC Market Data from SQL
*/
-void npc_market_delfromsql_sub(const char *npcname, unsigned short index) {
- if( index == USHRT_MAX ) {
+static void npc_market_delfromsql_sub(const char *npcname, int index)
+{
+ if (index == INT_MAX ) {
if( SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s'", map->npc_market_data_db, npcname) )
Sql_ShowDebug(map->mysql_handle);
} else {
@@ -1628,15 +1663,295 @@ void npc_market_delfromsql_sub(const char *npcname, unsigned short index) {
/**
* Removes persistent NPC Market Data from SQL
**/
-void npc_market_delfromsql(struct npc_data *nd, unsigned short index) {
+static void npc_market_delfromsql(struct npc_data *nd, int index)
+{
+ nullpo_retv(nd);
+ Assert_retv(index == INT_MAX || (index >= 0 && index < nd->u.scr.shop->items));
+ npc->market_delfromsql_sub(nd->exname, index == INT_MAX ? index : nd->u.scr.shop->item[index].nameid);
+}
+
+/**
+ * Loads persistent NPC Barter Data from SQL
+ **/
+static void npc_barter_fromsql(void)
+{
+ struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle);
+ char name[NAME_LENGTH + 1];
+ int itemid;
+ int amount;
+ int removeId;
+ int removeAmount;
+
+ if (SQL_ERROR == SQL->StmtPrepare(stmt, "SELECT `name`, `itemId`, `amount`, `priceId`, `priceAmount` FROM `%s`", map->npc_barter_data_db)
+ || SQL_ERROR == SQL->StmtExecute(stmt)
+ ) {
+ SqlStmt_ShowDebug(stmt);
+ SQL->StmtFree(stmt);
+ return;
+ }
+
+ SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof name, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &itemid, sizeof itemid, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 2, SQLDT_UINT32, &amount, sizeof amount, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 3, SQLDT_INT, &removeId, sizeof removeId, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 4, SQLDT_INT, &removeAmount, sizeof removeAmount, NULL, NULL);
+
+ while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) {
+ struct npc_data *nd = NULL;
+ unsigned short i;
+
+ if (!(nd = npc->name2id(name))) {
+ ShowError("npc_barter_fromsql: NPC '%s' not found! skipping...\n",name);
+ npc->barter_delfromsql_sub(name, INT_MAX, 0, 0);
+ continue;
+ } else if (nd->subtype != SCRIPT || !nd->u.scr.shop || !nd->u.scr.shop->items || nd->u.scr.shop->type != NST_BARTER) {
+ ShowError("npc_barter_fromsql: NPC '%s' is not proper for barter, skipping...\n",name);
+ npc->barter_delfromsql_sub(name, INT_MAX, 0, 0);
+ continue;
+ }
+
+ for (i = 0; i < nd->u.scr.shop->items; i++) {
+ struct npc_item_list *const item = &nd->u.scr.shop->item[i];
+ if (item->nameid == itemid && item->value == removeId && item->value2 == removeAmount) {
+ item->qty = amount;
+ break;
+ }
+ }
+
+ if (i == nd->u.scr.shop->items) {
+ ShowError("npc_barter_fromsql: NPC '%s' does not sell item %d (qty %d), deleting...\n", name, itemid, amount);
+ npc->barter_delfromsql_sub(name, itemid, removeId, removeAmount);
+ continue;
+ }
+ }
+ SQL->StmtFree(stmt);
+}
+
+/**
+ * Saves persistent NPC Barter Data into SQL
+ **/
+static void npc_barter_tosql(struct npc_data *nd, int index)
+{
nullpo_retv(nd);
- Assert_retv(index == USHRT_MAX || index < nd->u.scr.shop->items);
- npc->market_delfromsql_sub(nd->exname, index == USHRT_MAX ? index : nd->u.scr.shop->item[index].nameid);
+ Assert_retv(index >= 0 && index < nd->u.scr.shop->items);
+ const struct npc_item_list *const item = &nd->u.scr.shop->item[index];
+ if (item->qty == -1)
+ return;
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "REPLACE INTO `%s` VALUES ('%s', '%d', '%d', '%u', '%d')",
+ map->npc_barter_data_db, nd->exname, item->nameid, item->qty, item->value, item->value2)) {
+ Sql_ShowDebug(map->mysql_handle);
+ }
}
+
+/**
+ * Removes persistent NPC Barter Data from SQL
+ */
+static void npc_barter_delfromsql_sub(const char *npcname, int itemId, int itemId2, int amount2)
+{
+ if (itemId == INT_MAX) {
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s'", map->npc_barter_data_db, npcname))
+ Sql_ShowDebug(map->mysql_handle);
+ } else {
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s' AND `itemId`='%d' AND `priceId`='%d' AND `priceAmount`='%d' LIMIT 1",
+ map->npc_barter_data_db, npcname, itemId, itemId2, amount2)) {
+ Sql_ShowDebug(map->mysql_handle);
+ }
+ }
+}
+
+/**
+ * Removes persistent NPC Barter Data from SQL
+ **/
+static void npc_barter_delfromsql(struct npc_data *nd, int index)
+{
+ nullpo_retv(nd);
+ if (index == INT_MAX) {
+ npc->barter_delfromsql_sub(nd->exname, INT_MAX, 0, 0);
+ } else {
+ Assert_retv(index >= 0 && index < nd->u.scr.shop->items);
+ const struct npc_item_list *const item = &nd->u.scr.shop->item[index];
+ npc->barter_delfromsql_sub(nd->exname, item->nameid, item->value, item->value2);
+ }
+}
+
+
+/**
+ * Loads persistent NPC Expanded Barter Data from SQL
+ **/
+static void npc_expanded_barter_fromsql(void)
+{
+ struct SqlStmt *stmt = SQL->StmtMalloc(map->mysql_handle);
+ char name[NAME_LENGTH + 1];
+ int itemid;
+ int amount;
+ int zeny;
+ StringBuf buf;
+
+ StrBuf->Init(&buf);
+ StrBuf->AppendStr(&buf, "SELECT `name`, `itemId`, `amount`, `zeny`");
+ for (int k = 1; k < 11; k ++) {
+ StrBuf->Printf(&buf, ", `currencyId%d`, `currencyAmount%d`, `currencyRefine%d`", k, k, k);
+ }
+ StrBuf->Printf(&buf, " FROM `%s`", map->npc_expanded_barter_data_db);
+
+ if (SQL_ERROR == SQL->StmtPrepareStr(stmt, StrBuf->Value(&buf))
+ || SQL_ERROR == SQL->StmtExecute(stmt)
+ ) {
+ SqlStmt_ShowDebug(stmt);
+ SQL->StmtFree(stmt);
+ StrBuf->Destroy(&buf);
+ return;
+ }
+
+ struct npc_barter_currency tempCurrency[10];
+ SQL->StmtBindColumn(stmt, 0, SQLDT_STRING, &name, sizeof name, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 1, SQLDT_INT, &itemid, sizeof itemid, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 2, SQLDT_UINT32, &amount, sizeof amount, NULL, NULL);
+ SQL->StmtBindColumn(stmt, 3, SQLDT_UINT32, &zeny, sizeof zeny, NULL, NULL);
+ for (int k = 0; k < 10; k ++) {
+ SQL->StmtBindColumn(stmt, k * 3 + 4, SQLDT_INT, &tempCurrency[k].nameid, sizeof tempCurrency[k].nameid, NULL, NULL);
+ SQL->StmtBindColumn(stmt, k * 3 + 5, SQLDT_INT, &tempCurrency[k].amount, sizeof tempCurrency[k].amount, NULL, NULL);
+ SQL->StmtBindColumn(stmt, k * 3 + 6, SQLDT_INT, &tempCurrency[k].refine, sizeof tempCurrency[k].refine, NULL, NULL);
+ }
+
+ while (SQL_SUCCESS == SQL->StmtNextRow(stmt)) {
+ struct npc_data *nd = NULL;
+ unsigned short i;
+
+ if ((nd = npc->name2id(name)) == NULL) {
+ ShowError("npc_expanded_barter_fromsql: NPC '%s' not found! skipping...\n",name);
+ npc->expanded_barter_delfromsql_sub(name, INT_MAX, 0, 0, NULL);
+ continue;
+ } else if (nd->subtype != SCRIPT || nd->u.scr.shop == NULL || nd->u.scr.shop->items == 0 || nd->u.scr.shop->type != NST_EXPANDED_BARTER) {
+ ShowError("npc_expanded_barter_fromsql: NPC '%s' is not proper for barter, skipping...\n",name);
+ npc->expanded_barter_delfromsql_sub(name, INT_MAX, 0, 0, NULL);
+ continue;
+ }
+
+ for (i = 0; i < nd->u.scr.shop->items; i++) {
+ struct npc_item_list *const item = &nd->u.scr.shop->item[i];
+ if (item->nameid == itemid && item->value == zeny) {
+ int count = nd->u.scr.shop->item[i].value2;
+ if (count > 10)
+ count = 10;
+ int curIndex;
+ for (curIndex = 0; curIndex < count; curIndex ++) {
+ struct npc_barter_currency *currency = &nd->u.scr.shop->item[i].currency[curIndex];
+ struct npc_barter_currency *currency2 = &tempCurrency[curIndex];
+ if (currency->nameid != currency2->nameid ||
+ currency->amount != currency2->amount ||
+ currency->refine != currency2->refine) {
+ break;
+ }
+ }
+ if (curIndex == count) {
+ item->qty = amount;
+ break;
+ }
+ }
+ }
+
+ if (i == nd->u.scr.shop->items) {
+ ShowError("npc_expanded_barter_fromsql: NPC '%s' does not sell item %d (qty %d), deleting...\n", name, itemid, amount);
+ npc->expanded_barter_delfromsql_sub(name, itemid, zeny, 10, &tempCurrency[0]);
+ continue;
+ }
+ }
+ SQL->StmtFree(stmt);
+ StrBuf->Destroy(&buf);
+}
+
+/**
+ * Saves persistent NPC Expanded Barter Data into SQL
+ **/
+static void npc_expanded_barter_tosql(struct npc_data *nd, int index)
+{
+ nullpo_retv(nd);
+ Assert_retv(index >= 0 && index < nd->u.scr.shop->items);
+ const struct npc_item_list *const item = &nd->u.scr.shop->item[index];
+ if (item->qty == -1)
+ return;
+
+ npc->expanded_barter_delfromsql(nd, index);
+
+ StringBuf buf;
+ StrBuf->Init(&buf);
+ StrBuf->Printf(&buf, "INSERT INTO `%s` VALUES ('%s', '%d', '%d', '%u'", map->npc_expanded_barter_data_db, nd->exname, item->nameid, item->qty, item->value);
+ int currencyCount = item->value2;
+ if (currencyCount > 10)
+ currencyCount = 10;
+ int k;
+ for (k = 0; k < currencyCount; k++) {
+ struct npc_barter_currency *currency = &item->currency[k];
+ StrBuf->Printf(&buf, ", '%d', '%d', '%d'", currency->nameid, currency->amount, currency->refine);
+ }
+ for (; k < 10; k ++) {
+ StrBuf->Printf(&buf, ", '0', '0', '0'");
+ }
+ StrBuf->AppendStr(&buf, ")");
+
+ if (SQL_ERROR == SQL->QueryStr(map->mysql_handle, StrBuf->Value(&buf))) {
+ Sql_ShowDebug(map->mysql_handle);
+ }
+ StrBuf->Destroy(&buf);
+}
+
+/**
+ * Removes persistent NPC Expanded Barter Data from SQL
+ */
+static void npc_expanded_barter_delfromsql_sub(const char *npcname, int itemId, int zeny, int currencyCount, struct npc_barter_currency* currency)
+{
+ if (itemId == INT_MAX) {
+ if (SQL_ERROR == SQL->Query(map->mysql_handle, "DELETE FROM `%s` WHERE `name`='%s'", map->npc_expanded_barter_data_db, npcname))
+ Sql_ShowDebug(map->mysql_handle);
+ } else {
+ StringBuf buf;
+
+ StrBuf->Init(&buf);
+ StrBuf->Printf(&buf, "DELETE FROM `%s` WHERE `name`='%s' AND `itemId`='%d' AND `zeny`='%d'",
+ map->npc_expanded_barter_data_db, npcname, itemId, zeny);
+ int k = 0;
+ if (currencyCount > 10)
+ currencyCount = 10;
+ for (k = 0; k < currencyCount; k++) {
+ struct npc_barter_currency *currency1 = &currency[k];
+ StrBuf->Printf(&buf, " AND currencyId%d='%d' and currencyAmount%d='%d' and currencyRefine%d='%d'",
+ k + 1, currency1->nameid, k + 1, currency1->amount, k + 1, currency1->refine);
+ }
+ for (; k < 10; k ++) {
+ StrBuf->Printf(&buf, " AND currencyId%d='0' and currencyAmount%d='0' and currencyRefine%d='0'",
+ k + 1, k + 1, k + 1);
+ }
+ StrBuf->AppendStr(&buf, " LIMIT 1");
+
+ if (SQL_ERROR == SQL->QueryStr(map->mysql_handle, StrBuf->Value(&buf))) {
+ Sql_ShowDebug(map->mysql_handle);
+ }
+ StrBuf->Destroy(&buf);
+ }
+}
+
+
+/**
+ * Removes persistent NPC Expanded Barter Data from SQL
+ **/
+static void npc_expanded_barter_delfromsql(struct npc_data *nd, int index)
+{
+ nullpo_retv(nd);
+ if (index == INT_MAX) {
+ npc->expanded_barter_delfromsql_sub(nd->exname, INT_MAX, 0, 0, NULL);
+ } else {
+ Assert_retv(index >= 0 && index < nd->u.scr.shop->items);
+ const struct npc_item_list *const item = &nd->u.scr.shop->item[index];
+ npc->expanded_barter_delfromsql_sub(nd->exname, item->nameid, item->value, item->value2, &item->currency[0]);
+ }
+}
+
/**
* Judges whether to allow and spawn a trader's window.
**/
-bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) {
+static bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd)
+{
nullpo_retr(false, sd);
nullpo_retr(false, nd);
if( !nd->u.scr.shop || !nd->u.scr.shop->items )
@@ -1664,6 +1979,12 @@ bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) {
clif->npc_market_open(sd,nd);
}
break;
+ case NST_BARTER:
+ clif->npc_barter_open(sd, nd);
+ break;
+ case NST_EXPANDED_BARTER:
+ clif->npc_expanded_barter_open(sd, nd);
+ break;
default:
clif->cashshop_show(sd,nd);
break;
@@ -1676,7 +1997,8 @@ bool npc_trader_open(struct map_session_data *sd, struct npc_data *nd) {
*
* @param master id of the original npc
**/
-void npc_trader_update(int master) {
+static void npc_trader_update(int master)
+{
struct DBIterator *iter;
struct block_list* bl;
struct npc_data *master_nd = map->id2nd(master);
@@ -1700,7 +2022,8 @@ void npc_trader_update(int master) {
* @param nd shop
* @param sd player
**/
-void npc_trader_count_funds(struct npc_data *nd, struct map_session_data *sd) {
+static void npc_trader_count_funds(struct npc_data *nd, struct map_session_data *sd)
+{
char evname[EVENT_NAME_LENGTH];
struct event_data *ev = NULL;
@@ -1740,7 +2063,8 @@ void npc_trader_count_funds(struct npc_data *nd, struct map_session_data *sd) {
*
* @return bool whether it was successful (if the script does not respond it will fail)
**/
-bool npc_trader_pay(struct npc_data *nd, struct map_session_data *sd, int price, int points) {
+static bool npc_trader_pay(struct npc_data *nd, struct map_session_data *sd, int price, int points)
+{
char evname[EVENT_NAME_LENGTH];
struct event_data *ev = NULL;
@@ -1750,8 +2074,8 @@ bool npc_trader_pay(struct npc_data *nd, struct map_session_data *sd, int price,
snprintf(evname, EVENT_NAME_LENGTH, "%s::OnPayFunds",nd->exname);
if ( (ev = strdb_get(npc->ev_db, evname)) ) {
- pc->setreg(sd,script->add_str("@price"),price);
- pc->setreg(sd,script->add_str("@points"),points);
+ pc->setreg(sd,script->add_variable("@price"),price);
+ pc->setreg(sd,script->add_variable("@points"),points);
script->run_npc(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id);
} else
ShowError("npc_trader_pay: '%s' event '%s' not found, operation failed\n",nd->exname,evname);
@@ -1761,7 +2085,8 @@ bool npc_trader_pay(struct npc_data *nd, struct map_session_data *sd, int price,
/*==========================================
* Cash Shop Buy
*------------------------------------------*/
-int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int points) {
+static int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int points)
+{
struct npc_data *nd = NULL;
struct item_data *item;
struct npc_item_list *shop = NULL;
@@ -1786,11 +2111,16 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po
return ERROR_TYPE_ITEM_ID; // Invalid Item
if( nd->subtype != CASHSHOP ) {
- if( nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type != NST_ZENY && nd->u.scr.shop->type != NST_MARKET ) {
+ if (nd->subtype == SCRIPT && nd->u.scr.shop &&
+ nd->u.scr.shop->type != NST_ZENY &&
+ nd->u.scr.shop->type != NST_MARKET &&
+ nd->u.scr.shop->type != NST_BARTER &&
+ nd->u.scr.shop->type != NST_EXPANDED_BARTER) {
shop = nd->u.scr.shop->item;
shop_size = nd->u.scr.shop->items;
- } else
+ } else {
return ERROR_TYPE_NPC;
+ }
} else {
shop = nd->u.shop.shop_item;
shop_size = nd->u.shop.count;
@@ -1866,7 +2196,7 @@ int npc_cashshop_buy(struct map_session_data *sd, int nameid, int amount, int po
* @param item_list List of items.
* @return result code for clif->parse_NpcBuyListSend.
*/
-int npc_buylist(struct map_session_data *sd, struct itemlist *item_list)
+static int npc_buylist(struct map_session_data *sd, struct itemlist *item_list)
{
struct npc_data* nd;
struct npc_item_list *shop = NULL;
@@ -1935,7 +2265,11 @@ int npc_buylist(struct map_session_data *sd, struct itemlist *item_list)
break;
case ADDITEM_OVERAMOUNT:
+#if PACKETVER >= 20110705
+ return 9;
+#else
return 2;
+#endif
}
value = pc->modifybuyvalue(sd,value);
@@ -1989,7 +2323,7 @@ int npc_buylist(struct map_session_data *sd, struct itemlist *item_list)
/**
* Processes incoming npc market purchase list
**/
-int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
+static enum market_buy_result npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
{
struct npc_data* nd;
struct npc_item_list *shop = NULL;
@@ -2003,7 +2337,7 @@ int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
nd = npc->checknear(sd,map->id2bl(sd->npc_shopid));
if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || !nd->u.scr.shop || nd->u.scr.shop->type != NST_MARKET)
- return 1;
+ return MARKET_BUY_RESULT_ERROR;
shop = nd->u.scr.shop->item;
shop_size = nd->u.scr.shop->items;
@@ -2023,18 +2357,18 @@ int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
entry->id == itemdb_viewid(shop[j].nameid) //item_avail replacement
);
if (j == shop_size) /* TODO find official response for this */
- return 1; // no such item in shop
+ return MARKET_BUY_RESULT_ERROR; // no such item in shop
entry->id = shop[j].nameid; //item_avail replacement
if (entry->amount > (int)shop[j].qty)
- return 1;
+ return MARKET_BUY_RESULT_AMOUNT_TOO_BIG;
value = shop[j].value;
npc_market_qty[i] = j;
if (!itemdb->exists(entry->id)) /* TODO find official response for this */
- return 1; // item no longer in itemdb
+ return MARKET_BUY_RESULT_ERROR; // item no longer in itemdb
if (!itemdb->isstackable(entry->id) && entry->amount > 1) {
//Exploit? You can't buy more than 1 of equipment types o.O
@@ -2058,13 +2392,13 @@ int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
}
if (z > sd->status.zeny) /* TODO find official response for this */
- return 1; // Not enough Zeny
+ return MARKET_BUY_RESULT_NO_ZENY; // Not enough Zeny
if( w + sd->weight > sd->max_weight ) /* TODO find official response for this */
- return 1; // Too heavy
+ return MARKET_BUY_RESULT_OVER_WEIGHT; // Too heavy
if( pc->inventoryblank(sd) < new_ ) /* TODO find official response for this */
- return 1; // Not enough space to store items
+ return MARKET_BUY_RESULT_OUT_OF_SPACE; // Not enough space to store items
pc->payzeny(sd,(int)z,LOG_TYPE_NPC, NULL);
@@ -2074,7 +2408,7 @@ int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
j = npc_market_qty[i];
if (entry->amount > (int)shop[j].qty) /* wohoo someone tampered with the packet. */
- return 1;
+ return MARKET_BUY_RESULT_AMOUNT_TOO_BIG;
shop[j].qty -= entry->amount;
@@ -2092,14 +2426,292 @@ int npc_market_buylist(struct map_session_data *sd, struct itemlist *item_list)
}
}
- return 0;
+ return MARKET_BUY_RESULT_SUCCESS;
+}
+
+/**
+ * Processes incoming npc barter purchase list
+ **/
+static int npc_barter_buylist(struct map_session_data *sd, struct barteritemlist *item_list)
+{
+ struct npc_data* nd;
+ struct npc_item_list *shop = NULL;
+ int w, new_;
+ unsigned short shop_size = 0;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_list);
+
+ nd = npc->checknear(sd, map->id2bl(sd->npc_shopid));
+
+ if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 || !nd->u.scr.shop || nd->u.scr.shop->type != NST_BARTER)
+ return 11;
+
+ shop = nd->u.scr.shop->item;
+ shop_size = nd->u.scr.shop->items;
+
+ w = 0;
+ new_ = 0;
+
+ int items[MAX_INVENTORY] = { 0 };
+
+ // process entries in buy list, one by one
+ for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) {
+ struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+
+ const int n = entry->removeIndex;
+ if (n < 0 || n >= sd->status.inventorySize)
+ return 11; // wrong inventory index
+
+ if (entry->addAmount <= 0)
+ return 14; // not enough item amount in inventory
+
+ int removeId = sd->status.inventory[n].nameid;
+ const int j = entry->shopIndex;
+ if (j < 0 || j >= shop_size)
+ return 13; // no such item in shop
+ if (entry->addId != shop[j].nameid && entry->addId != itemdb_viewid(shop[j].nameid))
+ return 13; // no such item in shop
+ if (removeId != shop[j].value && removeId != itemdb_viewid(shop[j].value))
+ return 13; // no such item in shop
+ entry->addId = shop[j].nameid; // item_avail replacement
+ removeId = shop[j].value; // item_avail replacement
+
+ if (!itemdb->exists(entry->addId))
+ return 13; // item no longer in itemdb
+
+ const int removeAmount = shop[j].value2;
+
+ if ((int)shop[j].qty != -1 && entry->addAmount > (int)shop[j].qty)
+ return 14; // not enough item amount in shop
+
+ if (removeAmount * entry->addAmount > sd->status.inventory[n].amount)
+ return 14; // not enough item amount in inventory
+
+ items[n] += removeAmount * entry->addAmount;
+
+ if (items[n] > sd->status.inventory[n].amount)
+ return 14; // not enough item amount in inventory
+
+ entry->addId = shop[j].nameid; //item_avail replacement
+
+ npc_market_qty[i] = j;
+
+ if (!itemdb->isstackable(entry->addId) && entry->addAmount > 1) {
+ //Exploit? You can't buy more than 1 of equipment types o.O
+ ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n",
+ sd->status.name, sd->status.account_id, sd->status.char_id, entry->addAmount, entry->addId);
+ entry->addAmount = 1;
+ }
+
+ switch (pc->checkadditem(sd, entry->addId, entry->addAmount)) {
+ case ADDITEM_EXIST:
+ break;
+ case ADDITEM_NEW:
+ new_++;
+ break;
+ case ADDITEM_OVERAMOUNT: /* TODO find official response for this */
+ return 1;
+ }
+
+ w += itemdb_weight(entry->addId) * entry->addAmount;
+ w -= itemdb_weight(removeId) * removeAmount;
+ }
+
+ if (w + sd->weight > sd->max_weight)
+ return 2; // Too heavy
+
+ if (pc->inventoryblank(sd) < new_)
+ return 3; // Not enough space to store items
+
+ for (int i = 0; i < sd->status.inventorySize; ++i) {
+ const int removeAmountTotal = items[i];
+ if (removeAmountTotal == 0)
+ continue;
+ if (pc->delitem(sd, i, removeAmountTotal, 0, DELITEM_SOLD, LOG_TYPE_NPC) != 0) {
+ return 11; // unknown exploit
+ }
+ }
+
+ for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) {
+ struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+ const int shopIdx = npc_market_qty[i];
+
+ if ((int)shop[shopIdx].qty != -1) {
+ if (entry->addAmount > (int)shop[shopIdx].qty) /* wohoo someone tampered with the packet. */
+ return 14;
+ shop[shopIdx].qty -= entry->addAmount;
+ }
+
+ npc->barter_tosql(nd, shopIdx);
+
+ if (itemdb_type(entry->addId) == IT_PETEGG) {
+ pet->create_egg(sd, entry->addId);
+ } else {
+ struct item item_tmp;
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = entry->addId;
+ item_tmp.identify = 1;
+ pc->additem(sd, &item_tmp, entry->addAmount, LOG_TYPE_NPC);
+ }
+ }
+
+ return 12;
+}
+
+
+/**
+ * Processes incoming npc expanded barter purchase list
+ **/
+static int npc_expanded_barter_buylist(struct map_session_data *sd, struct barteritemlist *item_list)
+{
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_list);
+
+ struct npc_data* nd = npc->checknear(sd, map->id2bl(sd->npc_shopid));
+
+ if (nd == NULL || nd->subtype != SCRIPT || VECTOR_LENGTH(*item_list) == 0 ||
+ !nd->u.scr.shop || nd->u.scr.shop->type != NST_EXPANDED_BARTER) {
+ return 11;
+ }
+
+ struct npc_item_list *shop = nd->u.scr.shop->item;
+ unsigned short shop_size = nd->u.scr.shop->items;
+ int w = 0;
+ int new_ = 0;
+ int64 z = 0;
+ int items[MAX_INVENTORY] = { 0 };
+
+ // process entries in buy list, one by one
+ for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) {
+ struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+
+ if (entry->addAmount <= 0)
+ return 14; // not enough item amount in inventory
+
+ const int j = entry->shopIndex;
+ if (j < 0 || j >= shop_size)
+ return 13; // no such item in shop
+ if (entry->addId != shop[j].nameid && entry->addId != itemdb_viewid(shop[j].nameid))
+ return 13; // no such item in shop
+ entry->addId = shop[j].nameid; // item_avail replacement
+ if (!itemdb->exists(entry->addId))
+ return 13; // item no longer in itemdb
+
+ if ((int)shop[j].qty != -1 && entry->addAmount > (int)shop[j].qty)
+ return 14; // not enough item amount in shop
+
+ int currencyCount = shop[j].value2;
+ for (int currencyIndex = 0; currencyIndex < currencyCount; currencyIndex ++) {
+ struct npc_barter_currency *currency = &shop[j].currency[currencyIndex];
+ const int currencyItemId = currency->nameid;
+ const int currencyRefine = currency->refine;
+ int removeAmount = currency->amount * entry->addAmount;
+ if (removeAmount <= 0)
+ continue;
+ for (int n = 0; n < sd->status.inventorySize && removeAmount > 0; ++n) {
+ // check item id and existing amount
+ if (sd->status.inventory[n].nameid == currencyItemId && sd->status.inventory[n].amount > 0) {
+ // check item refine level
+ if (currencyRefine != -1 && sd->status.inventory[n].refine != currencyRefine)
+ continue;
+ if (sd->status.inventory[n].amount >= removeAmount) {
+ items[n] += removeAmount;
+ removeAmount = 0;
+ w -= itemdb_weight(currencyItemId) * removeAmount;
+ break;
+ } else {
+ items[n] += sd->status.inventory[n].amount;
+ removeAmount -= sd->status.inventory[n].amount;
+ w -= itemdb_weight(currencyItemId) * sd->status.inventory[n].amount;
+ }
+ }
+ if (items[n] > sd->status.inventory[n].amount)
+ return 14; // not enough item amount in inventory
+ }
+ if (removeAmount != 0) {
+ return 14; // not enough item amount in inventory
+ }
+ }
+
+ entry->addId = shop[j].nameid; //item_avail replacement
+
+ npc_market_qty[i] = j;
+
+ if (!itemdb->isstackable(entry->addId) && entry->addAmount > 1) {
+ //Exploit? You can't buy more than 1 of equipment types o.O
+ ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of non-stackable item %d!\n",
+ sd->status.name, sd->status.account_id, sd->status.char_id, entry->addAmount, entry->addId);
+ entry->addAmount = 1;
+ }
+
+ switch (pc->checkadditem(sd, entry->addId, entry->addAmount)) {
+ case ADDITEM_EXIST:
+ break;
+ case ADDITEM_NEW:
+ new_++;
+ break;
+ case ADDITEM_OVERAMOUNT: /* TODO find official response for this */
+ return 1;
+ }
+
+ z += (int64)shop[j].value * entry->addAmount;
+ w += itemdb_weight(entry->addId) * entry->addAmount;
+ }
+
+ if (z > sd->status.zeny)
+ return 3; // Not enough Zeny
+
+ if ((int64)w + sd->weight > sd->max_weight)
+ return 2; // Too heavy
+
+ if (pc->inventoryblank(sd) < new_)
+ return 3; // Not enough space to store items
+
+ for (int i = 0; i < sd->status.inventorySize; ++i) {
+ const int removeAmountTotal = items[i];
+ if (removeAmountTotal == 0)
+ continue;
+ if (pc->delitem(sd, i, removeAmountTotal, 0, DELITEM_SOLD, LOG_TYPE_NPC) != 0) {
+ return 11; // unknown exploit
+ }
+ }
+
+ pc->payzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
+
+ for (int i = 0; i < VECTOR_LENGTH(*item_list); ++i) {
+ struct barter_itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
+ const int shopIdx = npc_market_qty[i];
+
+ if ((int)shop[shopIdx].qty != -1) {
+ if (entry->addAmount > (int)shop[shopIdx].qty) /* wohoo someone tampered with the packet. */
+ return 14;
+ shop[shopIdx].qty -= entry->addAmount;
+ }
+
+ npc->expanded_barter_tosql(nd, shopIdx);
+
+ if (itemdb_type(entry->addId) == IT_PETEGG) {
+ pet->create_egg(sd, entry->addId);
+ } else {
+ struct item item_tmp;
+ memset(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = entry->addId;
+ item_tmp.identify = 1;
+ pc->additem(sd, &item_tmp, entry->addAmount, LOG_TYPE_NPC);
+ }
+ }
+
+ return 12;
}
/// npc_selllist for script-controlled shops
-int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd)
+static int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, struct npc_data *nd)
{
char npc_ev[EVENT_NAME_LENGTH];
char card_slot[NAME_LENGTH];
+ char opt_index_str[NAME_LENGTH];
+ char opt_value_str[NAME_LENGTH];
int i, j;
int key_nameid = 0;
int key_amount = 0;
@@ -2107,6 +2719,8 @@ int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, st
int key_attribute = 0;
int key_identify = 0;
int key_card[MAX_SLOTS];
+ int key_opt_idx[MAX_ITEM_OPTIONS];
+ int key_opt_value[MAX_ITEM_OPTIONS];
nullpo_ret(sd);
nullpo_ret(item_list);
@@ -2126,6 +2740,17 @@ int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, st
script->cleararray_pc(sd, card_slot, (void*)0);
}
+ for (j = 0; j < MAX_ITEM_OPTIONS; j++) { // Clear Each item option entry
+ key_opt_idx[j] = 0;
+ key_opt_value[j] = 0;
+
+ snprintf(opt_index_str, sizeof(opt_index_str), "@slot_opt_idx%d", j + 1);
+ script->cleararray_pc(sd, opt_index_str, (void*)0);
+
+ snprintf(opt_value_str, sizeof(opt_value_str), "@slot_opt_val%d", j + 1);
+ script->cleararray_pc(sd, opt_value_str, (void*)0);
+ }
+
// save list of to be sold items
for (i = 0; i < VECTOR_LENGTH(*item_list); i++) {
struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
@@ -2151,6 +2776,17 @@ int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, st
script->setarray_pc(sd, card_slot, i, (void*)card, &key_card[j]);
}
+ for (j = 0; j < MAX_ITEM_OPTIONS; j++) {
+ intptr_t opt_idx = item->option[j].index;
+ intptr_t opt_value = item->option[j].value;
+
+ snprintf(opt_index_str, sizeof(opt_index_str), "@slot_opt_idx%d", j + 1);
+ script->setarray_pc(sd, opt_index_str, i, (void*)opt_idx, &key_opt_idx[j]);
+
+ snprintf(opt_value_str, sizeof(opt_value_str), "@slot_opt_val%d", j + 1);
+ script->setarray_pc(sd, opt_value_str, i, (void*)opt_value, &key_opt_value[j]);
+ }
+
}
// invoke event
@@ -2163,7 +2799,7 @@ int npc_selllist_sub(struct map_session_data *sd, struct itemlist *item_list, st
///
/// @param item_list 'n' pairs <index,amount>
/// @return result code for clif->parse_NpcSellListSend
-int npc_selllist(struct map_session_data *sd, struct itemlist *item_list)
+static int npc_selllist(struct map_session_data *sd, struct itemlist *item_list)
{
int64 z;
int i,skill_t, skill_idx = skill->get_index(MC_OVERCHARGE);
@@ -2178,18 +2814,21 @@ int npc_selllist(struct map_session_data *sd, struct itemlist *item_list)
}
if( nd->subtype != SHOP ) {
- if( !(nd->subtype == SCRIPT && nd->u.scr.shop && nd->u.scr.shop->type == NST_ZENY) )
+ if (!(nd->subtype == SCRIPT && nd->u.scr.shop && (nd->u.scr.shop->type == NST_ZENY || nd->u.scr.shop->type == NST_MARKET)))
return 1;
}
z = 0;
+ if (sd->status.zeny >= MAX_ZENY && nd->master_nd == NULL)
+ return 1;
+
// verify the sell list
for (i = 0; i < VECTOR_LENGTH(*item_list); i++) {
struct itemlist_entry *entry = &VECTOR_INDEX(*item_list, i);
int nameid, value, idx = entry->id;
- if (idx >= MAX_INVENTORY || idx < 0 || entry->amount < 0) {
+ if (idx >= sd->status.inventorySize || idx < 0 || entry->amount < 0) {
return 1;
}
@@ -2230,10 +2869,17 @@ int npc_selllist(struct map_session_data *sd, struct itemlist *item_list)
}
}
+ // Achievements [Smokexyz/Hercules]
+ achievement->validate_item_sell(sd, sd->status.inventory[idx].nameid, entry->amount);
+
pc->delitem(sd, idx, entry->amount, 0, DELITEM_SOLD, LOG_TYPE_NPC);
+
}
- if( z > MAX_ZENY )
+ if (z + sd->status.zeny > MAX_ZENY && nd->master_nd == NULL)
+ return 1;
+
+ if (z > MAX_ZENY)
z = MAX_ZENY;
pc->getzeny(sd, (int)z, LOG_TYPE_NPC, NULL);
@@ -2256,7 +2902,8 @@ int npc_selllist(struct map_session_data *sd, struct itemlist *item_list)
//Atempt to remove an npc from a map
//This doesn't remove it from map_db
-int npc_remove_map(struct npc_data* nd) {
+static int npc_remove_map(struct npc_data *nd)
+{
int16 m,i;
nullpo_retr(1, nd);
@@ -2279,7 +2926,7 @@ int npc_remove_map(struct npc_data* nd) {
/**
* @see DBApply
*/
-int npc_unload_ev(union DBKey key, struct DBData *data, va_list ap)
+static int npc_unload_ev(union DBKey key, struct DBData *data, va_list ap)
{
struct event_data* ev = DB->data2ptr(data);
char* npcname = va_arg(ap, char *);
@@ -2294,7 +2941,7 @@ int npc_unload_ev(union DBKey key, struct DBData *data, va_list ap)
/**
* @see DBApply
*/
-int npc_unload_ev_label(union DBKey key, struct DBData *data, va_list ap)
+static int npc_unload_ev_label(union DBKey key, struct DBData *data, va_list ap)
{
struct linkdb_node **label_linkdb = DB->data2ptr(data);
struct npc_data* nd = va_arg(ap, struct npc_data *);
@@ -2304,119 +2951,193 @@ int npc_unload_ev_label(union DBKey key, struct DBData *data, va_list ap)
return 0;
}
-//Chk if npc matches src_id, then unload.
-//Sub-function used to find duplicates.
-int npc_unload_dup_sub(struct npc_data* nd, va_list args)
+/**
+ * Unloads a NPC if it's a duplicate of the passed one.
+ *
+ * @param nd The NPC to check.
+ * @param args List of arguments.
+ * @return Always 0.
+ *
+ **/
+static int npc_unload_dup_sub(struct npc_data *nd, va_list args)
{
- int src_id;
-
nullpo_ret(nd);
- src_id = va_arg(args, int);
+
+ const int src_id = va_arg(args, int);
+ const int unload_mobs = va_arg(args, int);
+
if (nd->src_id == src_id)
- npc->unload(nd, true);
+ npc->unload(nd, true, (unload_mobs == 1));
+
return 0;
}
-//Removes all npcs that are duplicates of the passed one. [Skotlex]
-void npc_unload_duplicates(struct npc_data* nd) {
+/**
+ * Unloads all NPCs which are duplicates of the passed one.
+ *
+ * @param nd The source NPC.
+ * @param unload_mobs If true, mobs spawned by duplicates will be removed.
+ *
+ * @author Skotlex
+ *
+ **/
+static void npc_unload_duplicates(struct npc_data *nd, bool unload_mobs)
+{
nullpo_retv(nd);
- map->foreachnpc(npc->unload_dup_sub,nd->bl.id);
+
+ map->foreachnpc(npc->unload_dup_sub, nd->bl.id, unload_mobs);
+}
+
+/**
+ * Removes a mob, which was spawned by a NPC (monster/areamonster/guardian/bg_monster/atcommand("@monster xy")).
+ *
+ * @param md The mob to remove.
+ * @param args List of arguments.
+ * @return 1 on success, 0 on failure.
+ *
+ * @author Kenpachi
+ *
+ **/
+static int npc_unload_mob(struct mob_data *md, va_list args)
+{
+ nullpo_ret(md);
+
+ const int npc_id = va_arg(args, int);
+
+ if (md->npc_id == npc_id) {
+ md->state.npc_killmonster = 1;
+ status_kill(&md->bl);
+ return 1;
+ }
+
+ return 0;
}
-//Removes an npc from map and db.
-//Single is to free name (for duplicates).
-int npc_unload(struct npc_data* nd, bool single)
+/**
+ * Removes a NPC from map and database.
+ *
+ * @param nd The NPC which should be removed.
+ * @param single If true, names are freed. (For duplicates.)
+ * @param unload_mobs If true, mobs spawned by the NPC will be removed.
+ * @return Always 0.
+ *
+ **/
+static int npc_unload(struct npc_data *nd, bool single, bool unload_mobs)
{
nullpo_ret(nd);
- if( nd->ud && nd->ud != &npc->base_ud ) {
+ if (nd->ud != NULL && nd->ud != &npc->base_ud)
skill->clear_unitgroup(&nd->bl);
- }
npc->remove_map(nd);
map->deliddb(&nd->bl);
- if( single )
+
+ if (single)
strdb_remove(npc->name_db, nd->exname);
- if (nd->chat_id) // remove npc chatroom object and kick users
+ if (nd->chat_id != 0) /// Remove NPC chatroom object and kick users.
chat->delete_npc_chat(nd);
- npc_chat->finalize(nd); // deallocate npc PCRE data structures
+ npc_chat->finalize(nd); /// Deallocate NPC PCRE data structures.
if (single && nd->path != NULL) {
npc->releasepathreference(nd->path);
nd->path = NULL;
}
- if( single && nd->bl.m != -1 )
- map->remove_questinfo(nd->bl.m,nd);
+ if (single && nd->bl.m != INDEX_NOT_FOUND)
+ map->remove_questinfo(nd->bl.m, nd);
+
+ npc->questinfo_clear(nd);
- if (nd->src_id == 0 && ( nd->subtype == SHOP || nd->subtype == CASHSHOP)) {
- //src check for duplicate shops [Orcao]
- aFree(nd->u.shop.shop_item);
+ if (nd->src_id == 0 && (nd->subtype == SHOP || nd->subtype == CASHSHOP)) {
+ aFree(nd->u.shop.shop_item); /// src check for duplicate shops. [Orcao]
} else if (nd->subtype == SCRIPT) {
- struct s_mapiterator *iter;
- struct map_session_data *sd = NULL;
+ char evname[EVENT_NAME_LENGTH];
+
+ snprintf(evname, ARRAYLENGTH(evname), "%s::OnNPCUnload", nd->exname);
+
+ struct event_data *ev = strdb_get(npc->ev_db, evname);
+
+ if (ev != NULL)
+ script->run_npc(nd->u.scr.script, ev->pos, 0, nd->bl.id); /// Run OnNPCUnload.
- if( single ) {
- npc->ev_db->foreach(npc->ev_db,npc->unload_ev,nd->exname); //Clean up all events related
- npc->ev_label_db->foreach(npc->ev_label_db,npc->unload_ev_label,nd);
+ if (single) {
+ npc->ev_db->foreach(npc->ev_db, npc->unload_ev, nd->exname); /// Clean up all related events.
+ npc->ev_label_db->foreach(npc->ev_label_db, npc->unload_ev_label, nd);
}
- iter = mapit_geteachpc();
- for (sd = BL_UCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) {
- if (sd->npc_timer_id != INVALID_TIMER ) {
+ struct s_mapiterator *iter = mapit_geteachpc();
+ struct map_session_data *sd = BL_UCAST(BL_PC, mapit->first(iter));
+
+ for (; mapit->exists(iter); sd = BL_UCAST(BL_PC, mapit->next(iter))) {
+ if (sd->npc_timer_id != INVALID_TIMER) {
const struct TimerData *td = timer->get(sd->npc_timer_id);
- if( td && td->id != nd->bl.id )
+ if (td != NULL && td->id != nd->bl.id)
continue;
- if( td && td->data )
+ if (td != NULL && td->data != 0)
ers_free(npc->timer_event_ers, (void*)td->data);
+
timer->delete(sd->npc_timer_id, npc->timerevent);
sd->npc_timer_id = INVALID_TIMER;
}
}
+
mapit->free(iter);
if (nd->u.scr.timerid != INVALID_TIMER) {
- const struct TimerData *td;
- td = timer->get(nd->u.scr.timerid);
- if (td && td->data)
+ const struct TimerData *td = timer->get(nd->u.scr.timerid);
+
+ if (td != NULL && td->data != 0)
ers_free(npc->timer_event_ers, (void*)td->data);
+
timer->delete(nd->u.scr.timerid, npc->timerevent);
}
- if (nd->u.scr.timer_event)
+
+ if (nd->u.scr.timer_event != NULL)
aFree(nd->u.scr.timer_event);
+
if (nd->src_id == 0) {
- if(nd->u.scr.script) {
+ if (nd->u.scr.script != NULL) {
script->free_code(nd->u.scr.script);
nd->u.scr.script = NULL;
}
- if (nd->u.scr.label_list) {
+
+ if (nd->u.scr.label_list != NULL) {
aFree(nd->u.scr.label_list);
nd->u.scr.label_list = NULL;
nd->u.scr.label_list_num = 0;
}
- if(nd->u.scr.shop) {
- if(nd->u.scr.shop->item)
+
+ if (nd->u.scr.shop != NULL) {
+ if (nd->u.scr.shop->item != NULL) {
+ for (int i = 0; i < nd->u.scr.shop->items; i ++) {
+ if (nd->u.scr.shop->item[i].currency != NULL)
+ aFree(nd->u.scr.shop->item[i].currency);
+ }
aFree(nd->u.scr.shop->item);
+ }
+
aFree(nd->u.scr.shop);
}
}
- if( nd->u.scr.guild_id )
+
+ if (nd->u.scr.guild_id > 0)
guild->flag_remove(nd);
}
- if( nd->ud && nd->ud != &npc->base_ud ) {
+ if (nd->ud != NULL && nd->ud != &npc->base_ud) {
aFree(nd->ud);
nd->ud = NULL;
}
- HPM->data_store_destroy(&nd->hdata);
+ if (unload_mobs)
+ map->foreachmob(npc->unload_mob, nd->bl.id);
+ HPM->data_store_destroy(&nd->hdata);
aFree(nd);
-
return 0;
}
@@ -2425,7 +3146,7 @@ int npc_unload(struct npc_data* nd, bool single)
//
/// Clears the npc source file list
-void npc_clearsrcfile(void)
+static void npc_clearsrcfile(void)
{
struct npc_src_list* file = npc->src_files;
@@ -2437,24 +3158,22 @@ void npc_clearsrcfile(void)
npc->src_files = NULL;
}
-/// Adds a npc source file (or removes all)
-void npc_addsrcfile(const char* name)
+/**
+ * Adds a npc source file.
+ *
+ * @param name The file name to add.
+ */
+static void npc_addsrcfile(const char *name)
{
struct npc_src_list* file;
struct npc_src_list* file_prev = NULL;
nullpo_retv(name);
- if( strcmpi(name, "clear") == 0 )
- {
- npc->clearsrcfile();
- return;
- }
// prevent multiple insert of source files
file = npc->src_files;
- while( file != NULL )
- {
- if( strcmp(name, file->name) == 0 )
+ while (file != NULL) {
+ if (strcmp(name, file->name) == 0)
return;// found the file, no need to insert it again
file_prev = file;
file = file->next;
@@ -2469,24 +3188,21 @@ void npc_addsrcfile(const char* name)
file_prev->next = file;
}
-/// Removes a npc source file (or all)
-void npc_delsrcfile(const char* name)
+/**
+ * Removes a npc source file.
+ *
+ * @param name The file name to remove.
+ */
+static void npc_delsrcfile(const char *name)
{
struct npc_src_list* file = npc->src_files;
struct npc_src_list* file_prev = NULL;
nullpo_retv(name);
- if( strcmpi(name, "all") == 0 )
- {
- npc->clearsrcfile();
- return;
- }
- while( file != NULL )
- {
- if( strcmp(file->name, name) == 0 )
- {
- if( npc->src_files == file )
+ while (file != NULL) {
+ if (strcmp(file->name, name) == 0) {
+ if (npc->src_files == file)
npc->src_files = file->next;
else
file_prev->next = file->next;
@@ -2504,15 +3220,15 @@ void npc_delsrcfile(const char* name)
* @param filepath The file path to retain.
* @return A retained reference to filepath.
*/
-const char *npc_retainpathreference(const char *filepath)
+static const char *npc_retainpathreference(const char *filepath)
{
struct npc_path_data * npd = NULL;
nullpo_ret(filepath);
- if (npc_last_path == filepath) {
- if (npc_last_npd != NULL)
- npc_last_npd->references++;
- return npc_last_ref;
+ if (npc->npc_last_path == filepath) {
+ if (npc->npc_last_npd != NULL)
+ npc->npc_last_npd->references++;
+ return npc->npc_last_ref;
}
if ((npd = strdb_get(npc->path_db,filepath)) == NULL) {
@@ -2527,9 +3243,9 @@ const char *npc_retainpathreference(const char *filepath)
npd->references++;
- npc_last_npd = npd;
- npc_last_ref = npd->path;
- npc_last_path = filepath;
+ npc->npc_last_npd = npd;
+ npc->npc_last_ref = npd->path;
+ npc->npc_last_path = filepath;
return npd->path;
}
@@ -2539,13 +3255,13 @@ const char *npc_retainpathreference(const char *filepath)
*
* @param filepath The file path to release.
*/
-void npc_releasepathreference(const char *filepath)
+static void npc_releasepathreference(const char *filepath)
{
struct npc_path_data* npd = NULL;
nullpo_retv(filepath);
- if (filepath != npc_last_ref) {
+ if (filepath != npc->npc_last_ref) {
npd = strdb_get(npc->path_db, filepath);
}
@@ -2558,7 +3274,8 @@ void npc_releasepathreference(const char *filepath)
/// Parses and sets the name and exname of a npc.
/// Assumes that m, x and y are already set in nd.
-void npc_parsename(struct npc_data* nd, const char* name, const char* start, const char* buffer, const char* filepath) {
+static void npc_parsename(struct npc_data *nd, const char *name, const char *start, const char *buffer, const char *filepath)
+{
const char* p;
struct npc_data* dnd;// duplicate npc
char newname[NAME_LENGTH];
@@ -2601,7 +3318,7 @@ void npc_parsename(struct npc_data* nd, const char* name, const char* start, con
do {
++i;
- snprintf(newname, ARRAYLENGTH(newname), "%d_%d_%d_%d", i, nd->bl.m, nd->bl.x, nd->bl.y);
+ safesnprintf(newname, ARRAYLENGTH(newname), "%d_%d_%d_%d", i, nd->bl.m, nd->bl.x, nd->bl.y);
} while( npc->name2id(newname) != NULL );
strcpy(this_mapname, (nd->bl.m == -1 ? "(not on a map)" : mapindex_id2name(map_id2index(nd->bl.m))));
@@ -2616,7 +3333,8 @@ void npc_parsename(struct npc_data* nd, const char* name, const char* start, con
// Parse View
// Support for using Constants in place of NPC View IDs.
-int npc_parseview(const char* w4, const char* start, const char* buffer, const char* filepath) {
+static int npc_parseview(const char *w4, const char *start, const char *buffer, const char *filepath)
+{
int val = FAKE_NPC, i = 0;
char viewid[1024]; // Max size of name from constants.conf, see script->read_constdb.
@@ -2650,7 +3368,7 @@ int npc_parseview(const char* w4, const char* start, const char* buffer, const c
// View is ID
// Checks if given view is an ID or constant.
-bool npc_viewisid(const char * viewid)
+static bool npc_viewisid(const char *viewid)
{
nullpo_retr(false, viewid);
if (atoi(viewid) != FAKE_NPC) {
@@ -2674,7 +3392,7 @@ bool npc_viewisid(const char * viewid)
* @param class_ The NPC view class.
* @return A pointer to the created NPC data (ownership passed to the caller).
*/
-struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, int y, uint8 dir, int16 class_)
+static struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, int y, enum unit_dir dir, int class_)
{
struct npc_data *nd;
@@ -2690,12 +3408,15 @@ struct npc_data *npc_create_npc(enum npc_subtype subtype, int m, int x, int y, u
nd->area_size = AREA_SIZE + 1;
nd->class_ = class_;
nd->speed = 200;
+ nd->vd = npc_viewdb[0]; // Copy INVISIBLE_CLASS view data. Actual view data is set by npc->add_to_location() later.
+ VECTOR_INIT(nd->qi_data);
return nd;
}
//Add then display an npc warp on map
-struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y) {
+static struct npc_data *npc_add_warp(char *name, short from_mapid, short from_x, short from_y, short xs, short ys, unsigned short to_mapindex, short to_x, short to_y)
+{
int i, flag = 0;
struct npc_data *nd;
@@ -2708,10 +3429,10 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short
flag = 1;
if (flag == 1)
- snprintf(nd->exname, ARRAYLENGTH(nd->exname), "warp_%d_%d_%d", from_mapid, from_x, from_y);
+ safesnprintf(nd->exname, ARRAYLENGTH(nd->exname), "warp_%d_%d_%d", from_mapid, from_x, from_y);
for( i = 0; npc->name2id(nd->exname) != NULL; ++i )
- snprintf(nd->exname, ARRAYLENGTH(nd->exname), "warp%d_%d_%d_%d", i, from_mapid, from_x, from_y);
+ safesnprintf(nd->exname, ARRAYLENGTH(nd->exname), "warp%d_%d_%d_%d", i, from_mapid, from_x, from_y);
safestrncpy(nd->name, nd->exname, ARRAYLENGTH(nd->name));
nd->u.warp.mapindex = to_mapindex;
@@ -2742,7 +3463,7 @@ struct npc_data* npc_add_warp(char* name, short from_mapid, short from_x, short
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_warp(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
+static const char *npc_parse_warp(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
int x, y, xs, ys, to_x, to_y, m;
unsigned short i;
@@ -2785,7 +3506,7 @@ const char *npc_parse_warp(const char *w1, const char *w2, const char *w3, const
nd->u.warp.y = to_y;
nd->u.warp.xs = xs;
nd->u.warp.ys = ys;
- npc_warp++;
+ npc->npc_warp++;
npc->add_to_location(nd);
@@ -2809,7 +3530,7 @@ const char *npc_parse_warp(const char *w1, const char *w2, const char *w3, const
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
+static const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
//TODO: could be rewritten to NOT need this temp array [ultramage]
// We could use nd->u.shop.shop_item to store directly the items, but this could lead
@@ -2818,7 +3539,7 @@ const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const
struct npc_item_list *items = NULL;
size_t items_count = 40; // Starting items size
- char *p;
+ const char *p;
int x, y, dir, m, i, class_;
struct npc_data *nd;
enum npc_subtype type;
@@ -2930,13 +3651,14 @@ const char *npc_parse_shop(const char *w1, const char *w2, const char *w3, const
npc->parsename(nd, w3, start, buffer, filepath);
nd->path = npc->retainpathreference(filepath);
- ++npc_shop;
+ ++npc->npc_shop;
npc->add_to_location(nd);
return strchr(start,'\n');// continue
}
-void npc_convertlabel_db(struct npc_label_list* label_list, const char *filepath) {
+static void npc_convertlabel_db(struct npc_label_list *label_list, const char *filepath)
+{
int i;
nullpo_retv(label_list);
@@ -2968,7 +3690,8 @@ void npc_convertlabel_db(struct npc_label_list* label_list, const char *filepath
}
// Skip the contents of a script.
-const char* npc_skip_script(const char* start, const char* buffer, const char* filepath, int *retval) {
+static const char *npc_skip_script(const char *start, const char *buffer, const char *filepath, int *retval)
+{
const char* p;
int curly_count;
@@ -3054,7 +3777,7 @@ const char* npc_skip_script(const char* start, const char* buffer, const char* f
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_script(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int options, int *retval)
+static const char *npc_parse_script(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int options, int *retval)
{
int x, y, dir = 0, m, xs = 0, ys = 0; // [Valaris] thanks to fov
struct script_code *scriptroot;
@@ -3136,7 +3859,7 @@ const char *npc_parse_script(const char *w1, const char *w2, const char *w3, con
if( options&NPO_TRADER )
nd->u.scr.trader = true;
nd->u.scr.shop = NULL;
- ++npc_script;
+ ++npc->npc_script;
npc->add_to_location(nd);
//-----------------------------------------
@@ -3174,7 +3897,7 @@ const char *npc_parse_script(const char *w1, const char *w2, const char *w3, con
*
* @param nd The NPC to register.
*/
-void npc_add_to_location(struct npc_data *nd)
+static void npc_add_to_location(struct npc_data *nd)
{
nullpo_retv(nd);
@@ -3198,7 +3921,7 @@ void npc_add_to_location(struct npc_data *nd)
/**
* Duplicates a script (@see npc_duplicate_sub)
*/
-bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
+static bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
{
int i;
bool retval = true;
@@ -3206,7 +3929,7 @@ bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, i
nullpo_retr(false, nd);
nullpo_retr(false, snd);
- ++npc_script;
+ ++npc->npc_script;
nd->u.scr.xs = xs;
nd->u.scr.ys = ys;
nd->u.scr.script = snd->u.scr.script;
@@ -3248,12 +3971,12 @@ bool npc_duplicate_script_sub(struct npc_data *nd, const struct npc_data *snd, i
/**
* Duplicates a shop or cash shop (@see npc_duplicate_sub)
*/
-bool npc_duplicate_shop_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
+static bool npc_duplicate_shop_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
{
nullpo_retr(false, nd);
nullpo_retr(false, snd);
- ++npc_shop;
+ ++npc->npc_shop;
nd->u.shop.shop_item = snd->u.shop.shop_item;
nd->u.shop.count = snd->u.shop.count;
@@ -3266,12 +3989,12 @@ bool npc_duplicate_shop_sub(struct npc_data *nd, const struct npc_data *snd, int
/**
* Duplicates a warp (@see npc_duplicate_sub)
*/
-bool npc_duplicate_warp_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
+static bool npc_duplicate_warp_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
{
nullpo_retr(false, nd);
nullpo_retr(false, snd);
- ++npc_warp;
+ ++npc->npc_warp;
nd->u.warp.xs = xs;
nd->u.warp.ys = ys;
nd->u.warp.mapindex = snd->u.warp.mapindex;
@@ -3298,7 +4021,7 @@ bool npc_duplicate_warp_sub(struct npc_data *nd, const struct npc_data *snd, int
* @param options The NPC options.
* @retval false if there were any issues while creating and validating the NPC.
*/
-bool npc_duplicate_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
+static bool npc_duplicate_sub(struct npc_data *nd, const struct npc_data *snd, int xs, int ys, int options)
{
nullpo_retr(false, nd);
nullpo_retr(false, snd);
@@ -3356,7 +4079,7 @@ bool npc_duplicate_sub(struct npc_data *nd, const struct npc_data *snd, int xs,
* @remark
* Only `NPO_ONINIT` is available trough the options argument.
*/
-const char *npc_parse_duplicate(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int options, int *retval)
+static const char *npc_parse_duplicate(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int options, int *retval)
{
int x, y, dir, m, xs = -1, ys = -1;
char srcname[128];
@@ -3448,7 +4171,7 @@ const char *npc_parse_duplicate(const char *w1, const char *w2, const char *w3,
* @return success state.
* @retval 0 in case of successful creation.
*/
-int npc_duplicate4instance(struct npc_data *snd, int16 m)
+static int npc_duplicate4instance(struct npc_data *snd, int16 m)
{
char newname[NAME_LENGTH];
int dm = -1, im = -1, xs = -1, ys = -1;
@@ -3504,7 +4227,7 @@ int npc_duplicate4instance(struct npc_data *snd, int16 m)
}
//Set mapcell CELL_NPC to trigger event later
-void npc_setcells(struct npc_data* nd)
+static void npc_setcells(struct npc_data *nd)
{
int16 m, x, y, xs, ys;
int i,j;
@@ -3538,7 +4261,7 @@ void npc_setcells(struct npc_data* nd)
}
}
-int npc_unsetcells_sub(struct block_list *bl, va_list ap)
+static int npc_unsetcells_sub(struct block_list *bl, va_list ap)
{
struct npc_data *nd = NULL;
int id = va_arg(ap, int);
@@ -3553,7 +4276,8 @@ int npc_unsetcells_sub(struct block_list *bl, va_list ap)
return 1;
}
-void npc_unsetcells(struct npc_data* nd) {
+static void npc_unsetcells(struct npc_data *nd)
+{
int16 m, x, y, xs, ys;
int i,j, x0, x1, y0, y1;
@@ -3593,7 +4317,7 @@ void npc_unsetcells(struct npc_data* nd) {
map->foreachinarea( npc->unsetcells_sub, m, x0, y0, x1, y1, BL_NPC, nd->bl.id );
}
-void npc_movenpc(struct npc_data* nd, int16 x, int16 y)
+static void npc_movenpc(struct npc_data *nd, int16 x, int16 y)
{
int16 m;
nullpo_retv(nd);
@@ -3612,21 +4336,22 @@ void npc_movenpc(struct npc_data* nd, int16 x, int16 y)
///
/// @param nd Target npc
/// @param newname New display name
-void npc_setdisplayname(struct npc_data* nd, const char* newname)
+static void npc_setdisplayname(struct npc_data *nd, const char *newname)
{
nullpo_retv(nd);
nullpo_retv(newname);
safestrncpy(nd->name, newname, sizeof(nd->name));
if( map->list[nd->bl.m].users )
- clif->charnameack(0, &nd->bl);
+ clif->blname_ack(0, &nd->bl);
}
/// Changes the display class of the npc.
///
/// @param nd Target npc
/// @param class_ New display class
-void npc_setclass(struct npc_data* nd, short class_) {
+static void npc_setclass(struct npc_data *nd, int class_)
+{
nullpo_retv(nd);
if( nd->class_ == class_ )
@@ -3640,8 +4365,20 @@ void npc_setclass(struct npc_data* nd, short class_) {
clif->spawn(&nd->bl);// fade in
}
+static void npc_refresh(struct npc_data *nd)
+{
+ nullpo_retv(nd);
+
+ if (map->list[nd->bl.m].users) {
+ // using here CLR_TRICKDEAD because other flags show effects.
+ // probably need use other flag or other way to refresh npc.
+ clif->clearunit_area(&nd->bl, CLR_TRICKDEAD); // fade out
+ clif->spawn(&nd->bl); // fade in
+ }
+}
+
// @commands (script based)
-int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const char* message, const char* eventname)
+static int npc_do_atcmd_event(struct map_session_data *sd, const char *command, const char *message, const char *eventname)
{
struct event_data* ev = (struct event_data*)strdb_get(npc->ev_db, eventname);
struct npc_data *nd;
@@ -3728,7 +4465,7 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_function(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
+static const char *npc_parse_function(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
struct DBMap *func_db;
struct DBData old_data;
@@ -3770,25 +4507,30 @@ const char *npc_parse_function(const char *w1, const char *w2, const char *w3, c
struct script_code *oldscript = (struct script_code*)DB->data2ptr(&old_data);
ShowWarning("npc_parse_function: Overwriting user function [%s] in file '%s', line '%d'.\n", w3, filepath, strline(buffer,start-buffer));
script->free_vars(oldscript->local.vars);
- aFree(oldscript->script_buf);
+ VECTOR_CLEAR(oldscript->script_buf);
aFree(oldscript);
}
return end;
}
-/*==========================================
- * Parse Mob 1 - Parse mob list into each map
- * Parse Mob 2 - Actually Spawns Mob
- * [Wizputer]
- *------------------------------------------*/
-void npc_parse_mob2(struct spawn_data* mobspawn)
+/**
+ * Spawns a mob by using the passed spawn data. (Permanent mob spawns.)
+ * npc_parse_mob() - Parses mob list into each map.
+ * npc_parse_mob2() - Actually spawns mob.
+ *
+ * @param mobspawn The mobs spawn data.
+ *
+ * @author Wizputer
+ *
+ **/
+static void npc_parse_mob2(struct spawn_data *mobspawn)
{
- int i;
-
nullpo_retv(mobspawn);
- for( i = mobspawn->active; i < mobspawn->num; ++i ) {
- struct mob_data* md = mob->spawn_dataset(mobspawn);
+
+ for (int i = mobspawn->active; i < mobspawn->num; ++i) {
+ struct mob_data *md = mob->spawn_dataset(mobspawn, 0);
+
md->spawn = mobspawn;
md->spawn->active++;
mob->spawn(md);
@@ -3812,7 +4554,7 @@ void npc_parse_mob2(struct spawn_data* mobspawn)
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
+static const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
int num, class_, m,x,y,xs,ys, i,j;
int mob_lv = -1, ai = -1, size = -1;
@@ -3827,7 +4569,12 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const
memset(&mobspawn, 0, sizeof(struct spawn_data));
- mobspawn.state.boss = (strcmp(w2,"boss_monster") == 0 ? 1 : 0);
+ if (strcmp(w2, "boss_monster") == 0)
+ mobspawn.state.boss = BTYPE_MVP;
+ else if (strcmp(w2, "miniboss_monster") == 0)
+ mobspawn.state.boss = BTYPE_BOSS;
+ else
+ mobspawn.state.boss = BTYPE_NONE;
// w1=<map name>,<x>,<y>,<xs>,<ys>
// w3=<mob name>{,<mob level>}
@@ -3889,7 +4636,7 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const
mobspawn.num = (unsigned short)num;
mobspawn.active = 0;
- mobspawn.class_ = (short) class_;
+ mobspawn.class_ = class_;
mobspawn.x = (unsigned short)x;
mobspawn.y = (unsigned short)y;
mobspawn.xs = (signed short)xs;
@@ -3919,10 +4666,10 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const
}
//Use db names instead of the spawn file ones.
- if(battle_config.override_mob_names==1)
- strcpy(mobspawn.name,"--en--");
- else if (battle_config.override_mob_names==2)
- strcpy(mobspawn.name,"--ja--");
+ if (battle_config.override_mob_names == 1)
+ strcpy(mobspawn.name, DEFAULT_MOB_NAME);
+ else if (battle_config.override_mob_names == 2)
+ strcpy(mobspawn.name, DEFAULT_MOB_JNAME);
else
safestrncpy(mobspawn.name, mobname, sizeof(mobspawn.name));
@@ -3967,7 +4714,7 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const
// spawn / cache the new mobs
if( battle_config.dynamic_mobs && map->addmobtolist(data->m, data) >= 0 ) {
data->state.dynamic = true;
- npc_cache_mob += data->num;
+ npc->npc_cache_mob += data->num;
// check if target map has players
// (usually shouldn't occur when map server is just starting,
@@ -3978,10 +4725,10 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const
} else {
data->state.dynamic = false;
npc->parse_mob2(data);
- npc_delay_mob += data->num;
+ npc->npc_delay_mob += data->num;
}
- npc_mob++;
+ npc->npc_mob++;
return strchr(start,'\n');// continue
}
@@ -3993,7 +4740,7 @@ const char *npc_parse_mob(const char *w1, const char *w2, const char *w3, const
*
* @see npc_parse_mapflag
*/
-void npc_parse_unknown_mapflag(const char *name, const char *w3, const char *w4, const char* start, const char* buffer, const char* filepath, int *retval)
+static void npc_parse_unknown_mapflag(const char *name, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
ShowError("npc_parse_mapflag: unrecognized mapflag '%s' in file '%s', line '%d'.\n", w3, filepath, strline(buffer,start-buffer));
if (retval)
@@ -4025,7 +4772,7 @@ void npc_parse_unknown_mapflag(const char *name, const char *w3, const char *w4,
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
+static const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
int16 m;
char mapname[32];
@@ -4055,8 +4802,7 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
if (!strcmpi(w3, "nosave")) {
char savemap[32];
int savex, savey;
- if (state == 0)
- ; //Map flag disabled.
+ if (state == 0); //Map flag disabled.
else if (w4 && !strcmpi(w4, "SavePoint")) {
map->list[m].save.map = 0;
map->list[m].save.x = -1;
@@ -4108,6 +4854,11 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
ShowWarning("npc_parse_mapflag: You can't set PvP and GvG flags for the same map! Removing GvG flags from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
}
+ if (state && map->list[m].flag.cvc) {
+ map->list[m].flag.cvc = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and PvP flags for the same map! Removing CvC flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) *retval = EXIT_FAILURE;
+ }
if( state && map->list[m].flag.battleground ) {
map->list[m].flag.battleground = 0;
ShowWarning("npc_parse_mapflag: You can't set PvP and BattleGround flags for the same map! Removing BattleGround flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
@@ -4160,6 +4911,11 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
ShowWarning("npc_parse_mapflag: You can't set PvP and GvG flags for the same map! Removing PvP flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
}
+ if (state && map->list[m].flag.cvc) {
+ map->list[m].flag.cvc = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and GvG flags for the same map! Removing CvC flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) *retval = EXIT_FAILURE;
+ }
if( state && map->list[m].flag.battleground ) {
map->list[m].flag.battleground = 0;
ShowWarning("npc_parse_mapflag: You can't set GvG and BattleGround flags for the same map! Removing BattleGround flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
@@ -4202,11 +4958,47 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
ShowWarning("npc_parse_mapflag: You can't set GvG and BattleGround flags for the same map! Removing GvG flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
}
+ if (map->list[m].flag.cvc) {
+ map->list[m].flag.cvc = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and BattleGround flags for the same map! Removing CvC flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) *retval = EXIT_FAILURE;
+ }
if( state && (zone = strdb_get(map->zone_db, MAP_ZONE_BG_NAME)) != NULL && map->list[m].zone != zone ) {
map->zone_change(m,zone,start,buffer,filepath);
}
}
+ else if (!strcmpi(w3, "cvc")) {
+ struct map_zone_data *zone;
+
+ map->list[m].flag.cvc = state;
+ if (state && (map->list[m].flag.gvg || map->list[m].flag.gvg_dungeon || map->list[m].flag.gvg_castle)) {
+ map->list[m].flag.gvg = 0;
+ map->list[m].flag.gvg_dungeon = 0;
+ map->list[m].flag.gvg_castle = 0;
+ ShowWarning("npc_parse_mapflag: You can't set GvG and CvC flags for the same map! Removing GvG flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) {
+ *retval = EXIT_FAILURE;
+ }
+ }
+ if (state && map->list[m].flag.pvp) {
+ map->list[m].flag.pvp = 0;
+ ShowWarning("npc_parse_mapflag: You can't set PvP and CvC flags for the same map! Removing PvP flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) {
+ *retval = EXIT_FAILURE;
+ }
+ }
+ if (state && map->list[m].flag.battleground) {
+ map->list[m].flag.battleground = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and BattleGround flags for the same map! Removing BattleGround flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) {
+ *retval = EXIT_FAILURE;
+ }
+ }
+ if (state && (zone = strdb_get(map->zone_db, MAP_ZONE_CVC_NAME)) != NULL && map->list[m].zone != zone) {
+ map->zone_change(m, zone, start, buffer, filepath);
+ }
+ }
else if (!strcmpi(w3,"noexppenalty"))
map->list[m].flag.noexppenalty=state;
else if (!strcmpi(w3,"nozenypenalty"))
@@ -4284,6 +5076,8 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
map->list[m].flag.reset=state;
else if (!strcmpi(w3,"notomb"))
map->list[m].flag.notomb=state;
+ else if (!strcmpi(w3, "noautoloot"))
+ map->list[m].flag.noautoloot = state;
else if (!strcmpi(w3,"adjust_unit_duration")) {
int skill_id, k;
char skill_name[MAP_ZONE_MAPFLAG_LENGTH], modifier[MAP_ZONE_MAPFLAG_LENGTH];
@@ -4301,7 +5095,8 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
}
}
- if( modifier[0] == '\0' ) {
+ if (state == 0); //Map flag disabled.
+ else if (modifier[0] == '\0') {
ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_unit_duration' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
} else if( !( skill_id = skill->name2id(skill_name) ) || !skill->get_unit_id( skill->name2id(skill_name), 0) ) {
@@ -4360,7 +5155,8 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
}
}
- if( modifier[0] == '\0' ) {
+ if (state == 0); //Map flag disabled.
+ else if (modifier[0] == '\0') {
ShowWarning("npc_parse_mapflag: Missing 5th param for 'adjust_skill_damage' flag! removing flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
} else if( !( skill_id = skill->name2id(skill_name) ) ) {
@@ -4432,6 +5228,14 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
map->list[m].flag.nocashshop = (state) ? 1 : 0;
} else if (!strcmpi(w3,"noviewid")) {
map->list[m].flag.noviewid = (state) ? atoi(w4) : 0;
+ } else if (!strcmpi(w3, "pairship_startable")) {
+ map->list[m].flag.pairship_startable = (state) ? 1 : 0;
+ } else if (!strcmpi(w3, "pairship_endable")) {
+ map->list[m].flag.pairship_endable = (state) ? 1 : 0;
+ } else if (!strcmpi(w3, "nostorage")) {
+ map->list[m].flag.nostorage = (state) ? cap_value(atoi(w4), 1, 3) : 0;
+ } else if (!strcmpi(w3, "nogstorage")) {
+ map->list[m].flag.nogstorage = (state) ? cap_value(atoi(w4), 1, 3) : 0;
} else {
npc->parse_unknown_mapflag(mapname, w3, w4, start, buffer, filepath, retval);
}
@@ -4456,7 +5260,7 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
* (EXIT_FAILURE) status. May be NULL.
* @return A pointer to the advanced buffer position.
*/
-const char *npc_parse_unknown_object(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
+static const char *npc_parse_unknown_object(const char *w1, const char *w2, const char *w3, const char *w4, const char *start, const char *buffer, const char *filepath, int *retval)
{
nullpo_retr(start, retval);
ShowError("npc_parsesrcfile: Unable to parse, probably a missing or extra TAB in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
@@ -4474,7 +5278,8 @@ const char *npc_parse_unknown_object(const char *w1, const char *w2, const char
* @retval EXIT_SUCCESS if filepath was loaded correctly.
* @retval EXIT_FAILURE if there were errors/warnings when loading filepath.
*/
-int npc_parsesrcfile(const char* filepath, bool runOnInit) {
+static int npc_parsesrcfile(const char *filepath, bool runOnInit)
+{
int success = EXIT_SUCCESS;
int16 m, x, y;
int lines = 0;
@@ -4644,7 +5449,7 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit) {
{
p = npc->parse_duplicate(w1,w2,w3,w4, p, buffer, filepath, (runOnInit?NPO_ONINIT:NPO_NONE), &success);
}
- else if( (strcmp(w2,"monster") == 0 || strcmp(w2,"boss_monster") == 0) )
+ else if (strcmp(w2,"monster") == 0 || strcmp(w2,"boss_monster") == 0 || strcmp(w2,"miniboss_monster") == 0)
{
p = npc->parse_mob(w1, w2, w3, w4, p, buffer, filepath, &success);
}
@@ -4662,7 +5467,7 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit) {
return success;
}
-int npc_script_event(struct map_session_data* sd, enum npce_event type)
+static int npc_script_event(struct map_session_data *sd, enum npce_event type)
{
int i;
if (type == NPCE_MAX)
@@ -4676,7 +5481,7 @@ int npc_script_event(struct map_session_data* sd, enum npce_event type)
return i;
}
-void npc_read_event_script(void)
+static void npc_read_event_script(void)
{
int i;
struct {
@@ -4739,7 +5544,7 @@ void npc_read_event_script(void)
/**
* @see DBApply
*/
-int npc_path_db_clear_sub(union DBKey key, struct DBData *data, va_list args)
+static int npc_path_db_clear_sub(union DBKey key, struct DBData *data, va_list args)
{
struct npc_path_data *npd = DB->data2ptr(data);
nullpo_ret(npd);
@@ -4751,7 +5556,7 @@ int npc_path_db_clear_sub(union DBKey key, struct DBData *data, va_list args)
/**
* @see DBApply
*/
-int npc_ev_label_db_clear_sub(union DBKey key, struct DBData *data, va_list args)
+static int npc_ev_label_db_clear_sub(union DBKey key, struct DBData *data, va_list args)
{
struct linkdb_node **label_linkdb = DB->data2ptr(data);
linkdb_final(label_linkdb); // linked data (struct event_data*) is freed when clearing ev_db
@@ -4762,7 +5567,8 @@ int npc_ev_label_db_clear_sub(union DBKey key, struct DBData *data, va_list args
* Main npc file processing
* @param npc_min Minimum npc id - used to know how many NPCs were loaded
**/
-void npc_process_files( int npc_min ) {
+static void npc_process_files(int npc_min)
+{
struct npc_src_list *file; // Current file
ShowStatus("Loading NPCs...\r");
@@ -4778,133 +5584,153 @@ void npc_process_files( int npc_min ) {
"\t-'"CL_WHITE"%d"CL_RESET"' Spawn sets\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs Cached\n"
"\t-'"CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
- npc_id - npc_min, npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+ npc->npc_id - npc_min, npc->npc_warp, npc->npc_shop, npc->npc_script, npc->npc_mob, npc->npc_cache_mob, npc->npc_delay_mob);
}
-//Clear then reload npcs files
-int npc_reload(void) {
- int npc_new_min = npc_id;
- struct s_mapiterator* iter;
- struct block_list* bl;
-
- if (map->retval == EXIT_FAILURE)
- map->retval = EXIT_SUCCESS; // Clear return status in case something failed before.
-
- /* clear guild flag cache */
- guild->flags_clear();
+/**
+ * Clears and then reloads all NPC files.
+ *
+ * @return Always 0.
+ *
+ **/
+static int npc_reload(void)
+{
+ if (map->retval == EXIT_FAILURE) /// Clear return status in case something failed before.
+ map->retval = EXIT_SUCCESS;
+ guild->flags_clear(); /// Clear guild flag cache.
npc->path_db->clear(npc->path_db, npc->path_db_clear_sub);
-
db_clear(npc->name_db);
db_clear(npc->ev_db);
npc->ev_label_db->clear(npc->ev_label_db, npc->ev_label_db_clear_sub);
+ npc->npc_last_npd = NULL;
+ npc->npc_last_path = NULL;
+ npc->npc_last_ref = NULL;
+
+ const int npc_new_min = npc->npc_id;
+ struct s_mapiterator *iter = mapit_geteachiddb();
+
+ /** Remove all NPCs/mobs. [Skotlex] **/
+ for (struct block_list *bl = mapit->first(iter); mapit->exists(iter); bl = mapit->next(iter)) {
+ switch (bl->type) {
+ case BL_NPC:
+ if (bl->id != npc->fake_nd->bl.id) /// Don't remove fake_nd.
+ npc->unload(BL_UCAST(BL_NPC, bl), false, false);
- npc_last_npd = NULL;
- npc_last_path = NULL;
- npc_last_ref = NULL;
-
- //Remove all npcs/mobs. [Skotlex]
- iter = mapit_geteachiddb();
- for (bl = mapit->first(iter); mapit->exists(iter); bl = mapit->next(iter)) {
- switch(bl->type) {
- case BL_NPC:
- if( bl->id != npc->fake_nd->bl.id )// don't remove fake_nd
- npc->unload(BL_UCAST(BL_NPC, bl), false);
- break;
- case BL_MOB:
- unit->free(bl,CLR_OUTSIGHT);
- break;
+ break;
+ case BL_MOB:
+ unit->free(bl, CLR_OUTSIGHT);
+ break;
+ default:
+ break;
}
}
+
mapit->free(iter);
- if(battle_config.dynamic_mobs) {// dynamic check by [random]
- int16 m;
- for (m = 0; m < map->count; m++) {
- int16 i;
- for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++) {
+ if (battle_config.dynamic_mobs) { /// Dynamic check. [random]
+ for (int m = 0; m < map->count; m++) {
+ for (int i = 0; i < MAX_MOB_LIST_PER_MAP; i++) {
if (map->list[m].moblist[i] != NULL) {
aFree(map->list[m].moblist[i]);
map->list[m].moblist[i] = NULL;
}
- if( map->list[m].mob_delete_timer != INVALID_TIMER )
- { // Mobs were removed anyway,so delete the timer [Inkfish]
+
+ if (map->list[m].mob_delete_timer != INVALID_TIMER) { /// Mobs were removed anyway, so delete the timer. [Inkfish]
timer->delete(map->list[m].mob_delete_timer, map->removemobs_timer);
map->list[m].mob_delete_timer = INVALID_TIMER;
}
}
+
if (map->list[m].npc_num > 0)
- ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map->list[m].npc_num, map->list[m].name);
+ ShowWarning("npc_reload: %d NPCs weren't removed from map %s!\n",
+ map->list[m].npc_num, map->list[m].name);
}
}
- // clear mob spawn lookup index
mob->clear_spawninfo();
-
- npc_warp = npc_shop = npc_script = 0;
- npc_mob = npc_cache_mob = npc_delay_mob = 0;
-
- // reset mapflags
+ npc->npc_warp = 0;
+ npc->npc_shop = 0;
+ npc->npc_script = 0;
+ npc->npc_mob = 0;
+ npc->npc_cache_mob = 0;
+ npc->npc_delay_mob = 0;
+ map->zone_reload();
map->flags_init();
-
- // Reprocess npc files and reload constants
itemdb->name_constants();
- npc_process_files( npc_new_min );
-
+ clan->set_constants();
+ npc_process_files(npc_new_min);
instance->reload();
-
map->zone_init();
-
- npc->motd = npc->name2id("HerculesMOTD"); /* [Ind/Hercules] */
-
- //Re-read the NPC Script Events cache.
+ npc->motd = npc->name2id("HerculesMOTD"); /// [Ind/Hercules]
npc->read_event_script();
- // Execute main initialisation events
- // The correct initialisation order is:
- // OnInit -> OnInterIfInit -> OnInterIfInitOnce -> OnAgitInit -> OnAgitInit2
- npc->event_do_oninit( true );
+ /**
+ * Execute main initialization events
+ * The correct initialization order is:
+ * OnInit -> OnInterIfInit -> OnInterIfInitOnce -> OnAgitInit -> OnAgitInit2
+ *
+ **/
+ npc->event_do_oninit(true);
+
npc->market_fromsql();
- // Execute rest of the startup events if connected to char-server. [Lance]
- // Executed when connection is established with char-server in chrif_connectack
- if( !intif->CheckForCharServer() ) {
- ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc->event_doall("OnInterIfInit"));
- ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc->event_doall("OnInterIfInitOnce"));
- }
- // Refresh guild castle flags on both woe setups
- // These events are only executed after receiving castle information from char-server
+ npc->barter_fromsql();
+ npc->expanded_barter_fromsql();
+
+ /*
+ * Execute rest of the startup events if connected to char-server. [Lance]
+ * Executed when connection is established with char-server in chrif_connectack().
+ */
+ if (intif->CheckForCharServer() == 0) {
+ ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n",
+ npc->event_doall("OnInterIfInit"));
+ ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n",
+ npc->event_doall("OnInterIfInitOnce"));
+ }
+
+ /*
+ * Refresh guild castle flags on both WoE setups.
+ * These events are only executed after receiving castle information from char-server.
+ */
npc->event_doall("OnAgitInit");
npc->event_doall("OnAgitInit2");
return 0;
}
-//Unload all npc in the given file
-bool npc_unloadfile(const char *filepath)
+/**
+ * Unloads all NPCs in the given file.
+ *
+ * @param filepath Path to the file which should be unloaded.
+ * @param unload_mobs If true, mobs spawned by NPCs in the file will be removed.
+ * @return true if at least one NPC was unloaded, otherwise false.
+ *
+ **/
+static bool npc_unloadfile(const char *filepath, bool unload_mobs)
{
+ nullpo_retr(false, filepath);
+
struct DBIterator *iter = db_iterator(npc->name_db);
- struct npc_data* nd = NULL;
bool found = false;
- nullpo_retr(false, filepath);
-
- for( nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter) ) {
- if( nd->path && strcasecmp(nd->path,filepath) == 0 ) { // FIXME: This can break in case-sensitive file systems
+ for (struct npc_data *nd = dbi_first(iter); dbi_exists(iter); nd = dbi_next(iter)) {
+ if (nd->path != NULL && strcasecmp(nd->path, filepath) == 0) { // FIXME: This can break in case-sensitive file systems.
found = true;
- npc->unload_duplicates(nd);/* unload any npcs which could duplicate this but be in a different file */
- npc->unload(nd, true);
+ npc->unload_duplicates(nd, unload_mobs); /// Unload any NPC which could duplicate this but be in a different file.
+ npc->unload(nd, true, unload_mobs);
}
}
dbi_destroy(iter);
- if( found ) /* refresh event cache */
+ if (found) /// Refresh event cache.
npc->read_event_script();
return found;
}
-void do_clear_npc(void) {
+static void do_clear_npc(void)
+{
db_clear(npc->name_db);
db_clear(npc->ev_db);
npc->ev_label_db->clear(npc->ev_label_db, npc->ev_label_db_clear_sub);
@@ -4913,7 +5739,8 @@ void do_clear_npc(void) {
/*==========================================
* Destructor
*------------------------------------------*/
-int do_final_npc(void) {
+static int do_final_npc(void)
+{
db_destroy(npc->ev_db);
npc->ev_label_db->destroy(npc->ev_label_db, npc->ev_label_db_clear_sub);
db_destroy(npc->name_db);
@@ -4924,7 +5751,8 @@ int do_final_npc(void) {
return 0;
}
-void npc_debug_warps_sub(struct npc_data* nd) {
+static void npc_debug_warps_sub(struct npc_data *nd)
+{
int16 m;
nullpo_retv(nd);
@@ -4952,49 +5780,57 @@ void npc_debug_warps_sub(struct npc_data* nd) {
}
}
-static void npc_debug_warps(void) {
+static void npc_debug_warps(void)
+{
int16 m, i;
for (m = 0; m < map->count; m++)
for (i = 0; i < map->list[m].npc_num; i++)
npc->debug_warps_sub(map->list[m].npc[i]);
}
+static void npc_questinfo_clear(struct npc_data *nd)
+{
+ nullpo_retv(nd);
+
+ for (int i = 0; i < VECTOR_LENGTH(nd->qi_data); i++) {
+ struct questinfo *qi = &VECTOR_INDEX(nd->qi_data, i);
+ VECTOR_CLEAR(qi->items);
+ VECTOR_CLEAR(qi->quest_requirement);
+ }
+ VECTOR_CLEAR(nd->qi_data);
+}
+
/*==========================================
* npc initialization
*------------------------------------------*/
-int do_init_npc(bool minimal) {
+static int do_init_npc(bool minimal)
+{
int i;
- memset(&npc->base_ud, 0, sizeof( struct unit_data) );
- npc->base_ud.bl = NULL;
- npc->base_ud.walktimer = INVALID_TIMER;
- npc->base_ud.skilltimer = INVALID_TIMER;
- npc->base_ud.attacktimer = INVALID_TIMER;
- npc->base_ud.attackabletime =
- npc->base_ud.canact_tick =
- npc->base_ud.canmove_tick = timer->gettick();
+ unit->init_ud(&npc->base_ud);
+ npc->base_ud.bl = NULL;
//Stock view data for normal npcs.
memset(&npc_viewdb, 0, sizeof(npc_viewdb));
- npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here.
+ npc_viewdb[0].class = INVISIBLE_CLASS; //Invisible class is stored here.
for( i = 1; i < MAX_NPC_CLASS; i++ )
- npc_viewdb[i].class_ = i;
+ npc_viewdb[i].class = i;
for( i = MAX_NPC_CLASS2_START; i < MAX_NPC_CLASS2_END; i++ )
- npc_viewdb2[i - MAX_NPC_CLASS2_START].class_ = i;
-
+ npc_viewdb2[i - MAX_NPC_CLASS2_START].class = i;
npc->ev_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, EVENT_NAME_LENGTH);
npc->ev_label_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, NAME_LENGTH);
npc->name_db = strdb_alloc(DB_OPT_BASE, NAME_LENGTH);
npc->path_db = strdb_alloc(DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA, 0);
- npc_last_npd = NULL;
- npc_last_path = NULL;
- npc_last_ref = NULL;
+ npc->npc_last_npd = NULL;
+ npc->npc_last_path = NULL;
+ npc->npc_last_ref = NULL;
// Should be loaded before npc processing, otherwise labels could overwrite constant values
// and lead to undefined behavior [Panikon]
itemdb->name_constants();
+ clan->set_constants();
if (!minimal) {
npc->timer_event_ers = ers_new(sizeof(struct timer_event_data),"clif.c::timer_event_ers",ERS_OPT_NONE);
@@ -5019,12 +5855,6 @@ int do_init_npc(bool minimal) {
timer->add_func_list(npc->timerevent,"npc_timerevent");
}
- if( script->lang_export_fp ) {
- ShowInfo("Lang exported to '%s'\n",script->lang_export_file);
- fclose(script->lang_export_fp);
- script->lang_export_fp = NULL;
- }
-
// Init dummy NPC
CREATE(npc->fake_nd, struct npc_data, 1);
npc->fake_nd->bl.m = -1;
@@ -5034,7 +5864,7 @@ int do_init_npc(bool minimal) {
strcpy(npc->fake_nd->name,"FAKE_NPC");
memcpy(npc->fake_nd->exname, npc->fake_nd->name, 9);
- npc_script++;
+ npc->npc_script++;
npc->fake_nd->bl.type = BL_NPC;
npc->fake_nd->subtype = SCRIPT;
@@ -5045,9 +5875,21 @@ int do_init_npc(bool minimal) {
return 0;
}
-void npc_defaults(void) {
+void npc_defaults(void)
+{
npc = &npc_s;
+ npc->npc_id = START_NPC_NUM;
+ npc->npc_warp = 0;
+ npc->npc_shop = 0;
+ npc->npc_script = 0;
+ npc->npc_mob = 0;
+ npc->npc_delay_mob = 0;
+ npc->npc_cache_mob = 0;
+ npc->npc_last_path = NULL;
+ npc->npc_last_ref = NULL;
+ npc->npc_last_npd = NULL;
+
npc->motd = NULL;
npc->ev_db = NULL;
npc->ev_label_db = NULL;
@@ -5114,6 +5956,7 @@ void npc_defaults(void) {
npc->unload_ev_label = npc_unload_ev_label;
npc->unload_dup_sub = npc_unload_dup_sub;
npc->unload_duplicates = npc_unload_duplicates;
+ npc->unload_mob = npc_unload_mob;
npc->unload = npc_unload;
npc->clearsrcfile = npc_clearsrcfile;
npc->addsrcfile = npc_addsrcfile;
@@ -5166,10 +6009,22 @@ void npc_defaults(void) {
npc->trader_pay = npc_trader_pay;
npc->trader_update = npc_trader_update;
npc->market_buylist = npc_market_buylist;
+ npc->barter_buylist = npc_barter_buylist;
+ npc->expanded_barter_buylist = npc_expanded_barter_buylist;
npc->trader_open = npc_trader_open;
npc->market_fromsql = npc_market_fromsql;
npc->market_tosql = npc_market_tosql;
npc->market_delfromsql = npc_market_delfromsql;
npc->market_delfromsql_sub = npc_market_delfromsql_sub;
+ npc->barter_fromsql = npc_barter_fromsql;
+ npc->barter_tosql = npc_barter_tosql;
+ npc->barter_delfromsql = npc_barter_delfromsql;
+ npc->barter_delfromsql_sub = npc_barter_delfromsql_sub;
+ npc->expanded_barter_fromsql = npc_expanded_barter_fromsql;
+ npc->expanded_barter_tosql = npc_expanded_barter_tosql;
+ npc->expanded_barter_delfromsql = npc_expanded_barter_delfromsql;
+ npc->expanded_barter_delfromsql_sub = npc_expanded_barter_delfromsql_sub;
npc->db_checkid = npc_db_checkid;
+ npc->refresh = npc_refresh;
+ npc->questinfo_clear = npc_questinfo_clear;
}