diff options
Diffstat (limited to 'npc/functions/npcmovegraph.txt')
-rw-r--r-- | npc/functions/npcmovegraph.txt | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/npc/functions/npcmovegraph.txt b/npc/functions/npcmovegraph.txt new file mode 100644 index 00000000..54e4e783 --- /dev/null +++ b/npc/functions/npcmovegraph.txt @@ -0,0 +1,485 @@ +// 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()) + { + consolemes(CONSOLEMES_DEBUG, "findmovegraphlabel: no argument"); + return -1; + } + if (!isstr(getarg(0))) + { + consolemes(CONSOLEMES_DEBUG, "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 + { + consolemes(CONSOLEMES_DEBUG, "execmovecmd: unknown WARP destination label: " + .@cmd$[1]); + } + } + else if (.@cmd$[0] == "call") + { + switch (getarraysize(.@cmd$)) + { + case 1: + consolemes(CONSOLEMES_DEBUG, "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; + npctalk implode(.@cmd$, " "); + } + else if (.@cmd$[0] == "debugmes") + { + deletearray .@cmd$[0], 1; + consolemes(CONSOLEMES_DEBUG, 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 + { + consolemes(CONSOLEMES_DEBUG, "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) + { + consolemes(CONSOLEMES_DEBUG, "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 + consolemes(CONSOLEMES_DEBUG, "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) + { + consolemes(CONSOLEMES_DEBUG, "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; +} |