// Fluffy hunting quest
// initially by Jenalya
// more by alastrim
// broken by o11c, then fixed
// Variables:
// global $@Fluffy_Hunting - state of the quest
// 0: nobody is hunting
// 1: somebody is hunting. The script checks every second if there is food on the ground.
// 2: not used anymore, was: food dropped but no spawn yet
// 3: not used anymore, was: monsters have been spawned
// global $@Fluffy_Time - the number of seconds since you entered the area
// global $@Fluffy_Spawn - how many fluffies have been spawned
// global $@Fluffy_Kills - how many fluffies you have killed so far
// global $@Fluffy_Min - how many fluffies you have to kill to get a reward
// global $@Fluffy_Alive - how many fluffies are currently alive
// global $@Fluffy_PC_Deaths - used to keep track of whether the fluffies kill you
// global $@Fluffy_Fighter$ - name of the person hunting fluffies (only used for ornamentation)
// global $@Fluffy_FighterID - ID of the person hunting fluffies
// nibble 0 of QUEST_Barbarians - state for this and adjacent NPCs
// 0: haven't talked about it
// 1: heard about it
// 2: finished quest
// 3: got reward
// Note: if you're going to reenable the "drop multiple times" feature
// 1: please do it every second, not just once you've killed everything - Done
// 2: you should change $@Fluffy_Spawn to $@Fluffy_Alive and decrement it on kills
// Kept $@Fluffy_Spawn and added $@Fluffy_Alive. The script spawns the amount related to what you drop at each specific time.
// 3: add a variable to spawn more fluffies if you drop more than 100 apples, as you kill them
// Also, the $@Fluffy_Extra behaviour should be reconsidered (hardly anything spawns ...)
// that's the only reason I didn't make that one a local variable like it really is
033-1.gat,74,32,0|script|Kimarr|218,{
if ($@Fluffy_FighterID == getcharid(3))
goto L_Attention;
set @Q_Barbarians_MASK, NIBBLE_0_MASK;
set @Q_Barbarians_SHIFT, NIBBLE_0_SHIFT;
set @state, ((QUEST_Barbarians & @Q_Barbarians_MASK) >> @Q_Barbarians_SHIFT);
if (@state >= 3) goto L_Again;
if (@state == 2) goto L_Reward;
if (@state == 1) goto L_Ask;
mes "[Barbarian]";
mes "\"Greetings, little person.\"";
next;
mes "\"I am Kimarr, hunter and warrior of the Mangarr.\"";
next;
mes "\"You are very small, better be careful in this snowy mountains. It is cold and dangerous monsters are here.\"";
menu
"Hello, my name is " + strcharinfo(0) + " and I'm not small. I'm a great warrior!",-,
"I'm " + strcharinfo(0) + ", don't underestimate me. I'm an experienced adventurer!" ,-,
"I'm the legendary " + strcharinfo(0) + ", I've fought countless battles.",-;
mes "Kimarr seems to be amused.";
mes "[Kimarr]";
mes "\"Really? Do you want to prove it?\"";
menu
"Sure! What shall I do?", -,
"No, I don't need to prove anything.", L_Close;
L_Explain:
mes "[Kimarr]";
mes "\"Every young member of our tribe proves his or her worth by hunting monsters.\"";
next;
mes "\"The first monsters to hunt are fluffies. Fluffies give a good meal for a young person and the fur can be used to make clothes and blankets.\"";
next;
set @state, 1;
callsub S_Update_Mask;
L_Explain_Game:
mes "[Kimarr]";
mes "\"In that cave there are living fluffies. They like to eat apples.\"";
next;
mes "\"I also saw one of them getting excited about one of that sweet things you call 'cake'.\"";
next;
mes "\"You should be careful, other monsters living here might like the food as well\"";
next;
mes "\"Go to the cave entrance and throw food on the floor to make them come out.\"";
next;
mes "\"Then hunt as many fluffies as you can until I tell you to stop.\"";
next;
mes "\"Drop more food when no fluffies are left.\"";
next;
// dialog starts here if you've asked about it but not done it (@state == 1)
L_Ask:
mes "[Kimarr]";
mes "\"So, are you going to try?\"";
menu
"Yeah, let's start!", L_Game,
"Could you explain again?", L_Explain_Game,
"Maybe later.", -;
goto L_Close;
L_AlreadyGotReward:
callsub S_Clean;
mes "Once again you prove you are worth as a hunter, " + strcharinfo(0) + ".";
goto L_Close;
L_Reward1:
if (@state >= 2)
goto L_AlreadyGotReward;
set @state, 2;
callsub S_Update_Mask;
callsub S_Clean;
// as far as I can tell, this fails because it won't resume from the "next"
// when the script is executed via the "OnFluffyDeath" callback
// (I haven't tried via the 301st call of OnTimer1000)
message strcharinfo(0), "Kimarr: talk to me for your reward";
goto L_Close;
// this label is reached on completion of the quest, or, if you inventory was
// full at the time, when you next initiate dialog (with @state == 2)
L_Reward:
mes "[Kimarr]";
mes "\"That was very impressive - now you can call yourself a hunter, " + strcharinfo(0) + ".\"";
next;
getinventorylist;
if (@inventorylist_count == 100)
goto L_Full_Inv;
set @inventorylist_count, 0;
mes "[Kimarr]";
mes "\"Take this as a symbol of your strength. You're a member of our tribe now.\"";
getitem "YetiSkinShirt", 1;
set @state, 3;
callsub S_Update_Mask;
goto L_Close;
L_Full_Inv:
mes "[Kimarr]";
mes "\"You can't carry the reward I want to give you.\"";
goto L_Close;
// dialog starts here after you've completed this quest
L_Again:
mes "[Kimarr]";
mes "\"Does the hunter " + strcharinfo(0) + " want to hunt some fluffies again?\"";
menu
"Yeah!", L_Game,
"Can you tell me who were the most successful fluffy hunters?", L_ShowRecord,
"Not now.", L_Close;
L_Game:
if ($@Fluffy_Hunting)
goto L_Someone_Else;
set $@Fluffy_Hunting, 1;
set $@Fluffy_Kills, 0;
set $@Fluffy_PC_Deaths, PC_DIE_COUNTER;
set $@Fluffy_Fighter$, strcharinfo(0);
set $@Fluffy_FighterID, getcharid(3);
set $@Fluffy_Time, 180;
//TODO: set minimum number of killed fluffies (maybe based on level?)
set $@Fluffy_Min, 2;
warp "033-1.gat", 79, 34;
initnpctimer;
goto L_Close;
L_Someone_Else:
mes "[Kimarr]";
mes "\"At the moment someone else is hunting. Let's wait until that hunting has ended.\"";
goto L_Close;
L_Attention:
message strcharinfo(0), "Kimarr: You should be focused on hunting fluffies, not talking.";
end;
OnTimer1000:
// Checking if player is logged
if (isloggedin($@Fluffy_FighterID) == 0)
goto L_GotOut;
attachrid($@Fluffy_FighterID);
// Checking if player is still in the map or used a towell or spell to get out
if (getareausers("033-1.gat", 79, 28, 88, 42) == 0)
goto L_GotOut;
if (PC_DIE_COUNTER > $@Fluffy_PC_Deaths)
goto L_Died;
// Checking if there is more than 1 player in the fight area
if (getareausers("033-1.gat", 79, 28, 88, 42) > 1)
areatimer "033-1.gat", 79, 28, 88, 42, 10, "Kimarr::OnTooMany";
if ($@Fluffy_Time == 180)
npctalk strcharinfo(0) + ", you have 3 minutes.";
if ($@Fluffy_Time == 120)
npctalk "You have 2 minutes left.";
if ($@Fluffy_Time == 60)
npctalk "You have 1 minute left.";
if ($@Fluffy_Time == 30)
npctalk "You have 30 seconds left.";
if ($@Fluffy_Time == 15)
npctalk "You have 15 seconds left.";
if ($@Fluffy_Time == 10)
npctalk "You have 10 seconds left.";
if ($@Fluffy_Time == 5)
npctalk "You have 5 seconds left.";
set $@Fluffy_Time, $@Fluffy_Time - 1;
if ($@Fluffy_Time < 0)
goto L_TimeOver;
goto L_CheckDrops;
L_ContinueTimer:
setnpctimer 0;
end;
L_GotOut:
// TODO: this is the only use of $@Fluffy_Fighter$, can we remove it?
npctalk "What a strange thing... " + $@Fluffy_Fighter$ + " just disappeared!";
callsub S_Clean;
end;
OnTooMany:
if (getcharid(3) == $@Fluffy_FighterID)
end;
npctalk "Hey " + strcharinfo(0) + "! What are you doing there? This hunt is for " + $@Fluffy_Fighter$ + " alone!";
warp "033-1.gat", 77, 34;
end;
L_Died:
warp "033-1.gat", 77, 34;
message strcharinfo(0), "You are dead.";
callsub S_Clean;
end;
L_TimeOver:
message strcharinfo(0), "Your time is over.";
goto L_MaybeRecordScore;
L_CheckDrops:
if ($@Fluffy_Alive >= 100)
goto L_TooManyFluffies;
set @Fluffy_Toomany, 0;
set @Fluffy_RedApple, getareadropitem("033-1.gat", 79, 29, 88, 42, "RedApple", 1);
set @Fluffy_XmasCake, getareadropitem("033-1.gat", 79, 29, 88, 42, "XmasCake", 1);
set @Fluffy_Cake, getareadropitem("033-1.gat", 79, 29, 88, 42, "Cake", 1);
set @Fluffy_GreenApple, getareadropitem("033-1.gat", 79, 29, 88, 42, "GreenApple", 1);
if (@Fluffy_RedApple || @Fluffy_XmasCake || @Fluffy_Cake || @Fluffy_GreenApple)
goto L_BeginHunting;
goto L_ContinueTimer;
L_TooManyFluffies:
if (@Fluffy_Toomany)
goto L_ContinueTimer;
message strcharinfo(0), "Wow, calm down, there are already too many fluffies around here.";
set @Fluffy_Toomany, 1;
goto L_ContinueTimer;
L_BeginHunting:
set $@Fluffy_Spawn, @Fluffy_RedApple + 5 * @Fluffy_XmasCake + 3 * @Fluffy_Cake + @Fluffy_GreenApple;
// limit the number of monsters that can be spawned, to prevent people creating lag with massive amount of monsters
if ($@Fluffy_Spawn > 100)
set $@Fluffy_Spawn, 100;
areamonster "033-1.gat", 79, 29, 88, 42, "", 1020, $@Fluffy_Spawn, "Kimarr::OnFluffyDeath";
// other monsters don't generate a real event
set $@Fluffy_Extra, 5 * @Fluffy_XmasCake + 3 * @Fluffy_Cake + 10 * BaseLevel;
if ((BaseLevel > 40) && (rand($@Fluffy_Extra) > 500)) // Ice Goblin
areamonster "033-1.gat", 79, 29, 88, 42, "", 1058, 1, "Kimarr::OnOtherDeath";
if ((BaseLevel > 60) && (rand($@Fluffy_Extra) > 550)) // Santa Slime
areamonster "033-1.gat", 79, 29, 88, 42, "", 1015, 1, "Kimarr::OnOtherDeath";
// TODO: add the wolvern as well when it has been added
if ((BaseLevel > 70) && (rand($@Fluffy_Extra) > 600)) // Yeti
areamonster "033-1.gat", 79, 29, 88, 42, "", 1072, 1, "Kimarr::OnOtherDeath";
set $@Fluffy_Extra, 0;
set @Fluffy_RedApple, 0;
set @Fluffy_XmasCake, 0;
set @Fluffy_Cake, 0;
set @Fluffy_GreenApple, 0;
set $@Fluffy_Alive, $@Fluffy_Alive + $@Fluffy_Spawn;
goto L_ContinueTimer;
OnOtherDeath:
// nothing happens
end;
OnFluffyDeath:
if ($@Fluffy_Hunting == 0)
end;
set $@Fluffy_Kills, $@Fluffy_Kills + 1;
set $@Fluffy_Alive, $@Fluffy_Alive - 1;
//TODO: remove announce after debugging
// message strcharinfo(0), "Fluffies alive: " + $@Fluffy_Alive;
// message strcharinfo(0), "Fluffies killed: " + $@Fluffy_Kills;
if ($@Fluffy_Alive != 0)
end;
if (attachrid($@Fluffy_FighterID))
goto L_Killedall;
goto L_GotOut;
end;
L_Killedall:
message strcharinfo(0), "Good job, but you still have time to throw more food on the ground.";
end;
S_Clean:
stopnpctimer;
set $@Fluffy_Hunting, 0;
set $@Fluffy_Time, 0;
set $@Fluffy_PC_Deaths, 0;
set $@Fluffy_Fighter$, "";
set $@Fluffy_FighterID, 0;
set $@Fluffy_Kills, 0;
set $@Fluffy_Spawn, 0;
set $@Fluffy_Alive, 0;
killmonster "033-1.gat", "Kimarr::OnOtherDeath";
killmonster "033-1.gat", "Kimarr::OnFluffyDeath";
return;
L_MaybeRecordScore:
warp "033-1.gat", 77, 34;
if ($@Fluffy_Kills < $@Fluffy_Min)
goto L_NotGoodEnough;
set @rank, 0;
L_MaybeInsertNext:
if ($@Fluffy_Kills > $Record_Fluffy_Kills[@rank])
goto L_InsertScore;
// you already had a better score
if (strcharinfo(0) == $Record_Fluffy_Name$[@rank])
goto L_Reward1;
set @rank, @rank + 1;
if (@rank == MAX_HIGH_SCORES)
goto L_Reward1;
goto L_MaybeInsertNext;
L_InsertScore:
set @loop, @rank;
L_FindLastScore:
// comment this out to allow the player to be in the list more than once
// though actually, it might be better just to assume the list is full
if (strcharinfo(0) == $Record_Fluffy_Name$[@loop])
goto L_MoveStuff;
set @loop, @loop + 1;
if (@loop == MAX_HIGH_SCORES)
goto L_MoveStuff;
goto L_FindLastScore;
L_MoveStuff:
if (@loop == @rank)
goto L_FinallyInsertMe;
set $Record_Fluffy_Kills[@loop], $Record_Fluffy_Kills[@loop - 1];
set $Record_Fluffy_Name$[@loop], $Record_Fluffy_Name$[@loop - 1];
set $Record_Fluffy_Date$[@loop], $Record_Fluffy_Date$[@loop - 1];
set @loop, @loop - 1;
goto L_MoveStuff;
L_FinallyInsertMe:
set $Record_Fluffy_Kills[@rank], $@Fluffy_Kills;
set $Record_Fluffy_Name$[@rank], strcharinfo(0);
callfunc "time_stamp";
set $Record_Fluffy_Date$[@rank], @ts_date$ + " " + @ts_time$;
set @ts_date$, "";
set @ts_time$, "";
goto L_Reward1;
L_NotGoodEnough:
callsub S_Clean;
L_ShowRecord:
set @rank, 0;
set @loop, 0;
L_ShowNextRecord:
if ($Record_Fluffy_Kills[@loop] == 0)
goto L_Close;
mes (@loop + 1) + " - " + $Record_Fluffy_Name$[@loop] + " - " + $Record_Fluffy_Kills[@loop] + " fluffies killed at " + $Record_Fluffy_Date$[@loop];
set @loop, @loop + 1;
goto L_ShowNextRecord;
L_Close:
// clear all temporary player variables that are not otherwise cleared
// it is not feasible to otherwise clear @loop
// but, not all jumpers to L_Close have necessarily used it ...
// still, I think it's a good precent to ALWAYS exit via L_Close
set @loop, 0;
// if you unset @state, @Q_Barbarians_MASK or @Q_Barbarians_SHIFT, it might break the script
// If only we had the concept of "local constants" ...
close;
S_Update_Mask:
set QUEST_Barbarians,
(QUEST_Barbarians & ~(@Q_Barbarians_MASK))
| (@state << @Q_Barbarians_SHIFT);
return;
}