diff options
43 files changed, 2893 insertions, 1091 deletions
diff --git a/db/constants.conf b/db/constants.conf index f73ee858..a339559a 100644 --- a/db/constants.conf +++ b/db/constants.conf @@ -5216,10 +5216,11 @@ more than one separator can be used in a row (so 12_3___456 is illegal). comment__: "Language constants" LANG_EN: 0 - LANG_FR: 1 - LANG_ES: 2 + LANG_PTBR: 1 + LANG_FR: 2 LANG_DE: 3 - MAX_LANG: 3 + LANG_ES: 4 + MAX_LANG: 4 comment__: "Daily Constants" DAILY_LOWLEVEL: 0 @@ -5634,6 +5635,7 @@ more than one separator can be used in a row (so 12_3___456 is illegal). AMMO_BOW:1 AMMO_SLING:2 AMMO_WAND:3 + // tmw npcs NPC45:45 NPC100:100 @@ -5814,6 +5816,7 @@ more than one separator can be used in a row (so 12_3___456 is illegal). NPC384:384 NPC385:385 NPC386:386 + NPC393:393 NPC394:394 NPC395:395 NPC400:400 diff --git a/db/pre-re/item_db.conf b/db/pre-re/item_db.conf index 9c31d7a7..d4b46dd2 100644 --- a/db/pre-re/item_db.conf +++ b/db/pre-re/item_db.conf @@ -874,7 +874,7 @@ item_db: ( BindOnEquip: false Subtype: "W_2HSTAFF" OnEquipScript: <" - set @bStat, Int; + set @bStat, readparam(bInt); set @minbStatVal, 60; callfunc "RequireStat"; "> @@ -900,7 +900,7 @@ item_db: ( BindOnEquip: false Subtype: "W_STAFF" OnEquipScript: <" - set @bStat, Int; + set @bStat, readparam(bInt); set @minbStatVal, 5; callfunc "RequireStat"; "> @@ -973,7 +973,7 @@ item_db: ( BindOnEquip: false Subtype: "W_BOW" OnEquipScript: <" - set @bStat, Dex; + set @bStat, readparam(bDex); set @minbStatVal, 80; callfunc "RequireStat"; bonus2 bHPDrainRate, 100, -2; @@ -6251,10 +6251,10 @@ item_db: ( BindOnEquip: false Delay: 0 OnEquipScript: <" - set @bStat, Int; + set @bStat, readparam(bInt); set @minbStatVal, 70; callfunc "RequireStat"; - set @bStat, Vit; + set @bStat, readparam(bVit); set @minbStatVal, 40; callfunc "RequireStat"; bonus bSpeedAddRate, -15; @@ -10923,13 +10923,13 @@ item_db: ( BindOnEquip: false Delay: 0 OnEquipScript: <" - set @bStat, Str; + set @bStat, readparam(bStr); set @minbStatVal, 70; callfunc "RequireStat"; - set @bStat, Vit; + set @bStat, readparam(bVit); set @minbStatVal, 50; callfunc "RequireStat"; - set @bStat, Luk; + set @bStat, readparam(bLuk); set @minbStatVal, 50; callfunc "RequireStat"; bonus bAtkRange, -10; diff --git a/npc/006-1/miriam.txt b/npc/006-1/miriam.txt index a42253e6..05e59d5e 100755 --- a/npc/006-1/miriam.txt +++ b/npc/006-1/miriam.txt @@ -144,7 +144,7 @@ L_Prepared: @weight = MaxWeight/Weight; if (@weight < 3) goto L_heavyweight; - if (Agi < 60) + if (readparam(bAgi) < 60) goto L_slow; if (BaseLevel < 60) goto L_LowLevel; @@ -175,7 +175,7 @@ L_heavymetal: L_slow: mes "[Miriam]"; - mes "Oh, wait, wait... " +Agi+ " Agility? Are you serious? What do you do with all your Character points? Put them all to 'Strength'? Really... I don't even want to know... Come back here when you have a decent character points distribution!\""; + mes "Oh, wait, wait... " +readparam(bAgi)+ " Agility? Are you serious? What do you do with all your Character points? Put them all to 'Strength'? Really... I don't even want to know... Come back here when you have a decent character points distribution!\""; close; L_LowLevel: @@ -194,7 +194,7 @@ L_Offer: L_TorsoNext: mes "[Miriam]"; - mes "\"I see. I like the way you distributed your Character Points too... " +Agi+ " points to Agility! You really know how to prepare yourself for a good fight!\""; + mes "\"I see. I like the way you distributed your Character Points too... " +readparam(bAgi)+ " points to Agility! You really know how to prepare yourself for a good fight!\""; next; mes "\"Well... Even if you have a potential to be a good fighter, moving the way you do will not help. You need to be fast to avoid the monsters. \""; next; diff --git a/npc/009-2/kfahr.txt b/npc/009-2/kfahr.txt index 8962e0a7..2d1b47cf 100755 --- a/npc/009-2/kfahr.txt +++ b/npc/009-2/kfahr.txt @@ -905,8 +905,8 @@ L_gs_wrestle: @KFAHR_AGI = 60 - @beer_count * 5; @kfahr_stamina = 120; - @PC_STR = Str; - @PC_AGI = Agi; + @PC_STR = readparam(bStr); + @PC_AGI = readparam(bAgi); @PC_MAX_STAMINA = Vit + 20; @pc_stamina = @PC_MAX_STAMINA; diff --git a/npc/016-1/gwendolyn.txt b/npc/016-1/gwendolyn.txt index b01ab292..87ad8031 100755 --- a/npc/016-1/gwendolyn.txt +++ b/npc/016-1/gwendolyn.txt @@ -414,7 +414,7 @@ L_target_hit: next; mes "\"Let me inspect it...\" She walks to the target."; next; - if( rand(500) > (2*Dex + Agi)) + if( rand(500) > (2*readparam(bDex) + readparam(bAgi))) goto L_target_nohit; .@Q_hawkseye = 6; callsub S_Update_Var; diff --git a/npc/018-2/caul.txt b/npc/018-2/caul.txt index e738dbde..e5c3626f 100755 --- a/npc/018-2/caul.txt +++ b/npc/018-2/caul.txt @@ -450,7 +450,7 @@ L_mo_auto_bad: L_mo_explode: mes "[Exploding monster oil]"; mes "Your brew explodes!"; - if (rand(Agi) > 40) + if (rand(readparam(bAgi)) > 40) goto L_mo_explode_dodge; mes "The burst of boiling monster brew hits you."; next; diff --git a/npc/commands/changesex.txt b/npc/commands/changesex.txt deleted file mode 100755 index de20d91b..00000000 --- a/npc/commands/changesex.txt +++ /dev/null @@ -1,31 +0,0 @@ -- script @changesex NPC32767,{ - callfunc "argv_splitter"; - .@n$ = if_then_else(@argv$[1] != "", "char", "") + "changecharsex()"; - if (GM < CMD_CHANGESex && GM < G_SYSOP) goto L_GM; // check if you can use it on self - .@target_id = BL_ID; - if (@argv$[1] != "") set .@target_id, getcharid(3, @argv$[1]); - if (@argv$[1] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self - if (@argv$[1] != "" && GM < CMD_CHARCHANGESex && GM < G_SYSOP) goto L_GM; // when target is not self, use charchangecharsex() permission - - set .@s, 3; // default to non-binary - if (@argv$[0] == "M" || @argv$[0] == "m") set .@s, 1; - if (@argv$[0] == "F" || @argv$[0] == "f") set .@s, 0; - Sex = .@s, .@target_id; - gmlog "@"+.@n$+" " + @args$; - message strcharinfo(0), .@n$+" : The operation succeeded."; - end; - -L_Failed: - // XXX: should we allow GMs to change Sex of users that are not logged in? - message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?"; - end; - -L_GM: - message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARCHANGESex, CMD_CHANGESex) +", but you are level " + GM; - end; - -OnInit: - registercmd chr(ATCMD_SYMBOL) + "changecharsex()", strnpcinfo(0); - registercmd chr(ATCMD_SYMBOL) + "charchangecharsex()", strnpcinfo(0); - end; -} diff --git a/npc/commands/class.txt b/npc/commands/class.txt deleted file mode 100755 index 81ac0c7c..00000000 --- a/npc/commands/class.txt +++ /dev/null @@ -1,30 +0,0 @@ -- script @class NPC32767,{ - callfunc "argv_splitter"; - .@n$ = if_then_else(@argv$[1] != "", "char", "") + "class"; - if (GM < CMD_CLASS && GM < G_SYSOP) goto L_GM; // check if you can use it on self - .@target_id = BL_ID; - if (@argv$[1] != "") set .@target_id, getcharid(3, @argv$[1]); - if (@argv$[1] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self - if (@argv$[1] != "" && GM < CMD_CHARCLASS && GM < G_SYSOP) goto L_GM; // when target is not self, use charclass permission - - set .@c, 1; // default to human - if (@argv[0] >= 1 || @argv[0] <= 32767) set .@c, @argv[0]; - Class = .@c, .@target_id; - gmlog "@"+.@n$+" " + @args$; - message strcharinfo(0), .@n$+" : The operation succeeded."; - end; - -L_Failed: - // XXX: should we allow GMs to change class of users that are not logged in? - message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?"; - end; - -L_GM: - message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARCLASS, CMD_CLASS) +", but you are level " + GM; - end; - -OnInit: - registercmd chr(ATCMD_SYMBOL) + "class", strnpcinfo(0); - registercmd chr(ATCMD_SYMBOL) + "charclass", strnpcinfo(0); - end; -} diff --git a/npc/commands/debug-quest.txt b/npc/commands/debug-quest.txt new file mode 100644 index 00000000..a4c6572f --- /dev/null +++ b/npc/commands/debug-quest.txt @@ -0,0 +1,93 @@ +// Evol Script +// Author: Gumi, Jesusalva + +// TODO: This script must be auto-generated from db/quests.conf to be of any use +function script GlobalQuestDebug { + do + { + clear; + setnpcdialogtitle l("Quest debug") + " - " + l("Other"); + mes l("This menu gives access to quest debug menus for @@ quests.", strtolower(l("Other"))); + next; + mes l("Please select a quest:"); + + menuint + menuimage("actions/back", l("Go back")), -1, + l("Custom"), -136; + + switch (@menuret) + { + case -1: return; + case -136: + mes "Determine the quest number, as stated in db/quests.conf"; + input .@quest; + if (!.@quest) return; + mes ""; + mes l("DEBUG: Changing @@, Values: (@@, @@, @@).", getquestlink(.@quest), getq(.@quest), getq2(.@quest), getq3(.@quest)); + select + "set 1", + "set 2", + "set 3", + "reset"; + mes l("DEBUG: Changing @@ field @@ to something else.", getquestlink(.@quest), @menu); + mes ""; + mes "Determine the new value (numeric only)"; + input .@value; + if (.@value < 0) return; + if (@menu == 1) + setq1 .@quest, .@value; + if (@menu == 2) + setq2 .@quest, .@value; + if (@menu == 3) + setq3 .@quest, .@value; + if (@menu == 4) + setq .@quest, .@value, 0, 0; + + return; + default: return; + } + } while (1); + return; +} + + + +- script @qdebug 32767,{ + end; + +OnCall: + if (!is_gm()) { + end; + } + GlobalQuestDebug; + closedialog; + end; + +OnSetq: + if (.@atcmd_numparameters < 2) { + dispbottom "setq called with invalid arguments (min. 2)"; + dispbottom "GM Command syntax: @setq <quest_id> <val1> <val2> <val3>"; + end; + } + .@q=atoi(.@atcmd_parameters$[0]); + switch (.@atcmd_numparameters) { + case 4: + setq3 .@q, atoi(.@atcmd_parameters$[3]); + case 3: + setq2 .@q, atoi(.@atcmd_parameters$[2]); + case 2: + setq1 .@q, atoi(.@atcmd_parameters$[1]); + dispbottom l("Quest @@ modified by GM", getquestlink(.@q)); + specialeffect 50, SELF, playerattached(); + break; + default: + dispbottom "setq called with invalid arguments (max. 4)"; + dispbottom "GM Command syntax: @setq <quest_id> <val1> <val2> <val3>"; + break; + } + end; + +OnInit: + bindatcmd "qdebug", "@qdebug::OnCall", 99, 99, 1; + bindatcmd "setq", "@qdebug::OnSetq", 99, 99, 1; +} diff --git a/npc/commands/debug.txt b/npc/commands/debug.txt index c1ebf81d..f14d1363 100755 --- a/npc/commands/debug.txt +++ b/npc/commands/debug.txt @@ -1,11 +1,6 @@ function script Debug { - if(!@debug_npc) goto L_Begin; - mes "The debug NPCs have been deprecated. Please use this command instead:"; - mes ""; - mes "%%E ##a@debug##0"; - @debug_npc = 0; - goto L_close; + goto L_Begin; L_Begin: @debug_mask = 65535; @@ -13,8 +8,8 @@ L_Begin: @mexp = ((MAGIC_EXPERIENCE & @debug_mask) >> @debug_shift); mes "What do you want to do?"; menu + "Reset stat points.", L_Level, "Change my level.", L_Level, - "Change my stats.", L_Status, "Change my basic skills.", L_BasicSkills, "Change my focus skills.", L_FocusSkills, "Change my magic skills.", L_MagicSkills, @@ -51,188 +46,6 @@ L_SameLevel: next; goto L_Begin; -L_Status: - mes "What do you want to do?"; - menu - "Set all of my stats myself.", L_ChangeStrength, - "Set one of my stats myself.", L_ChangeSingleStat, - "Get maximum points in all stats.", L_ChangeAllStats, - "Reset my status points.", L_ResetStatusPoints, - "Back to the main menu.", L_Begin, - "Close.", L_close; - -L_ChangeAllStats: - Str = 99; - Agi = 99; - Vit = 99; - Int = 99; - Dex = 99; - Luk = 99; - mes "You now have 99 in all stats."; - next; - goto L_Begin; - -L_ChangeStrength: - mes "How much strength do you want to have (min: 1 - max: 99)?"; - input @str; - if (@str < 1) - goto L_StatTooLow; - if (@str > 99) - goto L_StatTooHigh; - Str = @str; - goto L_ChangeAgility; - -L_ChangeAgility: - mes "How much agility do you want to have (min: 1 - max: 99)?"; - input @agi; - if (@agi < 1) - goto L_StatTooLow; - if (@agi > 99) - goto L_StatTooHigh; - Agi = @agi; - goto L_ChangeVitality; - -L_ChangeVitality: - mes "How much vitality do you want to have (min: 1 - max: 99)?"; - input @vit; - if (@vit < 1) - goto L_StatTooLow; - if (@vit > 99) - goto L_StatTooHigh; - Vit = @vit; - goto L_ChangeIntelligence; - -L_ChangeIntelligence: - mes "How much intelligence do you want to have (min: 1 - max: 99)?"; - input @int; - if (@int < 1) - goto L_StatTooLow; - if (@int > 99) - goto L_StatTooHigh; - Int = @int; - goto L_ChangeDexterity; - -L_ChangeDexterity: - mes "How much dexterity do you want to have (min: 1 - max: 99)?"; - input @dex; - if (@dex < 1) - goto L_StatTooLow; - if (@dex > 99) - goto L_StatTooHigh; - Dex = @dex; - goto L_ChangeLuck; - -L_ChangeLuck: - mes "How much luck do you want to have (min: 1 - max: 99)?"; - input @luk; - if (@luk < 1) - goto L_StatTooLow; - if (@luk > 99) - goto L_StatTooHigh; - Luk = @luk; - mes "You now have " + Str + " in strength."; - mes "You now have " + Agi + " in agility."; - mes "You now have " + Vit + " in vitality."; - mes "You now have " + Int + " in intelligence."; - mes "You now have " + Dex + " in dexterity."; - mes "You now have " + Luk + " in luck."; - next; - goto L_Begin; - -L_StatTooLow: - mes "Bad choice. Minimum stat value is 1. Aborting."; - next; - goto L_Status; - -L_StatTooHigh: - mes "Bad choice. Maximum stat value is 99. Aborting."; - next; - goto L_Status; - -L_ChangeSingleStat: - mes "Which stat do you want to change?"; - menu - "Strength.", L_ChangeStrengthSingle, - "Agility.", L_ChangeAgilitySingle, - "Vitality.", L_ChangeVitalitySingle, - "Intelligence.", L_ChangeIntelligenceSingle, - "Dexterity.", L_ChangeDexteritySingle, - "Luck.", L_ChangeLuckSingle, - "Back to the main menu.", L_Begin, - "Close.", L_close; - -L_ChangeStrengthSingle: - mes "How much strength do you want to have (min: 1 - max: 99)?"; - input @str; - if (@str < 1) - goto L_StatTooLow; - if (@str > 99) - goto L_StatTooHigh; - Str = @str; - mes "You now have " + Str + " in strength."; - next; - goto L_Begin; - -L_ChangeAgilitySingle: - mes "How much agility do you want to have (min: 1 - max: 99)?"; - input @agi; - if (@agi < 1) - goto L_StatTooLow; - if (@agi > 99) - goto L_StatTooHigh; - Agi = @agi; - mes "You now have " + Agi + " in agility."; - next; - goto L_Begin; - -L_ChangeVitalitySingle: - mes "How much vitality do you want to have (min: 1 - max: 99)?"; - input @vit; - if (@vit < 1) - goto L_StatTooLow; - if (@vit > 99) - goto L_StatTooHigh; - Vit = @vit; - mes "You now have " + Vit + " in vitality."; - next; - goto L_Begin; - -L_ChangeIntelligenceSingle: - mes "How much intelligence do you want to have (min: 1 - max: 99)?"; - input @int; - if (@int < 1) - goto L_StatTooLow; - if (@int > 99) - goto L_StatTooHigh; - Int = @int; - mes "You now have " + Int + " in intelligence."; - next; - goto L_Begin; - -L_ChangeDexteritySingle: - mes "How much dexterity do you want to have (min: 1 - max: 99)?"; - input @dex; - if (@dex < 1) - goto L_StatTooLow; - if (@dex > 99) - goto L_StatTooHigh; - Dex = @dex; - mes "You now have " + Dex + " in dexterity."; - next; - goto L_Begin; - -L_ChangeLuckSingle: - mes "How much luck do you want to have (min: 1 - max: 99)?"; - input @luk; - if (@luk < 1) - goto L_StatTooLow; - if (@luk > 99) - goto L_StatTooHigh; - Luk = @luk; - mes "You now have " + Luk + " in luck."; - next; - goto L_Begin; - L_ResetStatusPoints: resetstatus; mes "Stats successfully resetted."; @@ -240,113 +53,13 @@ L_ResetStatusPoints: goto L_Begin; L_BasicSkills: - @emote = getskilllv(SKILL_EMOTE); - @trade = getskilllv(SKILL_TRADE); - @party = getskilllv(SKILL_PARTY); - menu - "Overview of my basic skills.", L_BasicSkillsOverview, - "Add basic skills.", L_AddBasicSkills, - "Reset basic skills.", L_ResetBasicSkills, - "Back to main menu.", L_Begin, - "Close.", L_close; - -L_BasicSkillsOverview: - mes "Your level in the emote skill is " + @emote + "."; - mes "Your level in the trade skill is " + @trade + "."; - mes "Your level in the party skill is " + @party + "."; - next; - goto L_BasicSkills; - -L_AddBasicSkills: - menu - "Emote", L_ChangeEmoteSkill, - "Trade.", L_ChangeTradeSkill, - "Party.", L_ChangePartySkill, - "All basic skills to their maximum level.", L_AllBasicSkills, - "Back to the basic skills menu.", L_BasicSkills, - "Close.", L_close; - -L_ChangeEmoteSkill: - mes "Your level in the emote skill is " + @emote + ". What do you want to do?"; - menu - "Get level 0.", L_Next, - "Get level 1.", L_ChangeEmoteSkill1; - -L_Next: - if (@menu == 1) - addtoskill SKILL_EMOTE, 0; - mes "Emote skill changed to level 0."; - next; - goto L_BasicSkills; - -L_ChangeEmoteSkill1: - addtoskill SKILL_EMOTE, 1; - mes "Emote skill changed to level 1."; - next; - goto L_BasicSkills; - -L_ChangeTradeSkill: - mes "Your level in the trade skill is " + @trade + ". What do you want to do?"; - menu - "Get level 0.", L_Next1, - "Get level 1.", L_ChangeTradeSkill1; - -L_Next1: - if (@menu == 1) - addtoskill SKILL_TRADE, 0; - mes "Trade skill changed to level 0."; - next; - goto L_BasicSkills; - -L_ChangeTradeSkill1: - addtoskill SKILL_TRADE, 1; - mes "Trade skill changed to level 1."; - next; - goto L_BasicSkills; - -L_ChangePartySkill: - mes "Your level in the party skill is " + @trade + ". What do you want to do?"; - menu - "Get level 0.", L_Next2, - "Get level 1.", L_ChangePartySkill1, - "Get level 2.", L_ChangePartySkill2; - -L_Next2: - if (@menu == 1) - addtoskill SKILL_PARTY, 0; - mes "Party skill changed to level 0."; - next; - goto L_BasicSkills; - -L_ChangePartySkill1: - addtoskill SKILL_PARTY, 1; - mes "Party skill changed to level 1."; - next; - goto L_BasicSkills; - -L_ChangePartySkill2: - addtoskill SKILL_PARTY, 2; - mes "Party skill changed to level 2."; - next; - goto L_BasicSkills; - -L_AllBasicSkills: - addtoskill SKILL_EMOTE, 1; - addtoskill SKILL_TRADE, 1; - addtoskill SKILL_PARTY, 2; - mes "Basic skills added."; - next; - goto L_BasicSkills; - -L_ResetBasicSkills: - addtoskill SKILL_EMOTE, 0; - addtoskill SKILL_TRADE, 0; - addtoskill SKILL_PARTY, 0; - mes "Basic skills removed."; - next; - goto L_BasicSkills; + skill SKILL_EMOTE, 6, 0; + //addtoskill SKILL_TRADE, 0; + //addtoskill SKILL_PARTY, 0; + goto L_Begin; L_FocusSkills: + /* @pool = getskilllv(SKILL_POOL); @mallard = getskilllv(SKILL_MALLARDS_EYE); @brawling = getskilllv(SKILL_BRAWLING); @@ -546,6 +259,8 @@ L_ResetFocusSkills: mes "Focus skills removed."; next; goto L_FocusSkills; + */ + goto L_Begin; L_MagicSkills: @general = getskilllv(SKILL_MAGIC); @@ -973,12 +688,3 @@ OnInit: end; } -027-2,125,103,0 script Debug#5 NPC154,{ - @debug_npc = 1; - callfunc "Debug"; - end; -OnInit: - if (!debug) - disablenpc "Debug#5"; - end; -} diff --git a/npc/commands/destroynpc.txt b/npc/commands/destroynpc.txt deleted file mode 100755 index 156c8200..00000000 --- a/npc/commands/destroynpc.txt +++ /dev/null @@ -1,34 +0,0 @@ -- script @destroynpc NPC32767,{ - callfunc "argv_splitter"; - if (GM < CMD_DESTROYNPC && GM < G_SYSOP) - goto L_GM; - - .@id = getnpcid(@argv$[0]); - if (@argv$[0] == "" || .@id < 1) - goto L_Failed; - if (gettimetick(2) - @destroynpc[0] > 300 || @destroynpc[1] != .@id) - goto L_Confirm; - - gmlog "@destroynpc " + @args$; - message strcharinfo(0), "destroynpc : The operation succeeded."; - debugmes "!!! => npc destroyed: `"+@argv$[0]+"` ("+.@id+")"; - if (1==1) destroy .@id; // FIXME: allow destroy to work as a non-terminator when arg0 is given (TMWA) - end; - -L_Confirm: - message strcharinfo(0), "destroynpc : ##BDANGER ZONE!##b This command permanently destroys a npc and its puppets (if any). Use the command again to confirm."; - setarray @destroynpc[0], gettimetick(2), .@id; - end; - -L_Failed: - message strcharinfo(0), "destroynpc : Impossible to find the target npc. Did you try putting the name in \"quotation marks\"? Some npcs also have an invisible postfix in their name, ie `#_M`."; - end; - -L_GM: - message strcharinfo(0), "destroynpc : GM command is level "+ CMD_DESTROYNPC +", but you are level " + GM; - end; - -OnInit: - registercmd chr(ATCMD_SYMBOL) + "destroynpc", strnpcinfo(0); - end; -} diff --git a/npc/commands/hug.txt b/npc/commands/hug.txt deleted file mode 100755 index 4f7c086d..00000000 --- a/npc/commands/hug.txt +++ /dev/null @@ -1,24 +0,0 @@ -- script nonmagic-hug NPC32767,{ - explode .@name$[0], @args$, "*"; // strip the trailing * - @target_id = if_then_else(.@name$[0] != "", getcharid(3, .@name$[0]), BL_ID); - if (@target_id < 1 || !(isloggedin(@target_id))) set @target_id, BL_ID; // fallback to self - if (.@name$[0] == "Tree" || .@name$[0] == "tree") set @target_id, .tree_id; - .@range = if_then_else(@target_id == .tree_id, 3, 6); - if (distance(BL_ID, @target_id) >= .@range) end; - if (gettimetick(2) - @hugspell_time < 3) end; - - @hugspell_time = gettimetick(2); - misceffect FX_HUG, strcharinfo(0); - if (@target_id != BL_ID) misceffect FX_HUG, @target_id; - - if (@target_id != .tree_id) end; - @flag = 2; - callfunc "QuestTreeTrigger"; - close; - -OnInit: - .tree_id = getnpcid("#DruidTree0#_M"); - registercmd "*hugs", strnpcinfo(0); // eq: /me hugs (target) - registercmd "*hugs*", strnpcinfo(0); // eq: /me hugs - end; -} diff --git a/npc/commands/ipcheck.txt b/npc/commands/ipcheck.txt new file mode 100644 index 00000000..a241d3c0 --- /dev/null +++ b/npc/commands/ipcheck.txt @@ -0,0 +1,71 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// @ipcheck <player_name> +// #ipcheck <player_name> +// +// Returns user IP + + +- script @ipcheck 32767,{ + end; + +OnCall: + if (.@atcmd_numparameters == 0) + .@request$ = strcharinfo(0); + else + .@request$ = implode(.@atcmd_parameters$, " "); + dispbottom strip(.@request$)+": IP "+getcharip(.@request$); + //dispbottom strcharinfo(0)+": IP "+getcharip(.@request$); + end; + +OnBan: + if (.@atcmd_numparameters == 0) { + dispbottom col("Syntax: #ipban <reason>", 1); + } + // Do not allow banning high-level staff + if (is_admin() || is_gm()) + end; + .@target$=strcharinfo(0); + .@reason$ = implode(.@atcmd_parameters$, " "); + dispbottom col(l("You were permanently banned by the GM Team."), 1); + sleep2(200); + query_sql "INSERT INTO ipbanlist (list,btime,rtime,reason) VALUES ('"+getcharip(.@target$)+"','"+gettime(7)+"-"+gettime(6)+"-"+gettime(5)+" "+gettime(3)+":"+gettime(2)+":"+gettime(1)+"','2037-01-01 00:00:00','"+.@reason$+"')"; + logmes("was IP-Blocked, and will not connect again."), LOGMES_ATCOMMAND; + sleep2(2000); + charcommand("@kick "+.@target$); + end; + +OnInit: + bindatcmd "ipcheck", "@ipcheck::OnCall", 60, 100, 0; + bindatcmd "ipban", "@ipcheck::OnBan", 99, 100, 1; + end; +} + +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// @checkidle <player_name> +// #checkidle <player_name> +// +// Returns user idle time in seconds. +// Useful when the game prohibits warping to player. + + +- script @checkidle 32767,{ + end; + +OnCall: + if (.@atcmd_numparameters == 0) + .@request$ = strcharinfo(0); + else + .@request$ = implode(.@atcmd_parameters$, " "); + dispbottom strip(.@request$)+" idle time: "+checkidle(.@request$); + //dispbottom strcharinfo(0)+": IP "+getcharip(.@request$); + end; + +OnInit: + bindatcmd "checkidle", "@checkidle::OnCall", 60, 80, 0; + end; +} + diff --git a/npc/commands/kami.txt b/npc/commands/kami.txt new file mode 100644 index 00000000..1211fae0 --- /dev/null +++ b/npc/commands/kami.txt @@ -0,0 +1,86 @@ +// TMW2 Script +// +// @k <message> +// Broadcast, and broadcast to #world too +// +// @servmsg <message> +// Experimental, uses servicemessage() - requires up to date server + +- script @k 32767,{ + end; + +OnCall: + .@request$ = strcharinfo(0)+": "; + .@request$ += implode(.@atcmd_parameters$, " "); + channelmes("#world", .@request$); + announce l(.@request$), bc_all|bc_npc; + end; + +OnServMsg: + .@request$ = strcharinfo(0)+": "; + .@request$ += implode(.@atcmd_parameters$, " "); + // This can be slow, beware + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + message(.@players[.@i], .@request$); + } + end; + +OnBuff: + // Disabled command, used for debug purposes + .@c = getunits(BL_PC, .@players, MAX_CYCLE_PC); + for (.@i = 0; .@i < .@c; .@i++) { + attachrid(.@players[.@i]); + sc_start SC_INCMHPRATE, 300000, 100; + sc_start SC_INCMSPRATE, 300000, 100; + sc_start SC_INCFLEERATE, 300000, 100; + sc_start SC_INCHITRATE, 300000, 100; + sc_start SC_WALKSPEED, 300000, 150; + sc_start SC_ATTHASTE_POTION3, 300000, 50; + percentheal 100, 100; + dispbottom l("YOU WERE BLESSED BY JESUSALVA"); + dispbottom l("YOU CAN FEEL THE POWER FLOWING TROUGH YOU."); + detachrid(); + } + end; + +OnInstDestroy: + .@request = implode(.@atcmd_parameters$, " "); + if (.@request != 0) + instance_destroy(.@request); + end; + +OnInstCheck: + .@request$ = implode(.@atcmd_parameters$, " "); + dispbottom has_instance2(.@request$); + end; + +OnInit: + bindatcmd "k", "@k::OnCall", 60, 80, 1; + bindatcmd "servmsg", "@k::OnServMsg", 80, 99, 1; + + bindatcmd "blessing", "@k::OnBuff", 99, 100, 1; + bindatcmd "instcheck", "@k::OnInstCheck", 99, 100, 1; + bindatcmd "instdestr", "@k::OnInstDestroy", 99, 100, 1; + end; +} + +// kamibroadcast( message{, sender} ) +function script kamibroadcast { + .@msg$=getarg(0); + .@snd$=getarg(1, ""); + + // Send to #world + if (.@snd$ == "") + channelmes("#world", " "+.@msg$); + else + channelmes("#world", "[ "+.@snd$+" ] : "+.@msg$); + + // Make an announce + if (.@snd$ == "") + announce .@msg$, bc_all|bc_npc; + else + announce .@snd$+" : "+.@msg$, bc_all|bc_npc; + + return; +} diff --git a/npc/commands/language.txt b/npc/commands/language.txt new file mode 100644 index 00000000..ce7122f5 --- /dev/null +++ b/npc/commands/language.txt @@ -0,0 +1,60 @@ +// TMW2 Script +// Author: Jesusalva +// With code parts from Julia (Evol) + +// @lang atcommand +// Changes Language +// +// group lv: 0 +// group char lv: 0 +// log: False +// +// usage: +// @lang +// + +function script CMD_lang { + callfunc "checkclientversion"; + mesq l("Which language do you speak?"); + next; + asklanguage(LANG_IN_SHIP); + mes ""; + mesn; + mesq l("Ok, done."); + return; +} + +- script @lang 32767,{ + end; + +OnCall: + CMD_lang(); + close; + +OnTranslate: + // Implode, using a slash at whitespaces + .@request$ = implode(.@atcmd_parameters$, "%2F"); + // No NPC provided? + if (.@request$ == "") { + dispbottom l("Usage: @translate <npc file>"); + dispbottom l("Example: @translate Nard"); + dispbottom l("Example: @translate Elmo"); + dispbottom l("Example: @translate npc/002-1/arpan"); + dispbottom l("PS. Doesn't always work. You need an account at %s and to be at ManaPlus Team.", "@@https://www.transifex.com/arctic-games|Transifex@@"); + end; + } + // Add .txt extension of needed + if (!compare(.@request$, ".txt")) + .@request$ += ".txt"; + // Fix stuff for URL format + .@request$ = replacestr(.@request$, "/", "%2F"); + .@request$ = strtolower(.@request$); + // Give your translation link + dispbottom "@@https://www.transifex.com/arctic-games/moubootaur-legends/translate/#"+languagecode()+"/serverdata?q=occurrence%3A"+.@request$+"|Translate with Transifex@@"; + close; + +OnInit: + bindatcmd "lang", "@lang::OnCall", 0, 60, 0; + bindatcmd "translate", "@lang::OnTranslate", 0, 60, 0; + end; +} diff --git a/npc/commands/marry.txt b/npc/commands/marry.txt deleted file mode 100755 index ad2ed5fc..00000000 --- a/npc/commands/marry.txt +++ /dev/null @@ -1,68 +0,0 @@ -- script special-marry NPC32767,{ - .@target_id = getcharid(3, @args$); - if (.@target_id < 1 || !(isloggedin(.@target_id)) || .@target_id == BL_ID) goto L_NotFound; - if (PARTNER || get(PARTNER, .@target_id)) goto L_AlreadyMarried; - if (isin("014-1",29,36,34,39) == 0 && isin("001-1",20,27,22,27) == 0) goto L_NotInArea; - if (distance(BL_ID, .@target_id) != 1) goto L_AwayFromPartner; - if (BaseLevel < WEDDING_MIN_LEVEL || get(BaseLevel, .@target_id) < WEDDING_MIN_LEVEL) goto L_TooYoung; - if (getequipid(equip_shield) != 702 || getequipid(equip_shield, @args$) != 702) goto L_NoRing; - - if (get(@marriage[0], .@target_id) == BL_ID) goto L_Proceed; - - setarray @marriage[0], .@target_id, gettimetick(2); - addtimer (.timeout * 1000), strnpcinfo(0) + "::OnTimeout"; - announce strcharinfo(0) + " is asking " + strcharinfo(0, .@target_id) + " for marriage.", 2; - message strcharinfo(0, .@target_id), "Marriage : ##3##B" + strcharinfo(0) + " wishes to marry you. To accept, write `##1#marry "+strcharinfo(0)+"##3` within the next "+.timeout+" seconds."; - end; - -L_NotFound: - message strcharinfo(0), "Marriage : ##3##BThe target player is either not found or yourself."; - end; - -L_TooYoung: - message strcharinfo(0), "Marriage : ##3##BYou and your partner need to be at least level "+ WEDDING_MIN_LEVEL +"."; - end; - -L_NoRing: - message strcharinfo(0), "Marriage : ##3##BYou and your partner need to have ["+ getitemlink("WeddingRing") +"] equipped."; - end; - -L_AwayFromPartner: - message strcharinfo(0), "Marriage : ##3##BYou and your partner need to be standing next to each other."; - end; - -L_NotInArea: - message strcharinfo(0), "Marriage : ##3##BYou are not standing in a designated marriage area."; - end; - -L_Proceed: - if ((gettimetick(2) - .timeout) > get(@marriage[1], .@target_id)) goto L_TooLate; - PARTNER = CHAR_ID, .@target_id; - if (PARTNER == get(CHAR_ID, .@target_id)) goto L_Success; - PARTNER = 0, .@target_id; - PARTNER = 0; - end; - -L_Success: - announce strcharinfo(0) + " and " + strcharinfo(0, .@target_id) + " are now married.", 2; - end; - -OnTimeout: - goto L_TooLate; - -L_TooLate: - message strcharinfo(0), "Marriage : ##3##BThe proposal expired. Please try again."; - message strcharinfo(0, @marriage[0]), "Marriage : ##3##BThe proposal expired. Please try again."; - @marriage[0] = 0, @marriage[0]; - @marriage[0] = 0; - end; - -L_AlreadyMarried: - message strcharinfo(0), "Marriage : ##3##BYou"+ if_then_else(PARTNER, " are", "r partner is") +" already married."; - end; - -OnInit: - set .timeout, 30; // timeout for proposal - registercmd "#marry", strnpcinfo(0); // we NEED to use a # before `marry` because otherwise manaplus does not strip colors - end; -} diff --git a/npc/commands/music.txt b/npc/commands/music.txt deleted file mode 100755 index 5c62f008..00000000 --- a/npc/commands/music.txt +++ /dev/null @@ -1,33 +0,0 @@ -- script @music NPC32767,{ - callfunc "argv_splitter"; - if (GM < CMD_MUSIC && GM < G_SYSOP) goto L_GM; - if (@argv$[0] == "") goto L_Failed; - - gmlog "@music " + @args$; - .@find = array_search(@argv$[0], .find$); - if (.@find >= 0) - @argv$[0] = .replace$[max(.@find, 0)]; - .file$ = @argv$[0]; - areatimer 0, getmapname(), (POS_X - .range), (POS_Y - .range), (POS_X + .range), (POS_Y + .range), 0, strnpcinfo(0)+"::OnPC"; - message strcharinfo(0), "music : The music has ben temporarily changed."; - end; - -OnPC: - music .file$ + ".ogg"; - end; - -L_Failed: - message strcharinfo(0), "music : You must specify a music file."; - end; - -L_GM: - message strcharinfo(0), "music : GM command is level "+ CMD_MUSIC +", but you are level " + GM; - end; - -OnInit: - setarray .find$, "this", "cave", "chilling", "clouds", "dimond", "explorers", "gy", "reid", "magick", "mystique", "night", "ride", "sail", "snow", "forest", "xmas"; - setarray .replace$, "this", "cavesong", "chilling-environment", "clouds-calling", "dimonds-cove", "explorers-melody", "graveyard", "inquisitive-inn", "magick-real", "mystique-forest", "night-is-calling", "ride-of-the-valkyries", "sail-away", "snow-village", "the-forest", "white-christmas"; - set .range, 14; // FIXME: make this a const - registercmd chr(ATCMD_SYMBOL) + "music", strnpcinfo(0); - end; -} diff --git a/npc/commands/mute.txt b/npc/commands/mute.txt deleted file mode 100755 index 555fa724..00000000 --- a/npc/commands/mute.txt +++ /dev/null @@ -1,92 +0,0 @@ -- script @mute NPC32767,{ - callfunc "argv_splitter"; - if (GM < CMD_MUTE && GM < G_SYSOP) goto L_GM; - if (@argv$[1] == "") goto L_Failed; - - .@target_id = getcharid(3, @argv$[1]); - if (.@target_id < 1 || !(isloggedin(.@target_id))) goto L_Failed; - - if (@argv[0] > 120) - set @argv[0], 120; // max 2 hours - - gmlog "@mute " + @args$; - if (@argv[0] == 0) goto L_UnMute; - - message strcharinfo(0, .@target_id), "Server : ##BYou have been muted by a GM for "+@argv[0]+" minutes."; - MUTE_GLOBAL = 1, .@target_id; - #MUTE_UNTIL = (gettimetick(2) + (@argv[0] * 60)), .@target_id; - addtimer (@argv[0] * 60000) + 100, strnpcinfo(0) + "::OnCheckMute", .@target_id; - message strcharinfo(0), "mute : Player `"+strcharinfo(0, .@target_id)+"` has been muted for "+@argv[0]+" minutes."; - end; - -OnSTFU: - if (GM < CMD_MUTE && GM < G_SYSOP) goto L_GM; - callfunc "argv_splitter"; - if (@argv[0] < 1) set @argv[0], 1; - if (@argv[0] > 10) set @argv[0], 10; - gmlog "@stfu " + @argv[0]; - @stfu_nr = 0; - foreach 0, getmapname(), (POS_X - .range), (POS_Y - .range), (POS_X + .range), (POS_Y + .range), strnpcinfo(0)+"::OnSTFUPC"; - message strcharinfo(0), "mute : Every player within "+.range+" tiles have been muted for "+@argv[0]+" minutes. ["+@stfu_nr+"]"; - @stfu_nr = 0; - end; - -OnSTFUPC: - if (@target_id == BL_ID) end; - .@future = (gettimetick(2) + (@argv[0] * 60)); - if (get(#MUTE_UNTIL, @target_id) > .@future) end; // if player already has a mute, don't reduce it - MUTE_GLOBAL = 1, @target_id; - #MUTE_UNTIL = .@future, @target_id; - addtimer (@argv[0] * 60000) + 100, strnpcinfo(0) + "::OnCheckMute", @target_id; - @stfu_nr = @stfu_nr + 1; - end; - -OnPCLoginEvent: - if (#MUTE_UNTIL < 1) end; - .@s = (#MUTE_UNTIL - gettimetick(2)); - if (.@s < 5) goto L_ClearMute; - .@m = .@s / 60; - message strcharinfo(0), "Server : ##BYou have been muted for "+ max(1, .@m) +" minutes."; - MUTE_GLOBAL = 1; - addtimer (.@s * 1000) + 100, strnpcinfo(0) + "::OnCheckMute"; - end; - -L_ClearMute: - message strcharinfo(0), "Server : ##BYour mute has expired while you were away. You have been automatically unmuted."; - if ((#MUTE_UNTIL - gettimetick(2)) >= (0 - 900)) - wgm "=> Player `"+ strcharinfo(0) +"` has been automatically unmuted."; // only send if unmuted 15 minutes ago or less - #MUTE_UNTIL = 0; - end; - -OnCheckMute: - if (#MUTE_UNTIL < 1) end; - if (gettimetick(2) - #MUTE_UNTIL < 0) end; - message strcharinfo(0), "Server : ##BYou have been automatically unmuted."; - wgm "=> Player `"+ strcharinfo(0) +"` has been automatically unmuted."; - MUTE_GLOBAL = 0; - #MUTE_UNTIL = 0; - end; - -L_UnMute: - if (get(MUTE_GLOBAL, .@target_id)) - message strcharinfo(0, .@target_id), "Server : ##BYou have been unmuted by a GM."; - MUTE_GLOBAL = 0, .@target_id; - #MUTE_UNTIL = 0, .@target_id; - message strcharinfo(0), "mute : Player `"+strcharinfo(0, .@target_id)+"` has been unmuted."; - end; - -L_Failed: - message strcharinfo(0), "mute : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?"; - end; - -L_GM: - message strcharinfo(0), "mute : GM command is level "+ CMD_MUTE +", but you are level " + GM; - end; - -OnInit: - set .range, 14; // FIXME: this should be a const - registercmd chr(ATCMD_SYMBOL) + "mute", strnpcinfo(0); - registercmd chr(ATCMD_SYMBOL) + "stfu", strnpcinfo(0) + "::OnSTFU"; - registercmd chr(ATCMD_SYMBOL) + "areamute", strnpcinfo(0) + "::OnSTFU"; // alias of STFU - end; -} diff --git a/npc/commands/npctalk.txt b/npc/commands/npctalk.txt deleted file mode 100755 index b1179dc6..00000000 --- a/npc/commands/npctalk.txt +++ /dev/null @@ -1,22 +0,0 @@ -- script @npctalk NPC32767,{ - callfunc "argv_splitter"; - if (GM < CMD_NPCTALK && GM < G_SYSOP) goto L_GM; - if (@argv$[0] == "" || @argv$[1] == "") goto L_Failed; - if (getnpcid(@argv$[0]) < 1) goto L_Failed; - - gmlog "@npctalk " + @args$; - npctalk @argv$[0], @argv$[1]; - end; - -L_Failed: - message strcharinfo(0), "npctalk : Impossible to attach to the target npc. Did you try putting the name in \"quotation marks\"?"; - end; - -L_GM: - message strcharinfo(0), "npctalk : GM command is level "+ CMD_NPCTALK +", but you are level " + GM; - end; - -OnInit: - registercmd chr(ATCMD_SYMBOL) + "npctalk", strnpcinfo(0); - end; -} diff --git a/npc/commands/numa.txt b/npc/commands/numa.txt index 92cd5c9d..b8b0a924 100755 --- a/npc/commands/numa.txt +++ b/npc/commands/numa.txt @@ -1,8 +1,8 @@ - script SuperDebug NPC32767,{ if (GM < MAP_LOUNGE && GM < G_SYSOP && !debug) goto L_GM2; // make sure you can enter the gm lounge - if (target(BL_ID,getnpcid("Numa"),1)) goto L_Main; - npcaction 6, 12; + //if (target(BL_ID,getnpcid("Numa"),1)) goto L_Main; + //npcaction 6, 12; title "Numa"; goto L_Main; @@ -78,51 +78,18 @@ L_GM: goto L_close; L_Quest: - callfunc "QuestDebug"; + callfunc "GlobalQuestDebug"; goto L_close; L_close: close; OnInit: - registercmd chr(ATCMD_SYMBOL) + "numa", strnpcinfo(0); - registercmd chr(ATCMD_SYMBOL) + "superdebug", strnpcinfo(0); + registercmd "numa", strnpcinfo(0); + registercmd "superdebug", strnpcinfo(0); end; } 017-9,30,28,0 duplicate(SuperDebug) Numa NPC393 -function script QuestDebug { - goto L_ChooseContinent; -L_ChooseContinent: - mes "Choose a continent."; - next; - menu - "Argeas", L_Argeas, - "Close", L_Return; - -L_Argeas: - mes "Choose an area."; - next; - menu - "Woodland", L_Woodland, - "Choose a continent", L_ChooseContinent, - "Close", L_Return; - -L_Woodland: - mes "Choose a quest."; - next; - menu - "Illia Sisters", L_Valia, - "Choose an area", L_Argeas, - "Close", L_Return; - -L_Return: - return; - - -L_Valia: - callfunc "IlliaDebug"; - goto L_Return; -} diff --git a/npc/commands/pullrabbit.txt b/npc/commands/pullrabbit.txt deleted file mode 100755 index 8ca0a69a..00000000 --- a/npc/commands/pullrabbit.txt +++ /dev/null @@ -1,25 +0,0 @@ -- script @pullrabbit NPC32767,{ - if (GM < EVT_KILLTHEGM && GM < G_SYSOP) end; - if (getequipid(equip_head) != 888) end; - getinventorylist; - if ((checkweight("MurdererCrown", 1) == 0) || (@inventorylist_count == 100)) - goto L_Inventory; - // Get the current reward of the event. This may be changed later - getitem "MurdererCrown", 1; - // Set HP and SP to max - heal MaxHp, MaxSp; - // Display an effect - misceffect FX_CHANNELLING_CAST, strcharinfo(0); - // Log the usage of this spell - gmlog strcharinfo(0)+" used the Magic GM Top Hat."; - end; - -L_Inventory: - message strcharinfo(0), "You cannot create this item. You're too heavy or you don't have a free slot."; - end; - -OnInit: - registercmd chr(ATCMD_SYMBOL) + "pullrabbit", strnpcinfo(0); - registercmd chr(MAGIC_SYMBOL) + "pullrabbit", strnpcinfo(0); // former pullrabbit invocation - end; -} diff --git a/npc/commands/python.txt b/npc/commands/python.txt new file mode 100644 index 00000000..5273731c --- /dev/null +++ b/npc/commands/python.txt @@ -0,0 +1,27 @@ +// The Mana World script +// Author: Gumi <gumi@themanaworld.org> +// Author: Jesusalva <jesusalva@themanaworld.org> +// +// Stomp stomp stomp (use with caution) + +- script @python 32767,{ + end; + +OnCall: + specialeffect(34, AREA, playerattached()); + .@zone$=getmapinfo(MAPINFO_ZONE, .@mapa$); + if (.@zone$ == "MMO") + end; + sc_start SC_CASH_DEATHPENALTY, 1000, 1; + addtimer 380, .name$+"::OnKill"; + end; + +OnKill: + percentheal -100, -100; + //dispbottom l("Oh look, it is Cupid!"); + end; + +OnInit: + bindatcmd "python", "@python::OnCall", 60, 60, 1; + end; +} diff --git a/npc/commands/remotecmd.txt b/npc/commands/remotecmd.txt deleted file mode 100755 index 20f33f8c..00000000 --- a/npc/commands/remotecmd.txt +++ /dev/null @@ -1,61 +0,0 @@ -- script @remotecmd NPC32767,{ - callfunc "argv_splitter"; - if (GM < CMD_REMOTECMD && GM < G_SYSOP) goto L_GM; // check if you can use it on self - .@target_id = BL_ID; - .@t$ = @argv$[1]; - setarray @remotecmd[0], 0, 1, 1; // nr, to_self, multi_target - if (.@t$ == "map") goto L_Map; - if (.@t$ == "map!") goto L_AllMap; - if (.@t$ == "area") goto L_Area; - if (.@t$ == "area!") goto L_AllArea; - if (.@t$ != "") set .@target_id, getcharid(3, @argv$[1]); - if (.@t$ != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self - - @target_id = .@target_id; - set @remotecmd[2], 0; // only one target - addtimer 0, strnpcinfo(0) + "::OnPC"; - end; - -L_Map: - set @remotecmd[1], 0; // do not include self - goto L_AllMap; - -L_AllMap: - foreach 0, getmapname(), 0, 0, 32767, 32767, strnpcinfo(0)+"::OnPC"; - goto L_Success; - -L_Area: - set @remotecmd[1], 0; // do not include self - goto L_AllMap; - -L_AllArea: - foreach 0, getmapname(), (POS_X - .range), (POS_Y - .range), (POS_X + .range), (POS_Y + .range), strnpcinfo(0)+"::OnPC"; - goto L_Success; - -OnPC: - if (@target_id == BL_ID && @remotecmd[1] < 1) - end; - remotecmd @argv$[0], strcharinfo(0, @target_id); - @remotecmd[0] = @remotecmd[0] + 1; - if (@remotecmd[2] < 1) - goto L_Success; - end; - -L_Success: - gmlog "@remotecmd " + @args$; - message strcharinfo(0), "remotecmd : The operation succeeded. ["+ @remotecmd[0] + "]"; - end; - -L_Failed: - message strcharinfo(0), "remotecmd : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?"; - end; - -L_GM: - message strcharinfo(0), "remotecmd : GM command is level "+ CMD_REMOTECMD +", but you are level " + GM; - end; - -OnInit: - set .range, 14; // visible range - registercmd chr(ATCMD_SYMBOL) + "remotecmd", strnpcinfo(0); - end; -} diff --git a/npc/commands/resync.txt b/npc/commands/resync.txt new file mode 100644 index 00000000..246bfa88 --- /dev/null +++ b/npc/commands/resync.txt @@ -0,0 +1,45 @@ +// TMW2 script +// Author: Jesusalva <admin@tmw2.org> +// +// Introduces @resync +// +// It'll cast slide to your own position +// Hopefully making client update your real position without causing server warning +// +// This also introduces @resyncall +// Which is an alias for @refresh and causes client to reload the whole map, +// Including yourself and monsters. + +- script @resync 32767,{ + end; + +// Soft Resync +OnCall: + if (ispcdead()) { + dispbottom l("Impossible to resync: You are dead."); + end; + } + if (@rsync_delay > gettimetick(2)) { + dispbottom l("Not resync'ing to prevent flood."); + end; + } + getmapxy(.@m$, .@x, .@y, 0); + slide .@x, .@y; + @rsync_delay=gettimetick(2)+rand2(3,5); + end; + +// Hard Resync +OnCallRefresh: + if (@rsync_delay > gettimetick(2)) { + dispbottom l("Not resync'ing to prevent flood."); + end; + } + @rsync_delay=gettimetick(2)+rand2(3,5); + atcommand("@refresh"); + end; + +OnInit: + bindatcmd "resync", "@resync::OnCall", 0, 60, 0; + bindatcmd "resyncall", "@resync::OnCallRefresh", 0, 60, 0; + end; +} diff --git a/npc/commands/warp.txt b/npc/commands/warp.txt deleted file mode 100755 index d2eee4e2..00000000 --- a/npc/commands/warp.txt +++ /dev/null @@ -1,56 +0,0 @@ -- script @warp NPC32767,{ - callfunc "argv_splitter"; - if (@argv$[3] == "" && @argv$[2] == "" && @argv$[1] != "" && @argv[1] < 2) - @argv$[3] = @argv$[1]; - - .@n$ = if_then_else(@argv$[3] != "", "char", "") + "warp"; - if (GM < CMD_WARP && GM < G_SYSOP) goto L_GM; // check if you can use it on self - .@target_id = BL_ID; - if (@argv$[3] != "") set .@target_id, getcharid(3, @argv$[3]); - if (@argv$[3] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self - if (@argv$[3] != "" && GM < CMD_CHARWARP && GM < G_SYSOP) goto L_GM; // when target is not self, use charwarp permission - - if (@argv$[0] == "") - @argv$[0] = getmapname(); - - setarray @map_anchor$[0], "", "", "", ""; - callfunc "map2anchor"; - - gmlog "@"+.@n$+" " + @args$; - if (.@target_id != BL_ID) - message strcharinfo(0), .@n$+" : The operation succeeded."; - - @GMWARP_map$ = @map_anchor$[0]; - @GMWARP_x = if_then_else(@argv[1] > 1, @argv[1], @map_anchor$[1]); - @GMWARP_y = if_then_else(@argv[2] > 1, @argv[2], @map_anchor$[2]); - addtimer 0, strnpcinfo(0) + "::OnWarp", .@target_id; - - if (@map_anchor$[3] == "no" && @knows_anchors < 1) - goto L_SuggestAnchors; - end; - -L_SuggestAnchors: - message strcharinfo(0), .@n$+" : The warp command has been improved. You might want to consider using [@@https://www.themanaworld.org/index.php/Dev:GM_Commands/anchors|map anchors@@]."; - @knows_anchors = 1; - end; - -OnWarp: - warp @GMWARP_map$, @GMWARP_x, @GMWARP_y; - @GMWARP_map$ = ""; - @GMWARP_x = 0; - @GMWARP_y = 0; - end; - -L_Failed: - message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?"; - end; - -L_GM: - message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARWARP, CMD_WARP) +", but you are level " + GM; - end; - -OnInit: - registercmd chr(ATCMD_SYMBOL) + "warp", strnpcinfo(0); - registercmd chr(ATCMD_SYMBOL) + "charwarp", strnpcinfo(0); - end; -} diff --git a/npc/commands/zeny.txt b/npc/commands/zeny.txt deleted file mode 100755 index de1013eb..00000000 --- a/npc/commands/zeny.txt +++ /dev/null @@ -1,78 +0,0 @@ -- script @zeny NPC32767,{ - callfunc "argv_splitter"; - .@n$ = if_then_else(@argv$[1] != "", "char", "") + "Zeny"; - if (GM < CMD_ZENY && GM < G_SYSOP) goto L_GM; // check if you can use it on self - .@target_id = BL_ID; - if (@argv$[1] != "") set .@target_id, getcharid(3, @argv$[1]); - if (@argv$[1] != "" && !(isloggedin(.@target_id))) goto L_Failed; // do NOT fallback to self - if (@argv$[1] != "" && GM < CMD_CHARZENY && GM < G_SYSOP) goto L_GM; // when target is not self, use charZeny permission - if (@argv$[0] == "--") goto L_Remove; - if (@argv$[0] == "---") goto L_RemoveAll; - if (@argv$[0] == "++") goto L_Max; - if (@argv$[0] == "+++") goto L_MaxAll; - set .@delta, @argv[0]; // ± Zeny - set .@Zeny, get(Zeny, .@target_id); // get the number of Zeny in char - set .@bank, get(#BankAccount, .@target_id); // get number of Zeny in (world) account - set .@new_Zeny, .@Zeny + .@delta; // new balance in char - if (.@new_Zeny < 0) goto L_MaybeRemoveBank; // Zeny would be below 0 so check if we can take from bank - if (.@new_Zeny > .max_Zeny) goto L_MaybeAddBank; // Zeny would be over the limit so check if we can store in bank - Zeny = (.@Zeny + .@delta), .@target_id; - goto L_Success; - -L_Remove: - Zeny = 0, .@target_id; - goto L_Success; - -L_RemoveAll: - Zeny = 0, .@target_id; - #BankAccount = 0, .@target_id; - goto L_Success; - -L_Max: - Zeny = .max_Zeny, .@target_id; - goto L_Success; - -L_MaxAll: - Zeny = .max_Zeny, .@target_id; - #BankAccount = .max_int, .@target_id; - goto L_Success; - -L_MaybeAddBank: - .@new_bank = (.@bank + (.@new_Zeny - .max_Zeny)); - if (.@new_bank > .max_int || .@new_bank < 0) goto L_OutOfBounds; - Zeny = .max_Zeny, .@target_id; - #BankAccount = .@new_bank, .@target_id; - goto L_Success; - -L_MaybeRemoveBank: - if ((.@bank + .@new_Zeny) < 0) goto L_OutOfBounds; - Zeny = 0, .@target_id; - #BankAccount = (.@bank + .@new_Zeny), .@target_id; - goto L_Success; - -L_OutOfBounds: - // XXX: maybe we could also take from other chars from the same accout? - message strcharinfo(0), .@n$+" : Impossible to proceed! This would cause the player to have less than 0 Zeny or more than " + .max_int + "."; - end; - -L_Failed: - // XXX: should we allow GMs to change Zeny of users that are not logged in? - message strcharinfo(0), .@n$+" : Impossible to attach to the target player. Did you try putting the name in \"quotation marks\"?"; - end; - -L_Success: - gmlog "@Zeny " + @args$; - message strcharinfo(0), .@n$+" : The operation succeeded."; - end; - -L_GM: - message strcharinfo(0), .@n$+" : GM command is level "+ if_then_else(@argv$[1] != "", CMD_CHARZENY, CMD_ZENY) +", but you are level " + GM; - end; - -OnInit: - set .max_Zeny, 1000000000; // hardcoded in tmwa - set .max_int, 2147483647; // max int32 value - registercmd chr(ATCMD_SYMBOL) + "Zeny", strnpcinfo(0); - registercmd chr(ATCMD_SYMBOL) + "charZeny", strnpcinfo(0); - end; -} diff --git a/npc/functions/array.txt b/npc/functions/array.txt new file mode 100644 index 00000000..d433abd7 --- /dev/null +++ b/npc/functions/array.txt @@ -0,0 +1,464 @@ +// Evol Script +// Author: Gumi + +// array_pad(<array>, <size>, <value>) +// prepend or append <value> until the array is of <size> size +// returns the amount added on success, or false (0) if nothing changed + +function script array_pad { + .@index = getarrayindex(getarg(0)); // passed index + .@count = getarraysize(getarg(0)) - .@index; // actual size + .@size = getarg(1); // desired size + .@absolute = (.@size >= 0 ? .@size : -(.@size)); // |size| + .@delta = .@absolute - .@count; // amount to fill + + if (.@absolute <= .@count) { + return false; // nothing to do + } + + if (.@size < 0) { + copyarray(getelementofarray(getarg(0), .@index + .@delta), getarg(0), .@count); // shift to the right + cleararray(getarg(0), getarg(2), .@delta); // prepend + } else { + cleararray(getelementofarray(getarg(0), .@index + .@count), getarg(2), .@delta); // append + } + + return .@delta; +} + + + +// array_replace(<array>, <needle>, <replace>{, <neq>}) +// replace every occurence of <needle> with <replace> +// returns the number of replaced elements + +function script array_replace { + .@size = getarraysize(getarg(0)); + .@neq = getarg(3, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + set(getelementofarray(getarg(0), .@i), getarg(2)); + ++.@count; + } + } + + freeloop(false); + return .@count; +} + + + +// array_find(<array>, <needle>{, <neq>}) +// return the index of the first occurence of <needle> in <array> +// if not found it returns -1 + +function script array_find { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + freeloop(false); + return .@i; + } + } + + freeloop(false); + return -1; +} + + + +// array_rfind(<array>, <needle>{, <neq>}) +// return the index of the last occurence of <needle> in <array> +// if not found it returns -1 + +function script array_rfind { + .@min = getarrayindex(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = (getarraysize(getarg(0)) - 1); .@i >= .@min; --.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + freeloop(false); + return .@i; + } + } + + freeloop(false); + return -1; +} + + + +// array_exists(<array>, <needle>{, <neq>}) +// return true or false accordingly if <needle> is found in <array> + +function script array_exists { + return array_find(getarg(0), getarg(1), getarg(2, false)) > -1; +} + + + +// array_count(<array>, <needle>{, <neq>}) +// counts the number of occurrence of <needle> in the <array> + +function script array_count { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + ++.@count; + } + } + + freeloop(false); + return .@count; +} + + + +// array_entries(<array>) +// returns the number of non-empty entries + +function script array_entries { + if (isstr(getarg(0)) == 1) { + return array_count(getarg(0), "", true); + } + return array_count(getarg(0), 0, true); +} + + + +// array_remove(<array>, <needle>{, <neq>}) +// removes every occurrence of <needle> in the <array> while shifting left + +function script array_remove { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if ((.@neq && (getelementofarray(getarg(0), .@i) != getarg(1))) || + (!(.@neq) && (getelementofarray(getarg(0), .@i) == getarg(1)))) { + deletearray(getelementofarray(getarg(0), .@i), 1); // shift left + ++.@count; // increase the counter + --.@size; // reduce the size + --.@i; // step back + } + } + + freeloop(false); + return .@count; +} + + + +// array_reverse(<array>) +// reverses the array + +function script array_reverse { + .@index = getarrayindex(getarg(0)); + .@size = getarraysize(getarg(0)); + freeloop(true); + + for (.@i = .@index; .@i < ((.@size + .@index) / 2); ++.@i) { + swap(getelementofarray(getarg(0), .@i), getelementofarray(getarg(0), .@size + .@index - 1 - .@i)); // a <> b + } + + freeloop(false); + return true; +} + + + +// array_sum(<array>) +// return the sum of every element of the array + +function script array_sum { + .@size = getarraysize(getarg(0)); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@sum += getelementofarray(getarg(0), .@i); + } + + freeloop(false); + return .@sum; +} + + + +// array_difference(<array>) +// return the difference of every element of the array + +function script array_difference { + .@size = getarraysize(getarg(0)); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@diff -= getelementofarray(getarg(0), .@i); + } + + freeloop(false); + return .@diff; +} + + + +// array_shift(<array>) +// returns the first element of the array and removes it, while shifting left + +function script array_shift { + if (isstr(getarg(0)) == 1) { + .@val$ = getarg(0); + } else { + .@int = true; + .@val = getarg(0); + } + + deletearray(getarg(0), 1); // shift left + + return .@int ? .@val : .@val$; +} + + + +// array_unshift(<array>, <value>) +// adds <value> to the start of the array, while shifting right +// returns the new size + +function script array_unshift { + .@size = getarraysize(getarg(0)) + 1; + array_pad(getarg(0), -(.@size - getarrayindex(getarg(0))), getarg(1)); + return .@size; +} + + + +// array_pop(<array>) +// returns the last element of the array and removes it + +function script array_pop { + .@last = getarraysize(getarg(0)) - 1; + + if (isstr(getelementofarray(getarg(0), .@last)) == 1) { + .@val$ = getelementofarray(getarg(0), .@last); + } else { + .@int = true; + .@val = getelementofarray(getarg(0), .@last); + } + + deletearray(getelementofarray(getarg(0), .@last), 1); + + return .@int ? .@val : .@val$; +} + + + +// TODO: Rename to array_append >.< +// array_push(<array>, <value>) +// adds <value> to the end of the array +// returns the new size + +function script array_push { + .@size = getarraysize(getarg(0)); + set(getelementofarray(getarg(0), .@size), getarg(1)); + return .@size + 1; +} + + + +// array_shuffle(<array>) +// shuffles the array + +function script array_shuffle { + .@index = getarrayindex(getarg(0)); + .@size = getarraysize(getarg(0)) - .@index; + freeloop(true); + + if (isstr(getarg(0)) == 1) { + copyarray(.@tmp$[0], getarg(0), .@size); + for (; .@size >= 1; --.@size) { + set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp$[rand(.@size)])); + } + } else { + copyarray(.@tmp[0], getarg(0), .@size); + for (; .@size >= 1; --.@size) { + set(getelementofarray(getarg(0), .@index + .@size - 1), array_shift(.@tmp[rand(.@size)])); + } + } + + freeloop(false); + return true; +} + + + +// array_unique(<array>{, <threshold>}) +// allows entries to appear up to <threshold> in the array + +function script array_unique { + .@size = getarraysize(getarg(0)); + .@max = getarg(1, 1); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@count = 1; + for (.@e = .@i + 1; .@e < .@size; ++.@e) { + if (getelementofarray(getarg(0), .@i) == getelementofarray(getarg(0), .@e)) { + if (++.@count >= .@max) { + deletearray(getelementofarray(getarg(0), .@e), 1); + ++.@removed; // increase counter + --.@size; // reduce size + --.@e; // step back + } + } + } + } + + freeloop(false); + return .@removed; +} + + + +// array_diff(<array1>, <array2>{, <array>...}, <array>) +// compares array1 against one or more other arrays and fills the last array +// with the values in array1 that are not present in any of the other arrays +// returns the number of entries not matching + +function script array_diff { + .@size = getarraysize(getarg(0)); + .@index = getarrayindex(getarg(0)); + freeloop(true); + + for (.@a = 1; .@a < (getargcount() - 1); ++.@a) { + for (.@i = .@index; .@i < .@size; ++.@i) { + if (!array_exists(getarg(.@a), getelementofarray(getarg(0), .@i))) { + array_push(getarg(getargcount() - 1), getelementofarray(getarg(0), .@i)); + ++.@count; + } + } + } + + freeloop(false); + return .@count; +} + + + +// array_filter(<array>, "<function>") +// filters the array using a callback function + +function script array_filter { + .@size = getarraysize(getarg(0)); + .@neq = getarg(2, false); + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + .@eq = callfunc(getarg(1), getelementofarray(getarg(0), .@i)) != false; + if ((.@neq && .@eq) || (!(.@neq) && !(.@eq))) { + deletearray(getelementofarray(getarg(0), .@i), 1); // shift left + ++.@count; // increase the counter + --.@size; // reduce the size + --.@i; // step back + } + } + + freeloop(false); + return .@count; +} + +// array_highest(<array>) +// Returns the index of the highest value in <array> +// NOTE: Array must be an INT array! + +function script array_highest { + .@size = getarraysize(getarg(0)); + .@win=0; + .@idx=0; + .@dw=false; + freeloop(true); + + for (.@i = getarrayindex(getarg(0)); .@i < .@size; ++.@i) { + if (getelementofarray(getarg(0), .@i) > .@win) { + .@win=getelementofarray(getarg(0), .@i); + .@idx=.@i; + if (.@dw) { + deletearray .@draw; + .@dw=false; + } + } else if (getelementofarray(getarg(0), .@i) == .@win) { + if (!.@dw) + array_push(.@draw, .@idx); + array_push(.@draw, .@i); + .@dw=true; + } + } + + // Will we return .@idx or do we need to draw a loot? + freeloop(false); + if (.@dw) + return any_of(.@draw); + else + return .@idx; +} + +// relative_array_random(<array: 0, {[value, probability]..}>) +// returns a random entry from the array, by relative probability +// the first key of the array should be 0 and every entries are a tuple +// of [value, probability] + +function script relative_array_random { + .@is_str = getdatatype(getarg(0)) & DATATYPE_STR; + .@total_prob = getelementofarray(getarg(0), 0); + .@initial_index = getarrayindex(getarg(0)); + .@initial_index = .@initial_index ? .@initial_index : 1; + freeloop(true); + + if (.@total_prob < 1 || getarg(1, false)) + { + // first calculation, or forced re-calculation + .@total_prob = 0; + .@size = getarraysize(getarg(0)); + + for (.@i = .@initial_index + 1; .@i < .@size; .@i += 2) { + if (.@is_str) { + .@total_prob += max(1, atoi(getelementofarray(getarg(0), .@i))); + } else { + .@total_prob += max(1, getelementofarray(getarg(0), .@i)); + } + } + + // we cache on the first key + set(getelementofarray(getarg(0), 0), .@total_prob); + } + + .@target_sum = rand(0, .@total_prob); + + for (.@i = .@initial_index; .@sum < .@target_sum; .@i += 2) { + if (.@is_str) { + .@sum += atoi(getelementofarray(getarg(0), .@i + 1)); + } else { + .@sum += getelementofarray(getarg(0), .@i + 1); + } + + if (.@sum >= .@target_sum) { + break; + } + } + + freeloop(false); + return getelementofarray(getarg(0), .@i); +} + diff --git a/npc/functions/asklanguage.txt b/npc/functions/asklanguage.txt new file mode 100644 index 00000000..32e0f7bc --- /dev/null +++ b/npc/functions/asklanguage.txt @@ -0,0 +1,72 @@ +// TMW2 script +// Evol functions. +// Author: +// Reid, Jesusalva +// Description: +// Function setting the player language + +function script languagecode { + switch (Lang) { + case LANG_PTBR: + return "pt_BR"; + case LANG_FR: + return "fr"; + case LANG_DE: + return "de"; + case LANG_ES: + return "es"; + default: + return "en"; + } +} + +function script asklanguage { + + dispbottom col("We need help with translations. [@@help://translate|Learn more@@]", 1); + switch (getarg(0, LANG_IN_SHIP)) + { + case LANG_ON_SEA: + setarray .@messages$[0], "I hear you... (English)", // English + "Eu te ouço... (Português)", // Portuguese + "Je vous entends... (Français)", // French + "Ich höre euch... (Deutsch)", // German + "Te oigo... (Español)"; // Spanish + break; + case LANG_IN_SHIP: + setarray .@messages$[0], "I speak English.", // English + "Eu falo Português.", // Portuguese + "Je parle français.", // French + "Ich spreche Deutsch.", // German + "Hablo Español."; // Spanish + break; + default: + return; + } + + setarray .@flags$[0], "flags/en", + "flags/pt_BR", + "flags/fr", + "flags/de", + "flags/es"; + + .@menustr$ = ""; + .@separator$ = ":"; + + for (.@i = 0; .@i <= MAX_LANG; .@i++) + { + if (.@i == MAX_LANG) { + .@separator$ = ""; + } + .@menustr$ = .@menustr$ + .@flags$[.@i] + "|" + .@messages$[.@i] + .@separator$; + } + + select(.@menustr$); + + .@lang = @menu - 1; + + if (.@lang >= 0 || .@lang <= MAX_LANG) { + Lang = .@lang; + } + + return; +} diff --git a/npc/functions/filters.txt b/npc/functions/filters.txt new file mode 100644 index 00000000..01e3a856 --- /dev/null +++ b/npc/functions/filters.txt @@ -0,0 +1,126 @@ +// TMW2 scripts. +// Authors: +// Jesusalva +// Description: +// Several filters + +// filter_always( id ) +function script filter_always { + return true; +} + +// filter_onlyme( id ) +function script filter_onlyme { + return (getarg(0) == getcharid(3)); +} + +// filter_notme( id ) +function script filter_notme { + return (getarg(0) != getcharid(3)); +} + +// filter_sameguild( id ) +function script filter_sameguild { + if (getcharid(2) < 1) + return false; + return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); +} + +// filter_sameguildnotyou( id ) +function script filter_sameguildnotyou { + if (getcharid(2) < 1) + return false; + if (getarg(0) == getcharid(3)) + return false; + return (strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); +} + +// filter_sameparty( id ) +function script filter_sameparty { + if (getcharid(1) < 1 && getarg(0) != getcharid(3)) + return false; + return (strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1)); +} + +// filter_sameguildorparty( id ) +function script filter_sameguildorparty { + if (getcharid(2) < 1 && getcharid(1) < 1) + return false; + .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1)); + .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); + return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild)); +} + +// filter_sameguildorpartynotyou( id ) +function script filter_sameguildorpartynotyou { + if (getarg(0) == getcharid(3)) + return false; + if (getcharid(2) < 1 && getcharid(1) < 1) + return false; + .@party=(strcharinfo(1, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(1)); + .@guild=(strcharinfo(2, "~!<mk>@tmw2.org", getarg(0)) == strcharinfo(2)); + return ((getcharid(1) > 0 && .@party) || (getcharid(2) > 0 && .@guild)); +} + +// filter_hostile( id ) +function script filter_hostile { + //.@type=getunitdata(getarg(0), UDT_TYPE); + .@type=getunittype(getarg(0)); + .@chkid=getarg(0); + + // Players outside PVP + if (.@type == UNITTYPE_PC) { + getmapxy(.@m$, .@x, .@y, .@type, .@chkid); + if (!ispvpmap(.@m$)) + return false; + // FIXME: We already have !(filter_sameguildorparty()) + // We might be over-processing this + // Honor party flag + if (!getmapflag(.@mapa$, mf_pvp_noparty) && + getcharid(1) == getcharid(1, strcharinfo(0, "", .@chkid))) + return false; + // Honor guild flag + if (!getmapflag(.@mapa$, mf_pvp_noguild) && + getcharid(2) == getcharid(2, strcharinfo(0, "", .@chkid))) + return false; + } + + // Monsters + if (.@type == UNITTYPE_MOB) + return true; + + // NPCs + if (.@type == UNITTYPE_NPC) + return false; + + // Homunculus + if (.@type == UNITTYPE_HOM) + .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID)); + + // Pets + if (.@type == UNITTYPE_PET) + .@chkid=getunitdata(getarg(0), UDT_MASTERAID); + + // Mercenaries + if (.@type == UNITTYPE_MER) + .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID)); + + // Elementals + if (.@type == UNITTYPE_ELEM) + .@chkid=charid2rid(getunitdata(getarg(0), UDT_MASTERCID)); + + //debugmes "filter_hostile: Filtering %d (original %d) (BL %d)", .@chkid, getarg(0), .@type; + // Players (and slaves) + return !(filter_sameguildorparty(.@chkid)); +} + +// filter_friendly( id ) +function script filter_friendly { + return !(filter_hostile(getarg(0))); +} + +// filter_notboss( id ) +function script filter_notboss { + return (!(getunitdata(getarg(0), UDT_MODE) & MD_BOSS)); +} + diff --git a/npc/functions/goodbye.txt b/npc/functions/goodbye.txt new file mode 100644 index 00000000..b5214618 --- /dev/null +++ b/npc/functions/goodbye.txt @@ -0,0 +1,152 @@ +// Evol functions. +// Authors: +// gumi +// Reid +// Description: +// script terminator functions + + + +// goodbye_msg +// Tell a random goodbye sentence. +// Variables: +// .@rand = Random number between the number of "goodbye" choice. + +function script goodbye_msg { + setarray .byemsg$[0], + l("See you!"), + l("See you later!"), + l("See you soon!"), + l("Bye!"), + l("Farewell."), + l("Bye then!"), + l("Goodbye."), + l("Bye for now."), + l("Talk to you soon!"), + l("Talk to you later!"), + l("Have a good day!"), + l("Cheers!"), + l("Take care!"); + + return any_of(.byemsg$); +} + + + +// cwarp +// Closes the dialog, then warps the player. +// You almost always want to use this instead of `warp`. +// usage: +// cwarp; +// cwarp x, y; +// cwarp map, x, y; + +function script cwarp { + .@map$ = getarg(0, ""); + .@x = getarg(1, 0); + .@y = getarg(2, 0); + + if (getargcount() > 0 && getargcount() < 3) + { + .@npc$ = strnpcinfo(0); + .@map$ = getvariableofnpc(.map$, .@npc$); + .@x = getarg(0); + .@y = getarg(1); + } + + getmapxy .@pc_map$, .@pc_x, .@pc_y, UNITTYPE_PC; // get char location + + closedialog; // XXX: maybe send closeclientdialog in the future + + if (getargcount() < 1) + { + warp .@pc_map$, .@pc_x, .@pc_y; // no arguments, just refresh + close; + } + + if (.@map$ == .@pc_map$) + { + if (.@pc_x == .@x && .@pc_y == .@y) + { + close; // same location, don't move + } + + else + { + slide .@x, .@y; // same map, slide instead of full warp + close; + } + } + + warp .@map$, .@x, .@y; // different map, warp to given location + close; +} + + + +// cshop +// closes the dialog, then opens a shop +// if no npc is given, calls "#<npc> $" + +function script cshop { + closedialog; // XXX: maybe send closeclientdialog in the future + shop getarg(0, "#" + strnpcinfo(0) + " $"); + //close; => the shop buildin already sends close, and is a terminator itself +} + + + +// cstorage +// closes the dialog, then opens storage + +function script cstorage { + closedialog; // XXX: maybe send closeclientdialog in the future + openstorage; + close; +} + + + +// bye +// closes the dialog without waiting for the player to press close +// can also display an emote + +function script bye { + .@emote = getarg(0, -1); + closedialog; // XXX: maybe send closeclientdialog in the future + + if (.@emote >= 0) + emotion .@emote; + + close; +} + + + +// goodbye +// same as bye, but also displays a canned message +// can also display an emote + +function script goodbye { + npctalkonce(goodbye_msg()); + bye getarg(0, -1); +} + + + +// goodbye2 +// Waits for the player to press close, displays a canned message, +// ends execution. +// Can also display an emote + +function script goodbye2 { + .@emote = getarg(0, -1); + + close2; + npctalkonce(goodbye_msg()); + + if (.@emote >= 0) + emotion .@emote; + + end; +} diff --git a/npc/functions/inc_sc_bonus.txt b/npc/functions/inc_sc_bonus.txt new file mode 100644 index 00000000..8e6b6e5b --- /dev/null +++ b/npc/functions/inc_sc_bonus.txt @@ -0,0 +1,72 @@ +// TMW-2 Script. +// Author: +// Jesusalva +// Description: +// Applies effects for INC_* (STR doesn't exist) +// Valid values: INCAGI INCVIT INCINT INCDEX INCLUK INCHIT INCFLEE SC_FURY +// Doesn't works: SC_STRUP +// Works if .@min == .@max: INCMHP INCMHPRATE INCMSP INCMSPRATE +/// Untested Values: WALKSPEED (reverse logic) INVINCIBLE (broken) +// PS. SC_FURY causes crit rate to increase +// +// Variables: +// .@delay Second of buffing +// .@type SC_* +// .@min Min amount of type +// .@max Max amount of type (optional) + +// SC_Bonus(delay, SC, min{, max}) +function script SC_Bonus { + .@delay=getarg(0); + .@type=getarg(1); + .@min=getarg(2); + .@max=getarg(3, .@min); + if (.@delay <= 0) + return false; + + // Get the bonus value + if (.@min != .@max) + .@bonus=rand2(.@min, .@max); + else + .@bonus=.@min; + + // Remaining time and effect conversion + .@v=getstatus(.@type, 1); + .@t=getstatus(.@type, 5); + + // Convert remaining time to seconds, rounded down + if (.@t > 1000) + .@t=.@t/1000; + else + .@t=0; + + // If there was effect previously, get ponderate average + if (.@v > 0) + .@v=ponderate_avg(.@bonus, .@delay, .@v, .@t); + else + .@v=.@bonus; + + // Update time value to ms and to stack + .@t+=.@delay; + .@t*=1000; + + // Debug print if needed + if (debug || $@GM_OVERRIDE) + debugmes "Effect %d (+%d percent) for %d ms", .@type, .@bonus, .@t; + + // Restart the bonus + sc_end .@type; + sc_start .@type,.@t,.@v; + return true; +} + +- script inc_sc_bonus -1,{ +OnUse: + SC_Bonus(@delay, @type, @min, @max); + @delay=0; + @type=0; + @min=0; + @max=0; + end; +} + diff --git a/npc/functions/input.txt b/npc/functions/input.txt new file mode 100644 index 00000000..0a510b74 --- /dev/null +++ b/npc/functions/input.txt @@ -0,0 +1,110 @@ +// Evol functions. +// Author: +// 4144 +// Jesusalva +// Description: +// Input utility functions +// Variables: +// none + +function script menuint { + deletearray .@vals; + .@menustr$ = ""; + .@cnt = 0; + + for (.@f = 0; .@f < getargcount(); .@f = .@f + 2) + { + if (getarg(.@f) != "") + { + .@menustr$ = .@menustr$ + getarg(.@f) + ":"; + .@vals[.@cnt] = getarg(.@f + 1); + .@cnt ++; + } + } + + .@vals[.@cnt] = -1; + @menu = 255; + @menuret = -1; + select(.@menustr$); + if (@menu == 255) + return -1; + + @menu --; + if (@menu < 0 || @menu >= getarraysize(.@vals) - 1) + return -1; + + @menuret = .@vals[@menu]; + return @menuret; +} + +function script menustr { + deletearray .@vals$; + .@menustr$ = ""; + .@cnt = 0; + + for (.@f = 0; .@f < getargcount(); .@f = .@f + 2) + { + if (getarg(.@f) != "") + { + .@menustr$ = .@menustr$ + getarg(.@f) + ":"; + .@vals$[.@cnt] = getarg(.@f + 1); + .@cnt ++; + } + } + + @menu = 255; + @menuret = -1; + select(.@menustr$); + if (@menu == 255) + return ""; + + @menu --; + if (@menu < 0 || @menu >= getarraysize(.@vals$)) + return ""; + + @menuret$ = .@vals$[@menu]; + return @menuret$; +} + +// menuint2(<array>) +function script menuint2 { + .@menustr$=""; + + if (!(getdatatype(getarg(0)) & DATATYPE_VAR)) + Exception("Inadequate argument type - Must be var", RB_DEFAULT|RB_ISFATAL); + + copyarray(.@ar$, getarg(0), getarraysize(getarg(0))); + + if (getarraysize(.@ar$) % 2 != 0) + Exception("Invalid array size: "+getarraysize(.@ar$), RB_DEFAULT|RB_ISFATAL); + + freeloop(true); + for (.@f=0; .@f < getarraysize(.@ar$); .@f++) { + // String vs Int + if (.@f % 2 == 0) { + .@menustr$+=.@ar$[.@f]+":"; + } else { + array_push(.@vals, atoi(.@ar$[.@f])); + } + } + freeloop(false); + + // Do the request + // We have: .@vals and .@menustr$ + @menu = 255; + @menuret = -1; + select(.@menustr$); + //debugmes "Option %d", @menu; + //debugmes "Array size %d", getarraysize(.@vals); + + if (@menu == 255) + return -1; + + @menu-=1; + if (@menu < 0 || @menu > getarraysize(.@vals) - 1) + return -1; + + @menuret = .@vals[@menu]; + return @menuret; +} + diff --git a/npc/functions/inventoryplace.txt b/npc/functions/inventoryplace.txt new file mode 100644 index 00000000..76cdad21 --- /dev/null +++ b/npc/functions/inventoryplace.txt @@ -0,0 +1,36 @@ +// Evol functions. +// Authors: +// Qwerty Dragon +// Reid +// Description: +// Check if the player have enough place on his inventory to accept new items with arguments: +// getarg(even numbers) item ID, +// getarg(odd numbers) number of items, + +function script inventoryplace { + + .@argc = getargcount(); + + if (.@argc % 2 != 0) + { + Exception("inventoryplace: Wrong argument count.", RB_SPEECH|RB_ISFATAL|RB_PLEASEREPORT|RB_DEBUGMES); + } + + for (.@i = .@j = 0; .@i < .@argc; .@i += 2) + { + setarray .@item[.@j], getarg(.@i); + setarray .@amount[.@j], getarg(.@i + 1); + ++.@j; + } + + if (!checkweight2(.@item, .@amount)) + { + narrator S_FIRST_BLANK_LINE, + l("It looks like you can't carry anything else for now."), + l("You should come back when you have some free space."); + + close; + } + + return true; +} diff --git a/npc/functions/main.txt b/npc/functions/main.txt index a3e222e8..eac02526 100644 --- a/npc/functions/main.txt +++ b/npc/functions/main.txt @@ -307,6 +307,13 @@ function script die { return; } +// Returns if a map is on PVP Mode or Not +// ispvpmap( {mapid} ) +function script ispvpmap { + .@mapa$=getarg(0, getmapname()); + return (getmapflag(.@mapa$, mf_pvp) || getmapflag(.@mapa$, mf_pvp_noparty) || getmapflag(.@mapa$, mf_pvpnoguild)); +} + // TMW2 Custom Functions ///////////////////////////////////////////// @@ -602,33 +609,7 @@ function script registercmd { return; } -////////////////////////////////////////////////////////////////////// -// maptimer("<map>", <tick>, "<npc>::<event>") -function script maptimer { - .@c = getunits(BL_PC, .@players, false, getarg(0)); - for (.@i = 0; .@i < .@c; .@i++) { - addtimer(getarg(1), getarg(2), .@players[.@i]); - } - return .@i; -} - -// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>") -function script areatimer { - // Legacy - if (getargcount() > 7) - .@ox=1; - // Variables - .@m$=getarg(.@ox); .@ox+=1; - .@x1=getarg(.@ox); .@ox+=1; - .@y1=getarg(.@ox); .@ox+=1; - .@x2=getarg(.@ox); .@ox+=1; - .@y2=getarg(.@ox); .@ox+=1; - .@tk=getarg(.@ox); .@ox+=1; - .@e$=getarg(.@ox); .@ox+=1; - .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2); - for (.@i = 0; .@i < .@c; .@i++) { - addtimer(.@tk, .@e$, .@players[.@i]); - } - return .@i; +function script iscollision { + return checknpccell(getarg(0), getarg(1), getarg(2), cell_chkpass); } diff --git a/npc/functions/math.txt b/npc/functions/math.txt new file mode 100644 index 00000000..9c93fbb7 --- /dev/null +++ b/npc/functions/math.txt @@ -0,0 +1,123 @@ +// Evol functions. +// Authors: +// 4144 +// Reid +// Description: +// Math functions + + +// abs(<int>) +// returns the absolute value of the passed integer + +function script abs { + .@n = getarg(0); + return .@n >= 0 ? .@n : -.@n; +} + + + +// lognbaselvl({<multiplicator>{, <min value>}}) +// returns BaseLevel * logn (BaseLevel * alpha). + +function script lognbaselvl { + .@alpha = getarg(0, 1); + .@min = getarg(1, 1); + .@ret = 0; + .@pc_level = BaseLevel * .@alpha; + + while (.@pc_level >>= 1) + { + ++.@ret; + } + .@ret *= BaseLevel; + + if (.@ret <= .@min) + { + .@ret = .@min; + } + + return .@ret; +} + +// log2(<int>) +// returns the log base 2 of the passed integer, up to 20 (2**20=1.048.576) (round down always) + +function script log2 { + .@v=abs(getarg(0)); + .@ok=0; + .@i=0; + if (.@v < 1) + return -1; + + freeloop(true); + while (!.@ok) { + // exact match + if (2**.@i == .@v) { + .@ok=1; + // inexact match, or limit exceeded + } else if (2**.@i >= .@v || .@i > 20) { + .@ok=1; + .@i-=1; // round down + // not yet + } else { + .@i+=1; + } + } + freeloop(false); + + return .@i; +} + + +// result is: lower < target <= higher +// is_between ( lower, higher, target) +function script is_between { + .@val=getarg(2); + .@min=getarg(0); + .@max=getarg(1); + return (.@min < .@val && .@val <= .@max); +} + + +// forces the equation: lower <= target <= higher. +// Note it still works if higher and target values are swapped. +// limit ( lower, target, higher) +function script limit { + return max(getarg(0), min(getarg(1), getarg(2))); +} + + +// result is the ponderate average. +// ponderate_avg ( arg1, sub1, arg2, sub2) +function script ponderate_avg { + .@a1=getarg(0); + .@s1=getarg(1); + .@a2=getarg(2); + .@s2=getarg(3); + + .@h1=.@a1*.@s1; + .@h2=.@a2*.@s2; + .@dd=.@s1+.@s2; + + return (.@h1+.@h2)/.@dd; +} + +// bitmask_count(<int>) +// returns the number of bits set in <int> (max. 4096) + +function script bitmask_count { + .@n = getarg(0); // Number evaluated + .@p=0; // Bits set/unset + .@s=0; // Stack and Check + .@m=0; // Memory + + // Loop only as needed + while (.@s < .@n) { + .@s=2**.@m; + if (.@n & .@s) + .@p++; + .@m++; + } + return .@p; +} + diff --git a/npc/functions/npcmove.txt b/npc/functions/npcmove.txt new file mode 100644 index 00000000..612ab036 --- /dev/null +++ b/npc/functions/npcmove.txt @@ -0,0 +1,142 @@ +// Evol functions. +// Author: +// 4144 +// Description: +// Moving npc utility functions +// Variables: +// none + +function script initpath { + deletearray getvariableofnpc(.movepathcmd$, strnpcinfo(3)); + deletearray getvariableofnpc(.movepathy, strnpcinfo(3)); + deletearray getvariableofnpc(.movepathx, strnpcinfo(3)); + .@cnt = 0; + + for (.@f = 0; .@f < getargcount(); .@f = .@f + 3) + { + set getvariableofnpc(.movepathcmd$[.@cnt], strnpcinfo(3)), getarg(.@f); + set getvariableofnpc(.movepathx[.@cnt], strnpcinfo(3)), getarg(.@f + 1); + set getvariableofnpc(.movepathy[.@cnt], strnpcinfo(3)), getarg(.@f + 2); + .@cnt ++; + } + //debugmes "array size: " + str(getarraysize(getvariableofnpc(.movepath, strnpcinfo(3)))); + return; +} + +function script domoveaction { + //debugmes "domoveaction: " + str(getvariableofnpc(.movepos, strnpcinfo(3))); + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0) + return; + //debugmes "walking"; + .@cmd$ = getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3)); + //debugmes "cmd: " + .@cmd$; + + if (.@cmd$ == "move") + { + npcwalkto getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "dir") + { + setnpcdir getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "wait") + { + set getvariableofnpc(.waitticks, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "emote") + { + unitemote getnpcid(), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "class") + { + .class = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "warp") + { + movenpc strnpcinfo(3), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), getvariableofnpc(.movepathy[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "goto") + { + set getvariableofnpc(.movepos, strnpcinfo(3)), getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 0; + } + else if (.@cmd$ == "rmove") + { + getmapxy(.@mapName$, .@x, .@y, 1); + npcwalkto .@x + getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)), .@y + getvariableofnpc(.movepathy[.@pos], strnpcinfo(3)); + } + else if (.@cmd$ == "speed") + { + .speed = getvariableofnpc(.movepathx[.@pos], strnpcinfo(3)); + return 2; + } + else if (.@cmd$ == "sit") + { + npcsit; + } + else if (.@cmd$ == "stand") + { + npcstand; + } + return 1; +} + +function script movetonextpos { + .@wait = getvariableofnpc(.waitticks, strnpcinfo(3)); + if (.@wait > 0) + { + .@wait --; + //debugmes "wait"; + set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait; + return; + } + .@true = 1; + while (.@true) + { + .@true = 0; + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + //debugmes "movetonextpos: " + str(.@pos); + .@res = domoveaction(.@pos); + if (.@res == 1 || .@res == 2) + { + .@pos++; + if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3)))) + .@pos = 0; + set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos; + } + if (.@res == 0 || .@res == 2) + { + .@true = 1; + } + } + return; +} + +function script initialmove { + set getvariableofnpc(.movepos, strnpcinfo(3)), 0; + set getvariableofnpc(.waitticks, strnpcinfo(3)), -1; + movetonextpos; + return; +} + +function script getmovecmd { + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + if (.@pos >= getarraysize(getvariableofnpc(.movepathx, strnpcinfo(3))) || .@pos < 0) + return ""; + return getvariableofnpc(.movepathcmd$[.@pos], strnpcinfo(3)); +} + +function script domovestep { + if (isunitwalking()) + { + initnpctimer; + end; + } + movetonextpos; + initnpctimer; + end; +} diff --git a/npc/functions/npcmovegraph.txt b/npc/functions/npcmovegraph.txt new file mode 100644 index 00000000..0877b748 --- /dev/null +++ b/npc/functions/npcmovegraph.txt @@ -0,0 +1,489 @@ +// Evol functions. +// Author: +// Travolta +// Description: +// Moving npc utility functions (graph-based) +// Variables: +// none + +function script initmovegraph { + deletearray getvariableofnpc(.movegraphcmd$, strnpcinfo(3)); + deletearray getvariableofnpc(.movegraphlabels$, strnpcinfo(3)); + deletearray getvariableofnpc(.movegraphweight, strnpcinfo(3)); + deletearray getvariableofnpc(.movegraphflags, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_y1, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_x1, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_x2, strnpcinfo(3)); + deletearray getvariableofnpc(.movepos_y2, strnpcinfo(3)); + .@cnt = 0; + + for (.@f = 0; .@f < getargcount();) + { + set getvariableofnpc(.movegraphlabels$[.@cnt], strnpcinfo(3)), getarg(.@f++); + set getvariableofnpc(.movepos_x1[.@cnt], strnpcinfo(3)), getarg(.@f++); + set getvariableofnpc(.movepos_y1[.@cnt], strnpcinfo(3)), getarg(.@f++); + if (!isstr(getarg(.@f, "label"))) + { + set getvariableofnpc(.movepos_x2[.@cnt], strnpcinfo(3)), getarg(.@f++); + set getvariableofnpc(.movepos_y2[.@cnt], strnpcinfo(3)), getarg(.@f++); + } + .@cnt ++; + } + return; +} + +function script findmovegraphlabel { + if (!getargcount()) + { + debugmes "findmovegraphlabel: no argument"; + return -1; + } + if (!isstr(getarg(0))) + { + debugmes "findmovegraphlabel: need string argument"; + return -1; + } + + .@arg$ = getarg(0); + for (.@i = 0; .@i < getarraysize(getvariableofnpc(.movegraphlabels$, strnpcinfo(3))); .@i++) + { + if (getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) == .@arg$) + return .@i; + } + + npcdebug "findmovegraphlabel: label not found: " + getarg(0); + return -1; +} + +/* setmovegraphcmd(fromPositionLabel,toPositionLabel[,moveChanceWeight[,moveFlags]],postCommand, ...); + * This function manipulates NPC moving graph. Before calling it, make sure + * `initmovegraph' was called. The function accepts 3-5 parameters (many times): + * fromPositionLabel, toPositionLabel -- starting and ending position of NPC move + * moveChanceWeight -- positive integer, represents the chance of moving in given direction. (optional) + * moveFlags -- if .mg_flags & moveFlags != 0, move is possible. (optional) + * postCommand -- either "moveon" (start moving to next location straight after arriving from + * fromPositionLabel to toPositionLabel) or a semicolon-separated set of commands + * ("wait 3", "emote 5" etc, see `execmovecmd') that will be executed after arrival. + * The commands don't have to end with ";moveon", it's executed in the end by default. + */ +function script setmovegraphcmd { + .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3))); + + for (.@f = 0; .@f < getargcount();) + { + .@from = findmovegraphlabel(getarg(.@f++)); + .@to = findmovegraphlabel(getarg(.@f++)); + .@weight = 1; + if (!isstr(getarg(.@f))) + .@weight = getarg(.@f++); + .@flags = 0xffff; + if (!isstr(getarg(.@f))) + .@flags = getarg(.@f++); + .@cmd$ = getarg(.@f++); + .@index = .@from * .@size + .@to; // emulation of 2d array + set getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)), .@cmd$; + set getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)), .@weight; + set getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)), .@flags; + } + return; +} + +function script execmovecmd { + + explode(.@cmd$, getarg(0), " "); + + if (.@cmd$[0] == "moveon") + { + return 0; + } + else if (.@cmd$[0] == "dir") + { + .dir = atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "sit") + { + npcsit; + } + else if (.@cmd$[0] == "stand") + { + npcstand; + } + else if (.@cmd$[0] == "wait") + { + set getvariableofnpc(.waitticks, strnpcinfo(3)), atoi(.@cmd$[1]); + return 1; + } + else if (.@cmd$[0] == "emote") + { + unitemote getnpcid(), atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "class") + { + .class = atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "warp") + { + .@pos = -1; + .@map$ = ""; + .@pos_idx = 1; + if (getarraysize(.@cmd$) == 3) + { + .@map$ = .@cmd$[1]; + .@pos_idx = 2; + } + .@pos = findmovegraphlabel(.@cmd$[.@pos_idx]); + if (.@pos != -1) + { + .@x = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3)); + .@y = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3)); + if (getstrlen(.@map$) > 0) + unitwarp getnpcid(), .@map$, .@x, .@y; + else + movenpc strnpcinfo(3), .@x, .@y; + set getvariableofnpc(.movepos, strnpcinfo(3)), .@pos; + } + else + { + debugmes "execmovecmd: unknown WARP destination label: " + .@cmd$[1]; + } + } + else if (.@cmd$[0] == "call") + { + switch (getarraysize(.@cmd$)) + { + case 1: + debugmes "execmovecmd: CALL command needs some parameters"; + return 0; + case 2: + return callfunc(.@cmd$[1]); + break; + case 3: + return callfunc(.@cmd$[1], .@cmd$[2]); + case 4: + default: + return callfunc(.@cmd$[1], .@cmd$[2], .@cmd$[3]); + } + } + else if (.@cmd$[0] == "speed") + { + .speed = atoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "say") + { + deletearray .@cmd$[0], 1; + .@msg$=implode(.@cmd$, " "); + if (.@msg$ != "" && .@msg$ != " ") + npctalk .@msg$; + else + debugmes "Invalid message passed to execmovecmd/npctalk"; + } + else if (.@cmd$[0] == "debugmes") + { + deletearray .@cmd$[0], 1; + debugmes implode(.@cmd$, " "); + } + else if (.@cmd$[0] == "flags") + { + set getvariableofnpc(.mg_flags, strnpcinfo(3)), axtoi(.@cmd$[1]); + } + else if (.@cmd$[0] == "flags_0") + { + .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3)); + .@flags &= ~axtoi(.@cmd$[1]); + set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags; + } + else if (.@cmd$[0] == "flags_1") + { + .@flags = getvariableofnpc(.mg_flags, strnpcinfo(3)); + .@flags |= axtoi(.@cmd$[1]); + set getvariableofnpc(.mg_flags, strnpcinfo(3)), .@flags; + } + else + { + debugmes "Unknown move graph cmd: " + .@cmd$[0]; + } + return 0; +} + +function script getnextmovecmd { + .@cmds$ = getvariableofnpc(.nextcmd$, strnpcinfo(3)); + .@firstCmd$ = .@cmds$; + .@restCmd$ = "moveon"; + .@index = strpos(.@cmds$, ";"); + if (.@index >= 0) + { + .@firstCmd$ = substr(.@cmds$, 0, .@index - 1); + .@restCmd$ = substr(.@cmds$, .@index + 1, getstrlen(.@cmds$) - 1); + } + // npcdebug "firstCmd = " + .@firstCmd$ + " restCmd = " + .@restCmd$; + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@restCmd$; + return strip(.@firstCmd$); +} + +// getrandompoint(x1,y1,x2,y2) +// -- Get a random walkable point within a map rectangle +// x1, y1 -- top-left corner of rectangle +// x2, y2 -- bottom-right corner of rectangle +// Returns 0 on success and -1 on error; +// Since we cannot return multiple values, the random +// coordinates are stored in NPC variables .move__rand_x, .move__rand_y +function script getrandompoint { + if (getargcount() < 4) + { + debugmes "error: getrandompoint(x1, y1, x2, y2) takes 4 arguments"; + return -1; + } + + .@max_pokes = 10; + .@x1 = getarg(0); + .@y1 = getarg(1); + .@x2 = getarg(2); + .@y2 = getarg(3); + .@rx = -1; .@ry = -1; + + getmapxy(.@map$, .@cx, .@cy, 1); // npc location + + // let's try max_pokes random cells + for (.@poke = 0; .@poke < .@max_pokes; .@poke++) + { + .@rx = rand(.@x1, .@x2); + .@ry = rand(.@y1, .@y2); + if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass)) + goto L_Found; + } + + // we check each cell from random middle point to the end + for (;.@rx <= .@x2; .@rx++) + { + for (;.@ry <= .@y2; .@ry++) + if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass)) + goto L_Found; + .@ry = .@y1; + } + + // we check the rectangle from beginning to end + for (.@rx = .@x1; .@rx <= .@x2; .@rx++) + for (.@ry = .@y1; .@ry <= .@y2; .@ry++) + if (checknpccell(.@map$, .@rx, .@ry, cell_chkpass)) + goto L_Found; + + // finally, if we don't find anything + debugmes "error: getrandompoint: cannot find walkable cell in rectangle [(" + .@x1 + "," + .@y1 + ") , (" + .@x2 + "," + .@y2 + ")]"; + return -1; + +L_Found: + set getvariableofnpc(.move__rand_x, strnpcinfo(3)), .@rx; + set getvariableofnpc(.move__rand_y, strnpcinfo(3)), .@ry; + return 0; +} + +// wrapper function for npcwalkto. It can accept 4 parameters. +// If #3 and #4 params are set, the walkto location is chosen +// from rectangle (x1,y1,x2,y2). +// It sets the npc variables .move_target_x, .move_target_y +// that are used to resume NPC walking +// Returns 1 if walking is possible, 0 otherwise; +function script mg_npcwalkto { + if (getargcount() < 2) + { + debugmes "usage: mg_npcwalkto(x1,y1[,x2,y2])"; + return -1; + } + + .@x = getarg(0); + .@y = getarg(1); + .@x2 = getarg(2); + .@y2 = getarg(3); + + if (getargcount() >= 4 && .@x2 > 0 && .@y2 > 0) + if (!getrandompoint(.@x, .@y, .@x2, .@y2)) + { + .@x = getvariableofnpc(.move__rand_x, strnpcinfo(3)); + .@y = getvariableofnpc(.move__rand_y, strnpcinfo(3)); + } + else + return 0; + + if (npcwalkto(.@x, .@y)) + { + set getvariableofnpc(.move_target_x, strnpcinfo(3)), .@x; + set getvariableofnpc(.move_target_y, strnpcinfo(3)), .@y; + return 1; + } + return 0; +} + +function script movetonextpoint { + .@wait = getvariableofnpc(.waitticks, strnpcinfo(3)); + if (.@wait > 0) + { + .@wait--; + set getvariableofnpc(.waitticks, strnpcinfo(3)), .@wait; + return; + } + + .@nextcmd$ = ""; + while (.@nextcmd$ != "moveon") + { + .@nextcmd$ = getnextmovecmd(); + npcdebug " " + .@nextcmd$; + if (execmovecmd(.@nextcmd$)) + return; + } + + // choose a random path from all possible paths + .@size = getarraysize(getvariableofnpc(.movepos_x1, strnpcinfo(3))); + .@pos = getvariableofnpc(.movepos, strnpcinfo(3)); + .@curr_flags = getvariableofnpc(.mg_flags, strnpcinfo(3)); + .@cur = 0; + .@weight_sum = 0; + // .@dbg$ = getvariableofnpc(.movegraphlabels$[.@pos], strnpcinfo(3)) + ": "; + + for (.@i = 0; .@i < .@size; .@i++) + { + .@index = .@pos * .@size + .@i; + .@cmd$ = getvariableofnpc(.movegraphcmd$[.@index], strnpcinfo(3)); + .@flags = getvariableofnpc(.movegraphflags[.@index], strnpcinfo(3)); + if (.@cmd$ != "" && + .@curr_flags & .@flags) + { + .@nextpos[.@cur] = .@i; + .@weights[.@cur] = getvariableofnpc(.movegraphweight[.@index], strnpcinfo(3)); + // .@dbg$ += getvariableofnpc(.movegraphlabels$[.@i], strnpcinfo(3)) + "=" + .@weights[.@cur] + " "; + .@weight_sum += .@weights[.@cur]; + .@cur++; + } + } + // npcdebug .@dbg$; + + if (!.@weight_sum) + { + npcdebug("error: cannot pick next walk point. flags=" + + getvariableofnpc(.mg_flags, strnpcinfo(3))); + return; + } + + .@pick_tries = 0; +L_TryPick: + // pick a random number based on weight_sum + .@rnd = rand(.@weight_sum); + .@k = -1; .@weight_sum = 0; + while (.@rnd >= .@weight_sum) + { + .@k++; + .@weight_sum += .@weights[.@k]; + } + + .@next_idx = .@nextpos[.@k]; + .@next_x1 = getvariableofnpc(.movepos_x1[.@next_idx], strnpcinfo(3)); + .@next_y1 = getvariableofnpc(.movepos_y1[.@next_idx], strnpcinfo(3)); + .@next_x2 = getvariableofnpc(.movepos_x2[.@next_idx], strnpcinfo(3)); + .@next_y2 = getvariableofnpc(.movepos_y2[.@next_idx], strnpcinfo(3)); + + if (!mg_npcwalkto(.@next_x1, .@next_y1, .@next_x2, .@next_y2)) + { + if (.@pick_tries < 10) + { + .@pick_tries++; + goto L_TryPick; + } + + // move to a nearby position + .@x1 = getvariableofnpc(.movepos_x1[.@pos], strnpcinfo(3)); + .@y1 = getvariableofnpc(.movepos_y1[.@pos], strnpcinfo(3)); + .@x2 = getvariableofnpc(.movepos_x2[.@pos], strnpcinfo(3)); + .@y2 = getvariableofnpc(.movepos_y2[.@pos], strnpcinfo(3)); + mg_npcwalkto(.@x1, .@y1, .@x2, .@y2); + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), "wait 1"; + + return; + } + + if (getvariableofnpc(.debug, strnpcinfo(3))) + { + getmapxy(.@map$, .@cx, .@cy, 1); + .@dist = distance(.@cx, .@cy, .@next_x1, .@next_y1); + npcdebug("moving to " + getvariableofnpc(.movegraphlabels$[.@next_idx], strnpcinfo(3)) + + " ("+ getvariableofnpc(.move_target_x, strnpcinfo(3)) + + "," + getvariableofnpc(.move_target_y, strnpcinfo(3)) + + ") [distance=" + .@dist + + "] flags=" + getvariableofnpc(.mg_flags, strnpcinfo(3))); + } + + .@nextcmd$ = getvariableofnpc(.movegraphcmd$[.@pos * .@size + .@next_idx], strnpcinfo(3)); + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$; + set getvariableofnpc(.movepos, strnpcinfo(3)), .@next_idx; + return; +} + +// initial actions for npc when using move graphs. +// function can accept 2 arguments: +// 1: action sequence, for example "speed 200; dir 4". Default is "moveon" +// 2: start point label. Default is #0 from move graph labels +function script firstmove { + .@nextcmd$ = getarg(0, "moveon"); + .@initpos = findmovegraphlabel(getarg(1, "")); + if (.@initpos < 0) .@initpos = 0; + + set getvariableofnpc(.movepos, strnpcinfo(3)), .@initpos; + movenpc strnpcinfo(3), getvariableofnpc(.movepos_x1[.@initpos], strnpcinfo(3)), + getvariableofnpc(.movepos_y1[.@initpos], strnpcinfo(3)); + set getvariableofnpc(.nextcmd$, strnpcinfo(3)), .@nextcmd$; + set getvariableofnpc(.waitticks, strnpcinfo(3)), -1; + set getvariableofnpc(.mg_flags, strnpcinfo(3)), 0xffff; + movetonextpoint; + return; +} + +function script npc_pausemove { + stopnpctimer; + .@move_after = 0; + + if (isunitwalking()) + { + .@move_after = 1; + npcwalkto .x, .y; + npcstop; + } + set getvariableofnpc(.move_after_pause, strnpcinfo(3)), .@move_after; + + return 0; +} + +function script npc_resumemove { + startnpctimer; + + if (getvariableofnpc(.move_after_pause, strnpcinfo(3))) + { + .@x = getvariableofnpc(.move_target_x, strnpcinfo(3)); + .@y = getvariableofnpc(.move_target_y, strnpcinfo(3)); + npcwalkto .@x, .@y; + } + + return 0; +} + +// npc_turntoxy(x,y) +// turn npc toward an object at position (x,y) +function script npc_turntoxy { + .@target_x = getarg(0); + .@target_y = getarg(1); + .@dx = abs(.@target_x - .x); + .@dy = abs(.@target_y - .y); + + if (.@dx > .@dy) + .dir = .@target_x >= .x ? 6 : 2; + else + .dir = .@target_y >= .y ? 0 : 4; + + return 0; +} + +function script dographmovestep { + if (!isunitwalking()) + { + movetonextpoint; + } + initnpctimer; + end; +} diff --git a/npc/functions/permissions.txt b/npc/functions/permissions.txt new file mode 100644 index 00000000..96e69fde --- /dev/null +++ b/npc/functions/permissions.txt @@ -0,0 +1,35 @@ +// Evol scripts. +// Author: +// gumi +// Description: +// checks player permissions +// ** admins are implicitly everything + +// administrator +function script is_admin { + return has_permission(PERM_USE_ALL_COMMANDS, getarg(0, getcharid(CHAR_ID_ACCOUNT))); +} + +// any staff member +function script is_trusted { + return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) || + has_permission("show_client_version", getarg(0, getcharid(CHAR_ID_ACCOUNT))); +} + +// developer +function script is_dev { + return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) || + has_permission(PERM_RECEIVE_REQUESTS, getarg(0, getcharid(CHAR_ID_ACCOUNT))); +} + +// event coordinator +function script is_evtc { + return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) || + can_use_command("@monster", getarg(0, getcharid(CHAR_ID_ACCOUNT))); +} + +// game master +function script is_gm { + return is_admin(getarg(0, getcharid(CHAR_ID_ACCOUNT))) || + can_use_command("@jail", getarg(0, getcharid(CHAR_ID_ACCOUNT))); +} diff --git a/npc/functions/random-talk.txt b/npc/functions/random-talk.txt new file mode 100644 index 00000000..e6b6bee8 --- /dev/null +++ b/npc/functions/random-talk.txt @@ -0,0 +1,207 @@ +// TMW2 Script +// Author: +// Jesusalva +// Description: +// Random dialog for various random NPCs. + +// Functions: +// hello +// moubootalk +// villagertalk +// sailortalk +// legiontalk +// asleep + +// Evol authors (some strings and code): +// Reid +// Akko Teru +// Qwerty Dragon + +function script hello { + + switch (rand2(3)) { + case 0: + npctalkonce(l("Heya!")); + break; + case 1: + npctalkonce(l("Hi.")); + break; + case 2: + if ($EVENT$ == "Christmas") + npctalkonce(l("Merry Christmas!")); + else + npctalkonce(l("Nice day to you.")); + break; + } + + return; +} + +function script moubootalk { + switch (rand2(4)) { + case 0: + npctalkonce(l("Moooooo!")); + break; + case 1: + npctalkonce(l("Moo!")); + break; + case 2: + npctalkonce(l("Moooooooooooo!")); + break; + case 3: + npctalkonce(l("Moooo!")); + break; + } + return; +} + +function script sailortalk { + + .@rand = rand2(8); + if (.@rand == 0) goodbye; + if (.@rand == 1) npctalkonce(l("Arr, I'm bored!")); + if (.@rand == 2) npctalkonce(l("Hey! Good to hear from you!")); + if (.@rand == 3) npctalkonce(l("Yarr arr!")); + if (.@rand == 4) { + if ($EVENT$ == "Christmas") + npctalkonce(l("Merry Christmas, arr yarr!!")); + else { + speech( + l("A sunny and hot day,"), + l("a quiet place,"), + l("a ground!"), + l("What else do you need?")); + } + close; + } + if (.@rand == 5) npctalkonce(l("A-hoy matey!")); + if (.@rand == 6) npctalkonce(l("Arr!")); + if (.@rand == 7) npctalkonce(l("Howdy?")); + + // just to be sure + closedialog; + close; + end; +} + +function script villagertalk { + + function darn_or_smile { + .@darn = rand(42); + + if (.@darn < 26) { + emotion E_JOY; + hello; + } else if (.@darn > 26) { + emotion E_LOOKAWAY; + goodbye; + } else { + npctalkonce(l("Stop it!")); + } + return; + } + + switch (rand2(4)) { + case 0: + darn_or_smile(); + break; + case 1: + npctalkonce(l("It is a sunny day, don't you think?")); + break; + case 2: + npctalkonce(l("Go fly a kite.")); + break; + case 3: + npctalkonce(l("I just want to live my life in peace.")); + break; + default: + emotion E_HAPPY; + break; + } + + return; +} + +function script legiontalk { + switch (rand2(15)) { + case 0: + npctalkonce(l("Do I look like a tree? I feel like one.")); + //speech( + // l("Do you feel too weak even to do damage to this areas wishy-washy wildlife?"), + // l("Then concentrate your anger upon the trees hereabouts, you will gain experience whilst leveling your sword skill on them."), + // l("Oh, and a fruit may even fall for you if you are lucky! But stay alert to pick up your drops.")); + //close; + break; + case 1: + npctalkonce(l("I'm a little busy right now.")); + break; + case 2: + npctalkonce(l("Not in the mood to chat.")); + break; + case 3: + npctalkonce(l("My breath smells bad.")); + break; + case 4: + npctalkonce(l("Don't distract me, I have to stay alert.")); + break; + case 5: + npctalkonce(l("Give me some space.")); + break; + case 6: + if ($EVENT$ == "Christmas") + npctalkonce(l("Merry Christmas, adventurer.")); + else + npctalkonce(l("Can you please go away?")); + break; + case 7: + npctalkonce(l("Can't talk right now, I'm on patrol duty.")); + break; + case 8: + npctalkonce(l("What're you looking at?!")); + break; + case 9: + npctalkonce(l("I can't stay here and talk all day. I have a job to do.")); + break; + case 10: + npctalkonce(l("Keep moving pal.")); + break; + case 11: + npctalkonce(l("So you think you're tough? A warrior must also be loyal and patient.")); + break; + case 12: + emotion E_LOOKAWAY; + break; + case 13: + npctalkonce(l("Practice! There are no secrets to becoming a warrior.")); + break; + case 14: + npctalkonce(l("There is no honor in fighting a weak opponent.")); + break; + } + + return; +} + +function script asleep { + switch(rand2(5)) { + case 0: npctalkonce(l("Zzzzzzzzz...")); break; + case 1: npctalkonce(l("Rrrr... Pchhhh...")); break; + case 2: npctalkonce(l("Ggrmm... Grmmmm...")); break; + case 3: npctalkonce(l("Hm... Shhhh...")); break; + default: emotion(E_SLEEPY); + } + end; +} + +function script studenttalk { + switch(rand2(6)) { + case 0: npctalkonce(l("I want to sleep...")); break; + case 1: npctalkonce(l("I have homework to do...")); break; + case 2: npctalkonce(l("I need to finish studying for my test...")); break; + case 3: npctalkonce(l("Ah, the Professors will get mad at me again...")); break; + case 4: npctalkonce(l("I'm a little busy right now.")); break; + + default: emotion(E_SLEEPY); + } + end; +} diff --git a/npc/functions/string.txt b/npc/functions/string.txt new file mode 100644 index 00000000..2a38d90d --- /dev/null +++ b/npc/functions/string.txt @@ -0,0 +1,211 @@ +// Evol Script +// Author: Gumi + +// safe string manipulation functions +// ** does not require PCRE + + +// str(<int>) +// returns whatever is passed, converted to string + +function script str { + return "" + getarg(0); +} + + + +// startswith("<string>", "<search>") +// returns true if <string> begins with <search> + +function script startswith { + return substr(getarg(0), 0, getstrlen(getarg(1)) - 1) == getarg(1); +} + + + +// endswith("<string>", "<search>") +// returns true if <string> ends with <search> + +function script endswith { + .@t = getstrlen(getarg(0)); // total length + .@n = getstrlen(getarg(1)); // substring length + return substr(getarg(0), .@t - .@n, .@t - 1) == getarg(1); +} + + + +// capitalize("<string>") +// returns <string> with its first letter capitalized + +function script capitalize { + return setchar(getarg(0), strtoupper(charat(getarg(0), 0)), 0); +} + + + +// titlecase("<string>" {, "<delimiter>" {, <camel>}}) +// returns <string> with the first letter of each word capitalized +// if <camel> is true, the string is joined in a camelCase fashion + +function script titlecase { + .@delimiter$ = getarg(1, " "); + .@c = getarg(2, 0); + explode(.@words$, getarg(0), .@delimiter$); + + for (.@i = (.@c ? 1 : 0); .@i < 255; ++.@i) + { + if (.@words$[.@i] == "") + { + break; + } + + .@words$[.@i] = setchar(.@words$[.@i], strtoupper(charat(.@words$[.@i], 0)), 0); + } + + return implode(.@words$, (.@c ? "" : .@delimiter$)); +} + + + +// camelcase("<string" {, "<delimiter>"}) + +function script camelcase { + return titlecase(getarg(0), getarg(1, " "), true); +} + + + +// zfill("<string>" {, <width> {, "<padding>"}}) +// returns <string> padded to the left with <padding> up to width + +function script zfill { + .@str$ = getarg(0); + .@width = getarg(1, 8); + .@padding$ = getarg(2, "0"); + + for (.@s = getstrlen(.@str$); .@s < .@width; ++.@s) + { + .@str$ = .@padding$ + .@str$; + } + + return .@str$; +} + + + +// format_number(<integer> {, "<separator>"}) +// formats a number properly + +function script format_number { + .@number$ = str(getarg(0)); + .@len = getstrlen(.@number$); + .@separator$ = getarg(1, ","); + + if (getargcount() < 2 && playerattached()) { + // get from user language + switch (Lang) { + case LANG_FR: .@separator$ = " "; break; // French + case LANG_DE: .@separator$ = "."; break; // Germanic + case LANG_PTBR: .@separator$ = "."; break; // Brazilian + default: .@separator$ = ","; // English (default) + } + } + + for (.@i = .@len - 3; .@i > 0; .@i -= 3) { + .@number$ = insertchar(.@number$, .@separator$, .@i); + } + + return .@number$; +} + + + +// fnum(<integer>) +// alias for format_number + +function script fnum { + return format_number(getarg(0)); +} + + + +// strip("<string>") +// removes spaces at the start and end + +function script strip { + .@s$ = getarg(0); + if (.@s$ == "") { + return ""; + } + .@start = 0; + .@end = getstrlen(.@s$) - 1; + for (.@i = .@start; .@i < .@end; .@i++) + { + if (charat(.@s$, .@i) != " ") { + break; + } else { + .@start++; + } + } + for (.@i = .@end; .@i >= .@start; .@i--) + { + if (charat(.@s$, .@i) != " ") { + break; + } else { + .@end--; + } + } + //debugmes "STRIP.DEBUG MODE ENABLED BY JESUSALVA. PASSING SUBSTRING PARAMS"; + //debugmes "String \""+.@s$+"\" from "+str(.@start)+" to "+str(.@end); + return substr(.@s$, .@start, .@end); +} + + + +// reverse("<string>") +// returns <string> reversed + +function script reverse { + .@str$ = getarg(0); + .@len = getstrlen(.@str$); + + for (.@i = 0; .@i < (.@len / 2); ++.@i) { + .@tmp$ = charat(.@str$, .@i); + .@str$ = setchar(.@str$, charat(.@str$, (.@len - 1 - .@i)), .@i); // a <= b + .@str$ = setchar(.@str$, .@tmp$, (.@len - 1 - .@i)); // b <= a + } + + return .@str$; +} + + + +// repeat("<string>", <multiplier>) +// repeats <string> many times and returns it + +function script repeat { + .@mul = getarg(1); + + for (.@i = 0; .@i < .@mul; ++.@i) { + .@str$ += getarg(0); + } + + return .@str$; +} + + + +// shuffle("<string>") +// returns <string> shuffled + +function script shuffle { + .@str$ = getarg(0); + + for (.@len = getstrlen(.@str$); .@len > 0; --.@len) { + .@rnd = rand(.@len); + .@out$ += charat(.@str$, .@rnd); + .@str$ = delchar(.@str$, .@rnd); + } + + return .@out$; +} diff --git a/npc/functions/time.txt b/npc/functions/time.txt index 21b94ac5..e6e4c70a 100755 --- a/npc/functions/time.txt +++ b/npc/functions/time.txt @@ -1,159 +1,117 @@ +// Evol Script +// Authors: Gumi, Jesusalva +function script now { + return gettimetick(2); +} -function script time_stamp { - // local variables - // if there is reasonable demand, these might be exported - // (that is what the builtin is likely to do) - @ts_year = gettime(7); - @ts_month = gettime(6); - @ts_mday = gettime(5); - //set @ts_wday, gettime(4); - @ts_hour = gettime(3); - @ts_minute = gettime(2); - @ts_second = gettime(1); - - // locals used to generate leading zeroes - @ts_month_pad$ = ""; - @ts_mday_pad$ = ""; - @ts_hour_pad$ = ""; - @ts_minute_pad$ = ""; - @ts_second_pad$ = ""; - - if (@ts_month < 10) - @ts_month_pad$ = "0"; - if (@ts_mday < 10) - @ts_mday_pad$ = "0"; - if (@ts_hour < 10) - @ts_hour_pad$ = "0"; - if (@ts_minute < 10) - @ts_minute_pad$ = "0"; - if (@ts_second < 10) - @ts_second_pad$ = "0"; - - @ts_date$ = @ts_year + "-" + @ts_month_pad$ + @ts_month + "-" + @ts_mday_pad$ + @ts_mday; - @ts_time$ = @ts_hour_pad$ + @ts_hour + ":" + @ts_minute_pad$ + @ts_minute + ":" +@ts_second_pad$ + @ts_second; - - // cleanup - @ts_year = 0; - @ts_month = 0; - @ts_mday = 0; - @ts_hour = 0; - @ts_minute = 0; - @ts_second = 0; - @ts_month_pad$ = ""; - @ts_mday_pad$ = ""; - @ts_hour_pad$ = ""; - @ts_minute_pad$ = ""; - @ts_second_pad$ = ""; - - return; +// Returns current time. A SQL update superseeded this. +// santime( ) +function script santime { + return gettimetick(2); } +function script time_from_ms { + return now() + (getarg(0) / 1000); +} +function script time_from_seconds { + return now() + getarg(0); +} +function script time_from_minutes { + return now() + (getarg(0) * 60); +} +function script time_from_hours { + return now() + (getarg(0) * 3600); +} + +function script time_from_days { + return now() + (getarg(0) * 86400); +} -function script HumanTime { - @time$ = "now"; - if(@seconds) set @ms, @ms + (@seconds * 1000); - if(@minutes) set @ms, @ms + (@minutes * 60000); - if(@days) set @ms, @ms + (@days * 1440000); - if(@ms < 1000) goto L_Millis; // under 1 second we have nothing to count - @seconds = @ms / 1000; - @ms = @ms % 1000; - if(@seconds < 60) goto L_Seconds; - @minutes = @seconds / 60; - @seconds = @seconds % 60; - if(@minutes < 60) goto L_Minutes; - @hours = @minutes / 60; - @minutes = @minutes % 60; - if(@hours < 24) goto L_Hours; - @days = @hours / 24; - @hours = @hours % 24; - if(@days) goto L_Days; - goto L_Clean; - -L_Millis: - @time$ = @ms + "ms"; - return; - -L_Seconds: - @unit$ = "second"; - if(@seconds > 1) set @unit$, "seconds"; - @unit2$ = "millisecond"; - if(@ms > 1) set @unit2$, "milliseconds"; - @time$ = @seconds + " " + @unit$; - if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit2$; - goto L_Clean; - -L_Minutes: - @unit$ = "minute"; - if(@minutes > 1) set @unit$, "minutes"; - @unit2$ = "second"; - if(@seconds > 1) set @unit2$, "seconds"; - @unit3$ = "millisecond"; - if(@ms > 1) set @unit3$, "milliseconds"; - @time$ = @minutes + " " + @unit$; - @separator$ = " and "; - if(@ms) set @separator$, ", "; - if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit2$; - if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit3$; - goto L_Clean; - -L_Hours: - @unit$ = "hour"; - if(@hours > 1) set @unit$, "hours"; - @unit2$ = "minute"; - if(@minutes > 1) set @unit2$, "minutes"; - @unit3$ = "second"; - if(@seconds > 1) set @unit3$, "seconds"; - @unit4$ = "millisecond"; - if(@ms > 1) set @unit4$, "milliseconds"; - @time$ = @hours + " " + @unit$; - @separator$ = " and "; - if(@seconds || @ms) set @separator$, ", "; - if(@minutes) set @time$, @time$ + @separator$ + @minutes + " " + @unit2$; - @separator$ = " and "; - if(@ms) set @separator$, ", "; - if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit3$; - if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit4$; - goto L_Clean; - -L_Days: - @unit$ = "day"; - if(@hours > 1) set @unit$, "days"; - @unit2$ = "hour"; - if(@hours > 1) set @unit2$, "hours"; - @unit3$ = "minute"; - if(@minutes > 1) set @unit3$, "minutes"; - @unit4$ = "second"; - if(@seconds > 1) set @unit4$, "seconds"; - @unit5$ = "millisecond"; - if(@ms > 1) set @unit5$, "milliseconds"; - @time$ = @days + " " + @unit$; - @separator$ = " and "; - if(@minutes || @seconds || @ms) set @separator$, ", "; - if(@hours) set @time$, @time$ + @separator$ + @hours + " " + @unit2$; - @separator$ = " and "; - if(@seconds || @ms) set @separator$, ", "; - if(@minutes) set @time$, @time$ + @separator$ + @minutes + " " + @unit3$; - @separator$ = " and "; - if(@ms) set @separator$, ", "; - if(@seconds) set @time$, @time$ + @separator$ + @seconds + " " + @unit3$; - if(@ms) set @time$, @time$ + " and " + @ms + " " + @unit4$; - goto L_Clean; - -L_Clean: - @unit$ = ""; - @unit2$ = ""; - @unit3$ = ""; - @unit4$ = ""; - @unit5$ = ""; - @seconds = 0; - @minutes = 0; - @hours = 0; - @days = 0; - @separator$ = ""; - return; +// FuzzyTime(<unix timestamp>{, <options>{, <precision>}}) +// gives time in a human-readable format +// +// <options> is bitmasked: +// 1 do not show "ago" when in past +// 2 do not show "in" when in the future +// 4 show "from now" instead of "in" when in the future +// +// <precision> is the number of units to show, +// by default uses two (eg. 2m30s or 1h20m). +// Use '99' for max precision + +function script FuzzyTime { + .@future = getarg(0, now()); + .@options = getarg(1, 3); + .@precision = getarg(2, 2); + .@diff = (.@future - now()); + + // check if in the past, or in the future + if (.@diff < 0) { + .@diff *= -1; + .@past = true; + } + + .@diff = max(1, .@diff); + + if (.@diff >= 31536000) { + .@years = (.@diff / 31536000); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 31536000)); + .@ret$ += sprintf("%d %s", .@years, (.@years > 1 ? "years" : "year")); + } + + if (.@diff >= 86400) { + .@days = (.@diff / 86400); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 86400)); + + if (.@s > 1) { + .@ret$ += (.@diff > 0 ? ", " : " and "); + } + + .@ret$ += sprintf("%d %s", .@days, (.@days > 1 ? "days" : "day")); + } + + if (.@diff >= 3600) { + .@hours = (.@diff / 3600); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 3600)); + + if (.@s > 1) { + .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and "); + } + + .@ret$ += sprintf("%d %s", .@hours, (.@hours > 1 ? "hours" : "hour")); + } + + if (.@diff >= 60) { + .@minutes = (.@diff / 60); + .@diff = (++.@s == .@precision ? 0 : (.@diff % 60)); + + if (.@s > 1) { + .@ret$ += (.@diff > 0 ? ", " : (.@s >= 3 ? ", " : " ") + "and "); + } + + .@ret$ += sprintf("%d %s", .@minutes, (.@minutes > 1 ? "minutes" : "minute")); + } + + if (.@diff >= 1) { + if (++.@s > 1) { + .@ret$ += (.@s >= 3 ? ", " : " ") + "and "; + } + + .@ret$ += sprintf("%d %s", .@diff, (.@diff > 1 ? "seconds" : "second")); + } + + if (.@past && !(.@options & 1)) { + .@ret$ += " ago"; + } + + if (!(.@past) && !(.@options & 2)) { + .@ret$ = ((.@options & 4) ? sprintf("%s from now", .@ret$) : sprintf("in %s", .@ret$)); + } + + return .@ret$; } diff --git a/npc/functions/timer.txt b/npc/functions/timer.txt new file mode 100644 index 00000000..27e09f13 --- /dev/null +++ b/npc/functions/timer.txt @@ -0,0 +1,89 @@ +// Evol Script +// Authors: Gumi, Jesusalva + +// areatimer("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>") +function script areatimer { + // Legacy + if (getargcount() > 7) + .@ox=1; + // Variables + .@m$=getarg(.@ox); .@ox+=1; + .@x1=getarg(.@ox); .@ox+=1; + .@y1=getarg(.@ox); .@ox+=1; + .@x2=getarg(.@ox); .@ox+=1; + .@y2=getarg(.@ox); .@ox+=1; + .@tk=getarg(.@ox); .@ox+=1; + .@e$=getarg(.@ox); .@ox+=1; + .@c = getunits(BL_PC, .@players, false, .@m$, .@x1, .@y1, .@x2, .@y2); + for (.@i = 0; .@i < .@c; .@i++) { + addtimer(.@tk, .@e$, .@players[.@i]); + } + return .@i; +} + +// areadeltimer("<map>", <x1>, <y1>, <x2>, <y2>, "<npc>::<event>") +function script areadeltimer { + .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(5), .@players[.@i]); + } + return .@i; +} + +// areatimer2("<map>", <x1>, <y1>, <x2>, <y2>, <tick>, "<npc>::<event>") +function script areatimer2 { + .@c = getunits(BL_PC, .@players, false, getarg(0), getarg(1), getarg(2), getarg(3), getarg(4)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(6), .@players[.@i]); + addtimer(getarg(5), getarg(6), .@players[.@i]); + } + return .@i; +} + +// addtimer2(<tick>, "<npc>::<event>") +function script addtimer2 { + deltimer(getarg(1)); + addtimer(getarg(0), getarg(1)); + return; +} + + +// maptimer("<map>", <tick>, "<npc>::<event>") +function script maptimer { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + addtimer(getarg(1), getarg(2), .@players[.@i]); + } + return .@i; +} + +// Same as maptimer() but deletes any previously running timer +// maptimer2("<map>", <tick>, "<npc>::<event>") +function script maptimer2 { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(2), .@players[.@i]); + addtimer(getarg(1), getarg(2), .@players[.@i]); + } + return .@i; +} + +// mapdeltimer("<map>", "<npc>::<event>") +function script mapdeltimer { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + deltimer(getarg(1), .@players[.@i]); + } + return .@i; +} + +// partytimer("<map>", <tick>, "<npc>::<event>", partyid) +function script partytimer { + .@c = getunits(BL_PC, .@players, false, getarg(0)); + for (.@i = 0; .@i < .@c; .@i++) { + if (getcharid(2, strcharinfo(0,"",.@players[.@i]) ) == getarg(3)) + addtimer(getarg(1), getarg(2), .@players[.@i]); + } + return .@i; +} + diff --git a/npc/scripts.conf b/npc/scripts.conf index 568123ff..5f1d6335 100755 --- a/npc/scripts.conf +++ b/npc/scripts.conf @@ -1,6 +1,25 @@ +// Critical functions "npc/functions/main.txt", +"npc/functions/string.txt", +"npc/functions/array.txt", +"npc/functions/math.txt", +"npc/functions/permissions.txt", + +// General-Purpose Framework Functions +"npc/functions/input.txt", +"npc/functions/time.txt", +"npc/functions/timer.txt", +"npc/functions/goodbye.txt", + +// Pre-Loading Functions "npc/functions/clear_vars.txt", +"npc/functions/asklanguage.txt", +"npc/functions/inventoryplace.txt", +"npc/functions/random-talk.txt", +"npc/functions/inc_sc_bonus.txt", +"npc/commands/kami.txt", +// Main Functions "npc/functions/banker.txt", "npc/functions/barber.txt", "npc/functions/dailyquest.txt", @@ -29,6 +48,7 @@ "npc/functions/motd.txt", "npc/functions/motdconfig.txt", +// Items "npc/items/purification_potion.txt", "npc/items/scissors.txt", "npc/items/pickled_beets.txt", @@ -45,8 +65,16 @@ //@include "npc/magic/_import.txt", -//@include "npc/commands/_import.txt", +// Commands +"npc/commands/debug-quest.txt", +"npc/commands/debug.txt", +"npc/commands/ipcheck.txt", +"npc/commands/language.txt", +"npc/commands/numa.txt", +"npc/commands/python.txt", +"npc/commands/resync.txt", +// Events "npc/functions/gm_island.txt", "npc/annuals/fathertime.txt", "npc/annuals/check_time.txt", @@ -65,5 +93,8 @@ "npc/annuals/halloween/munro.txt", "npc/annuals/halloween/trick_or_treat.txt", +// Post Loading Functions +"npc/functions/filters.txt", + @include "npc/_import.txt" |