Jump to content
Euphy

UseSkillToPC

Recommended Posts

declare UseSkillToPC    nnnn  ENUM++ // Use skill [skill_ID] [skill_Level] [stat_Point] [npc_Level]

Description: NPC casts the specified skill on the attached player.

lasEv.png

We've been writing workarounds to this command for as long as I remember (e.g. sc_start/heal/specialeffect2), and this really should be addressed.

We could potentially modify 'unitskilluseid' to fit this purpose, but there's currently no way to retrieve NPC GID (or is there?) and the command doesn't allow for Stat_Point/npc_Level arguments. It'll most likely require a new command altogether.

Link to comment
Share on other sites

getnpcid(<type>{,"<npc name>"}) ---> return npc's Unit ID

some of skills will involve with status (str,dex...) and else, will need to be solved.

Edited by goddameit
  • Upvote 1
Link to comment
Share on other sites

I completely missed that command, thanks for pointing it out! o.o

All NPCs have zero stats, and it seems like skills which rely on stats (ex. Heal) are currently crashing the mapserver:

unitskilluseid getnpcid(0),"AL_HEAL",10,getcharid(3);

So, would extra parameters to 'unitskilluseid' be the best way to do this?

Link to comment
Share on other sites

I'll take a look at it, and post when I've gotten a chance to--as I'm about to leave. I've noted this thread though, Euphy.

If any other Core Developers have a better idea than the idea Euphy proposed above, please feel free to add on! :)

Thanks,

-Cookie

Link to comment
Share on other sites

they're gonna be pretty much the same anyway but we could give you something more generic or without unit_id to facilitate, something like this :

http://upaste.me/1a921649a2c14a5b, thus ground will kinda suck since it will cast on self and it's kinda duplicate unitskillusepos just below.

*useskill,<skillid>,<skilllv,>,{<targetid|"targetname">};

What would be the usage of statspoint, npc_level for ??

Link to comment
Share on other sites

What would be the usage of statspoint, npc_level for ??

NPCs have no level or stats, so skills using them always miss and skills requiring them crash the mapserver. Honestly, I don't know how the Stat_Point argument is supposed to work, though.

Examples:

// Will work using unitskilluseid
UseSkillToPC 34 0 0 0

// Crashes mapserver (probably because lvl is required)
UseSkillToPC 28 10 50 70
UseSkillToPC 28 10 99 60

Link to comment
Share on other sites

Oh wait. ._. Stat_Point should just set all stats to the given value. Not sure why I didn't think of that earlier...


So here's a better proposal, now:

*unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>{,<stat point>,<level>}};

Both "stat point" and "level", if specified, would affect the caster (so we're allowing regular players to cast skills with modified stat/level values, as well, rather than limiting it to NPCs).

Link to comment
Share on other sites

Ohh I see yeah that smart, but we'll need like an array for stats point imo, so you could pass for all.

unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>{,<ref stat point>,<level>}};

where :

stpoint[0] = str;

stpoint[1] = agi;

stpoint[2] = vit;

stpoint[3] = int;

stpoint[4] = dex;

stpoint[5] = luk;

Also unitskilluseid won't cover ground skill, well we could auto resolve that, but it's more like what I did in useskill

Link to comment
Share on other sites

I think an array is unnecessary. The official scripts only pass one value, and I can't see many benefits of passing six (honestly, how many people are going to use this? xD). And it's not as convenient to script.

Link to comment
Share on other sites

Ok but skill usually use int and dex etc, will right now the statsthing would be a pain since we always using the one from player, need a whole new layer to support that.

(Or temporary replace player stats but dunno if that a good idea)

Link to comment
Share on other sites

If it's too much trouble, don't bother with the player stat-swapping XD. We do need to be able to specify stats for NPCs, though - temporarily replacing the NPC stats sounds like it'd be fine.

Link to comment
Share on other sites

To real skill usage for NPCs, NPCs certainly need stats.

However forcing the scripters to specify all stats for a skill feels a bit cumbersome for me.

And do not forget, there are several possible data that skills might use beside stats. It involves crit, dodge, def, atk, matk, items etc. Some of them can not be manipulated by stats, such as the item part of def. Some skills are use certain item stats to calculate their formulas. I see this feature as the source of many troubles. Some skill may not work as expected this way, or cause crashes.

Link to comment
Share on other sites

How about implementing some kind of 'virtual' player. The player can be shared between NPCs and all existing player modification functions can be used.

Used something like this...

[email protected]_gid = CreateVirtualPlayer();
attachrid([email protected]_gid);
statusup2(bStr, 100);
UseSkillToPC([email protected]_gid, <skill_id>, <skill_level>, <target_gid>);
UseSkillToPC([email protected]_gid, <skill_id>, <skill_level>, <map>, <x>, <y>);

