Jump to content

meko

Core Developers
  • Content Count

    363
  • Joined

  • Days Won

    46

Posts posted by meko


  1. If what you want is create a NPC that you can only interact with every 15 minutes you could do something similar to this:

    // create a NPC template:
    -	script	giver	FAKE_NPC,{
    	// get the item name from the NPC name
    	.@item$ = strnpcinfo(NPC_NAME_HIDDEN);
    
    	// check when the player last obtained this item
    	.@last = .obtained[playerattached()];
    
    	if (.@last <= time_from_minutes(-15)) {
    		// give the item
    		getitem(.@item$, 1);
    
    		// update the last obtained date
    		.obtained[playerattached()] = now();
    
    		mes("Enjoy!");
    	} else {
    		// calculate the difference
    		.@seconds = .@last - time_from_minutes(-15);
    
    		// tell the player to wait
    		mesf("Don't be greedy! Try again %s.", FuzzyTime(time_from_seconds(.@seconds)));
    		// This would print, ie: "Don't be greedy! Try again in 5 minutes and 32 seconds."
    	}
    
    	close;
    }
    
    // now make duplicates:
    einbech,172,113,4	duplicate(giver)	new name#Jellopy	4_M_EINMAN
    // ^ this would spawn a NPC named "new name" that gives a Jellopy every 15 minutes

    This creates a template NPC that gets its parameters from the NPC name so you can create several duplicates that all give different items.

     

    PS: to make it easier to read this script uses the Date and Time functions file


  2. inarray() is not a script command of Hercules (see the docs in doc/script_commands.txt) and is also not included in the "Array manipulation functions" script

     

    You should either replace it with a for() loop or if you want to use Array manipulation functions you should use array_exists() like so:

    // with Array manipulation functions:
    
    if (!array_exists(.@unique_id, get_unique_id())) {
    	...
    }
    
    
    
    
    // with a for() loop:
    
    .@size = getarraysize(.@unique_id);
    for (.@k = 0; .@k < .@size; ++.@k) {
    	if (.@unique_id[.@k] == get_unique_id()) {
    		break;
    	}
    
    	...
    }

     

    Keep in mind that get_unique_id() is also not part of Hercules so if you don't have a plugin that provides it or source mods (not recommended) it will not work


  3. mesf(format, ...param) is just a shorthand for mes(sprintf(format, ...param)) so you don't have to use both mesf() and sprintf().

    You can find the documentation in doc/script_commands.txt: mesf(), sprintf()
     

    Replace this line:

    mes "^FF0000Your Latest Record^000000: "+.@s+"."+.@ms+" Seconds.";

    With this line:

    mesf("^FF0000Your Latest Record^000000: %d.%03d Seconds.", .@s, .@ms);

     


    Replace this line:

    mes "^0000FFTime Taken^000000: [ "+.@emp_s+"."+.@emp_ms+" Sec. ]";

    With this line:

    mesf("^0000FFTime Taken^000000: [ %d.%03d Sec. ]", .@emp_s, .@emp_ms);

     

     

    For the areaannounce() lines you can use sprintf(), like this:

    areaannounce("map name", .x1, .y1, .x2, .y2, sprintf("Your time is %dm %02ds %03dms", .m, .s, .ms), bc_area);

     


  4. By two more zeroes do you mean zero-padding so that the numbers are in the same format, like 008 (always the same width)?

    You can do zero-padding with sprintf() like so:

    sprintf("%03d", 5); // 005

    so you would use it like this:

    mesf("^0000FFTime Taken^000000: [ %d.%03d Sec. ]", @emp_s, @emp_ms);
    // this would print, for example: Time taken: [ 5.095 Sec.]

     


  5. The getmonsterinfo() command expects to see a mob unit ID but you passed an account ID. This is because killerrid can be any kind of unit (player, mob, homunculus, ...) but you are using this unit id as-is without checking what kind of unit it is. From your if() condition it seems you are only trying to see if the player was killed by a mob on the .Map$ map so you could replace it with this:
     

    if (strcharinfo(PC_MAP) == .Map$ && getunittype(.@killerrid) == UNITTYPE_MOB) {
    	...
    }

     


  6. This error is quite explicit: the ID of your character in your SQL database (table `char`) is less than 150000 (the initial ID for auto-increment). This could happen if you manually added a character and explicitly specified an ID lower than 150000. Since the column is on auto-increment you should not specify a char id when adding rows to the table. You can find the offending rows with a simple query:

    SELECT * FROM `char` WHERE `char_id` < 150000;

    Keep in mind that if you end up changing the ID of a char it needs to be changed in every table that has a char_id column and should only be done when the server is offline.

     

    You can reset the auto increment with this query:

    ALTER TABLE `char` AUTO_INCREMENT = 1;

    Setting auto increment to a value less than or equal the highest value will reset it to highest + 1


  7. You can hook into the chat and whisper-related functions with a HPM plugin and then call the Google Cloud Translation API but this will be very costly because you have to translate every single message into the local language of every listening player so if you have a player speaking English and there's 50 players around them and they all use a different language you end up sending 50 translation requests to the API. You could do the translation client-side but you would have to build your google translate logic directly into the RO client. Keep in mind that this will also increase lag because you wait on API responses before displaying the message.


  8. You'd want to do the translation offline (not on a live server, on-the-fly) using a HPM plugin (such as generate-translations) to generate your po files and then you would use a script (maybe in python, so you could use that python lib you linked) to call the google "cloud translation" api and translate your po file. You could also put both the po generator and translation routine in the same HPM plugin (using generate-translations as base and expanding from there)


  9. pow() has been replaced by the exponentiation operator (x ** y),

    see here: 

     

     

     

    In your case you would need to do these changes:

    // change this
    set .@num$, .@num % pow(10,.@i+1) / pow(10,.@i) + .@num$;
    
    // to this:
    .@num$ = .@num % (10 ** (.@i + 1)) / (10 ** .@i) + .@num$;

     


  10. instead of using item names in your array you should be using item constants if what you want is item IDs, so something like

    setarray(.@items$,
    	"Jellopy",
    	"Large_Jellopy",
    	...);

    becomes

    setarray(.@items,
    	Jellopy,
    	Large_Jellopy,
    	...);

    so you use the unquoted AegisName from the item db, which is a constant of its ID
     

     

    but anyway, the getitem() command does work with both the item ID and the AegisName so if you are using the name it should work fine.

     

    // all of these are equivalent:
    getitem(501, 1);
    getitem(Red_Potion, 1);
    getitem("Red_Potion", 1);
    
    // but this won't work:
    getitem("Red Potion", 1);

     


  11. It's IOT_CHAR, not IM_CHAR

    .@char = getcharid(CHAR_ID_CHAR);
    .@account = getcharid(CHAR_ID_ACCOUNT);
    .@map_name$ = "mapname"; // the map you want to instance
    .@instance_name$ = sprintf("%s@%i%d", .@map_name$, .@char, gettime(GETTIME_SECOND)); // a unique name for your instance
    .@instance = instance_create(.@instance_name$, .@account, IOT_CHAR); // create the instance and attach it to the char
    .@instanced_map_name$ = sprintf("%s@%i", .@map_name$, .@instance); // a unique name for the instanced map
    .@map$ = instance_attachmap(.@map_name$, .@instance, false, .@instanced_map_name$); // attach the instanced map to your instance
    instance_timeout(600, 1, .@instance); // alive and idle timeout, in seconds
    
    // setup is ready so init the instance and warp the player
    instance_init(.@instance);
    warpchar(.@map$, 20, 20, .@char); // the (x,y) starting point

  12. if you want to give an item to every guild member on the current map you can use getunits(), something like

    .@guild = getcharid(CHAR_ID_GUILD);
    .@map$ = getmapinfo(MAPINFO_NAME);
    
    // check to make sure we're not iterating for nothing
    if (.@guild && getmapguildusers(.@map$, .@guild) > 1) {
      // get every player on the current map
      .@count = getunits(BL_PC, .@units, false, .@map$);
      // iterate over all those players
      for (.@i = 0; .@i < .@count; ++.@i) {
        // check if they are in the same guild
        if (.@units[.@i] != playerattached() && getcharid(CHAR_ID_GUILD, strcharinfo(PC_NAME, "", .@units[.@i])) == .@guild) {
          getitem({{item}}, {{amount}}, .@units[.@i]);
        }
      }
    }

    To give to offline guild members you could use the RoDEX system


  13. the login and guild tables should not be used to store variables, it is better to use variables directly, but if you must then you want a query that spans multiple tables, something like

    -- give 100 "pontosgwot" to the player that killed it
    UPDATE `login` SET `pontosgwot`=`pontosgwot`+100 WHERE `account_id`={{GID}};
    
    -- give 50 "pontosgwot" and 1 "baus" to everyone else in the guild
    UPDATE `login` SET `login`.`pontosgwot`=`login.pontosgwot`+50 WHERE `login`.`account_id`=`char.account_id` AND `char`.`guild_id`={{GUILD}} AND `char`.`char_id`<>{{GID}};
    UPDATE `login` SET `login`.`baus`=`login.baus`+1 WHERE `login`.`account_id`=`char.account_id` AND `char`.`guild_id`={{GUILD}} AND `char`.`char_id`<>{{GID}};

    Replace {{GID}} with the account id and {{GUILD}} with the guild id

     

    This is very slow though so don't do this unless you really need to


  14. The documentation resides in the /doc folder of your cloned git repository and the scripting engine documentation is in /doc/script_commands.txt

    The version you have locally may not be the version that is currently on GitHub so always use the documentation in the /doc subfolder of the emulator root folder since its version is always the same as the version of your Hercules emulator (when properly cloning and pulling with git)

     

    Please be aware that some commands have been deprecated over the years and replaced with other commands. The documentation will tell you when a command is deprecated and you can find more details in the Deprecation thread.


  15. // set the unit ID of the target
    .@target_id = XXXXX;
    
    // get the location of the target (change TYPE to PC/MOB/...)
    getmapxy(.@map$, .@x, .@y, UNITTYPE_{{TYPE}}, .@target_id);
    
    // get all mobs within a 3 cell radius around the target
    .@count = getunits(BL_MOB, .@units[0], false, .@map$, max(0, .@x - 3), max(0, .@y - 3), .@x + 3, .@y + 3);
    
    // iterate over the mob units
    for (.@i = 0; .@i < .@count; ++.@i) {
        .@unit = .@units[.@i];
        // ... do something with your .@unit
    }

     


  16. 10 to the power of 10 is 10,000,000,000 but the scripting engine uses signed 32-bit integers and the maximum value that can be held by such integers is 2,147,483,647 (2^31 − 1).

    Any value above this will cause the integer to overflow so you have to be aware of this and manually check the boundaries. In your case this would mean checking that getarg(0) is shorter than 10 characters long so

    if ( .@num == 0 || .@num >= 10 ** 9 ) return getarg(0);
    
    instead of
    
    if ( .@num == 0 || .@num >= 2147483647 ) return getarg(0);

    to avoid the case where getstrlen is above 10


  17. the exponentiation operator (**) has higher precedence than the addition operator (+) so this means A ** B+C is interpreted as (A ** B) + C because the exponentiation operation has priority over the addition operation

     

    also worth noting that exponentiation has higher precedence than multiplication/division so a more complex formula like A + B * C ** D would be interpreted as A + (B * (C ** D)) so you have to manually reorder the operations with explicit parentheses if you want to change the precedence, like (A + (B * C)) ** D


  18. no need for SQL nor a timer: just store the date instead of a true/false boolean

    // check whether the last reward was given today or another day
    if (gettime(GETTIME_DAYOFYEAR) == #LAST_REWARD) {
        mes("you already got a reward today");
    } else {
        mes("here's your reward");
        ... // give reward
        #LAST_REWARD = gettime(GETTIME_DAYOFYEAR); // set it to today
    }

     

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.