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 situation.'}, happy_dance=>{cost=>'1',damage=>'-10',description=>'What? You need a reason?'}, tansu_dance=>{cost=>'5',damage=>'20',description=>'A vigorous dance involving throwing an excessively heavy chest of drawers.'}, tangle_tango=>{cost=>'20',damage=>'0',description=>'Trip up your partner.'}, disco_fever=>{cost=>'1',damage=>'0',description=>'Face it. Disco is dead.'}, sword_dance=>{cost=>'40',damage=>'100',description=>'One of the more violent 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 move 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 best attack. A giant blue beam.'}, kamehameha=>{cost=>'50',damage=>'500',description=>'Wow. Gokuu still uses this old ass technique.'}, ki_sense=>{cost='20',damage=>'0',description=>'Sensing the energy of lost people.'}, instant_transmissio=>{cost=>'0',description=>'Moving super fast and way 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 entirely of candy!'}, scan=>{cost=>'5',damage=>'0',description=>'Learn about an enemy\'s state.'}, 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 self 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 just 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 until 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 concealment.'}, 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 sticky 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 censored]'}, regenerate=>{cost=>'50',damage=>'-400',description=>'Supernatural healing.'}, 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 indeed!'}, raise=>{cost=>'50',damage=>'0',description=>'Raise the recently deceased.'}, 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 betrayal of the healer\'s art.'}, rbite=>{cost=>'1',damage=>'0',description=>'This venom has been infused 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 unhygienic 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! Break 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 rainbow?'}, hurl_thunderbolt=>{cost=>'1000',damage=>'10000',description=>'Zeus does 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,spells=>{}}, 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=>100,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_skill=>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,damage=>50,spells=>{web=>1,rbite=>1}}, black_drake=>{user=>0,hp=>400,mp=>150,xp=>1200,hitsas=>14,damage=>20,spells=>{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,spells=>{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=>{flare=>1}}, stupid_looking_dragon=>{user=>0,hp=>100,mp=>120,xp=>900,damage=>20,spells=>{really_bad_breath=>1}}, green_dragon=>{user=>0,hp=>500,mp=>120,xp=>1200,damage=>20,spells=>{poison_breath=>1}}, red_dragon=>{user=>0,hp=>1000,mp=>200,xp=>2400,damage=>30,spells=>{fire_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,spells=>{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,spells=>{breath_weapon_barrage=>1}}, jehovah=>{user=>0,hp=>25000,mp=>20000,xp=>25000,hitsas=>200,damage=>500,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=>10,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,meteo=>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=>{preach=>1}}, jesus=>{user=>0,hp=>10,mp=>100,xp=>1,hitsas=>50,damage=>0,spells=>{preach=>1}}, twink=>{user=>1,hp=>500,mp=>100,xp=>10,damage=>50,spells=>{flare=>1,cure=>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,damage=>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,devilish_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_transmission=>36,final_flash=>43,sword_flash_attack=>55,final_kamehameha=>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}{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$object\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 creatures."); return; } if($characters{$samurai}->{isresting}){ sayto($hunter, "You're resting. You can't hunt while resting."); return; } if($characters{$samurai}->{delay}{level}); $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 learn to summon them."); return; } if($characters{$student}->{isresting}){ sayto($student, "You're resting. You can't study while resting."); 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 creature you can already summon."); return; } if($characters{$student}->{delay}{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 longer 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}->{staticid}); 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 is 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_wyrms'), '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 seriously 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, ")); 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}{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}{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 long."); 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 toll!"); 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 toll!"); 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 toll!"); 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 torso!", 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 \2radioactive_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 to 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,$victor); 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 'twink')){ $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 ordeal)"); 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}{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}{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{$target}); 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 20 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 anything."); }elsif($characters{$name}->{delay}{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 \2flee\2)"); return; } if($characters{$name}->{delay}{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}{hp}+=int($characters{$name}->{maxhp}*$factor); $characters{$name}->{mp}+=int($characters{$name}->{maxmp}*$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 casting/ability use syntax shortened ('cast' is no longer necessary)"); say("Latest bug fixes: resting monster bug fixed, non-summoner study bug, smoke_cloud fast-rest bug, kill-raise leveling (resurrection hurts now), unattentive concealed ninja leveling (some monsters can hit 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\/msg 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 one other person can read your password. And, of course, everybody you're chatting with knows your IP address. Don't use an important password. Better to use an insecure password like 'password' or your character's name backward, and have someone steal your character, than to use an important password and have someone break into your computer."); 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$class\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, \2fighter\2, \2mage\2, and \2healer\2."); }elsif(/^wander/i){ sayto($from,"Use '\2wander AREA\2' to find a monster to fight!"); }elsif(/^area/i){ sayto($from,"Wander any of these areas: \2".join(' ',keys(%areas)). "\2"); }elsif(/^front/i){ sayto($from,"Move to the front of the line, protect your allies."); }elsif(/^back/i){ sayto($from,"Move behind your allies, let them take the 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 \2study\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 actions."); }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 [TARGET]\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 [TARGET]\2' '\2summons\2' for summoners only,", "'\2hunt [MONSTER] in [area]\2' for hunters only,", "and '\2mimic [SUBJECT] at [TARGET]\2' for mimics only,", "All commands must be started with '\2BEng\2' (BattleEngine)", "(Try '\2beng intro\2' '\2beng help spells\2' and '\2beng 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($creature)} =~ /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){ # ...