DestroyVirtualPlayer([email protected]_gid);

Casting source can be set to the invoking NPC or nothing in the case of Floating NPCs.

Link to comment
Share on other sites

Most of those skills at rely on item stats are usually offensive skills and in this case not affect the server in any real way, since we aren't going to be using the NPC to attack players (Though we could). However, as for stating stats, just add another parameter, with a bit type reading. 1 = Str, 2 = Vit, 4 = Agi, 8 = Int, 16 = Dex, 32 = Luk... And 3 = Str/Vit OR 7 = Str,Vit,Agi... You get the idea. But this would imo effectively allow us to specify which stats we want to be affected.

Link to comment
Share on other sites

@Toasty:

That's even more complicated than lighta's array suggestion... it would solve the problems with non-base stats, but I don't think we need to go that far beyond what's needed to achieve official functionality. Unless you can think of other uses?

@GmOcean:

Sounds good, but same comment as above -- what would we use it for?

Link to comment
Share on other sites

Well, for use, I can only think of this command being used for NPC for hire/ controlled mercenary scripts. Other than that, I have no idea for the use lol.

Link to comment
Share on other sites

not my,but i have this

maybe who fix it

[EXPAMLE]
npcsetstatus STATUS_STR, 1;
npcsetstatus STATUS_AGI, 9;
npcsetstatus STATUS_VIT, 1;
npcsetstatus STATUS_INT, 40;
npcsetstatus STATUS_DEX, 55;
npcsetstatus STATUS_LUK, 1;
npcsetstatus STATUS_LEVEL, 65;
npcsetstatus STATUS_JOBLV, 50;
npcsetstatus STATUS_RECALC, 1;
[NPC.H]
struct npc_status {
int
 blevel, jlevel;
struct status_data sd;
short
 head_top, head_mid, head_bottom;
int
 castrate, delayrate, hprate, sprate,
 atk_rate,hprecov_rate,sprecov_rate,
 matk_rate,critical_rate,hit_rate,flee_rate,flee2_rate,
 def_rate,def2_rate,mdef_rate,mdef2_rate,
 double_rate, double_add_rate,
 perfect_hit, perfect_hit_add, splash_range, splash_add_range;
};
struct npc_status* status;
[sTATUS.C]
int status_calc_npc_(struct npc_data* nd) {
struct npc_status *status;
const struct status_change *sc = &nd->sc;
int i;
struct item_data* itd;
nullpo_ret(nd);
nullpo_ret(nd->status);
nullpo_ret(nd->status->sd);
status = nd->status;
status->sd.max_hp =
status->sd.max_sp = 0;
memset(&status->sd.batk, 0, sizeof(struct status_data)
  -sizeof(status->sd.hp)
  -sizeof(status->sd.sp)
  -sizeof(status->sd.max_hp)
  -sizeof(status->sd.max_sp)
  -sizeof(status->sd.str)
  -sizeof(status->sd.agi)
  -sizeof(status->sd.vit)
  -sizeof(status->sd.int_)
  -sizeof(status->sd.dex)
  -sizeof(status->sd.luk)
);
status->castrate =
status->delayrate =
status->hprate =
status->sprate =
status->atk_rate =
status->hprecov_rate =
status->sprecov_rate =
status->matk_rate =
status->critical_rate =
status->hit_rate =
status->flee_rate =
status->flee2_rate =
status->def_rate =
status->def2_rate =
status->mdef_rate =
status->mdef2_rate = 100;
status->sd.mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK);
status->sd.size = (nd->class_&JOBL_BABY)?SZ_SMALL:SZ_MEDIUM;
status->sd.speed = DEFAULT_WALK_SPEED;
status->sd.aspd_rate = 1000;
status->sd.ele_lv = 1;
status->sd.race = RC_DEMIHUMAN;
if( status->sd.rhw.range < 1 ) status->sd.rhw.range = 1;
if( status->sd.lhw.range < 1 ) status->sd.lhw.range = 1;
if( status->sd.rhw.range < status->sd.lhw.range )
 status->sd.rhw.range = status->sd.lhw.range;
status->double_rate += status->double_add_rate;
status->perfect_hit += status->perfect_hit_add;
status->splash_range += status->splash_add_range;
// Relative modifiers from equipment
if( status->hprate < 0 )
 status->hprate = 0;
if( status->hprate != 100 )
 status->sd.max_hp = status->sd.max_hp * status->hprate/100;
if( battle_config.hp_rate != 100 )
 status->sd.max_hp = status->sd.max_hp * battle_config.hp_rate/100;
if( status->sd.max_hp > (unsigned int)battle_config.max_hp )
 status->sd.max_hp = battle_config.max_hp;
else if( !status->sd.max_hp )
 status->sd.max_hp = 1;
// set the current HP to max HP if current HP is zero.
if( status->sd.hp == 0 )
 status->sd.hp = status->sd.max_hp;
//----- SP Calculation
i = 10 + status->blevel*sp_coefficient[pc_class2idx(nd->class_)]/100;
i += i * status->sd.int_/100;
if( nd->class_&JOBL_UPPER )
 i += i * 25/100;
else if ( nd->class_&JOBL_BABY )
 i -= i * 30/100;
status->sd.max_sp = cap_value(i, 0, INT_MAX);
if( status->sprate < 0 )
 status->sprate = 0;
if( status->sprate != 100 )
 status->sd.max_sp = status->sd.max_sp * status->sprate/100;
if( battle_config.sp_rate != 100 )
 status->sd.max_sp = status->sd.max_sp * battle_config.sp_rate/100;
if( status->sd.max_sp > (unsigned int)battle_config.max_sp )
 status->sd.max_sp = battle_config.max_sp;
else if( !status->sd.max_sp )
 status->sd.max_sp = 1;
//----- Misc Calculation
status_calc_misc(&nd->bl, &status->sd, status->blevel);
if( status->matk_rate < 0 )
 status->matk_rate = 0;
if( status->matk_rate != 100 ) {
 status->sd.matk_max = status->sd.matk_max * status->matk_rate/100;
 status->sd.matk_min = status->sd.matk_min * status->matk_rate/100;
}
if( status->hit_rate < 0 )
 status->hit_rate = 0;
if( status->hit_rate != 100 )
 status->sd.hit = status->sd.hit * status->hit_rate/100;
if( status->flee_rate < 0 )
 status->flee_rate = 0;
if( status->flee_rate != 100 )
 status->sd.flee = status->sd.flee * status->flee_rate/100;
if( status->def2_rate < 0 )
 status->def2_rate = 0;
if( status->def2_rate != 100 )
 status->sd.def2 = status->sd.def2 * status->def2_rate/100;
if( status->mdef2_rate < 0 )
 status->mdef2_rate = 0;
if( status->mdef2_rate != 100 )
 status->sd.mdef2 = status->sd.mdef2 * status->mdef2_rate/100;
if( status->critical_rate < 0 )
 status->critical_rate = 0;
if( status->critical_rate != 100 )
 status->sd.cri = status->sd.cri * status->critical_rate/100;
if( status->flee2_rate < 0 )
 status->flee2_rate = 0;
if( status->flee2_rate != 100 )
 status->sd.flee2 = status->sd.flee2 * status->flee2_rate/100;
if( status->def_rate < 0 )
 status->def_rate = 0;
if( status->def_rate != 100 ) {
 i = status->sd.def * status->def_rate/100;
 status->sd.def = cap_value(i, CHAR_MIN, CHAR_MAX);
}
if( !battle_config.magic_defense_type && status->sd.mdef > battle_config.max_def ) {
 status->sd.mdef2 += battle_config.over_def_bonus*(status->sd.mdef - battle_config.max_def);
 status->sd.mdef = (signed char)battle_config.max_def;
}
//----- ASPD Calculation
 // PCs call function status_base_amotion_pc
i=1000; // instead I set it to this 
status->sd.amotion = cap_value(i,battle_config.max_aspd,2000);
status->sd.adelay = 2*status->sd.amotion;
//----- DMOTION
i = 800-status->sd.agi*4;
status->sd.dmotion = cap_value(i, 400, 800);
if( battle_config.pc_damage_delay_rate != 100 )
 status->sd.dmotion = status->sd.dmotion*battle_config.pc_damage_delay_rate/100;
return 1;
}
if( flag&SCB_BASE ) {// calculate the object's base status too
 switch( bl->type ) {
  case BL_PC:  status_calc_pc_(BL_CAST(BL_PC,bl), first); break;
  case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break;
  case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break;
  case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl),first); break;
  case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break;
  case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first);  break;
