diff options
Diffstat (limited to 'world/map/npc')
-rw-r--r-- | world/map/npc/functions/teleport_manager.txt | 589 | ||||
-rw-r--r-- | world/map/npc/scripts.conf | 1 |
2 files changed, 590 insertions, 0 deletions
diff --git a/world/map/npc/functions/teleport_manager.txt b/world/map/npc/functions/teleport_manager.txt new file mode 100644 index 00000000..6ef24d5b --- /dev/null +++ b/world/map/npc/functions/teleport_manager.txt @@ -0,0 +1,589 @@ +// GateBuilders toolkit aka "arbitrary teleports". +// GateBuilders toolkit aka "arbitrary teleports". +// Author: Hello=). Credits go to Freeyorp and HoraK for some insights. + +// This lacks limits of previously existing teleport systems. +// * New teleports can be instatiated as needed, running side by side. +// * Teleports can have finite lifetime, disappearing themself. +// * Teleports can be removed, leaving no side effects. +// * No leftovers or side effects after teleport removed. +// * Up to about 100 teleports can coexist. +// * Flexible condition check based on cookie. If some gate got cookie set +// then it only lets players with matching variable to pass. +// * Aspects like timeout, FX and their timing, etc can be customized. +// * Teleports meant to be registered in TeleportManager. However, its +// possible to create "unmanaged" teleports (e.g. for spells). +// * In future it can allow custom hooks/checks on teleporters (planned) + +// Technically, TeleportManager NPC keeps teleports as array of integers. +// with structure .teleports[.index] = Teleport's NPC ID. +// Up to 100 teleports allowed (.index = 1..100, .index = 0 is unused). +// if .teleports[.index] = 0 -> empty/reusable slot. +// All teleport specific data stored in puppet NPC itself. +// Teleport NPCs are puppets of TeleportManager and inherit its code. + +// Attempt will be made to make it reasonably robust. That is, +// * Try to handle usage bugs/bogus data/etc. +// * No actions causing big server side fallouts. +// * No out of bounds, nonexistent NPC references, etc. + +// Teleports have the following variables (some are set on teleport_create) +// .srcmap$ - source map of teleport's npc +// .src_x - source map of teleport's npc, X coord +// .src_y - source map of teleport's npc, Y coord +// .dstmap$ - destination map of teleport's npc +// .dst_x - destination map of teleport's npc, X coord +// .dst_y - destination map of teleport's npc, Y coord +// .lifetime - teleport's lifetime, in ms. 0 = permanent (until server restart) +// .cookie - if set, only teleport players with teleport_cookie == NPC's .cookie +// .fx, - FX effect to play on teleport. +// .fx_time, - Time FX allowed to play before teleport actually happens. +// .IS_MANAGER - Indicates TeleportManager NPC. Puppets wouldnst have that. +// .managed - Indicates its teleport managed by TeleportManager. Its possible to +// create unmanaged teleports, e.g. transient teleports by spells. +// .teleport - Set to 1 to be able to distinguish teleport NPCs from other NPCs +// .info$ - Description, shown to user when clicking NPC +// .inactive - set to disable OnTouch reaction. Useful for staged shutdown or +// e.g. temporarily deactivating teleport for whatever reason. + +// Key functcions: teleport_add and teleport_del. Rest are "helpers". + +// Main NPC code, runs on both TeleportManager and teleport puppets. +// Do not rename NPC: functions below rely on NPC name. +-|script|TeleportManager|32767 +{ + message strcharinfo(0), .info$; // Display info about teleport, var allows message change. + end; + +// Manager code below - puppets dont use OnCmd* events +// Invoked when someone adds teleport via @teleportadd +OnCmdAdd: + debugmes "OnAdd called"; + if (call("teleport_access_check")) end; // Not allowed to use this. + set .@idx, call("teleport_find_slot", 0); // Try to find empty slot (containing 0) + if (.@idx <= 0) goto L_AddNoRoom; // No empty slots? NB: teleports[0] not used. + if (call ("teleportadd_parseargs") <= 0) goto L_AddArgsFail; // @VARs like @POS_X, @NAME$, etc set by teleportadd_parseargs() +// Create actual teleport NPC ------- src map - src x src y --- name - NPC ---- Sz Sz dst map - dst x - dst y -- time - cook managed? + set .@tp, call("teleport_create", getmap(), @POS_X, @POS_Y, @NAME$, @NPCSPRITE, 0, 0, @DSTMAP$, @DST_X, @DST_Y, @TIMEOUT, 0, 1); + if (.@tp <= 0) goto L_AddPuppetFail; // Has puppet() failed? + set .teleports[.@idx], .@tp; // Store NPC ID -> .teleports[.@idx] slot. Access safe: teleport_find_slot() == 1..100 + gmlog strcharinfo(0) + " accessed TeleportManager: @teleportadd " + @args$; + wgm strcharinfo(0) + " accessed TeleportManager: @teleportadd " + @args$; + message strcharinfo(0), "[TeleportManager] : Added: [" + .@idx + "] " + + (get(.srcmap$, .@tp)) + " " + (get(.src_x, .@tp)) + "," + (get(.src_y, .@tp)) + + " -> " + (get(.dstmap$, .@tp)) + " " + (get(.dst_x, .@tp)) + "," + (get(.dst_y, .@tp)) + + " lifetime:" + ((get(.lifetime, .@tp)) / 1000) + "s, Name:" + strnpcinfo(0, .@tp) + " (" + .@tp + ")"; + debugmes "OnAdd: npc="+.@tp+" idx="+.@idx; + end; + +L_AddNoRoom: + message strcharinfo(0), "[TeleportManager] : Too many teleports open, max 100 tracked teleports"; + message strcharinfo(0), "[TeleportManager] : use @teleportlist to list and @teleportdel to remove some."; + end; + +L_AddArgsFail: + if (@DSTMAP$ != "help") message strcharinfo(0), "[TeleportManager] : try @teleportadd help or @teleporthelp"; + end; + +L_AddPuppetFail: + message strcharinfo(0), "[TeleportManager] : failed to add teleport, check params. Duplicate NPC name maybe?"; + end; + +// Invoked when someone removes teleport via @teleportdel +OnCmdDel: + debugmes "OnDel called"; + if (call("teleport_access_check")) end; // Not allowed to use this. + callfunc "argv_splitter"; + if (@argv[0] <= 0) goto L_DelFail; + if (call("teleport_delete", @argv[0]) <= 0) goto L_DelNotFound; + message strcharinfo(0), "[TeleportManager] : requested teleport ID [" + @argv[0] + "] to terminate"; + gmlog strcharinfo(0) + " accessed TeleportManager: @teleportdel " + @args$; + wgm strcharinfo(0) + " accessed TeleportManager: @teleportdel " + @args$; + end; + +L_DelNotFound: + message strcharinfo(0), "[TeleportManager] : teleport ID [" + @argv[0] + "] not found."; + end; + +L_DelFail: + message strcharinfo(0), "[TeleportManager] : Bad parameters."; + callfunc("teleport_help_del"); + end; + +// Invoked when someone lists teleports via @teleportlist +OnCmdList: + debugmes "OnList called"; + if (call("teleport_access_check")) end; // Not allowed to use this. + callfunc("teleports_list"); + gmlog strcharinfo(0) + " accessed TeleportManager: @teleporlist" + @args$; + end; + +// Invoked when someone requests help via @teleporthelp +// (doesn't needs access checks - does nothing dangerous) +OnCmdHelp: + debugmes "OnHelp called"; + message strcharinfo(0), "[TeleportManager] : Commands: @teleportadd @teleportdel @teleportlist @teleporthelp"; + message strcharinfo(0), "[TeleportManager] : @teleportlist - display list of active teleports"; + callfunc("teleport_help_del"); + callfunc("teleport_help_add"); + end; + +// Puppets (teleportation pads) logic below. +// Invoked when player steps on npc. Manager NPC ignores this. +OnTouch: + debugmes "OnTouch"; + if (.IS_MANAGER) end; // Manager NPC isnt teleport -> no interaction. + if (.inactive) end; // If gate deactivated -> no interaction. + if ((.cookie) && (teleport_cookie != .cookie)) goto L_CantPass; + set teleport_cookie, 0; // Clear teleport cookie of player on teleport use. + sc_start SC_SLOWMOVE, .fx_time+100, 100000; // Slow player temporarily to avoid movement VS warp DCs + addtimer .fx_time, strnpcinfo(0)+"::OnTeleport"; // time before teleporting away + misceffect .fx; // Default .fx set in teleport_add, other code can override it. + end; +L_CantPass: + message strcharinfo(0), .cantpass$; // NPC var allows to change message. + end; // "keyed" teleport and player didnt had proper cookie. + +// Teleportation timer event (attached to player). Queued by OnTouch. +OnTeleport: + debugmes "OnTeleport -> " + .dstmap$ + " X=" + .dst_x + " Y=" + .dst_y; + warp .dstmap$, .dst_x, .dst_y; + end; + +// Invoked on timed teleport's NPC timer expired. +OnTeleportExpired: + debugmes "OnTeleportExpired"; + set .inactive, 1; // Flag shutdown so OnTouch ignores incoming players. + addnpctimer (.fx_time + 1000), strnpcinfo(0)+"::OnTeleportShutdown"; // Give time to in-flight players to teleport. + end; + +// Does actual teleport shutdown. +OnTeleportShutdown: + debugmes "OnTeleportShutdown"; + void call("teleport_mgr_clean"); // Cleanups adequately both "managed" and "unmanaged" teleports. + if !(.IS_MANAGER) destroy; // Dont ever try to destroy manager npc. + end; + +OnInit: + set .IS_MANAGER, 1; // Only Manager NPC would have this, puppets wouldnt. + set .info$, "TeleportManager : This thing is unlike anything you've seen before"; // Manager's NPC message on click. + registercmd "@teleportadd", "TeleportManager::OnCmdAdd"; + registercmd "@teleportdel", "TeleportManager::OnCmdDel"; + registercmd "@teleportlist", "TeleportManager::OnCmdList"; + registercmd "@teleporthelp", "TeleportManager::OnCmdHelp"; + registercmd "@teleport", "TeleportManager::OnCmdHelp"; + end; +} + +// PUBLIC API. Creates requested teleport NPC. Arg 0..6 are like puppet(). +// This function designed to run in ANY context, whether RID attached or not. +// It creates puppet + setups all relevant data in created NPC. +// arg(0): Teleport's map. +// arg(1): Teleport's X +// arg(2): Teleport's Y +// arg(3): Teleport's NPC label +// arg(4): Teleport's NPC sprite ID +// arg(5): Teleport's X size +// arg(6): Teleport's Y size +// arg(7): Destination map name. +// arg(8): Destination X +// arg(9): Destination Y +// arg(10):Teleport lifetime ( > 0 or -1 = indefinite) +// arg(11):Teleport magic cookie, 0 = everyone allowed, otherwise checks match. +// arg(12):Whether teleport managed, 0 = unmanaged, != 0 means it is. +// Return: On success: NPC ID of teleport created. +// On failure: 0 if puppet failed, -1 if arg check failed. +function|script|teleport_create +{ + debugmes "teleport_create -> Enter"; + set .@map$, getarg(0, ""); // Get args (+failsafe defailts) + set .@x, getarg(1, -10); // Get args (+failsafe defailts) + set .@y, getarg(2, -10); // Get args (+failsafe defailts) + set .@name$, getarg(3, ""); // Get args (+failsafe defailts) + set .@sprite, getarg(4, -10); // Get args (+failsafe defailts) + set .@xsz, getarg(5, -10); // Get args (+failsafe defailts) + set .@ysz, getarg(6, -10); // Get args (+failsafe defailts) + set .@dstmap$, getarg(7, -10); // Get args (+failsafe defailts) + set .@dst_x, getarg(8, -10); // Get args (+failsafe defailts) + set .@dst_y, getarg(9, -10); // Get args (+failsafe defailts) + set .@lifetime, getarg(10, -10); // Get args (+failsafe defailts) + set .@cookie, getarg(11, -10); // Get args (+failsafe defailts) + set .@managed, getarg(12, -10); // Get args (+failsafe defailts) + set .@res, -1; // Validate what caller gave + if !(call("teleport_map_valid", .@map$ )) goto L_Fail; // Validate src map$ + if !(call("teleport_map_valid", .@dstmap$)) goto L_Fail; // Validate dst map$ + if ((.@x < 0) || (.@y < 0)) goto L_Fail; // SRC X/Y cant be < 0 + if ((.@dst_x < 0) || (.@dst_y < 0)) goto L_Fail; // DST X/Y cant be < 0 + if ((.@xsz < 0) || (.@ysz < 0)) goto L_Fail; // NPC X/Y size cant be <= 0 + if (.@name$ == "") goto L_Fail; // NPC name cant be empty + if (.@sprite <= 0) goto L_Fail; // NPC sprite cant be <= 0 + if (.@lifetime < -1 || (.@lifetime) == 0) goto L_Fail; // Lifetime either > 0 or -1 = infinite + if ((.@cookie < 0) || (.@managed < 0)) goto L_Fail; // Cookie and managed cant be < 0 + set .@res, puppet(.@map$, .@x, .@y, .@name$, .@sprite, .@xsz, .@ysz); // instatiate teleport NPC + if (.@res <= 0) goto L_Fail; + //NPC's var value NPC ID -- teleport defaults + set .srcmap$, .@map$, .@res; // set .srcmap$ of NPC + set .src_x, .@x, .@res; // set .src_x of NPC + set .src_y, .@y, .@res; // set .src_y of NPC + set .dstmap$, .@dstmap$, .@res; // set .dstmap$ of NPC + set .dst_x, .@dst_x, .@res; // set .dst_x of NPC + set .dst_y, .@dst_y, .@res; // set .dst_y of NPC + set .lifetime, .@lifetime, .@res; // set .lifetime of NPC + set .cookie, .@cookie, .@res; // set .cookie of NPC + set .fx, 41, .@res; // set default teleport FX + set .fx_time, 350, .@res; // set default FX time (warp delay) + set .managed, .@managed, .@res; // If > 0, npc managed by TeleportManager (unmanaged temp TPs can be e.g. spells) + set .teleport, 1, .@res; // All teleports created by teleport_create have this. + set .info$, "Teleport : strange structure of unknown origins", .@res; // Default on-click message. + set .cantpass$, "Teleport : structure seems to ignore you", .@res; // Default "can't pass" message. + // If timeout requested, set up teardown timer. + if (.@lifetime > 0) addnpctimer .@lifetime, .@name$+"::OnTeleportExpired"; + debugmes "teleport_create <- Puppet: Map="+.@map$+" X="+.@x+" Y="+.@y+" Nm="+.@name$+" SpID="+.@sprite+" xsz="+.@xsz+" ysz="+.@ysz+" .@res="+.@res; + return .@res; + +L_Fail: + debugmes "arg0:"+getarg(0)+" arg1:"+getarg(1)+" arg2:"+getarg(2)+" arg3:"+getarg(3)+" arg4:"+getarg(4)+" arg5:"+getarg(5)+" arg6:"+getarg(6)+" arg7:"+getarg(7)+" arg8:"+getarg(8)+" arg9:"+getarg(9)+" arg10:"+getarg(10)+" arg11:"+getarg(11)+" arg12:"+getarg(12); + debugmes "teleport_create <- Leave, call failed ["+.@res+"]"; + return .@res; +} + +// PUBLIC API. This function deletes teleport. +// This function designed to run in ANY context, whether RID attached or not. +// Teleport removed by NPC destroy + setting its .teleports[.index] = 0. +// Inputs: arg[0] is either slot index (1..100) or NPC ID to remove. +// Return: 1 on success, <= 0 on failure. +function|script|teleport_delete +{ + debugmes "teleports_delete -> enter"; + set .@npctodel, getarg(0, -1); // This is either slot # or NPC ID + if (.@npctodel <= 0) goto L_Error; + if (.@npctodel > 100) goto L_GotNpcId; // If > 100 assume its NPC ID, not slot. + set .@npctodel, call("teleport_get_slot_val", .@npctodel); // Get NPC id from teleport manager slot. + if (.@npctodel <= 0) goto L_Error; // Failed to get NPC ID? + goto L_GotNpcId; + +L_GotNpcId: + if !(get(.teleport, .@npctodel)) goto L_Error; // Sanity check its teleport NPC indeed + donpcevent strnpcinfo(0, .@npctodel)+"::OnTeleportShutdown"; // Request teleport NPC to perform shutdown. + return 1; + +L_Error: + debugmes "teleports_delete <- leave, failed"; + return 0; +} + +// This function prints teleports known to TeleportManager. +// This function MUST run in player context with RID attached to send messages. +// Inputs: nothing, all data taken from TeleportManager NPC. +// Return: nothing, just prints info as it iterates manager's slots. +function|script|teleports_list +{ + debugmes "teleports_list -> enter"; + set .@idx, 0; + freeloop 1; // Loops via 100 slots -> can time out. + message strcharinfo(0), "[TeleportManager] : ---- Active teleports ----"; + goto L_NextSlot; + +L_NextSlot: + set .@idx, (.@idx+1); + if (.@idx > 100) goto L_Done; // Iterated whole array. + set .@npc, call("teleport_get_slot_val", .@idx); + debugmes "teleport_list idx=" + .@idx + " val=" + .@npc; + if (.@npc < 0) goto L_Error; // Abort iteration on error and report it. + if (.@npc > 0) goto L_PrintSlot; // Print slot data. + goto L_NextSlot; // .@npc == 0 // just iterate to next slot + +L_PrintSlot: + if !(get(.managed, .@npc)) goto L_Error; // Sanity check its really "managed teleport" NPC + debugmes "teleport_list: after managed check"; + // Display info about teleport, data taken from NPC. + message strcharinfo(0), "[TeleportManager] : [" + .@idx + "] " + + (get(.srcmap$, .@npc)) + " " + (get(.src_x, .@npc)) + "," + (get(.src_y, .@npc)) + + " -> " + (get(.dstmap$, .@npc)) + " " + (get(.dst_x, .@npc)) + "," + (get(.dst_y, .@npc)) + + " lifetime:" + ((get(.lifetime, .@npc)) / 1000) + "s, Name:" + strnpcinfo(0, .@npc) + " (" + .@npc + ")"; + goto L_NextSlot; + +L_Done: + freeloop 0; + message strcharinfo(0), "[TeleportManager] : ---- End ----"; + debugmes "teleports_list <- Leave, ok"; + return; + +L_Error: + freeloop 0; + debugmes "teleports_list <- Leave, error"; + message strcharinfo(0), "[TeleportManager] : Error iterating TeleportManager slots (bug?!)"; + return; +} + +// This function finds slot in TeleportManager with given NPC ID. +// This function designed to run in ANY context, whether RID attached or not. +// Inputs: arg[0]: NPC ID to find, 0 means "find free slot", +// Return: slot index in 1..100 range if slot found, <= 0 on fail. +function|script|teleport_find_slot +{ + debugmes "teleport_find_slot -> Enter"; + if (getarg(0) < 0) goto L_Fail; // Caller gave some crap? + set .@wanted, getarg(0); // NPC ID to find (or 0 to find free slot) + set .@manager, getnpcid("TeleportManager"); + if (.@manager <= 0) goto L_Fail; // Manager NPC not found? + set .@i, 1; // 0 slot not used. + freeloop 1; + goto L_TrySlot; // Start iterating via teleport slots on TeleportManager. + +L_TrySlot: + set .@npcid, get(.teleports[.@i], .@manager); + debugmes "teleport_find_slot: @i="+.@i+" npcid="+.@npcid; + if (.@npcid == .@wanted) goto L_Found; //.teleports[.@i] == desired ID? + if (.@i > 100) goto L_Fail; // No free slots in teleports[1..100] + set .@i, (.@i + 1); goto L_TrySlot; // Try next slot + +L_Found: + debugmes "teleport_find_slot <- Leave, found slot .@i="+.@i; + freeloop 0; + return .@i; + +L_Fail: + debugmes "teleport_find_slot <- Leave, call failed"; + freeloop 0; + return 0; +} + +// This function returns value in TeleportManager's slot with given indes. +// This function designed to run in ANY context, whether RID attached or not. +// Inputs: arg[0]: slot ID, must be 1 .. 100; +// Return: -1 on error, 0 on empty slot, or NPC ID of teleport in given sslot. +function|script|teleport_get_slot_val +{ + debugmes "teleport_get_slot_val -> Enter"; + if ((getarg(0) < 1) || (getarg (0) > 100)) goto L_Fail; // Bogus index? + set .@index, getarg(0); + set .@manager, getnpcid("TeleportManager"); + if (.@manager <= 0) goto L_Fail; // Manager NPC not found? + set .@ret, get(.teleports[.@index], .@manager); + debugmes "teleport_get_slot_val <- Leave, ok"; + return .@ret; + +L_Fail: + debugmes "teleport_get_slot_val <- Leave, failed"; + return -1; +} + +// This function sets slot in TeleportManager with given value. +// This function designed to run in ANY context, whether RID attached or not. +// Inputs: arg[0]: slot ID, must be 1 .. 100; +// arg[1]: value to store to slot; +// Return: 1 on success, <= 0 on failure. +function|script|teleport_set_slot_val +{ + debugmes "teleport_set_slot_val -> Enter"; + if ((getarg(0) < 1) || (getarg (0) > 100)) goto L_Fail; // Bogus index? + if (getarg(1) < 0) goto L_Fail; // TP slots are NPC ID or 0 for empty + set .@index, getarg(0); + set .@val, getarg(1); + set .@manager, getnpcid("TeleportManager"); + if (.@manager <= 0) goto L_Fail; // Manager NPC not found? + set .teleports[.@i], .@val, .@manager; + debugmes "teleport_set_slot_val <- Leave, ok"; + return 1; + +L_Fail: + debugmes "teleport_set_slot_val <- Leave, failed"; + return 0; +} + +// PRIVATE: This function cleans up slot in TeleportManager on teleport shutdown. +// This function ONLY meant to be invoked by teleport NPC puppet on shutdown! +// Inputs: nothing. Gets data from its calling NPC. +// Return: 1 on success, <= 0 on failure. +function|script|teleport_mgr_clean +{ + if !(.managed) goto L_RetOk; // Teleport not managed by TeleportManager -> no cleanup + set .@my_id, getnpcid(); + if (.@my_id <= 0) goto L_RetFail; // Give up on cleanup, slot will leak + // Call chaining OK: teleport_set_slot_val() checks slot # sanity, so teleport_find_slot() fail handled. + set .@res, call("teleport_set_slot_val", call("teleport_find_slot", .@my_id), 0); + if (.@res != 1) goto L_RetFail; + goto L_RetOk; +L_RetOk: + return 1; + +L_RetFail: + debugmes "OnTeleportShutdown: TeleportManaer cleanup failure -> will leak slot. Likely bug!"; + return 0; +} + +// PRIVATE: This function validates @teleportadd args and prepares for teleport_add() call. +// This function MUST be invoked with player RID attached, by TeleportManager NPC +// Inputs: nothing, but assumes args$ set. +// Return: <= 0 on failure, 1 on success. +// Return: sets @POS_X, @POS_X, @DSTMAP$, @DST_X, @DST_Y, +function|script|teleportadd_parseargs +{ + debugmes "teleportadd_parseargs -> Enter"; + // FIXME check arg presence$ if () + callfunc "argv_splitter"; + debugmes "teleportadd_parseargs @argv$[0]"+@argv$[0]+" @argv[1]"+@argv[1]+" @argv[2]"+@argv[2]+" @argv$[3]"+@argv$[3]+" @argv[4]"+@argv[4]+" @argv[5]"+@argv[5]; + set @DSTMAP$, @argv$[0]; // Destination map + set @DST_X, @argv[1]; // Dst warp coordinates + set @DST_Y, @argv[2]; // Dst warp coordinates + set @NAME$, @argv$[3]; // Teleport's label + set @TIMEOUT, @argv[4]; // Teleport's lifetime + set @NPCSPRITE, @argv[5]; // Teleport's NPC sprite (optional) + debugmes "teleportadd_parseargs 1 DSTMAP$="+@DSTMAP$+" @DST_X="+@DST_X+" @DST_Y="+@DST_Y+" @NAME$="+@NAME$+" @TIMEOUT="+@TIMEOUT+" @NPCSPRITE="+@NPCSPRITE; + // Check DST map is okay + if ((@DSTMAP$ == "help") || (@DSTMAP$ == "")) goto L_DisplayHelp; // @teleportadd help or @teleportadd + if !(call("teleport_map_valid", @DSTMAP$)) goto L_FailBadmap; // DST: invalid map? + // Check DST X,Y sane + if ((@DST_X <= 0) || (@DST_Y <= 0)) goto L_FailBadDstXY1; // DST: invalids coords <= 0? + if ((getmapmaxx(@DSTMAP$) < @DST_X) || (getmapmaxy(@DSTMAP$) < @DST_Y)) goto L_FailBadDstXY2; // Outside of map? + // Check if DST is collision + if (iscollision(@DSTMAP$, @DST_X, @DST_Y)) goto L_FailDstCollide; + // Try adaptive NPC placement. Above caller if there's room or on caller if not. + set @POS_X, POS_X; + if ((POS_Y > 2) && !(iscollision(getmap(), POS_X, (POS_Y-2)))) set @POS_Y, (POS_Y - 2); + else set @POS_Y, POS_Y; // Overhead placement failed, use caller's tile + // NPC name checks + if (@NAME$ == "") goto L_FailNPCName; + // TIMEOUT checks and setup + if ((@TIMEOUT < -1) || (@TIMEOUT == 0) || (@TIMEOUT > 2000000)) goto L_FailTimeout; + if (@TIMEOUT > 0) set @TIMEOUT, (@TIMEOUT * 1000); // translate seconds -> ms to make more wieldy numbers + // NPC SPRITE configuration + if ((@NPCSPRITE != 424) && (@NPCSPRITE != 369) && (@NPCSPRITE != 368) + && (NPCSPRITE != 325) && (@NPCSPRITE != 324)) set @NPCSPRITE, 424; + // All checks complete + debugmes "teleportadd_parseargs 2 DSTMAP$="+@DSTMAP$+" @DST_X="+@DST_X+" @DST_Y="+@DST_Y+" @NAME$="+@NAME$+" @TIMEOUT="+@TIMEOUT+" @NPCSPRITE="+@NPCSPRITE; + return 1; // Everything OK + +L_FailBadmap: + message strcharinfo(0), "[TeleportManager] : @teleportadd: unknown destination map:" + @DSTMAP$; + debugmes "teleportadd_parseargs <- Leave, fail Badmap"; + return -1; + +L_FailBadDstXY1: + message strcharinfo(0), "[TeleportManager] : @teleportadd: destination X,Y must be > 0! Given X=" + @DST_X + " Y=" + @DST_Y; + debugmes "teleportadd_parseargs <- Leave, fail BadDstXY1"; + return -2; + +L_FailBadDstXY2: + message strcharinfo(0), "[TeleportManager] : @teleportadd: destination X,Y outside of map! Given X=" + @DST_X+ " Y=" + @DST_Y; + debugmes "teleportadd_parseargs <- Leave, fail BadDstXY2"; + return -3; + +L_FailDstCollide: + message strcharinfo(0), "[TeleportManager] : @teleportadd: destination MAP=" + @DSTMAP$ + " X=" + @DST_X + " Y=" + @DST_Y + " is a collision (impassable)"; + debugmes "teleportadd_parseargs <- Leave, fail DstCollide"; + return -4; + +L_FailNPCName: + message strcharinfo(0), "[TeleportManager] : @teleportadd: NPCNAME can't be empty!"; + debugmes "teleportadd_parseargs <- Leave, fail NPCName"; + return -5; + +L_FailTimeout: + message strcharinfo(0), "[TeleportManager] : @teleportadd: timeout must be either -1, or > 0 and < 2000000 (seconds)"; + debugmes "teleportadd_parseargs <- Leave, fail NPCName"; + return -6; + +L_DisplayHelp: + void call("teleport_help_add"); + return -7; +} + +// PUBLIC API: This function checks if map name known and OK to use. +// This function designed to run in any context. +// Inputs: arg$[0] is map name to check for sanity. +// Return: 1 for known maps, 0 for unknown +function|script|teleport_map_valid +{ + set .@inputmap$, getarg(0, ""); + set .@i, 0; + setarray .@maps1$, "001-1", "001-2", "001-3", "002-1", "002-2", "002-3", + "002-4", "002-5", "003-1", "003-4", "004-1", "004-3", + "004-4", "004-5", "005-3", "006-1", "006-2", "006-3", + "007-1", "007-2", "008-1", "009-1", "009-2", "009-3", + "009-4", "009-5", "009-6", "009-7", "009-8", "010-1", + "010-2", "011-1", "011-3", "011-4", "011-6", "012-1", + "012-3", "012-4", "013-1", "013-2", "013-3", "014-1", + "014-3", "015-1", "015-3", "016-1", "016-2", "017-1", + "017-2", "017-3", "017-4", "017-9", "018-1", "018-2", + "018-3", "019-1", "019-3", "019-4", "020-1", "020-2", + "020-3", "021-3", "023-1", "023-2", "023-3", "025-1", + "025-3", "025-4", "026-1", "026-2", "027-1", "027-2", + "027-3", "027-4", "027-5", "027-6", "027-7", "027-8", + "028-1", "028-3", "029-1", "029-2", "029-3", "029-4", + "030-1", "030-2", "030-3", "030-4", "031-1", "031-2", + "031-3", "031-4", "032-3", "033-1", "034-1", "034-2", + "035-2", "036-2", "041-1", "042-1", "043-1", "043-3", + "043-4", "045-1", "046-1", "046-3", "047-1", "047-3"; +// Had to split to 2 arrays as its too big for array initializer + setarray .@maps2$, "048-2", "051-1", "051-3", "052-1", "052-2", "055-1", + "055-3", "056-2", "057-1", "058-1", "058-2", "069-2", + "070-1", "070-3", "099-1", "099-2", "099-3", "099-4", + "099-5", "099-6", "099-7", "099-8", "botcheck"; + set .@arr_sz1, getarraysize(.@maps1$[0]); + set .@arr_sz2, getarraysize(.@maps2$[0]); + freeloop 1; // Needed to iterate over array of about 150 maps + goto L_NextMap; // Start iterating over array of maps. + +L_NextMap: + if (.@inputmap$ == .@maps1$[.@i]) goto L_Found; // Found map in arr 1? + if (.@inputmap$ == .@maps2$[.@i]) goto L_Found; // Found map in arr 2? + set .@i, (.@i + 1); // increment .@maps$[] index + if ((.@i >= .@arr_sz1) && (.@i >= .@arr_sz2)) goto L_NotFound; // Abort if whole arrays scanned + goto L_NextMap; // Try next map in .@maps$[] + +L_NotFound: + freeloop 0; + return 0; + +L_Found: + freeloop 0; + return 1; +} + +// PRIVATE: This function displays usage help for TeleportManager - @teleportadd +// This function should be invoked by TeleportManager with RID attached. +// Inputs: nothing. +// Return: nothing, just shows usage -> caller. +function|script|teleport_help_add +{ + message strcharinfo(0), "[TeleportManager] : @teleportadd <MAP> <X> <Y> <NPCNAME> <TIMEOUT> [sprite]"; + message strcharinfo(0), "[TeleportManager] : <MAP> <X> <Y>: teleport's destination map and coordinates"; + message strcharinfo(0), "[TeleportManager] : <NPCNAME>: name of NPC, unique and nonempty. If it stats with # its hidden"; + message strcharinfo(0), "[TeleportManager] : <TIMEOUT>: teleport lifetime (sec), < 2 000 000 sec, -1 = persistent"; + message strcharinfo(0), "[TeleportManager] : [sprite]: optional, NPC sprite (424,369,368,325 and 324 accepted)"; + message strcharinfo(0), "[TeleportManager] : Example: @teleportadd 009-1 52 39 Hurns -1 adds permanent teleport to Hurns menhir"; + message strcharinfo(0), "[TeleportManager] : Example: @teleportadd 009-1 52 39 Hurns 600 324 - same but 10 min, and red circle"; + return; +} + +// PRIVATE: This function displays usage help for TeleportManager - @teleportdel +// This function should be invoked by TeleportManager with RID attached. +// Inputs: nothing. +// Return: nothing, just shows usage -> caller. +function|script|teleport_help_del +{ + message strcharinfo(0), "[TeleportManager] : @teleportdel <ID>"; + message strcharinfo(0), "[TeleportManager] : ID either slot# (1..100) or teleport NPC ID"; + return; +} + +// Access checks for TeleportManager. Based on cut-down BossPowers checks. +function|script|teleport_access_check +{ + if ($BP_DISABLE) goto L_Killswitch; // If things go wrong, TeleportManager can be disabled. + if (#BP_DISABLE) goto L_Killswitch; // If someone abuses feature, there's _per-account_ DENY flag. + if (GM >= 40) goto L_Allowed; // GM >= 40 can use boss actions. + if (IS_EVENTER == 42) goto L_Allowed; // Trusted player(s) could be allowed to access Eventer "magic" + if (debug) goto L_Allowed; // Allow on debug. + message strcharinfo(0), "[TeleportManager] : You can't use this feature at this time. Sorry. [1]"; + return 1; // Not allowed by default. + +L_Allowed: + return 0; // Whoever gets here allowed to invoke BossPowers spells + +L_Killswitch: + message strcharinfo(0), "[TeleportManager] : You can't use this feature at this time. Sorry. [2]"; + return 2; +} diff --git a/world/map/npc/scripts.conf b/world/map/npc/scripts.conf index 448e0947..7fbe10b4 100644 --- a/world/map/npc/scripts.conf +++ b/world/map/npc/scripts.conf @@ -36,6 +36,7 @@ npc: npc/functions/motdconfig.txt npc: npc/functions/ghost.txt npc: npc/functions/vault.txt npc: npc/functions/global_event_handler.txt +npc: npc/functions/teleport_manager.txt // Item Functions npc: npc/items/purification_potion.txt |