summaryrefslogblamecommitdiff
path: root/npc/functions/siege.txt
blob: 9d6b479d74ca15d477a63ad056040481b6d42e6e (plain) (tree)


















                                                                                                                                                      







                                                                                 
             
 
                                                       
                                     




                                                         
                                                 





                                               



                            
 
                                      



                
                                                                                           
 





                            
                                                                                
                         
                                             


                                         
                                          
 
                                                                        
                                                                                                          
                                          
     








                                                                                   
                                                                                


                                                  
                       


                                                              
                     
 
                                
                                                                       
                                                                     








                                     



                                          





                                                                         

















                                            
                                       














                                                        
                                                             
                                         
                      
                                         
                   
 



                                                                          



                                                                                                  
                                                                      
                                                                          


           



















                                                                  




                                         

                                                               

                                                                      

                                              




                                           
                                 



                         









                                                                    
                                                     




























                                                                                  
                                                                                                                                                                     
     


























                                                                           
 




                                            


                         
                         















                                                                                                         

                        


                                                                                                             
 
                                                                     


           











                                                           



                                                                      


                                       
                                                          


                                       
                                                          


                                       
                                                          


           
 




















































































                                                                                                          



                                            







                                                    
                    
                
                   





                                                                                    
                                    
                                                                                      

        





                                                                                   
               

                                     
                                 
                                                                                   

        






                                                                                   

 
// TMW2 scripts.
// Authors:
//    Jesusalva
// Description:
//    Town Siege utilities

// Siege Spawn
// Can be used anywhere to spawn on the whole map, margins respected.
// siege_spawn( map, mobID, Amount, eventID )
function	script	siege_spawn	{
    .@mp$=getarg(0);
    .@mid=getarg(1);
    .@qnt=getarg(2);
    .@ev$=getarg(3);

    areamonster(.@mp$, 20, 20, getmapinfo(MAPINFO_SIZE_X, .@mp$)-20, getmapinfo(MAPINFO_SIZE_Y, .@mp$)-20, strmobinfo(1, .@mid), .@mid, .@qnt, .@ev$);
    return;
}

// Calculate player average level
// if highest is set, it will return highest player level, with minimum the value
// passed. (A level "0" is clearly not valid, of course)
// siege_calcdiff ( map{, highest_lvl} )
function	script	siege_calcdiff	{
    .@bsum=0;
    .@highest=getarg(1, false);
    .@c = getunits(BL_PC, .@players, false, getarg(0));
    .@skip=0;

    // There is at least one player, do things properly
    for (.@i = 0; .@i < .@c; .@i++) {
        /*
        // Dead players are not counted
        if (ispcdead(strcharinfo(0, "", .@players[.@i])))
            continue;
        */
        .@b=readparam(BaseLevel, .@players[.@i]);

        // GMs are not counted
        if (getgroupid(.@players[.@i]) >= 60) {
            .@skip+=1;
            .@b=0;
        }
        .@bsum+=.@b;
        if (.@b > .@highest)
            .@highest=.@b;
    }

    // Sanitize and fallback if needed
    .@c-=.@skip;
    if (!.@c)
        .@c=1;

    //debugmes "calcdiff: Total %d Average %d Highest %d", .@bsum, (.@bsum/.@c), .@highest;

    if (getarg(1,false))
        return .@highest;
    else
        return (.@bsum/.@c);
}

// push to $@SIEGE_TMPMOBS the <mobID> if their level is within a x levels range
// above or below <level>
// siege_push ( mobID, {level{, variation}} )
function	script	siege_push	{
    .@mi=getarg(0);
    .@lv=getarg(1,0);
    .@var=getarg(2,10); // Old Default: 15

    if ( is_between(.@lv-.@var, .@lv+(.@var/2), strmobinfo(3, .@mi)) ) {
        //debugmes "Monster %s (%d) level %d ~= %d", strmobinfo(1, .@mi), .@mi, strmobinfo(3, .@mi), .@lv;
        array_push($@SIEGE_TMPMOBS, .@mi);
    }
    return;
}

// Selects a monster based on player average strength and base difficulty
// It'll select monsters from 20 levels below to 20 levels above
// tp_mask is the same as TP_* constants and changes mobs present. Only TP_TULIM,
// TP_HURNS and TP_NIVAL are supported. You can use them with the "|" operand, eg.,
// TP_TULIM | TP_HURNS.
//
// It currently only creates $@SIEGE_TMPMOBS, you need to use any_of() manually.
// siege_selectmob ( blvl, difficulty{, tp_mask} )
function	script	siege_selectmob	{
    .@blv=getarg(0);
    .@dif=getarg(1, 0);
    .@tp=getarg(2, 0);

    // We don't need .@dif, so we convert difficulty to levels
    .@blv+=(.@dif/3);

    deletearray $@SIEGE_TMPMOBS;
    setarray $@SIEGE_TMPMOBS, ManaGhost, CandiedSlime, Bif, SlimeBlast;
    // Now we must select mobs, using array_push() to $@SIEGE_TMPMOBS
    // First, mobs on all envs
    siege_push(BlackScorpion, .@blv);
    siege_push(GreenSlime, .@blv);
    siege_push(CaveMaggot, .@blv);
    siege_push(MagicGoblin, .@blv);
    siege_push(AngryBat, .@blv);
    siege_push(BlackSlime, .@blv);
    siege_push(BlackScorpion, .@blv);
    siege_push(Forain, .@blv);
    siege_push(Terranite, .@blv);
    siege_push(JackO, .@blv);
    siege_push(BlackMamba, .@blv);
    siege_push(TerraniteProtector, .@blv);
    siege_push(Reaper, .@blv);

    // What if we are trying to select a boss and they're... overleveled?
    // We need a level 105, 120 and a level 135 monsters
    siege_push(FallenKing2, .@blv);
    siege_push(TerraniteKing, .@blv);

    // Now, mobs on only certain envs
    if (.@tp & TP_TULIM) {
        siege_push(AngryScorpion, .@blv);
        siege_push(AngryRedScorpion, .@blv);
        siege_push(DesertBandit, .@blv);
        siege_push(LavaSlime, .@blv);
        siege_push(OldSnake, .@blv);
        siege_push(Snake, .@blv);
    }
    if (.@tp & TP_HURNS) {
        siege_push(RedSlime, .@blv);
        siege_push(Bandit, .@blv);
        siege_push(RedMushroom, .@blv);
        siege_push(RobinBandit, .@blv);
        siege_push(AngryYellowSlime, .@blv);
        siege_push(GrassSnake, .@blv);
        siege_push(WickedMushroom, .@blv);
        siege_push(GreenDragon, .@blv);
    }
    if (.@tp & TP_NIVAL) {
        siege_push(Bluepar, .@blv);
        siege_push(Wolvern, .@blv);
        siege_push(Yeti, .@blv);
        siege_push(Moggun, .@blv); // PS. Not aggressive
    }

    // Removed
    //siege_push(DarkLizard, .@blv);
    //siege_push(Crafty, .@blv);

    return;
}

/////////////////////////////////////////////////////////////
// Prepare a siege with optional announce
// siege_setup ( map )
function	script	siege_setup	{
    .@m$=getarg(0);

    // Save old map zone
    $@MZONE$[getmapinfo(MAPINFO_ID, .@m$)]=getmapinfo(MAPINFO_ZONE, .@m$);

    // Apply changes
    addmapmask .@m$, MASK_MATTACK;
    changemusic .@m$, any("mythica.ogg", "eric_matyas_ghouls.ogg", "misuse.ogg", "Arabesque.ogg");
    disablenpc("Mana Stone");
    pvpon(.@m$);
    setmapflag(.@m$,mf_zone,"MMO"); // MMO Zone: Overrides GM Commands
    setmapflag(.@m$,mf_bexp,rand(120,140)); // 20~40% EXP UP on siege maps
    return;
}

// Check if boss was killed or not
// siege_check ( map )
function	script	siege_check	{
    .@m$=getarg(0);
    .@mb=0;

    .@mb+=mobcount(.@m$, "#SiegeCtrl::OnSergeantDeath");
    .@mb+=mobcount(.@m$, "#SiegeCtrl::OnLieutenantDeath");
    .@mb+=mobcount(.@m$, "#SiegeCtrl::OnCaptainDeath");
    .@mb+=mobcount(.@m$, "#SiegeCtrl::OnColonelDeath");
    .@mb+=mobcount(.@m$, "#SiegeCtrl::OnGeneralDeath");

    // Players failed, so reduce score in 1
    if (.@mb) {
        $MK_TEMPVAR-=1;
        kamibroadcast("Players failed to defend the city!!"+.@mb);
    }
    return;
}

// Revert what siege_setup did
// siege_revert ( map )
function	script	siege_revert	{
    .@m$=getarg(0);

    // Revert map zone (to town, or to blank) and delete backup
    removemapflag(.@m$,mf_zone);
    //setmapflag(.@m$,mf_zone,$@MZONE$[getmapinfo(MAPINFO_ID, .@m$)]);
    setmapflag(.@m$,mf_zone,"Normal");
    $@MZONE$[getmapinfo(MAPINFO_ID, .@m$)]="";

    removemapmask .@m$, MASK_MATTACK;
    changemusic .@m$, "caketown.ogg"; // :>
    enablenpc("Mana Stone");
    pvpoff(.@m$);
    removemapflag(.@m$,mf_bexp);
    setmapflag(.@m$,mf_bexp,100);
    killmonsterall(.@m$);
    return;
}

// Create the Siege Boss for #SiegeCtrl utility, DO NEVER CAST TWICE
// siege_boss ( map, difficulty )
function	script	siege_boss	{
    .@m$=getarg(0);
    .@s=getarg(1,0);

    // If Difficulty is 0: There is no boss
    if (!.@s)
        return;

    /* Uncomment this to use common monsters as staff
    // We will now prepare the boss
    // It must be stronger than players in at least 15 levels, so the mob group
    // is different. The siege difficulty, as usual, gives an extra level to them.
    .@val=siege_calcdiff(.@m$)+15;
    .@val+=.@s;

    // We must cap this at 100 to prevent running out of monsters
    // Also, .@val can NEVER be less than 50, to prevent cheating :<
    if (.@val > 100)
        .@val=100;
    else if (.@val < 50)
        .@val=50;

    // Get their event/designation
    if (.@val >= 80)
        .@ts$="Colonel";
    else
        .@ts$="Lieutenant";

    // Select a type for them (saved as .@mobId)
    siege_selectmob(siege_calcdiff(.@m$, .@val), .@s);
    array_remove($@SIEGE_TMPMOBS, Bif);
    array_remove($@SIEGE_TMPMOBS, CandiedSlime);
    array_remove($@SIEGE_TMPMOBS, ManaGhost);
    array_remove($@SIEGE_TMPMOBS, SlimeBlast);
    if (array_entries($@SIEGE_TMPMOBS) > 0) {
        .@mobId=any_of($@SIEGE_TMPMOBS);
    } else {
        .@mobId=Yetifly;
        Exception("[WARNING] Insufficient monsters in database: Highlight @jesusalva - Broken reference: "+siege_calcdiff(.@m$, .@val), RB_DEBUGMES|RB_IRCBROADCAST);
    }
    */

    // We now select based on player average level and a seed of randomness
    .@val=siege_calcdiff(.@m$)+.@s;

    // Nobody is on map: Be TRULY random
    if (.@val < 10) {
        .@val=rand(20,100);
    }

    // Switch an adequate boss
    if (.@val < 20) {
        .@mobId=MonsterSergeant;
        .@ts$="Sergeant";
    } else if (.@val < 40) {
        .@mobId=MonsterLieutenant;
        .@ts$="Lieutenant";
    } else if (.@val < 60) {
        .@mobId=MonsterCaptain;
        .@ts$="Captain";
    } else if (.@val < 80) {
        .@mobId=MonsterColonel;
        .@ts$="Colonel";
    } else {
        .@mobId=MonsterGeneral;
        .@ts$="General";
    }

    // We want spawn point to be fixed
    .@lx=array_find($@LOCMASTER_MAP$, .@m$);
    .@xm=$@LOCMASTER_X[.@lx];
    .@ym=$@LOCMASTER_Y[.@lx];

    .@xm=.@xm+rand(-1,1);
    .@ym=.@ym+rand(-1,1);

    // Announce and spawn
    .@mGID=monster(.@m$, .@xm, .@ym, strmobinfo(1, .@mobId), .@mobId, 1, "#SiegeCtrl::On"+.@ts$+"Death");

    // Boost the boss stats based on difficulty and nº of players online
    .@bhp=getunitdata(.@mGID, UDT_MAXHP);
    .@bat=getunitdata(.@mGID, UDT_ATKMAX);
    .@bdf=getunitdata(.@mGID, UDT_DEF);
    .@bcr=getunitdata(.@mGID, UDT_CRIT);

    .@s+=getusers(1);

    setunitdata(.@mg, UDT_MAXHP,    .@bhp+(.@s*45));
    setunitdata(.@mg, UDT_HP,       .@bhp+(.@s*45));

    setunitdata(.@mg, UDT_ATKMAX,   .@bat+(.@s*5));
    setunitdata(.@mg, UDT_DEF,      .@bdf+(.@s*4));
    setunitdata(.@mg, UDT_CRIT,     .@bcr+(.@s*3));

    // Spawn some scouts
    areamonster(.@m$, .@xm-1, .@ym-1, .@xm+1, .@ym+1, "Scout", any(GreenSlime,RedSlime,AngryYellowSlime), 2);
    areamonster(.@m$, .@xm-1, .@ym-1, .@xm+1, .@ym+1, "Scout", any(GreenSlime,RedSlime,AngryYellowSlime), 2);
    areamonster(.@m$, .@xm-1, .@ym-1, .@xm+1, .@ym+1, "Scout", any(GreenSlime,RedSlime,AngryYellowSlime), 2);

    announce("##1The Monster "+.@ts$+" arrived! Watch out!", bc_all);
    return;
}

// Spawn some monsters
// siege_cast ( map, NPCName, {, difficulty{, tpflag}} )
function	script	siege_cast	{
    // mz - map ; n - name ; d - difficulty ; tp - teleport
    // a - ammount ; e - mobId
    .@mz$=getarg(0);
    .@n$=getarg(1);
    .@d=getarg(2,0);
    .@tp=getarg(3,0);

    siege_selectmob(siege_calcdiff(.@mz$), .@d, .@tp);

    // How many monsters? This value is multiplied by 3;
    // 1 per player (= 3 mobs), 1 each 5 difficulty, 1 always present.
    .@a=getmapusers(.@mz$);
    .@a=(.@d/5)+1;

    .@e=any_of($@SIEGE_TMPMOBS);
    array_remove($@SIEGE_TMPMOBS, .@e);
    siege_spawn(.@mz$, .@e, .@a, "#SiegeCtrl::OnRespawn");

    .@e=any_of($@SIEGE_TMPMOBS);
    array_remove($@SIEGE_TMPMOBS, .@e);
    siege_spawn(.@mz$, .@e, .@a, "#SiegeCtrl::OnRespawn");

    .@e=any_of($@SIEGE_TMPMOBS);
    array_remove($@SIEGE_TMPMOBS, .@e);
    siege_spawn(.@mz$, .@e, .@a, "#SiegeCtrl::OnRespawn");
    return;
}


////////////////////////////////////////////
// Utility Function
// do_siege ( town, outskirts, varcode, flag, npc, timer )
function	script	do_siege	{
    .@m$=getarg(0);
    .@o$=getarg(1);
    .@c$=getarg(2);
    .@tp=getarg(3);
    .@n$=getarg(4);
    .@t=getarg(5);

    switch (.@t) {
    // Warmup
    case 70:
    case 95:
        siege_cast(.@m$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
    case 35:
        siege_cast(.@o$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
        break;
    // Setup and casts
    case 0:
        siege_setup(.@o$);
        siege_cast("014-3", .name$, 0, TP_HURNS);
        break;
    case 60:
        siege_setup(.@m$);
        siege_cast(.@o$, .@n$, 0, .@tp);
        mapannounce(.@m$, "##2Message to all NPCs in town: Take shelter!", bc_map);
        break;
    case 90:
        siege_cast(.@m$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
        siege_cast(.@o$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
        break;
    // Boss stage
    case 280:
        siege_boss(.@m$, getd("$@SIEGE_"+.@c$));
        siege_cast(.@m$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
        break;
    // Difficulty Raisers
    case 310:
    case 520:
    case 640:
        .@varsig=getd("$@SIEGE_"+.@c$);
        setd("$@SIEGE_"+.@c$, .@varsig+1);
    // Regular flow
    case 195:
    case 220:
    case 265:
    //case 280: BOSS WAVE
    //case 310: difficulty raiser
    case 355:
    case 400:
    case 445:
    case 490:
    //case 520: difficulty raiser
    case 535:
    case 580:
    case 625:
    //case 640: difficulty raiser
    case 670:
        siege_cast(.@m$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
        break;
    // Ending flow
    case 700:
        mapannounce("012-1", "##1The Monster Army is preparing the retreat in about one minute!", bc_map);
        siege_cast(.@m$, .@n$, getd("$@SIEGE_"+.@c$), .@tp);
        break;
    case 760:
        mapannounce("012-1", "##1The Monster Army is withdrawing within 30 seconds!", bc_map);
        $@MK_SCENE=MK_NONE;
        $@MK_AGGRO=$@MK_AGGRO/20;
        break;
    // Check db/constants.conf to change this value!
    case MK_SIEGE_DURATION:
        siege_check(.@m$);
        siege_revert(.@m$);
        siege_revert(.@o$);
        enablenpc("Mana Stone");
        setd("$@SIEGE_"+.@c$, 0);
        break;
    }

    return;
}

// Utility NPC
-	script	#SiegeCtrl	NPC_HIDDEN,{
    end;

OnRespawn:
    if (playerattached()) {
        getmapxy(.@m$,.@x,.@y,0);
        if (rand(10000) <= $coinsrate)
            makeitem StrangeCoin, 1, .@m$, .@x, .@y;
    }
    end;

// Boss Death Labels
OnSergeantDeath:
    $MK_TEMPVAR+=1;
    getitem StrangeCoin, rand(1,5);
    announce("##2The Monster Sergeant was defeated by "+strcharinfo(0)+"!", bc_all);
    end;

OnLieutenantDeath:
    $MK_TEMPVAR+=3;
    getitem StrangeCoin, rand(5,10);
    announce("##2The Monster Lieutenant was defeated by "+strcharinfo(0)+"!", bc_all);
    end;

OnCaptainDeath:
    $MK_TEMPVAR+=5;
    getitem StrangeCoin, rand(10,15);
    announce("##2The Monster Captain was defeated by "+strcharinfo(0)+"!", bc_all);
    end;

OnColonelDeath:
    $MK_TEMPVAR+=7;
    getitem StrangeCoin, rand(15,20);
    $MOST_HEROIC$=strcharinfo(0);
    announce("##2The Monster Colonel was defeated by "+strcharinfo(0)+"!", bc_all);
    end;

OnGeneralDeath:
    $MK_TEMPVAR+=9;
    getitem StrangeCoin, rand(20,25);
    $MOST_HEROIC$=strcharinfo(0);
    announce("##2The Monster General was defeated by "+strcharinfo(0)+"!", bc_all);
    end;

}