+   case BL_NPC: status_calc_npc_(BL_CAST(BL_NPC,bl));   break;
 }
}
[sTATUS.H]
#define status_calc_npc(nd) status_calc_bl_(&(nd)->bl, SCB_ALL, 0)
[CONST.TXT]
STATUS_HP	   0
STATUS_SP	   1
STATUS_MAXHP	2
STATUS_MAXSP	3
STATUS_STR	  4
STATUS_AGI	  5
STATUS_VIT	  6
STATUS_INT	  7
STATUS_DEX	  8
STATUS_LUK	  9
STATUS_MATK_MIN 10
STATUS_MATK_MAX 11
STATUS_SPEED	12
STATUS_AMOTION  13
STATUS_ADELAY   14
STATUS_DMOTION  15
STATUS_MODE	 16
STATUS_HIT	  17
STATUS_FLEE	 18
STATUS_CRI	  19
STATUS_PDODGE   20
STATUS_DEF2	 21
STATUS_MDEF2	22
STATUS_ASPD	 23
STATUS_DEF_ELE  24
STATUS_ELE_LV   25
STATUS_SIZE	 26
STATUS_RACE	 27
STATUS_DEF	  28
STATUS_MDEF	 29
STATUS_LEVEL	30
STATUS_JOBLV	31
STATUS_RECALC   32
[sCRIPT.C]
BUILDIN_DEF(npcsetstatus, "ii"),
BUILDIN_FUNC(npcsetstatus) {
TBL_NPC* nd = (TBL_NPC*)map_id2bl(st->oid);
int type = script_getnum(st,2);
int value = script_getnum(st,3);
struct status_data* status;
if( !nd ) {
 ShowError("script:npcsetstatus: No NPC o.O\n");
 return 0;
}
if( !nd->status ) {
 struct npc_status* status;
 status = (struct npc_status*)aMalloc(sizeof(struct npc_status));
 memset(status, 0, sizeof(struct npc_status));
 nd->status = status;
 status_calc_npc(nd);
}
status = status_get_status_data(&nd->bl);
switch( type ) {
 case 0: // HP
  status->hp = value;
  if( status->max_hp < status->hp ) { // should this be an error or a feature?
status->max_hp = value;
  }
  break;
 case 1: // SP
  status->sp = value;
  if( status->max_sp < status->sp ) { // should this be an error or a feature?
status->max_sp = value;
  }
  break;
 case 2: // MAX_HP
  status->max_hp = value;
  if( status->hp > status->max_hp ) { // this I'm calling an error
ShowError("script:npcsetstatus: NPC new max hp (%d) is less than current hp (%d).  Setting current hp to max hp.\n", value, status->hp);
status->hp = status->max_hp;
  }
  break;
 case 3: // MAX_SP
  status->max_sp = value;
  if( status->sp > status->max_sp ) { // this too!
ShowError("script:npcsetstatus: NPC new max sp (%d) is less than current sp (%d).  Setting current sp to max sp.\n", value, status->sp);
status->sp = status->max_sp;
  }
  break;
 case 4: // str
  status->str = value;
  break;
 case 5: // agi
  status->agi = value;
  break;
 case 6: // vit
  status->vit = value;
  break;
 case 7: // int_
  status->int_ = value;
  break;
 case 8: // dex
  status->dex = value;
  break;
 case 9: // luk
  status->luk = value;
  break;
 case 10: // matk_min
  status->matk_min = value;
  break;
 case 11: // matk_max
  status->matk_max = value;
  break;
 case 12: // speed
  status->speed = value;
  break;
 case 13: // amotion
  status->amotion = value;
  break;
 case 14: // adelay
  status->adelay = value;
  break;
 case 15: // dmotion
  status->dmotion = value;
  break;
 case 16: // mode (I don't think this will do anything ><!)
  status->mode = value;
  break;
 case 17: // hit
  status->hit = value;
  break;
 case 18: // flee
  status->flee = value;
  break;
 case 19: // cri
  status->cri = value;
  break;
 case 20: // perfect dodge
  status->flee2 = value;
  break;
 case 21: // def2
  status->def2 = value;
  break;
 case 22: // mdef2
  status->mdef2 = value;
  break;
 case 23: // aspd_rate
  status->aspd_rate = value;
  break;
 case 24: // def_ele
  status->def_ele = value;
  break;
 case 25: // ele_lv
  status->ele_lv = value;
  break;
 case 26: // size
  status->size = value;
  break;
 case 27: // race
  status->race = value;
  break;
 case 28: // def
  status->def = value;
  break;
 case 29: // mdef
  status->mdef = value;
  break;
 case 30: // level
  nd->status->blevel = value;
  break;
 case 31: // jlevel
  nd->status->jlevel = value;
  break;
 case 32: // RECALCULATE VALUES BASED ON STATS
  status_calc_npc(nd);
  break;
 default:
  ShowError("script:npcsetstatus: Unknown type: %d\n", type);
  return 1;
}
return 0;
}

Edited by kadze
  • Upvote 1
Link to comment
Share on other sites

I like kadze's idea. It would give a wider range of support when wanting to vary the stats of the NPC rather than having to provide the same stat value for all types (STR, AGI, etc.).

Link to comment
Share on other sites

@Aleos: Most official scripts do not rely on many parameters for skill casting, so it's inefficient to specify all of them (and annoying, too). Again, I don't really see much other use for this, so there's not any point adding so much custom content. :<

Link to comment
Share on other sites

Think the sd attachment is a limitation that should be rethinked.

(no point to have a player attached for ground skill (only need m,x,y))

Also probably easier to specify target instead taking attached player, so you won't have to detach attach other rid but that less annoying; more optimisation than real issue.

Link to comment
Share on other sites



×
×
  • Create New...

Important Information

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