Skip to content

Instantly share code, notes, and snippets.

@jalbright015
Created April 10, 2021 00:41
Show Gist options
  • Save jalbright015/ae3c658b33b32240fe6976206dc49383 to your computer and use it in GitHub Desktop.
Save jalbright015/ae3c658b33b32240fe6976206dc49383 to your computer and use it in GitHub Desktop.
A weapon that is used by a very high level, special mob
#include <mudlib.h>
#include <status.h>
#include <daemons.h>
#include <properties.h>
#include "../maze.h"
#include "/u/j/jezu/std/debug.c"
inherit WEAPON;
#define ANSI_OBJ "/obj/ansi_effects/bicolor.c"
#define KILLS_TO_TRACK 50
int _healing_count, _cur_index = -1, const_souls = 5+random(110);
mapping _kill_data = ([]);
object *_minions = ({}), _owner;
mapping _tmp_kills;
int query_kill_level(object killer);
void do_healing(object *targets, object owner);
int rotate_arr(string name, int i);
varargs mixed query_kill_data(object killer)
{
if ( !objectp(killer) )
return _kill_data;
return _kill_data[(string)killer->query("name")];
}
varargs void set_kill_data(object killer, int kill_total, int level_total)
{
string name;
name = (string)killer->query("name");
_kill_data[name] = ([ "kill_total" : kill_total, "level_total" : level_total ]);
if ( !sizeof(_kill_data[name]["kills"]) )
_kill_data[name]["kills"] = allocate(KILLS_TO_TRACK);
}
void create()
{
seteuid(getuid());
add("id", ({ "scythe", "harvester scythe", "slender scythe" }));
set("short", "a long, slender scythe");
set("idshort", "%^BOLD%^%^BLACK%^Voraz%^RESET%^, %^RESET%^%^ORANGE%^the Harvester's Scythe%^RESET%^");
set("long", "@@query_long");
set("name", "scythe");
set("mass", 75);
set("bulk", 15);
set("second", 0);
set("no_second", 1);
set("value", ({ 46, "crown" }) );
set("weapon", 30);
set("damage", ({ 15, 28 }) );
set("type", "cutting");
set_verbs(({ "slash", "cut", "maim" }) );
set_verbs2(({ "slashes", "cuts", "maims" }) );
set("canwield", ([ "everyone" : 20 ]) );
set("hit_func", "do_special");
set("artifact", 1);
set("legend",
"Amidst rare whispers, some speak in strained voices, cracked by the screams that those same throats have born, \
lips parched and drawn white in the shadows of the asylums and monasteries that those poor souls often now call \
home. Those hoarse voices speak of terrible things in words ancient and complex, but the most fearful words spoken \
by those mad men are simple, \"It is more! Not mere tools, not steel and iron! Some remain! Some dark few, fetid \
and raw, pallid and awful. Some wait! Dark spiders, they! Crawling, creeping, their knives hunger as they do!\". \
The cries of mad and broken man perhaps, tales from a brooding mind broken by age and fear. But for those who \
are willing to seek and for those without fear, the strong or the foolish... perhaps there is something more. \n\n\
Behold, a dark weapon from a place darker still: Voraz, the devourer of souls. The ancient mages spoke of the \
weapons that the shadows once bore in the time before the harmony. When the great and powerful mages of the old "+"\
magic took up arms and cleaved the fabric of the world itself asunder, those wild and terrible creatures from \
beyond the rift bore such weapons, tools wrought in the pale places that lay in the forbidden depths of the ethereal \
void. Those feral and hungry creatures hunted and fed upon the mortal world until the council banded together to \
force them back and banned the old magics that made their arrival possible. Yet, the story does not end there... \
Some speak in hushed whispers and insist that some other of those voidwrought tools remain, abandoned by their \
owners and waiting for another to take them up so that they may return to their unknowable eternal duty.");
set("no_vault", 1);
set("no_closet", 1);
set("wield_func", "do_wield");
set("unwield_func", "do_unwield");
}
void identify()
{
if(query_verb() == "legend") return;
message("", "As you focus your power on the scythe, it shimmers and begins to expand in \
a very peculiar fashion. The weapon seems to 'unfold' in such an utterly bizarre and impossible way that you \
almost lose your grip on the haft before you realize that the weapon itself has not changed but rather your \
perception of it. You see the scythe for what it is, a tool that harvests not wheat and chaff, nor even flesh \
and bone but instead consumes essence that lies within. A voidwrought weapon that absorbs the very lifeforce of \
those slain by its touch. A feeling of unease suddenly falls over you and you let the haft fall limply from \
your fingertips; having cast off the weapon's disguise you no longer need to suffer its touch to know it for \
what it is. You hold Voraz, the devourer of souls.\n", TO);
return;
}
string query_long()
{
int kills;
if(query("identified"))
{
kills = sizeof(_kill_data);
return "A strange object, warped and distorted as though it somehow exist in more than three dimensions, \
its outward appearance shifting in a headache-inducing fashion that defies explanation. Briefly and \
inconsistently the tool or weapon's liminal nature appears to allow it to be seen as a somewhat \
bland looking scythe but its utterly incomprehensible construction gives open lie to that visual \
falsehood. The scythe's blade is a perfect black, a dark so utterly and completely consuming of \
light that it could be a portal to the sheer darkness of the void itself and off of which traceries of \
searing voidfrost slowly roil. The haft is a shimmering metal ceaselessly shifting in hue like brazed \
copper held too close to a flame. The dazzling rainbow patterns quickly become mesmerizing, but the \
alien beauty is suddenly overshadowed by the sense of a wild and mindless ravening that creeps outward \
in a palpable aura.\n\n\The scythe currently contains "+kills+" souls.";
}
kills = sizeof(_kill_data) + const_souls;
return "Fully eight feet long and devoid of the curved haft commonly seen in a traditional farming \
implement, this scythe is a tool clearly not intended for use in the harvesting of more \
traditional crops. The blade's is broadly curved edge is wickedly serrated on the inside \
of the arch and ground to a razor polish on the back. The haft itself is long and straight, \
carved of some pallid hardwood; heavy, thick and deeply stained with some gritty varnish of \
dark rusty brown. Numerous notches line the haft in clusters of five notches; four vertical and \
a single angular horizontal stroke marking the fifth. Currently there are "+kills+" notches carved \
into the wood. The weapon's mundane appearance does little to suppress a palpable aura of \
hunger that lays upon the weapon; not malevolence or evil, but a sort of inhuman ravening that \
is somehow far more unnerving. The weapon is surprisingly well balanced for its shape.";
}
int do_wield()
{
if ( !objectp(ETO) )
return 0;
ETO->set("kill_func", TO);
message("", "The scythe makes clear its wishes with a simple and overpowering command whispered in a \
language as powerful and undeniable as it is utterly monstrous and alien. It speaks only two words that it bids you to obey: \"%^BOLD%^%^RED%^FEED ME.%^RESET%^\"\n", ETO);
return 1;
}
int do_unwield()
{
if ( !objectp(ETO) )
return 0;
message("", "You feel a sense of relief as you release your grip from the scythe and the defiling caress of its ravenous hunger slowly leaves you. \
You blink like a dreamer waking from a nightmare, your terrible experience leaving only a slowly fading memory.\n", ETO);
ETO->delete("kill_func");
return 1;
}
varargs int get_spec_lev(int is_random)
{
int i1, i2, k;
if ( query_kill_data(ETO) == _kill_data )
return _kill_data["last_kill_level"];
k = query_kill_level(ETO);
if ( undefinedp(k) )
return 0;
if ( !is_random )
return k;
i1 = to_int(round(dimval(to_float(k), 4.0, 4.0, 0.0, 0.0, 2.0)));
i2 = to_int(round(dimval(to_float(k), 4.0, 4.0, 0.0, 0.0, 1.14)));
if ( i1 < 1 )
i1 = 1;
else if ( i2 > 4 )
i2 = 4;
k = random(i2);
k = k < --i1 ? i1 : k;
_kill_data["last_kill_level"] = k;
return k;
}
int do_special(object attacker, object target)
{
int hp_percent, i, j, k, l, spec_lev;
object *targets, mon;
mixed *specials;
string *s;
mapping sdata;
if ( random(4) )
return 0;
targets = (object*)attacker->query_attackers();
spec_lev = get_spec_lev(1);
switch ( random(3) )
{
// Group damage
case 0 :
{
i = sizeof(targets);
specials = ({
({ 30+(random(10)*i), ({ "%^BOLD%^%^BLACK%^", "%^ORANGE%^" }),
({ "%^BOLD%^%^BLACK%^Ebony tendrils swirl towards you and burrow themselves into your chest!%^RESET%^",
"%^BOLD%^%^BLACK%^Ebony tendrils swirl towards {N} and burrow themselves into {P} chest.%^RESET%^" }) }),
({ 35+(random(15)*i), ({ "%^BOLD%^%^BLACK%^", "%^GREEN%^" }),
({ "Dark tendrils swirl towards you and emenate a green glow as they ravage your body!",
"Dark tendrils swirl towards {N} and emenate a green glow as they ravage {P} body!"}) }),
({ 40+(random(20)*i), ({ "%^BOLD%^%^BLACK%^", "%^BLUE%^" }),
({ "Dark tendrils, dancing to and fro, begin to glow a dark indigo as they rip into you!",
"Dark tendrils, dancing to and fro, begin to glow a dark indigo as they rip into {N}!."}) }),
({ 45+(random(25)*i), ({ "%^BOLD%^%^BLACK%^", "%^MAGENTA%^" }),
({ "Black tendrils snake towards you and illuminate in deep purple as the eviscerate your body!",
"Black tendrils snake towards {N} and illuminate in deep purple as the eviscerate {P} body!",}) }),
});
contextmsg("combat", attacker,
"%^BOLD%^%^BLACK%^With a wide swing overhead you slam your scythe down, anchoring it deep within the chest of {n}. Ebon tendrils explode from the wound and into the surrounding area.%^RESET%^",
"%^BOLD%^%^BLACK%^With a wide swing overhead, {N} slams {P} scythe down, anchoring it deep within the chest of {n}. Ebon tendrils explode from the wound and into the surrounding area.%^RESET%^",
target,
"%^BOLD%^%^BLACK%^With a wide swing overhead, {N} slams {P} scythe down, anchoring it deep within your chest. Ebon tendrils explode from the wound and into the surrounding area.%^RESET%^");
targets->set("casting", 0);
//EVAL_START;
j = i;
for ( i = 0; i < j; i++ )
{
sdata = specials[get_spec_lev(1)];
target = targets[i];
sdata[2][0] = ANSI_OBJ->bicolor_fade(
process_macro_context((string)sdata[2][0], target, attacker),
(string*)sdata[1]);
sdata[2][1] = ANSI_OBJ->bicolor_fade(
process_macro_context((string)sdata[2][1], target, attacker),
(string*)sdata[1]);
contextmsg("combat", target,
sdata[2][0],
sdata[2][1]);
target->receive_damage(sdata[0], attacker, "internal");
//EVAL_CONT;
}
//EVAL_STOP;
break;
}
// Group stun
case 1 :
{
specials = ({
({ 40, 4,
({ "%^BOLD%^%^WHITE%^Hardened frost creeps over your body, stiffening your movements.%^RESET%^",
"%^BOLD%^%^WHITE%^Hardened frost creeps over {PN} body, stiffening {P} movements.%^RESET%^"}) }),
({ 60, 6,
({ "%^CYAN%^A layer of ice crystals creep up your body and hold you in place.%^RESET%^",
"%^CYAN%^A layer of ice crystals creep up {PN} body and hold {O} in place.%^RESET%^"}) }),
({ 80, 8,
({ "%^BOLD%^%^CYAN%^Jagged ice shards sprout from your body as a bitter Rime frost consumes you.%^RESET%^",
"%^BOLD%^%^CYAN%^Jagged ice shards sprout from {PN} body as a bitter Rime frost consumes {O}.%^RESET%^"}) }),
({ 100, 10,
({ "Your body is eviscerated as blades of ice rip out from your body's core.",
"{PN} body is eviscerated as blades of ice rip out from {P} body's core.",}) }),
});
i = sizeof(targets);
contextmsg("combat", attacker,
"%^BOLD%^%^BLACK%^As you swing your scythe in a wide sweep an arc of icy frost spews out covering the battlefield and encumbering your foes.%^RESET%^",
"%^BOLD%^%^BLACK%^As {N} swings {P} scythe in a wide sweep an arc of icy frost spews out covering the battlefield and encumbering {P} foes.%^RESET%^");
targets->set("casting", 0);
//EVAL_START;
j = sizeof(specials) - 1;
while ( i-- )
{
spec_lev = get_spec_lev(1);
sdata = specials[spec_lev];
target = targets[i];
contextmsg("combat", target,
( spec_lev == j ? ANSI_OBJ->bicolor(process_macro_context(sdata[2][0], target, attacker), ({"%^CYAN%^", "%^BOLD%^%^CYAN%^"}), 1.9) : sdata[2][0]),
( spec_lev == j ? ANSI_OBJ->bicolor(process_macro_context(sdata[2][1], target, attacker), ({"%^CYAN%^", "%^BOLD%^%^CYAN%^"}), 1.9) : sdata[2][1]));
target->block_attack(sdata[1]);
target->block_move(sdata[1]);
target->receive_damage(sdata[0], attacker, "cold");
//EVAL_CONT;
}
//EVAL_STOP;
break;
}
case 2 :
{
if ( find_call_out("end_minions") != -1 )
return do_special(attacker, target);
seteuid(getuid());
s = ({ "crow", "shambling_scarecrow", "hulking_scarecrow" });
contextmsg("combat", attacker,
"%^BOLD%^%^BLACK%^You raise your scythe above your head and shout, %^WHITE%^\"My children... Awaken from your slumber and join me at my side!\"%^RESET%^",
"%^BOLD%^%^BLACK%^{N} raises {P} scythe above {P} head and shouts, %^WHITE%^\"My children... Awaken from your slumber and join me at my side!\"%^RESET%^");
i = 1 + random(get_spec_lev(1));
while ( i-- )
{
spec_lev = get_spec_lev(1)-1;
if ( spec_lev < 0 )
spec_lev = 0;
else if ( spec_lev > sizeof(s)-1 )
spec_lev = sizeof(s)-1;
if ( !objectp(mon = clone_object(MON_DIR+s[spec_lev])) )
continue;
contextmsg("combat", attacker,
"%^BOLD%^%^BLACK%^A {n} emerges from the shadows and joins you.%^RESET%^",
"%^BOLD%^%^BLACK%^A {n} emerges from the shadows and joins {N}.%^RESET%^",
mon);
attacker->add_minion(mon, 1, 1, 0);
mon->set_minion_of(attacker);
mon->move(environment(attacker));
mon->delete("group");
mon->set("kill_func", TO);
mon->set("redirect_xp", attacker);
mon->kill_ob(target);
_minions += ({ mon });
}
_owner = attacker;
call_out("end_minions", (get_spec_lev()*10));
break;
}
default :
hp_percent = to_int(ceil((int)attacker->query_hp()/((int)attacker->query_max_hp()/100.0)));
//DEBUG(hp_percent);
switch( hp_percent )
{
// Zerk
case 51..105:
{
specials = ({
({ "cutting", 20,
({
"\n%^ORANGE%^You strike {n} with a forward slash of the scythe's blade!%^RESET%^\n",
"\n%^ORANGE%^{N} strikes {n} with a forward slash of the scythe's blade!%^RESET%^\n",
"\n%^ORANGE%^{N} strikes you with a forward slash of {P} blade!%^RESET%^\n",}) }),
({ "blunt", 20,
({
"\n%^GREEN%^You perform a swift jab at {n} with the scythe's handle!%^RESET%^\n",
"\n%^GREEN%^{N} performs a swift jab at {n} with the scythe's handle!%^RESET%^\n",
"\n%^GREEN%^{N} performs a swift strike and jabs you with the scythe's handle!%^RESET%^\n" }) }),
({ "internal", 40,
({
"\n%^BLUE%^You drive the blade into {pn} chest and use the handle to hammer it in further.%^RESET%^\n",
"\n%^BLUE%^{N} drives the blade into {pn} chest and uses the handle to hammer it in further.%^RESET%^\n",
"\n%^BLUE%^{N} drives the blade into your chest and hammers it in using the handle.%^RESET%^\n" }) }),
({ "internal", 60,
({
"\n%^MAGENTA%^Spinning around like a whirling dirvish, {N} slash, slices and cuts {n} into ribbons of flesh.%^RESET%^\n",
"\n%^MAGENTA%^Spinning around like a whirling dirvish, {N} slashes, slices and cuts {n} into ribbons of flesh.%^RESET%^\n",
"\n%^MAGENTA%^Spinning around like a whirling dirvish, {N} slashes, slices and cuts you into ribbons of flesh.%^RESET%^\n"}) })
});
i = sizeof(targets);
contextmsg("combat", attacker,
"%^BOLD%^%^BLACK%^Your eyes turn dark as you instinctively seperate the scythe's blade from its handle. With a rapid flurry, you dart towards your foes and unleash a brutal assault!%^RESET%^",
"%^BOLD%^%^BLACK%^{PN} eyes turn dark as {S} effortlessly seperates the scythe's blade from its handle. With a rapid flurry, {S} darts towards {P} foes and unleashes a brutal assault!%^RESET%^");
//EVAL_START;
while ( i-- )
{
j = 1+random(to_int(to_float(spec_lev)/6.0));
while ( j-- )
{
sdata = specials[get_spec_lev(1)];
contextmsg("combat", attacker,
sdata[2][0],
sdata[2][1],
targets[i],
sdata[2][2]);
targets[i]->receive_damage(sdata[1], attacker, sdata[0]);
}
//EVAL_CONT;
}
//EVAL_STOP;
targets->set("casting", 0);
break;
}
// HP drain based on number of attackers
default:
if ( find_call_out("do_healing") != -1 )
return do_special(attacker, target);
contextmsg("combat", attacker,
"%^BOLD%^%^BLACK%^You point your scythe in front of you, and motes of life force begin to tear away from your foes. They twirl and dance on a spiral path and absorb into you.%^RESET%^",
"%^BOLD%^%^BLACK%^{N} points {P} scythe in front of {O}, and motes of life force begin to tear away from {P} foes. They twirl and dance on a spiral path and absorb into {N}.%^RESET%^");
_healing_count = (spec_lev ? spec_lev : 1) * 2;
do_healing(targets, attacker);
break;
}
break;
}
return 1;
}
void end_minions()
{
int i;
_minions = filter_array(_minions, FILTER_LOADED_OBJECTS, FILTER_ARRAY);
i = sizeof(_minions);
while ( i-- )
{
if ( objectp(_owner) )
contextmsg("combat", _owner,
"%^BOLD%^%^BLACK%^Your {n} slips back into the shadows.%^RESET%^",
"%^BOLD%^%^BLACK%^{PN} {n} slips back into the shadows.%^RESET%^",
_minions[i]);
all_inventory(_minions[i])->remove();
_minions[i]->remove();
}
_minions = ({});
}
void do_healing(object *targets, object owner)
{
int i, spec_lev;
mixed specials;
mixed *sdata;
if ( !_healing_count )
return;
spec_lev = get_spec_lev(1);
specials = ({
({ 4, 4, ({ "%^ORANGE%^", "%^BOLD%^%^BLACK%^" }),
({ "Faintly glowing motes of life flutter from {n} into you.",
"Faintly glowing motes of life flutter from {n} into {N}.",
"Faintly glowing motes of life flutter from you into {N}."}) }),
({ 6, 6, ({ "%^GREEN%^", "%^BOLD%^%^BLACK%^" }),
({ "Motes of life bob into you as they seperate from {pn} being.",
"Motes of life bob into {N} as they seperate from {pn} being.",
"Motes of life bob into {N} as they seperate from your being."}) }),
({ ({8,6}), 8, ({ "%^BLUE%^", "%^BOLD%^%^BLACK%^" }),
({ "Shards of life rip away from {n} and coalesce into your being.",
"Shards of life rip away from {n} and coalesce into {PN} being.",
"Shards of life rip away from you and coalesce into {PN} being."}) }),
({ ({10,8}), 10, ({ "%^MAGENTA%^", "%^BOLD%^%^BLACK%^" }),
({ "A swath of {pn} life force is torn away and is drawn into you.",
"A swath of {pn} life force is torn away and is drawn into {N}.",
"A swath of your life force is torn away and is drawn into {N}."}) }),
});
targets = filter_array(targets, FILTER_LIVING_OBJECTS, FILTER_ARRAY);
i = sizeof(targets);
while ( i-- )
{
sdata = specials[get_spec_lev(1)];
contextmsg("combat", owner,
ANSI_OBJ->first_last_letter( process_macro_context(
sdata[3][0], owner, targets[i]), sdata[2]),
ANSI_OBJ->first_last_letter( process_macro_context(
sdata[3][1], owner, targets[i]), sdata[2]),
targets[i],
ANSI_OBJ->first_last_letter( process_macro_context(
sdata[3][2], owner, targets[i]), sdata[2]));
if ( pointerp(sdata[0]) )
{
owner->receive_healing(sdata[0][0]);
owner->delta_sp(sdata[0][1]);
}
else
{
owner->receive_healing(sdata[0]);
}
}
--_healing_count;
call_out("do_healing", 2, targets, owner, spec_lev);
}
int kill_func(object victim, object killer)
{
string name;
int level;
if ( !objectp(killer) )
return 0;
if ( killer != ETO )
return 0;
if ( !objectp(victim) )
return 0;
name = (string)killer->query("name");
if ( !mapp(_kill_data[name]) )
_kill_data[name] = ([ "kills" : allocate(KILLS_TO_TRACK) ]);
level = (int)victim->query("level");
_kill_data[name]["kill_total"]++;
_kill_data[name]["level_total"] += level - rotate_arr( name, level);
killer->receive_healing(level);
killer->delta_sp(level);
killer->delta_ep(level);
message("info", "%^BOLD%^%^WHITE%^Your scythe pulses with a brief glow.%^RESET%^\n", ETO);
return 1;
}
int query_kill_level(object killer)
{
int num_kills, sum_kill_levels;
string name;
mapping data;
mixed level;
if ( !objectp(killer) )
return 0;
if ( !mapp(data = query_kill_data(killer)) )
return 0;
num_kills = data["kill_total"] > KILLS_TO_TRACK ? KILLS_TO_TRACK : data["kill_total"];
//sum_kill_levels = data["level_total"];
//level = to_int(num_kills/((data["level_total"]/to_float(KILLS_TO_TRACK))/KILLS_TO_TRACK));
//level = data["kill_total"]*((KILLS_TO_TRACK*(data["level_total"]/to_float(KILLS_TO_TRACK))/to_float(KILLS_TO_TRACK))/5000);
//level = data["kill_total"]/1000.0+(((data["level_total"]/to_float(KILLS_TO_TRACK))/to_float(data["kill_total"]))/20);
level = (data["kill_total"]*0.001)+(data["level_total"]/to_int(num_kills))/15.0;
//level = to_int(round((*2) + num_kills/KILLS_TO_TRACK;
//DEBUG(level);
level = to_int(round(level));
//DEBUG(level);
//DEBUG(num_kills);
switch ( (int)data["kill_total"] )
{
case 0..25 :
if ( level > 0 )
return 0;
break;
case 26..49 :
if ( level > 1 )
return 1;
break;
case 50..74 :
if ( level > 2 )
return 2;
case 75..99 :
if ( level > 3 )
return 3;
break;
}
return level > 4 ? 4 : level;
}
void remove()
{
if ( sizeof(_minions) )
end_minions();
::remove();
}
// Replace current kill index with new killed mob level
// and return the value of index being replaced.
// See circular buffer for details on data structure and method.
int rotate_arr(string name, int i)
{
int tmp;
if ( _cur_index == (KILLS_TO_TRACK - 1) )
_cur_index = 0;
else
++_cur_index;
tmp = _kill_data[name]["kills"][_cur_index];
_kill_data[name]["kills"][_cur_index] = i;
return tmp;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment