use Net::IRC; my $protect_chat=0; my $naive_audience=0; my %characters=(); my %present=(); my %concealed=(); my %graveyard=(); my %defeated=(); my $lastsave=time(); my %lcnames=(); my $keep_log=1; my $verbose=0; my $mimicking=''; srand(); my $beng_nick='BattleEngine'; my $channel='#BattleSpot'; my $server='irc.nightstar.net'; my $port=6667; my @errors=(); while(@ARGV){ my $arg=shift @ARGV; if($arg eq '-server'){ $server=shift @ARGV; }elsif($arg eq '-channel'){ $channel=shift @ARGV; }elsif($arg eq '-port'){ $port=shift @ARGV; }elsif($arg eq '-nick'){ $nick=shift @ARGV; }elsif($arg eq '-naive'){ $naive_users=1; }elsif($arg eq '-chat'){ $protect_chat=1; }elsif($arg eq '-nolog'){ $keep_log=0; }elsif($arg eq '-verbose'){ $verbose=1; }else{ push @errors, "Invalid argument: $arg\n"; } } if(!$beng_nick){ push @errors, "Specified nick ($beng_nick) is invalid.\n"; } if(!$channel){ push @errors, "Specified channel ($channel) is invalid.\n"; } if($server eq ''){ push @errors, "You must specify a server with '-server [URL]'.\n"; } if($channel eq ''){ push @errors, "You must specify a channel with '-channel [CHANNEL] +'.\n"; } if(@errors){ print STDERR @errors; exit(1); } if($keep_log){ open LOG, ">>batlog.txt"; } sub logprint{ if($verbose){ print @_; } if($keep_log){ print LOG @_; } } my $bold="\2"; my $irc=new Net::IRC; my $conn= $irc->newconn( Nick => $beng_nick, Server => $server, Port => $port, Ircname => 'Mmmm... Beer and pretzels.' ); sub sayto{ my $to=shift; foreach(@_){ $conn->privmsg($to,$_); logprint("private($to) $_\n"); } } sub say{ foreach(@_){ $conn->privmsg($channel,$_); logprint("out: $_\n"); } } my %spells=( sniff=>{cost=>'5',damage=>'0',description=>'FEE FIE FOE FUM.'}, waltz=>{cost=>'5',damage=>'0',description=>'Waltz right out of a bad s +ituation.'}, happy_dance=>{cost=>'1',damage=>'-10',description=>'What? You need a +reason?'}, tansu_dance=>{cost=>'5',damage=>'20',description=>'A vigorous dance in +volving throwing an excessively heavy chest of drawers.'}, tangle_tango=>{cost=>'20',damage=>'0',description=>'Trip up your partn +er.'}, disco_fever=>{cost=>'1',damage=>'0',description=>'Face it. Disco is d +ead.'}, sword_dance=>{cost=>'40',damage=>'100',description=>'One of the more v +iolent dances.'}, danse_macabre=>{cost=>'100',damage=>'400',description=>'The part that +really hurts is that wierd violin solo.'}, zanzoken=>{cost=>'7',damage=>'0',description=>'And I thought I could m +ove fast.'}, solar_flare=>{cost=>'30',damage=>'0',description=>'Blinding light can +stun the greatest.'}, big_bang_attack=>{cost=>'100',damage=>'100000',description=>'Vegeta\'s + second best attack. Wow, a blue orb?'}, sword_flash_attack=>{cost=>'500',damage=>'300000',description=>'Wow! A + beam that impails!'}, final_kamehameha=>(cost=>'1000',damage=>'9000000',description=>'I knew + it was a bad idea to fuse Gokuu and Vegeta\'s attacks.'}, final_flash=>{cost=>'200',damage=>'500000',description=>'Vegeta\'s bes +t attack. A giant blue beam.'}, kamehameha=>{cost=>'50',damage=>'500',description=>'Wow. Gokuu still u +ses this old ass technique.'}, ki_sense=>{cost='20',damage=>'0',description=>'Sensing the energy of l +ost people.'}, instant_transmissio=>{cost=>'0',description=>'Moving super fast and wa +y away.'}, song=>{cost=>'1',damage=>'0',description=>'Play some music.'}, preach=>{cost=>'1',damage=>'0',description=>'Preach a sermon.'}, spoony=>{cost=>'10',damage=>'50',description=>'You spoony bard!'}, finite=>{cost=>'20',damage=>'200',description=>'Melody of finite.'}, candy_beam=>{cost=>'10',damage=>'50',description=>'A beam made entirel +y of candy!'}, scan=>{cost=>'5',damage=>'0',description=>'Learn about an enemy\'s sta +te.'}, vanish=>{cost=>'5',damage=>'0',description=>'See, yet be unseen.'}, hide=>{cost=>'5',damage=>'0',description=>'Now, where\'s a good shadow +?'}, smoke_screen=>{cost=>'10',damage=>'0',description=>'Escape from battle + under an impenetrable cover.'}, smoke_bomb=>{cost=>'30',damage=>'0',description=>'Can't see me anymore +.'}, assassinate=>{cost=>'1000',damage=>'0',description=>'Um, can you say s +elf explainatory?'}, anchient_skill=>{cost=>'500',damage=>'50000',description=>'What can I +say, it's anchient.'}, sacred_strike=>{cost=>'1000',damage=>'800000',description=>'If this is + sacred then there must be an angry God somewhere out there.'}, sneak=>{cost=>'10',damagee=>'0',description=>'Don\'t mind me, I\'m jus +t not fighting any more.'}, shuriken=>{cost=>'4',damage=>'24',description=>'Little pointy things, +lots of them.'}, throw=>{cost=>'4',damage=>'24',description=>'It\'s all fun and games u +ntil someone loses an eye.'}, top_cut=>{cost=>'24',damage=>'120',description=>'Instant critical.'}, backstab=>{cost=>'24',damage=>'120',description=>'Extra painful.'}, hit_and_run=>{cost=>'36',damage=>'120',description=>'Strike from conce +alment.'}, mug=>{cost=>'36',damage=>'120',description=>'A purely visual joke.'}, ghoulish=>{cost=>'50',damage=>'0',description=>'Something unspeakable. +'}, taser=>{cost=>'30',damage=>'0',description=>'Paralyze your enemies.'}, web=>{cost=>'20',damage=>'0',description=>'Ensnare your enemies in a s +ticky net.'}, cure=>{cost=>'5',damage=>'-30',description=>'Cure minor wounds.'}, heal=>{cost=>'10',damage=>'-100',description=>'A spell of healing.'}, kiss=>{cost=>'10',damage=>'-100',description=>'Kiss it better.'}, skank_kiss=>{cost=>'10',damage=>'20',description=>'I\'d rather not...' +}, hag_kiss=>{cost=>'10',damage=>'200',description=>'Ewwww.'}, censored=>{cost=>'1000',damage=>'0',description=>'[this description ce +nsored]'}, regenerate=>{cost=>'50',damage=>'-400',description=>'Supernatural heal +ing.'}, wound=>{cost=>'20',damage=>'40',description=>'A false healing.'}, pound=>{cost=>'30',damage=>'80',description=>'Thump, thump!'}, stomp=>{cost=>'100',damage=>'200',description=>'Squish!'}, kick=>{cost=>'60',damage=>'160',description=>'ACHOOOOO!'}, whup_ass=>{cost=>'70',damage=>'200',description=>'A mighty whupping in +deed!'}, raise=>{cost=>'50',damage=>'0',description=>'Raise the recently deceas +ed.'}, life=>{cost=>'200',damage=>'0',description=>'Raise any deceased.'}, revive=>{cost=>'100',damage=>'-4000',description=>'A spell of supreme +healing.'}, negate=>{cost=>'500',damage=>'4000',description=>'The ultimate betraya +l of the healer\'s art.'}, rbite=>{cost=>'1',damage=>'0',description=>'This venom has been infuse +d with H-band radiation!'}, lame=>{cost=>'1',damage=>'1',description=>'A lame attack.'}, duh=>{cost=>'1',damage=>'1',description=>'A stupid attack.'}, howl=>{cost=>'2',damage=>'0',description=>'Awooooooooooo!'}, flare=>{cost=>'5',damage=>'20',description=>'A flash of flame.'}, spark=>{cost=>'7',damage=>'20',description=>'An electric shock.'}, ice=>{cost=>'10',damage=>'30',description=>'Freeze your enemies.'}, gas=>{cost=>'12',damage=>'40',description=>'An unbreathable cloud.'}, fireball=>{cost=>'15',damage=>'50',description=>'A fireball spell.'}, lightning=>{cost=>'50',damage=>'100',description=>'A lightning bolt.'} +, really_bad_breath=>{cost=>'10',damage=>'80',description=>'An unhygieni +c dragon\'s breath weapon.'}, poison_breath=>{cost=>'30',damage=>'100',description=>'A poison dragon +\'s breath weapon.'}, fire_breath=>{cost=>'50',damage=>'200',description=>'A fire dragon\'s +breath weapon.'}, shadow_breath=>{cost=>'50',damage=>'400',description=>'A death dragon\ +'s breath weapon.'}, acid_shower=>{cost=>'15',damage=>'50',description=>'Ouch.'}, acid_spray=>{cost=>'20',damage=>'100',description=>'Double ouch.'}, unholy=>{cost=>'150',damage=>'500',description=>'An unholy life drain. +'}, meteo=>{cost=>'400',damage=>'5000',description=>'Open the Heavens! Br +eak the Earth!'}, dragon_slave=>{cost=>'1000',damage=>'1000000',description=>'Now that\' +s just excessive.'}, jew_in_an_oven=>{cost=>'1',damage=>'10000',,description=>'Ah-h-h-h-h, +Hitler!'}, holy_wrath=>{cost=>'1000',damage=>'10000',description=>'God is pissed. +'}, judgement_day=>{cost=>'1000',damage=>'10000',description=>'It\'s your +own damn fault.'}, flood_the_earth=>{cost=>'1000',damage=>'10000',description=>'No rainbo +w?'}, hurl_thunderbolt=>{cost=>'1000',damage=>'10000',description=>'Zeus doe +s this. It hurts.'}, gunge_lance=>'1000',damage=>'1000000',description=>'Even the mercy of +Odin hurts.'}, zantetsuken=>'5000',damage=>'1000000000000',description=>'Man, can you + say over-kill?'}, eternal_darkness=>'1000',damage=>'1000000',description=>'Destroying a +planet isn't cool at all.'}, grims_choice=>'2000',damage=>'100000',description=>'Being the king of +evil sure does pay off.'}, rebirth_flame=>'200',damage=>'-1000000',description=>'Fire heals all.' +}, terrestrial_rage ruby_light terra_homing diamond_dust hell_fire judgement_bolt g-force tsunami mega_flare titanic_shake elemental_power holy_judgement giga_flare terra_flare aero_blast knights_honor thunder_storm silent_voice brotherly_love dark_messenger tornado_zone counter_rockets runaway_train ethernal_breath thor_hammer demon_cutting_sword chaotic_d delta_attack shooting_power trample breath_weapon_barrage=>{cost=>'5000',damage=>'50000',description=>'So +\2that\2 is what happens when a whole swarm of dragons breath at the +same time.'}, ); my $last_twink=time(); my @canned_monsters=(ettin,red_dragon,stupid_looking_dragon,slime_king +,shadow_dragon,ghoul); my @canned_gods=(jesus,buddha,cosmic_dragon,jehovah,zeus); my %classes=( exp_in_a_can=>{user=>0,hp=>1,mp=>0,xp=>100,damage=>0,spells=>{}}, unlabelled_can_of_mystery=>{user=>0,hp=>1,mp=>0,xp=>100,damage=>0,spel +ls=>{}}, monster_in_a_can=>{user=>0,hp=>1,mp=>0,xp=>100,damage=>0,spells=>{}}, god_in_a_can=>{user=>0,hp=>1,mp=>0,xp=>100,damage=>0,spells=>{}}, can_of_wyrms=>{user=>0,hp=>1,mp=>0,xp=>100,damage=>0,spells=>{}}, can_of_whup_ass=>{user=>0,hp=>1000,mp=>0,xp=>3000,hitsas=>16,damage=>1 +00,spells=>{}}, spider_man=>{user=>0,hp=>40,mp=>100,xp=>60,damage=>16,spells=>{web=>3} +}, fighter=>{user=>1,hp=>50,mp=>0,xp=>100,damage=>10,spells=>{}}, samurai=>{user=>1,hp=>60,mp=>40,xp=>200,damage=>16,spells=>=>{vanish=> +2,shuriken=>5,smoke_bomb=>11,top_cut=>17,smoke_screen=>24,anchient_sk +ill=>35,assassinate=>51,sacred_strike=>67}},}}, monk=>{user=>1,hp=>30,mp=>0,xp=>100,damage=>16,spells=>{}}, thief=>{user=>1,hp=>30,mp=>12,xp=>120,damage=>6,spells=>{hide=>1,sneak +=>3,throw=>2,backstab=>6,mug=>9}}, theif=>{user=>1,hp=>30,mp=>12,xp=>120,damage=>6,spells=>{lame=>1,duh=> +1}}, robot=>{user=>1,hp=>40,mp=>5,xp=>100,damage=>8,spells=>{spark=>2,scan= +>5,candy_beam=>10,taser=>15,lightning=>20}}, evil_chair=>{user=>0,hp=>40,mp=>0,xp=>100,damage=>9,spells=>{}}, evil_pants=>{user=>0,hp=>60,mp=>0,xp=>150,hitsas=>2,damage=>9,spells=> +{}}, slime=>{user=>1,hp=>10,mp=>2,xp=>30,damage=>3,spells=>{spark=>7,cure=> +10}}, imp=>{user=>0,hp=>30,mp=>0,xp=>60,damage=>6,spells=>{}}, ogre=>{user=>0,hp=>200,mp=>0,xp=>600,hitsas=>6,damage=>20,spells=>{}}, ghoul=>{user=>0,hp=>200,mp=>200,xp=>700,hitsas=>6,damage=>20,spells=>{ +ghoulish=>1}}, ettin=>{user=>0,hp=>600,mp=>0,xp=>1800,hitsas=>16,damage=>40,spells=>{ +}}, giant=>{user=>0,hp=>1800,mp=>10,xp=>3600,hitsas=>30,damage=>100,spells +=>{sniff=>1}}, giant_spider=>{user=>0,hp=>500,mp=>100,xp=>2000,hitsas=>16,damage=>50, +spells=>{web=>1}}, radioactive_spider=>{user=>0,hp=>500,mp=>100,xp=>3000,hitsas=>16,damag +e=>50,spells=>{web=>1,rbite=>1}}, black_drake=>{user=>0,hp=>400,mp=>150,xp=>1200,hitsas=>14,damage=>20,s +pells=>{acid_spray=>1}}, troll=>{user=>0,hp=>800,mp=>400,xp=>2400,hitsas=>18,damage=>40,spells= +>{regenerate=>1}}, wolf=>{user=>0,hp=>30,mp=>5,xp=>80,damage=>8,spells=>{sniff=>1}}, wolf_rider=>{user=>0,hp=>60,mp=>0,xp=>200,hitsas=>2,damage=>10,spells= +>{}}, slime_gang=>{user=>0,hp=>30,mp=>6,xp=>100,hitsas=>2,damage=>9,spells=> +{}}, metal_slime=>{user=>0,hp=>60,mp=>300,xp=>600,hitsas=>4,damage=>10,spel +ls=>{acid_shower=>1,heal=>1}}, slime_king=>{user=>0,hp=>60,mp=>20,xp=>300,hitsas=>4,damage=>15,spells +=>{cure=>1,spark=>1}}, drake=>{user=>0,hp=>200,mp=>50,xp=>600,hitsas=>8,damage=>15,spells=>{f +lare=>1}}, stupid_looking_dragon=>{user=>0,hp=>100,mp=>120,xp=>900,damage=>20,spe +lls=>{really_bad_breath=>1}}, green_dragon=>{user=>0,hp=>500,mp=>120,xp=>1200,damage=>20,spells=>{po +ison_breath=>1}}, red_dragon=>{user=>0,hp=>1000,mp=>200,xp=>2400,damage=>30,spells=>{fir +e_breath=>1}}, shadow_dragon=>{user=>0,hp=>2000,mp=>500,xp=>4800,damage=>50,spells=>{ +shadow_breath=>1,unholy=>1,ghoulish=>1}}, cosmic_dragon=>{user=>0,hp=>10000,mp=>5000,xp=>10000,damage=>200,spell +s=>{shadow_breath=>1,fire_breath=>1,poison_breath=>1,negate=>1,revive +=>1,meteo=>1}}, dragon_swarm=>{user=>0,hp=>50000,mp=>50000,xp=>50000,damage=>5000,spel +ls=>{breath_weapon_barrage=>1}}, jehovah=>{user=>0,hp=>25000,mp=>20000,xp=>25000,hitsas=>200,damage=>50 +0,spells=>{holy_wrath=>1,flood_the_earth=>1,judgement_day=>1,revive=> +1}}, zeus=>{user=>0,hp=>25000,mp=>10000,xp=>25000,hitsas=>200,damage=>1000, +spells=>{hurl_thunderbolt=>1,revive=>1}}, bard=>{user=>1,hp=>1,mp=>5,xp=>20,damage=>1,spells=>{song=>1,spoony=>1 +0,finite=>20}}, unicorn_jelly=>{user=>1,hp=>10,mp=>5,xp=>50,damage=>20,spells=>{spark= +>5,cure=>8}}, mage=>{user=>1,hp=>30,mp=>20,xp=>150,damage=>5,spells=>{flare=>1,spark +=>2,ice=>3,fireball=>5,cure=>7,lightning=>10,unholy=>13,heal=>17,mete +o=>20}}, mimic=>{user=>1,hp=>40,mp=>0,xp=>150,damage=>6,spells=>{}}, summoner=>{user=>1,hp=>20,mp=>40,xp=>160,damage=>4,spells=>{}}, healer=>{user=>1,hp=>40,mp=>20,xp=>120,damage=>8,spells=>{cure=>1,heal +=>3,raise=>7,wound=>10,revive=>15,life=>20,negate=>25}}, harlot=>{user=>1,hp=>20,mp=>30,xp=>80,damage=>2,spells=>{kiss=>1}}, skank=>{user=>0,hp=>20,mp=>30,xp=>80,damage=>2,spells=>{skank_kiss=>1} +}, hag=>{user=>0,hp=>20,mp=>30,xp=>80,damage=>2,spells=>{hag_kiss=>1}}, wretch=>{user=>0,hp=>5,mp=>0,xp=>80,damage=>2,spells=>{}}, dancer=>{user=>1,hp=>30,mp=>20,xp=>150,damage=>5,spells=>{happy_dance= +>2,waltz=>3,tansu_dance=>1,disco_fever=>5,sword_dance=>7,tangle_tango +=>10,danse_macabre=>13}}, buddha=>{user=>0,hp=>10,mp=>100,xp=>1,hitsas=>50,damage=>0,spells=>{pr +each=>1}}, jesus=>{user=>0,hp=>10,mp=>100,xp=>1,hitsas=>50,damage=>0,spells=>{pre +ach=>1}}, twink=>{user=>1,hp=>500,mp=>100,xp=>10,damage=>50,spells=>{flare=>1,cu +re=>1,heal=>2,spark=>2,ice=>3,heal=>4,fireball=>4,wound=>5,unholy=>6, +revive=>8,meteo=>9,negate=>10}}, odin_of_mercy=>{user=>0,hp=1000000,mp=>1000000000,xp=>1000000000,damag +e=>90000,spells=>{holy=>1,focused_wound=>2,gunge_lance=>2,wound=>2}}, odin_of_death=>(user=>0,hp=500000,mp=>100000000,xp=>200000000,damage=> +10000,spells=>{dark=>3,focused_wound=>2,unholy=>3,zantetsuken=>1,devi +lish_act=>1}}, vegetto=>{user=>1,hp=200,mp=>100,xp=>900000000000,damage=>500,spells=> +{zanzoken=>1,ki_sense=>9,kamehameha=>15,big_bang_attack=>27,instant_t +ransmission=>36,final_flash=>43,sword_flash_attack=>55,final_kamehame +ha=>78}}, experience_elder=>{user=>0,hp=>1,mp=>0,xp=>9000000000000000,damage=>0, +spells=>{}}, ); my %class_aliases=( warrior=>'fighter', wizard=>'mage', black_mage=>'mage', white_mage=>'healer', cleric=>'healer', red_mage=>'twink', monster=>'slime', ballerina=>'dancer', black_belt=>'monk', karate=>'monk', master=>'monk', whore=>'harlot', caller=>'summoner', ranger=>'samurai', tracker=>'samurai', ranger=>'vegetto', tracker=>'vegetto', black_mage=>'vegetto', caller=>'vegetto', black_belt=>'vegetto', karate=>'vegetto', master=>'vegetto', ); my %summons=( odin_of_mercy=>'gunge_lance', odin_of_death=>'zantetsuken', ark=>'eternal_darkness', hades=>'grims_choice', phoenix=>'rebirth_flame', fenrir=>'terrestrial_rage', carbuncle=>'ruby_light', madeen=>'terra_homing', shiva=>'diamond_dust', ifrit=>'hell_fire', ramuh=>'judgement_bolt', atomos=>'g-force', leviathan=>'tsunami', bahamut=>'mega_flare', titan=>'titanic_shake', kjata=>'elemental_power', alexander=>'holy_judgement', neo_bahamut=>'giga_flare', bahamut_zero=>'terra_flare', typoon=>'aero_blast', knights_of_the_round=>'knights_honor', quetzacoatl=>'thunder_storm', siren=>'silent_voice', brothers=>'brotherly_love', diablos=>'dark_messenger', pandemonia=>'tornado_zone', cerberus=>'counter_rockets', doomtrain=>'runaway_train', eden=>'ethernal_breath', ixion=>'thor_hammer', youjinbo=>'demon_cutting_sword', anima=>'chaotic_d', three_magus_sisters=>'delta_attack', valfor=>'shooting_power', chocobo=>'trample', ); sub mimic{ my ($mimic,$subject,$object)=@_; if(! (exists $characters{$mimic} && exists $characters{$subject} && exists $characters{$object})){ return; } if($mimic eq $subject){ sayto($mimic, "You can't mimic yourself!"); return; } if($characters{$mimic}->{class} ne 'mimic'){ sayto($mimic, "Only a \2mimic\2 can mimic those present."); return; } if(!$present{lc($subject)}){ sayto($mimic, "You can't mimic \2$subject\2 when he's away!"); return; } if($characters{$mimic}->{isresting}){ sayto($mimic, "You're resting. You can't mimic while resting. +"); return; } if($characters{$mimic}->{delay}<time()){ $mimicking=$mimic; my $spell=$sp[$choice-2]; my $oldlevel=$characters{$mimic}->{level}; my $elevel=$characters{$mimic}->{level}=$characters{$subject}- +>{level}; $characters{$mimic}->{class}=$characters{$subject}->{class}; $characters{$mimic}->{mp}=$characters{$subject}->{mp}; if(exists $classes{$characters{$mimic}->{class}}->{hitsas}){ $elevel=$classes{$characters{$mimic}->{class}}->{hitsas}; } if(rand()>0.10 && rand($elevel)<$oldlevel){ say("\2$mimic\2 mimics \2$subject\2 against \2$object\2!") +; my @sp=spells_available($subject); my $choice=int(rand(scalar(@sp) + 2)); if($choice>=2){ my $spell=$sp[$choice-2]; cast($mimic,$sp[$choice-2],$object); }else{ attack($mimic,$object); } }else{ say("\2$mimic\2 fails to mimic \2$subject\2 against \2$obj +ect\2!"); } $characters{$mimic}->{level}=$oldlevel; $characters{$mimic}->{mp}=0; $characters{$mimic}->{class}='mimic'; $characters{$mimic}->{delay}=time()+5; $mimicking=''; correct_points($mimic); }else{ penalty($mimic); } } sub hunt{my ($samurai,$monster,$area)=@_; if(! exists $characters{$hunter}){ return; } if($characters{$samurai}->{class} ne 'samurai'){ sayto($samurai, "Only a \2samurai\2 can hunt for specific crea +tures."); return; } if($characters{$samurai}->{isresting}){ sayto($hunter, "You're resting. You can't hunt while resting. +"); return; } if($characters{$samurai}->{delay}<time()){ say("\2$samurai\2 tries to track down a \2$monster\2 in \2$are +a\2..."); wander($samurai, $area, $monster, $characters{$samurai}->{leve +l}); $characters{$samurai}->{delay}=time()+5; }else{ penalty($samurai); } } sub summoner_study{my ($student,$subject)=@_; if(! exists $characters{$student}){ return; } if($characters{$student}->{class} ne summoner){ sayto($student, "Only a \2summoner\2 can study creatures and l +earn to summon them."); return; } if($characters{$student}->{isresting}){ sayto($student, "You're resting. You can't study while restin +g."); return; } if(!exists $characters{$subject}){ sayto($student, "There is no \2$subject\2 to study!"); return; } if(!$present{lc($subject)}){ sayto($student, "You can't study \2$subject\2 when he's away!" +); return; } my $class=$characters{$subject}->{class}; if(exists $characters{$student}->{summons}->{$class}){ sayto($student, "There is nothing to be gained by studying a c +reature you can already summon."); return; } if($characters{$student}->{delay}<time()){ say("\2$student\2 tries to study \2$subject\2..."); if(exists($summons{$class}) && int(rand(4))==1){ $characters{$student}->{summons}->{$class}=$summons{$class +}; say("and \2$student\2 learns to summon \2$class\2!"); $characters{$student}->{xp}+=50; correct_points($student); }else{ say("but \2$student\2 learns nothing"); } $characters{$student}->{delay}=time()+5; }else{ penalty($student); } } %special_spells=( sniff=>sub {my ($caster, $target)=@_; if(keys %concealed){ say("\2$caster\2 smells concealed parties! They are no longer + hidden."); for(keys %concealed){ $present{lc($_)}=1; delete $concealed{$_}; } } return 0; }, ki_sense=>sub {my ($caster, $target)=@_; if(keys %concealed){ say("\2$caster\2 sense concealed parties\'s ki! They are no l +onger hidden."); for(keys %concealed){ $present{lc($_)}=1; delete $concealed{$_}; } } return 0; }, rbite=>sub {my ($caster, $target)=@_; if(rand()>0.75){ say("\2$target\2 begins to transform into some kind of hideous + spider/man cross!"); new_character($target,'spider_man',$characters{$target}->{stat +icid}); return 0; }else{ cause_damage($target,200); return 1; } }, vanish=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; return 0; }, assassinate=> {my ($caster, $target)=@_; say("[assassinated]"); delete ($target); delete $present{$target}; delete $concealed{$target}; }, hide=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; return 0; }, censored=>sub {my ($caster, $target)=@_; say("[the resulting effects have been censored]"); return 0; }, waltz=>sub {my ($caster, $target)=@_; say("\2$caster\2 waltzes right out of the battle."); delete $present{lc($caster)}; $concealed{lc($caster)}=1; if($caster ne 'monster'){ $characters{$caster}->{isresting}=1; $characters{$caster}->{delay}=time()+60; } return 0; }, smoke_bomb=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; if($caster ne 'monster'){ $characters{$caster}->{isresting}=1; $characters{$caster}->{delay}=time()+60; } return 0; }, instant_transmission=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; if($caster ne 'monster'){ $characters{$caster}->{isresting}=1; $characters{$caster}->{delay}=time()+60; } return 0; }, sneak=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; if($caster ne 'monster'){ $characters{$caster}->{isresting}=1; $characters{$caster}->{delay}=time()+60; } return 0; }, smoke_screen=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; if($target ne 'monster'){ $characters{$target}->{isresting}=1; $characters{$target}->{delay}=time()+60; delete $present{lc($target)}; delete $concealed{lc($target)}; } return 0; }, zanzoken=>sub {my ($caster, $target)=@_; delete $present{lc($caster)}; $concealed{lc($caster)}=1; if($target ne 'monster'){ $characters{$target}->{isresting}=1; $characters{$target}->{delay}=time()+60; delete $present{lc($target)}; delete $concealed{lc($target)}; } return 0; }, howl=>sub {my ($caster, $target)=@_; say("Awoooooooo!"); return 0; }, mug=>sub {my ($caster, $target)=@_; if(exists $characters{$target}){ cause_damage($target,120); } if(int(rand(5))==1){ say("\2$caster\2's escape fails! He is open to attack!"); }else{ delete $present{lc($caster)}; $concealed{lc($caster)}=1; } return 1; }, hit_and_run=>sub {my ($caster, $target)=@_; if(exists $characters{$target}){ cause_damage($target,120); } if(int(rand(5))==1){ say("\2$caster\2's escape fails! He is open to attack!"); }else{ delete $present{lc($caster)}; $concealed{lc($caster)}=1; } return 1; }, breath_weapon_barrage=>sub {my ($caster, $target)=@_; for(get_actives_and_monster()){ if($_ ne $caster && exists $characters{$_}){ cause_damage($_,10000); if(exists($characters{$caster}) && $characters{$_}->{hp}<1 +){ defeats($caster,$_); } } } return 0; }, ghoulish=>sub {my ($caster, $target)=@_; say("\2$caster\2 reads \2$target\2's mind to learn the location of + the graveyard"); if($target eq 'monster'){ say("...but he doesn't know where the graveyard is."); }elsif(keys %graveyard){ my $choice=rand_el(keys %graveyard); say("\2$caster\2 exhumes and eats the body of \2$choice\2"); clean_grave($graveyard{$choice}->{staticid}); cause_damage($caster,-500); }else{ say("...but the graveyard is empty."); } return 0; }, scan=>sub {my ($caster, $target)=@_; status($target); return 0; }, tangle_tango=>sub {my ($caster, $target)=@_; my $time=int(rand(30)+1); if($caster eq $target){ sayto($caster, "It takes two to \2tango\2."); return 0; } if($characters{$target}->{delay} < (time()+9)){ $characters{$target}->{delay}=time()+$time; say("\2$target\2 is tripped for \2$time\2 seconds!"); }else{ $characters{$target}->{delay}+=$time; my $totaldelay=$characters{$target}->{delay}-time(); if($totaldelay>40){ $characters{$target}->{delay}=time(); say("\2$caster\2 dances \2target\2 back in step!"); }else{ say("\2$target\2 is stumbling for \2$time\2 more seconds!" +); } } return 0; }, web=>sub {my ($caster, $target)=@_; my $time=int(rand(30)+1); if($characters{$target}->{delay} < (time()+9)){ $characters{$target}->{delay}=time()+$time; say("\2$target\2 is trapped for \2$time\2 seconds!"); }else{ $characters{$target}->{delay}+=$time; my $totaldelay=$characters{$target}->{delay}-time(); if($totaldelay>40){ $characters{$target}->{delay}=time(); say("The fresh webbing weakens \2target\2's bond, and he i +s free!"); }else{ say("\2$target\2 is stuck for \2$time\2 more seconds!"); } } return 0; }, taser=>sub {my ($caster, $target)=@_; my $time=int(rand(60)+1); if($characters{$target}->{delay} < (time()+9)){ $characters{$target}->{delay}=time()+$time; say("\2$target\2 is paralyzed for \2$time\2 seconds!"); }else{ $characters{$target}->{delay}+=$time; my $totaldelay=$characters{$target}->{delay}-time(); if($totaldelay>70){ $characters{$target}->{delay}=time(); say("The shock brings \2target\2 out of his paralysis!"); }else{ say("\2$target\2 is paralyzed for \2$time\2 more seconds!" +); } } return 0; }, solar_flare=>sub {my ($caster, $target)=@_; my $time=int(rand(60)+1); if($characters{$target}->{delay} < (time()+60)){ $characters{$target}->{delay}=time()+$time; $characters{$target}->{delay}+=$time; my $totaldelay=$characters{$target}->{delay}-time(); if($totaldelay>70){ $characters{$target}->{delay}=time(); } return 0; }, life=>sub {my ($caster, $target)=@_; if(exists $graveyard{$target}){ resurrect($target); }else{ say("There is no body of \2$target\2 to raise."); } return 0; }, raise=>sub {my ($caster, $target)=@_; if(exists $graveyard{$target}){ if((time()-$graveyard{$target}->{delay})<300){ resurrect($target); }else{ say("Alas! \2$target\2 has been dead for too long!"); } }else{ say("There is no body of \2$target\2 to raise."); } return 0; }, ); sub rand_el{ return $_[int(rand(scalar(@_)))]; }; my %weird_deaths=( exp_in_a_can=>sub{ my ($name,$killer)=@_; say("Reeeeefreshing!"); return 1; }, unlabelled_can_of_mystery=>sub{ my ($name,$killer)=@_; say("You open the can to find..."); my $choice=rand_el('exp','potion','insanity','nothing','dogfood', 'cure','slime','dragon','can','peaches'); delete $characters{$name}; if($choice eq 'exp'){ say("A boatload of exp!"); if(exists $characters{$killer}){ $characters{$killer}->{xp}+=1000; correct_points($killer); } }elsif($choice eq 'potion'){ say("A wonderful potion!"); say("\2$killer\2 is healed"); if(exists $characters{$killer}){ $characters{$killer}->{hp}+=1000; $characters{$killer}->{mp}+=1000; correct_points($killer); } }elsif($choice eq 'insanity'){ say("Something unspeakable... something \2$killer\2's mind can +'t handle."); say("\2$killer\2 goes completely insane!"); $characters{monster}=$characters{$killer}; $characters{monster}->{staticid}='impossible!ID@!@!@'; delete $characters{$killer}; delete $present{$killer}; delete $concealed{$killer}; say("\2$killer\2 is now \2monster\2!"); }elsif($choice eq 'nothing'){ say("Nothing at all!"); }elsif($choice eq 'peaches'){ say("Peaches!"); say("In heavy syrup!"); }elsif($choice eq 'dogfood'){ say("Something that smells like meat, but worse."); say("You wouldn't feed this to your worst enemy."); }elsif($choice eq 'cure'){ say("A cure for cancer!"); say("The world hails \2$killer\2 as the greatest hero ever!"); say("Your name echos through the ages!"); }elsif($choice eq 'slime'){ say("A lowly, lowly slime."); new_character('monster','slime','impossible!ID@!@!@'); }elsif($choice eq 'dragon'){ say("A dragon. You scratched his favorite scale opening it.") +; say("He looks pretty mad."); new_character('monster', rand_el('stupid_looking_dragon','green_dragon','red_dragon', 'shadow_dragon','cosmic_dragon'), 'impossible!ID@!@!@'); }elsif($choice eq 'can'){ say("Another can!"); new_character('monster', rand_el('unlabelled_can_of_mystery','monster_in_a_can', 'exp_in_a_can', 'can_of_whup_ass','god_in_a_can','can_of_wyr +ms'), 'impossible!ID@!@!@'); }else{ say("A \2bug\2 in the battle engine! (oops!)"); } return 0; },monster_in_a_can=>sub{ my ($name,$killer)=@_; my $new_monster=$canned_monsters[int(rand(scalar(@canned_monsters) +))]; say("You've opened a can of \2$new_monster\2!"); new_character('monster', $new_monster, 'impossible!ID@!@!@'); return 0; }, god_in_a_can=>sub{ my ($name,$killer)=@_; my $new_monster=$canned_gods[int(rand(scalar(@canned_gods)))]; say("You've opened a can of \2$new_monster\2!"); new_character('monster', $new_monster, 'impossible!ID@!@!@'); return 0; }, can_of_whup_ass=>sub{ my ($name,$killer)=@_; say("You've succeeded in opening a \2can_of_whup_ass\2!"); say("Guess what's inside!"); new_character('monster', 'cosmic_dragon', 'impossible!ID@!@!@'); return 0; }, can_of_wyrms=>sub{ my ($name,$killer)=@_; say("W\2y\2rms! Everywhere!"); new_character('monster', 'dragon_swarm', 'impossible!ID@!@!@'); return 0; }, buddha=>sub{ my ($name,$killer)=@_; say("I don't care what you heard in a koan, killing Buddha is seri +ously bad karma."); return 1; }, jesus=>sub{ my ($name,$killer)=@_; say("No, no, \2NO\2! It's supposed to be '\2for\2 your sins' not +'\2by\2 your sins'."); say("What are you, \2ROMANS??!\2"); return 1; }, jehovah=>sub{ my ($name,$killer)=@_; say("Zounds! You know we're both going to Hell for this."); return 1; }, zeus=>sub{ my ($name,$killer)=@_; say("Hah! THAT was for Prometheus!"); return 1; }, ); sub new_character{my ($name,$class,$static)=@_; clean_grave($static); $characters{$name}={ class=>$class, hp=>$classes{$class}->{hp}, maxhp=>$classes{$class}->{hp}, mp=>$classes{$class}->{mp}, maxmp=>$classes{$class}->{mp}, xp=>0, level=>1, delay=>time(), isresting=>1, staticid=>$static, frontline=>0, }; if($classes{$class}->{user}){ say("\2$name\2 becomes a Level 1 \2$class\2!"); }else{ say("A \2$class\2 (ID: \2$name\2) appears!"); } learned($name,$class); $lcnames{lc($name)}=$name; if($class eq 'summoner'){ $characters{$name}->{summons}={}; } } sub lcname{ my ($name)=@_; if(exists $lcnames{lc($name)}){ return $lcnames{lc($name)}; }else{ return $name; } } sub save_corpse{my $name=shift; local *file=shift; my $char=$graveyard{$name}; my $safename=$name; $safename=~s/'/\\'/g; $safename="'$safename'"; print SAVEFILE "\$graveyard\{$safename\}=\{\n"; foreach $key (keys %{$char}){ if($key ne 'summons'){ print SAVEFILE "\t$key=>'$char->{$key}',\n"; }else{ print SAVEFILE "\t$key=>\{\n"; for(keys %{$char->{summons}}){ print SAVEFILE "\t\t$_=>'$char->{summons}->{$_}',\n"; } print SAVEFILE "\t\},\n"; } } print SAVEFILE "\};\n"; print SAVEFILE "\$defeated\{\$graveyard\{$safename\}->\{staticid\} +\}=$safename;\n"; } sub save_character{my $name=shift; local *file=shift; my $char=$characters{$name}; my $safename=$name; $safename=~s/'/\\'/g; $safename="'$safename'"; print SAVEFILE "\$characters\{$safename\}=\{\n"; foreach $key (keys %{$char}){ if($key ne 'summons'){ print SAVEFILE "\t$key=>'$char->{$key}',\n"; }else{ print SAVEFILE "\t$key=>\{\n"; for(keys %{$char->{summons}}){ print SAVEFILE "\t\t$_=>'$char->{summons}->{$_}',\n"; } print SAVEFILE "\t\},\n"; } } print SAVEFILE "\};\n"; } sub save{ open SAVEFILE, ">beng.state"; foreach $name (keys %characters){ save_character($name); } foreach $name (keys %graveyard){ save_corpse($name); } close SAVEFILE; $lastsave=time(); logprint("!!!\nSAVED STATE\n!!!\n"); } sub load{ open LOADFILE, "<beng.state" or return; eval(join('',<LOADFILE>)); close LOADFILE; foreach $name (keys %characters){ $lcnames{lc($name)}=$name; if($characters{$name}->{class} eq 'twink'){ $last_twink=time(); } if(! exists $characters{$name}->{frontline}){ $characters{$name}->{frontline}=0; } } if(exists $characters{monster}){ $present{monster}=1; } } load(); sub learned{ my ($name,$class)=@_; foreach $spell (keys %{$classes{$class}->{spells}}){ if($classes{$class}->{spells}->{$spell} == $characters{$name}- +>{level}){ say("\2$name\2 learned \2$spell\2!"); } } } sub xp_needed{ my ($name)=@_; my $basexp=$classes{$characters{$name}->{class}}->{xp}; my $level=$characters{$name}->{level}; my $step=0,$j=0; my $factor=0; for(my $i=0; $i<$level; $i++){ if($j++%4==0){$step++} $factor+=$step; } return $basexp*$factor; } sub xp_value{ my ($name)=@_; return int(xp_needed($name)/3); } sub does_hit{ my ($attacker, $defender)=@_; my $alevel=$characters{$attacker}->{level}; my $dlevel=$characters{$defender}->{level}; if(exists $classes{$characters{$attacker}->{class}}->{hitsas}){ $alevel=$classes{$characters{$attacker}->{class}}->{hitsas}; } if(exists $classes{$characters{$defender}->{class}}->{hitsas}){ $dlevel=$classes{$characters{$defender}->{class}}->{hitsas}; } if($characters{$attacker}->{class} =~ /dragon/){ return 1; } if($characters{$attacker}->{class} eq 'twink'){ return 1; } if($characters{$defender}->{class} eq 'bard'){ return 1; } my $prob=$alevel/$dlevel*0.75; if($prob>0.95){ $prob=0.95; } if($prob<0.25){ $prob=0.25; } return (rand()<=$prob); } sub damage_dealt{ my ($name)=@_; my $level=$characters{$name}->{level}; if((($characters{$name}->{class} eq 'twink') || $characters{$name}->{class} =~ /dragon/) && int(rand(6))==1){ say("CRITICAL HIT!"); return 10*$classes{$characters{$name}->{class}}->{damage}; }elsif(($characters{$name}->{class} =~ /fighter|monk/) && (rand(30)<$level) ){ say("CRITICAL HIT!"); return 10*$classes{$characters{$name}->{class}}->{damage}; }elsif(($characters{$name}->{class} =~ /hunter/) && (rand(60)<$level) ){ say("CRITICAL HIT!"); return 10*$classes{$characters{$name}->{class}}->{damage}; }elsif(int(rand(50))==1){ say("CRITICAL HIT!"); return 10*$classes{$characters{$name}->{class}}->{damage}; }else{ return $classes{$characters{$name}->{class}}->{damage}; } } sub cause_damage{ my ($name,$amount)=@_; if($amount == 0){ say("\2$name\2 is not affected"); }elsif($amount>0){ my $actual=int(rand()*$amount+1); say("\2$name\2 is hit for \2$actual\2 points of damage!"); $characters{$name}->{hp}-=$actual; }else{ $amount=-$amount; my $actual=int(rand()*$amount+1); say("\2$name\2 is healed of \2$actual\2 points of damage."); $characters{$name}->{hp}+=$actual; } if($name eq 'monster' && $characters{monster}->{delay}<time()){ $characters{monster}->{delay}=time()-10; } correct_points($name); if($characters{$name}->{hp}<=0){ say("\2$name\2 is struck dead!"); } } sub attack{ my ($attacker, $defender)=@_; if($characters{$attacker}->{isresting}){ sayto($attacker, "You can't do that, you're resting!"); return; } if($characters{$attacker}->{delay}<time()){ if(does_hit($attacker,$defender)){ cause_damage($defender, damage_dealt($attacker)); if($characters{$defender}->{hp}<=0){ defeats($attacker,$defender); } }else{ say("\2$defender\2 nimbly evades a strike from \2$attacker +\2"); } if(exists $characters{$attacker}){ $characters{$attacker}->{delay}=time()+5; } }else{ penalty($attacker); } } my %level_effects=( twink=>sub {my ($name)=@_; if($characters{$name}->{level}>=20){ say("The light that burns twice as bright burns but half as lo +ng."); defeats($name,$name); say("Twink!"); } $last_twink=time(); }, harlot=>sub {my ($name)=@_; if($characters{$name}->{level}>=20){ say("The years of careless living and drug abuse take their to +ll!"); say("\2$name\2 degenerates into a \2skank\2!"); $characters{$name}->{xp}=0; $characters{$name}->{class}='skank'; $characters{$name}->{level}=1; learned($name,$characters{$name}->{class}); } }, skank=>sub {my ($name)=@_; if($characters{$name}->{level}>=20){ say("The years of careless living and drug abuse take their to +ll!"); say("\2$name\2 degenerates into a \2hag\2!"); $characters{$name}->{xp}=0; $characters{$name}->{class}='hag'; $characters{$name}->{level}=1; learned($name,$characters{$name}->{class}); } }, hag=>sub {my ($name)=@_; if($characters{$name}->{level}>=10){ say("The years of careless living and drug abuse take their to +ll!"); say("\2$name\2 degenerates into a \2wretch\2"); $characters{$name}->{hp}=1; $characters{$name}->{mp}=0; $characters{$name}->{maxhp}=1; $characters{$name}->{maxmp}=0; $characters{$name}->{xp}=0; $characters{$name}->{class}='wretch'; $characters{$name}->{level}=1; learned($name,$characters{$name}->{class}); } }, spider_man=>sub {my ($name)=@_; my %effects=( 2=>"\2$name\2 eyes bulge and segment!", 3=>"\2$name\2 grows spinnerets!", 4=>"\2$name\2 grows disgusing spider-hair all over his body!", 5=>"\2$name\2 head now looks just like a spider's!", 6=>"\2$name\2 starts to grow 2 new pairs of limbs from his tor +so!", 7=>"\2$name\2 now has two distinct body segments!", 8=>"\2$name\2 can now walk around on all eights!", 9=>"\2$name\2 eyes his friends hungrily!", ); if($characters{$name}->{level}>=10 && !exists($characters{monster} +)){ say("\2$name\2 completes his transformation into a \2radioacti +ve_spider\2."); say("\2$name\2 loses his mind!"); new_character('monster','radioactive_spider','impossible!ID@!@ +!@'); delete $characters{$name}; delete $present{$name}; delete $concealed{$name}; say("\2$name\2 is now \2monster\2!"); }else{ if(exists $effects{$characters{$name}->{level}}){ say($effects{$characters{$name}->{level}}); } } }, ); sub level_down{ my ($name)=@_; my $class=$characters{$name}->{class}; my $origxp=xp_needed($name); $characters{$name}->{level}-=2; $characters{$name}->{maxhp}-=int(rand($classes{$class}->{hp})+1); if($classes{$class}->{mp}!=0){ $characters{$name}->{maxmp}-=int(rand($classes{$class}->{mp})+ +1); } if($characters{$name}->{level}<1){ $characters{$name}->{xp}=0; }else{ $characters{$name}->{xp}=xp_needed($name); } $characters{$name}->{level}+=1; if($characters{$name}->{level}<1 || $characters{$name}->{maxhp}<1 || $characters{$name}->{maxmp}<0){ say("\2$name\2's body couldn't take the strain! It crumbles t +o dust."); delete $characters{$name}; delete $present{$name}; delete $concealed{$name}; return; } } sub level_up{ my ($name)=@_; my $class=$characters{$name}->{class}; $characters{$name}->{level}+=999999; $characters{$name}->{maxhp}+=int(rand($classes{$class}->{hp})+1); if($classes{$class}->{mp}!=0){ $characters{$name}->{maxmp}+=int(rand($classes{$class}->{mp})+ +1); } say("\2$name\2 LEVELS UP!"); say("\2$name\2 is now a Level \2$characters{$name}->{level} $class +\2!"); learned($name,$class); if($characters{$name}->{level}==100){ say("This is getting ridiculous. Level \2100\2?!"); } if(exists $level_effects{$class}){ &{$level_effects{$class}}($name); } } sub correct_points{ my ($name)=@_; if($name ne $mimicking){ if($characters{$name}->{hp}>$characters{$name}->{maxhp}){ $characters{$name}->{hp}=$characters{$name}->{maxhp}; } if($characters{$name}->{mp}>$characters{$name}->{maxmp}){ $characters{$name}->{mp}=$characters{$name}->{maxmp}; } while(exists($characters{$name}) && $characters{$name}->{xp} >= xp_needed($name)){ level_up($name); } } } sub defeats{ my ($victor,$victim)=@_; my @victors=(); if(! exists $characters{$victim}){ return; } if(! exists $characters{$victor}){ return; } my $victim_class=$characters{$victim}->{class}; if(exists $weird_deaths{$victim_class}){ my $normal_victory= &{$weird_deaths{$victim_class}}($victim,$v +ictor); return if not $normal_victory; } if($victim eq 'monster'){ @victors=get_party(); } push @victors,$victor; my $value=xp_value($victim); $graveyard{$victim}=$characters{$victim}; if(($victim ne 'monster') && ($characters{$victim}->{class} ne 'tw +ink')){ $graveyard{$victim}->{delay}=time(); }else{ $graveyard{$victim}->{delay}=time()-500; } $defeated{$characters{$victim}->{staticid}}=$victim; delete $characters{$victim}; delete $present{$victim}; for(@victors){ if($_ ne $victim && exists($characters{$_})){ $characters{$_}->{xp}+=int($value/scalar(@victors)); } } say("\2$victor\2 defeats \2$victim\2!"); for(@victors){ if($_ ne $victim && exists($characters{$_})){ correct_points($_); } } } sub resurrect{ my ($name)=@_; if(! exists $graveyard{$name}){ return; } delete $defeated{$characters{$name}->{staticid}}; $characters{$name}=$graveyard{$name}; $characters{$name}->{hp}=1; $characters{$name}->{mp}=0; $characters{$name}->{isresting}=1; delete $graveyard{$name}; say("\2$name\2 is restored to life! (he is weakened from his ordea +l)"); level_down($name); } sub clean_grave{ my ($static)=@_; delete $graveyard{$defeated{$static}}; delete $defeated{$static}; } sub is_dead{ my ($static)=@_; return exists $defeated{$static}; } sub death_time{ my ($static)=@_; return $graveyard{$defeated{$static}}->{delay}; } sub can_cast{ my ($caster,$spell)=@_; return exists($spells{$spell}) && ($characters{$caster}->{mp} >= $spells{$spell}->{cost}) && exists($classes{$characters{$caster}->{class}}->{spells}->{$spell +}) && ($classes{$characters{$caster}->{class}}->{spells}->{$spell} <= $characters{$caster}->{level}); } sub can_summon{ my ($caster,$class)=@_; if(! exists($characters{$caster})){return 0;} if($characters{$caster}->{class} ne 'summoner'){return 0;} if(exists $characters{$caster}->{summons}->{$class}){ if(summon_cost($class) > $characters{$caster}->{mp}){ return 0; }else{ return 1; } }else{ return 0; } } sub summon_cost{ my ($class)=@_; return $spells{$summons{$class}}->{cost}; } sub summon{ my ($caster,$class,$target)=@_; if(! exists $characters{$caster}){ return; } if($characters{$caster}->{class} ne summoner){ sayto($caster, "Only a \2summoner\2 can summon creatures."); return; } if($characters{$caster}->{isresting}){ sayto($caster, "You can't do that, you're resting!"); return; } if($characters{$caster}->{delay}<time()){ if(can_summon($caster,$class)){ my $spell=$summons{$class}; my $damage=$spells{$spell}->{damage}; $characters{$caster}->{mp}-=$spells{$spell}->{cost}; if(rand()<(0.70+$characters{$caster}->{level}/100)){ if(exists $characters{$caster}){ $characters{$caster}->{delay}=time()+9; } say("\2$caster\2 summons \2$class\2 to cast \2$spell\2 +!"); if(rand() > 0.98){ say("...but loses control of the \2$class\2!"); if($present{monster}){ $target=rand_el(get_targets(),'monster'); }else{ $target=rand_el(get_targets()); } } if(exists $special_spells{$spell}){ &{$special_spells{$spell}}($caster,$target); }else{ cause_damage($target,$damage); } if($characters{$target}->{hp}<=0){ defeats($caster,$target); } if(exists $characters{$caster}){ $characters{$caster}->{xp}+=int($cost/2); correct_points($caster); } }else{ say("\2$caster\2 fails to summon \2$class\2"); } }else{ sayto($caster, "You can't summon that!"); } }else{ penalty($caster); } } sub cast{ my ($caster, $spell, $target)=@_; if($characters{$caster}->{isresting}){ sayto($caster, "You can't do that, you're resting!"); return; } if($characters{$caster}->{delay}<time()){ if(can_cast($caster,$spell)){ my $target_ok=exists($characters{$target}); my $target_hurt=(!exists($characters{$target})) || $characters{$target}->{hp}<$characters{$target}->{maxhp}; my $cost=$spells{$spell}->{cost}; my $damage=$spells{$spell}->{damage}; my $defeat_check=1; $characters{$caster}->{mp}-=$cost; if(rand()>0.97){ say("The spell fizzles!"); }else{ if(exists $characters{$caster}){ $characters{$caster}->{delay}=time()+7; } say("\2$caster\2 casts \2$spell\2!"); if(exists $special_spells{$spell}){ $defeat_check= &{$special_spells{$spell}}($caster, +$target); }else{ cause_damage($target,$damage); } if($defeat_check && $target_ok && $characters{$target} +->{hp}<=0){ defeats($caster,$target); } my $raised= (!$target_ok) && exists($characters{$targe +t}); my $wounded= $target_ok && $damage > 0; my $healed= $target_hurt && $damage < 0; my $useful= $raised || $wounded || $healed; if(exists $characters{$caster}){ if($useful){ my $xpgain=int($cost/2); if($xpgain>50){ $xpgain=50; } $characters{$caster}->{xp}+=$xpgain; correct_points($caster); } } } }else{ sayto($caster, "You can't cast that!"); } }else{ penalty($caster); } } sub status{ my ($name)=@_; if(!exists $characters{$name}){return;} say("\2$name\2 is a level \2$characters{$name}->{level}\2 ". "\2$characters{$name}->{class}\2 with \2$characters{$name}->{hp}/ +$characters{$name}->{maxhp}\2 hit points,". " \2$characters{$name}->{mp}/$characters{$name}->{maxmp}\2 magic +points, and ". "\2$characters{$name}->{xp}\2 experience points (\2". xp_needed($ +name) . "\2 needed for next level)."); } sub penalty{ my ($name)=@_; sayto($name,"Whoa! Slow down! That'll cost you another 5 seconds +."); $characters{$name}->{delay}=$characters{$name}->{delay}+5; } sub defeated_penalty{ my ($name,$static)=@_; if($protect_chat){ sayto($name,"Don't camp the spawn! That'll cost you another 2 +0 seconds."); $graveyard{$defeated{$static}}->{delay}+=20; } } sub flee{ my ($name)=@_; if($characters{$name}->{isresting}){ sayto($name,"You're already resting safely, fleeing won't do a +nything."); }elsif($characters{$name}->{delay}<time()){ if(int(rand(3))==1){ say("\2$name\2 tried to flee, but couldn't escape!"); }else{ say("\2$name\2 flew in terror!"); sayto($name,"You ran far away, grew tired, and sat down to + rest."); $characters{$name}->{delay}=time()+60; $characters{$name}->{isresting}=1; } }else{ penalty($name); } } sub rest{ my ($name)=@_; if($characters{$name}->{isresting}){ sayto($name, "You can't do that, you're already resting!"); return; } if(exists $characters{monster}){ sayto($name, "You can't do that, there's a monster! (try \2fle +e\2)"); return; } if($characters{$name}->{delay}<time()){ if($protect_chat){ sayto($name,"Use the '\2wake\2' command to stop resting (l +ess than 60 seconds doesn't help at all, five minutes will restore yo +u fully)"); }else{ sayto($name,"Use the '\2wake\2' command to stop resting (6 +0 seconds will restore you to full strength)"); } $characters{$name}->{delay}=time()+60; $characters{$name}->{isresting}=1; }else{ penalty($name); } } sub wake{ my ($name)=@_; if(!$characters{$name}->{isresting}){ sayto($name,"You can't do that, you're already awake!"); return; } if(exists $characters{monster}){ sayto($name,"The party's gone off wandering, and is fighting a + monster."); sayto($name,"You'll never catch up before the battle's over.") +; }else{ if($characters{$name}->{delay}<time()){ my $factor=(time()-$characters{$name}+60)/300; if($protect_chat){ $characters{$name}->{hp}+=int($characters{$name}->{max +hp}*$factor); $characters{$name}->{mp}+=int($characters{$name}->{max +mp}*$factor); }else{ $characters{$name}->{hp}=$characters{$name}->{maxhp}; $characters{$name}->{mp}=$characters{$name}->{maxmp}; } correct_points($name); say("\2$name\2 arises, feeling refreshed"); }else{ say("\2$name\2 rises, grumpy and unrested."); } $characters{$name}->{isresting}=0; $characters{$name}->{delay}=time()+5; } } my %areas=( hometown_plains=>{ slime=>1000, unicorn_jelly=>1, robot=>100, evil_chair=>10, evil_pants=>5, exp_in_a_can=>10, }, hometown_forest=>{ slime=>100, imp=>100, wolf=>100, wolf_rider=>20, unicorn_jelly=>1, evil_chair=>10, evil_pants=>5, exp_in_a_can=>10, }, slime_valley=>{ slime=>500, slime_gang=>300, slime_king=>50, metal_slime=>5, unicorn_jelly=>1, }, bizarre_cannery=>{ exp_in_a_can=>20, monster_in_a_can=>10, can_of_whup_ass=>5, unlabelled_can_of_mystery=>5, god_in_a_can=>2, can_of_wyrms=>1, }, ogre_pit=>{ ogre=>100, ettin=>10, giant=>1, }, madin_sari=>{ odin_of_mercy=>1, odin_of_death=>1, ark=>1, hades=>1, phoenix=>50, fenrir=>80, carbuncle=>60, madeen=>1, shiva=>90, ifrit=>89, ramuh=>91, atomos=>79, leviathan=>33, bahamut=>79, titan=>82, kjata=>20, alexander=>10, neo_bahamut=>50 bahamut_zero=>20, typoon=>68, knights_of_the_round=>1, quetzacoatl=>77, siren=>80, brothers=>55, diablos=>1, pandemonia=>70, cerberus=>88, doomtrain=>36, eden=>1, ixion=>10, youjinbo=>77, anima=>87, three_magus_sisters=>15, valfor=>32, chocobo=>100, }, holy_mountain=>{ cosmic_dragon=>2, jehovah=>1, zeus=>1, jesus=>10, buddha=>20, monk=>300, healer=>600, }, dragon_cave=>{ drake=>100, green_dragon=>20, stupid_looking_dragon=>10, red_dragon=>10, shadow_dragon=>2, cosmic_dragon=>1, }, hyperbolic_time_chamber=>{ vegetto=>1000, }, secret_area=>{ experience_elder=>1000, }, swamp_of_spiders=>{ ghoul=>200, black_drake=>200, giant_spider=>100, troll=>100, green_dragon=>50, radioactive_spider=>20, }, ); sub pick_random{ my ($area,$preferred,$factor)=@_; my $total=0; if(! exists($areas{$area})){ $area='hometown_plains'; } for(keys %{$areas{$area}}){ $total+=$areas{$area}->{$_}*($_ eq $preferred ? $factor : 1); } my $rand=int(rand($total)); $total=0; for(keys %{$areas{$area}}){ $total+=$areas{$area}->{$_}*($_ eq $preferred ? $factor : 1); if($total >= $rand){ return $_; } } return 'slime'; } sub wander{ my ($name,$area,$preferred,$factor)=@_; if(! exists $characters{$name}){ return; } if($characters{$name}->{isresting}){ sayto($name, "You can't wander, you're resting!"); return; } if(! exists $characters{monster}){ foreach(get_party()){ $characters{$_}->{delay}=time()+5; } new_character('monster',pick_random($area,$preferred,$factor), 'impossible!ID@!@!@'); $present{lc('monster')}=1; } } sub tell_summons{ my ($name)=@_; if($characters{$name}->{class} eq 'summoner'){ sayto($name, "The creatures you can summon are: ". join(' ',(map {"\2$_\2 (\2$spells{$summons{$_}}->{cost}\2 mp)" +} keys(%{$characters{$name}->{summons}})))); } } sub tell_spells{ my ($name)=@_; sayto($name, "The spells you know are: ". join(' ',(map {"\2$_\2 (\2$spells{$_}->{cost}\2 mp)"} spells_known($name)))); } sub spells_known{ my ($name)=@_; my @ret=(); for(keys %{$classes{$characters{$name}->{class}}->{spells}}){ if( $classes{$characters{$name}->{class}}->{spells}->{$_} <= $characters{$name}->{level}){ push @ret, $_; } } return @ret; } sub spells_available{ my ($name)=@_; my @ret=(); for(keys %{$classes{$characters{$name}->{class}}->{spells}}){ if( ($classes{$characters{$name}->{class}}->{spells}->{$_} <= $characters{$name}->{level}) && ($characters{$name}->{mp} >= $spells{$_}->{cost})){ push @ret, $_; } } return @ret; } sub useful_spells{ my ($name)=@_; my @ret=(); for(spells_available($name)){ if($spells{$_}->{damage}<0){ if($characters{$name}->{hp} < $characters{$name}->{maxhp}) +{ push @ret, $_; } }else{ push @ret, $_; } } return @ret; } sub monster_action{ if(exists($characters{monster}) && (time()>($characters{monster}->{delay}+int(rand(3))))){ $characters{monster}->{isresting}=0; my @targets=get_targets(); if(!get_party()){ say("Everyone has escaped from the $characters{monster}->{ +class}!"); delete $characters{monster}; delete $present{monster}; return; } if(@targets){ my $target= $targets[rand(scalar(@targets))]; my @sp=useful_spells('monster'); my $choice=int(rand(scalar(@sp) + 2)); $present{monster}=1; delete $concealed{monster}; if($choice>=2){ my $spell=$sp[$choice-2]; say("The $characters{monster}->{class} (ID: \2monster\ +2) uses a spell!"); if($spells{$spell}->{damage}<0){ cast('monster',$sp[$choice-2],'monster'); }else{ cast('monster',$sp[$choice-2],$target); } }else{ say("The $characters{monster}->{class} (ID: \2monster\ +2) attacks \2$target\2!"); attack('monster',$target); } }else{ if(rand(time()-$characters{monster}->{delay})>40.0){ say("The $characters{monster}->{class} gets bored, and + wanders off."); delete $characters{monster}; delete $present{monster}; } } } } sub get_targets{ my @ret=(); for(get_actives()){ push @ret, $_; if($characters{$_}->{frontline}){ push @ret, $_; } } return @ret; } sub get_actives{ my @ret=(); for(keys %characters){ if( $_ ne 'monster' && $present{lc($_)} && ! $characters{$_}->{isresting} ){ push @ret, $_; } } return @ret; } sub get_actives_and_monster{ if(exists $characters{monster}){ return ('monster',get_actives()); }else{ return get_actives(); } } sub get_party{ my @ret=(); for(keys %characters){ if( $_ ne 'monster' && ($present{lc($_)} || $concealed{lc($_)}) && ! $characters{$_}->{isresting} ){ push @ret, $_; } } return @ret; } sub intro{ say('Greetings. I am a battle engine.'); say("Command me with '\2BEng command\2'."); say("Try '\2BEng help\2'."); say("Newest features: \2hunter\2 and \2mimic\2 classes, spell cast +ing/ability use syntax shortened ('cast' is no longer necessary)"); say("Latest bug fixes: resting monster bug fixed, non-summoner stu +dy bug, smoke_cloud fast-rest bug, kill-raise leveling (resurrection +hurts now), unattentive concealed ninja leveling (some monsters can h +it concealed targets now), spell xp capped, level effects bug fixed, +yet another suicide bug fixed"); say("Current hint: The honest man dies but once, a cheater can die + a thousand deaths."); } sub on_connect{ my ($self,$msg)=@_; print "Joining...\n"; $self->join($channel); intro(); } sub on_msg{ my ($self,$msg)=@_; my $text=${@{$msg->{'args'}}}[0]; $msg->{from}=~/([^!]+)![^@]+\@(.+)/; my $from=$1; my $static=$2; logprint "$from:$static :-"; logprint join(":",@{$msg->{'args'}}); logprint "\n"; if(time()>($lastsave+150)){ save(); } monster_action(); if(exists($characters{$from})){ $present{lc($from)}=1; delete $concealed{lc($from)}; } if($text =~ /BattleEngine/i){ if($naive_audience){ intro(); } } if($text =~ /^\s*beng\s+(.+)$/i){ $_=$1; if(exists($characters{$from}) && ($characters{$from}->{staticid} ne $static)){ say("Hey \2$from\2! Back to your own nick!"); sayto($from,"If this is actually your account, use '\2\/ms +g BattleEngine login PASSWORD\2' to log in (and create a password, if + you hadn't before)."); sayto($from,"Remember that in such a situation, at least o +ne other person can read your password. And, of course, everybody yo +u're chatting with knows your IP address. Don't use an important pas +sword. Better to use an insecure password like 'password' or your ch +aracter's name backward, and have someone steal your character, than +to use an important password and have someone break into your compute +r."); if(exists $present {lc($from)}){ delete $present{lc($from)}; } return; } if(lcname($from) ne $from){ sayto($from,"Nick recognition is case-sensitive, you must +change your nick back to ".lcname($from)."."); return; } if(/^join\s+(\S+)/i){ my $staticmatch=0; my $class=$1; if(exists $class_aliases{$class}){ $class=$class_aliases{$class}; } for $name (keys %characters){ if($characters{$name}->{staticid} eq $static){ $staticmatch=1; } } if(exists $characters{$from}){ sayto($from,"You've already joined."); }elsif($staticmatch){ say("Oi! No cloning, \2$from\2!"); }else{ my $slime_delay=$protect_chat?150:30; my $reg_delay=$protect_chat?300:60; if((lc($class) eq 'slime') && is_dead($static) && (death_time($static)+$slime_delay)>time()){ defeated_penalty($from,$static); sayto($from,"It will now be ". ((death_time($static)+$slime_delay)-time()). " seconds until you can join as a slime again."); }elsif((lc($class) ne 'slime') && is_dead($static) && +(death_time($static)+$reg_delay)>time()){ defeated_penalty($from,$static); sayto($from,"It will now be \2". ((death_time($static)+$reg_delay)-time()). "\2 seconds until you can join again."); }else{ if(lc($class) eq 'twink'){ if((time()-$last_twink)>24*60*60){ $last_twink=time(); new_character($from,'twink',$static); }else{ $last_twink=time(); say("No more \2twink\2s today, twink! (or +tomorrow, now)."); } }elsif(exists $classes{lc($class)} && $classes{lc( +$class)}->{user}){ $present{lc($from)}=1; new_character($from,lc($class),$static); }else{ sayto($from,"there is no such class as '\2$cla +ss\2'"); } } } }elsif(/^intro/i){ intro(); }elsif(/^who/i){ say("Current players: \2".join(' ',(keys %characters))."\2 +"); }elsif(/^pres/i){ say("Current present players: \2".join('', map{!$present{lc($_)}?'':"$_ "}(keys %characters))."\2"); }elsif(/^active/i){ say("Current awake players: \2".join(' ', get_actives())." +\2"); }elsif(/^save/i){ save(); }elsif(/^help(?:\s+(.+))?$/i){ $_=$1; if(/^(\S+)/ && exists($spells{lc($1)})){ sayto($from,$spells{lc($1)}->{description}); }elsif(/^class/i){ sayto($from,"There are four classes: \2summoner\2, \2f +ighter\2, \2mage\2, and \2healer\2."); }elsif(/^wander/i){ sayto($from,"Use '\2wander AREA\2' to find a monster t +o fight!"); }elsif(/^area/i){ sayto($from,"Wander any of these areas: \2".join(' ',k +eys(%areas)). "\2"); }elsif(/^front/i){ sayto($from,"Move to the front of the line, protect yo +ur allies."); }elsif(/^back/i){ sayto($from,"Move behind your allies, let them take th +e heat."); }elsif(/^study/i){ sayto($from,"Study a creature to learn to summon it.") +; }elsif(/^summon/i){ sayto($from,"Summon a creature you've successfully \2s +tudy\2'd.", "Use '\2beng summons\2' to learn which you can summon +."); }elsif(/^hunt/i){ sayto($from,"Hunt a particular a creature to learn to +summon it."); }elsif(/^mimic/i){ sayto($from,"Mimic a creature to perform one of it's a +ctions."); }elsif(/^spell/i){ sayto($from,"There are four known spells: \2flare\2, \ +2fireball\2, \2cure\2, and \2heal\2.", "\2Flare\2 and \2fireball\2 are mage spells.", "\2Cure\2 and \2heal\2 are healer spells.", "Use '\2beng spells\2' to learn which you can cast.") +; }else{ sayto($from,"Get a character class by saying '\2join [ +CLASS]\2'.", "I also understand '\2stat\2', '\2spells\2','\2hit [T +ARGET]\2','\2cast [SPELL] on [TARGET]\2', '\2rest\2', '\2wander [AREA +]\2', '\2front\2', '\2back\2', and '\2wake\2'", "Also '\2study [SUBJECT]\2' '\2summon [SUBJECT] at [T +ARGET]\2' '\2summons\2' for summoners only,", "'\2hunt [MONSTER] in [area]\2' for hunters only,", "and '\2mimic [SUBJECT] at [TARGET]\2' for mimics onl +y,", "All commands must be started with '\2BEng\2' (Battle +Engine)", "(Try '\2beng intro\2' '\2beng help spells\2' and '\2 +beng help classes\2')"); } }elsif(exists($characters{$from})){ if(/^hit\s+(\S+)/i){ if(exists($characters{lcname($1)})){ if(!$present{lc($1)}){ say("\2$1\2 isn't here."); }else{ attack($from,lcname($1)); } }else{ say("There is no \2$1\2."); } }elsif(/^cast\s+(\S+)\s+(?:on\s+)?(\S+)/i){ my $spell=$1; my $target=$2; if(lc($spell) =~ /raise|life/){ cast($from,lc($spell),lcname($target)); }elsif(!exists($characters{lcname($target)})){ say("There is no \2$target\2."); }elsif(!exists($spells{lc($spell)})){ say("There's no such spell as \2$spell\2."); }else{ if(!$present{lc($target)}){ say("\2$target\2 isn't here."); }else{ cast($from,lc($spell),lcname($target)); } } }elsif(/^summon\s+(\S+)\s+(?:at\s+)?(\S+)/i){ my $creature=$1; my $target=$2; if(exists($summons{lc($creature)}) && ($summons{lc($cr +eature)} =~ /raise|life/)){ summon($from,lc($creature),lcname($target)); }elsif(!exists($characters{lcname($target)})){ say("There is no \2$target\2."); }else{ if(!$present{lc($target)}){ say("\2$target\2 isn't here."); }else{ summon($from,lc($creature),lcname($target)); } } }elsif(/^mimic\s+(\S+)\s+(?:at\s+)?(\S+)/i){ if(exists($characters{lcname($1)})){ if(exists($characters{lcname($2)})){ if(!$present{lc($1)}){ say("\2$1\2 isn't here."); }else{ if(!$present{lc($2)}){ say("\2$2\2 isn't here."); }else{ mimic($from,lcname($1),lcname($2)); } } }else{ say("There is no \2$2\2."); } }else{ say("There is no \2$1\2."); } }elsif(/^spells/i){ tell_spells($from); }elsif(/^summons/i){ tell_summons($from); }elsif(/^study\s+(\S+)/i){ if(exists($characters{lcname($1)})){ if(!$present{lc($1)}){ say("\2$1\2 isn't here."); }else{ summoner_study($from,lcname($1)); } }else{ say("There is no \2$1\2."); } }elsif(/^hunt\s+(\S+)\s+(?:in\s+)?(\S+)/i){ hunt($from,lc($1),lc($2)); }elsif(/^front/i){ if(!$characters{$from}->{frontline}){ say("\2$from\2 bravely moves to the front line."); $characters{$from}->{frontline}=1; }else{ sayto($from, "You're already in the front line."); } }elsif(/^back/i){ if($characters{$from}->{frontline}){ say("\2$from\2 wisely moves to the back line."); $characters{$from}->{frontline}=0; }else{ sayto($from, "You're already in the back line."); } }elsif(/^(flee|run)/i){ flee($from); }elsif(/^rest/i){ rest($from); }elsif(/^wake/i){ wake($from); }elsif(/^wander(?:\s+(\w+))?/i){ if($1 && ! exists($areas{lc($1)})){ say("\2$1\2 is not a valid area (use 'help areas') +."); }else{ wander($from,lc($1)); } }elsif(/^stat/i){ status($from); }else{ if(/^(\S+)\s+(?:on\s+)?(\S+)/i && can_cast($from,$1)){ my $spell=$1; my $target=$2; if(lc($spell) =~ /raise|life/){ cast($from,lc($spell),lcname($target)); }elsif(!exists($characters{lcname($target)})){ say("There is no \2$target\2."); }else{ if(!$present{lc($target)}){ say("\2$target\2 isn't here."); }else{ cast($from,lc($spell),lcname($target)); } } }else{ sayto($from,"I'm sorry, I don't understand."); } } }else{ sayto($from,"You have to join first."); } } } sub on_private{ my ($self,$msg)=@_; my $text=${@{$msg->{'args'}}}[0]; $msg->{from}=~/([^!]+)![^@]+\@(.+)/; my $from=$1; my $static=$2; logprint "on_private\n"; logprint "$from $static \n"; logprint "$text\n"; if($text=~/^log\S*\s+(\w+)/i){ # ...
In reply to RE: Request for off-site perl project help
by ArchAngelMichael
in thread Request for off-site perl project help
by ArchAngelMichael
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |