diff --git a/kod/include/blakston.khd b/kod/include/blakston.khd index 380986d482..3a6cbd91a4 100644 --- a/kod/include/blakston.khd +++ b/kod/include/blakston.khd @@ -825,6 +825,16 @@ RID_DESERTRIVER1 = 1510 RID_DESERTRIVER2 = 1511 RID_DESERTBRIDGE = 1512 + RID_DESERTCAVERAFT = 1513 + RID_ELEMENTAL_DUNGEON1 = 1520 + RID_ELEMENTAL_DUNGEON2 = 1521 + RID_ELEMENTAL_DUNGEON3 = 1522 + RID_DESERTSANDBAR = 1523 + RID_DESERTALDUNES = 1524 + RID_DESERTALDUNES2E = 1525 + RID_DESERTALDUNES3E = 1526 + RID_DESERTALDUNES4E = 1527 + RID_DESERT_END = 1550 % Settlement and Island 2000-2499 % Needs to be seperate so it counts as a different 'zone' @@ -2215,6 +2225,13 @@ SID_MEDITATE = 187 SID_UNHOLY_TOUCH = 188 + SID_ACID_STORM_BUFF = 220 + SID_SHOCK_STORM_BUFF = 221 + SID_HEAT_STORM_BUFF = 222 + SID_COLD_STORM_BUFF = 223 + + SID_GREY_GHOST = 300 + % Depreciated spell IDs SID_LIGHTNING = 1 % use SID_LIGHTNING_BOLT SID_HEAL = 2 % use SID_MINOR_HEAL @@ -2291,6 +2308,12 @@ SS_RIIJA = 5 SS_JALA = 6 SS_DM_COMMAND = 7 + SS_WITCHERY = 8 + + % Subschools + SS_WITCHERY_XAERDUN = 1 + SS_WITCHERY_NEQUZON = 2 + SS_WITCHERY_IWODACH = 3 % We may use skill an spell schools in the same routines, thus no overlap. SKS_FENCING = 10 diff --git a/kod/object.kod b/kod/object.kod index 52ad01f182..d5be7a9bb0 100644 --- a/kod/object.kod +++ b/kod/object.kod @@ -1011,6 +1011,18 @@ messages: return viObject_flags | piDrawEffectFlag; } + RemoveDrawingEffects(value=0) + { + piDrawfx = piDrawfx & ~value; + return; + } + + AddDrawingEffects(value=0) + { + piDrawfx = piDrawfx | value; + return; + } + GetDrawingEffects() "This returns any special drawing effect the object has. Default is " "no drawing effect." @@ -1316,6 +1328,36 @@ messages: { return plObject_attributes; } + + HasObjectAttribute(cClass=&ShockStorm) + { + local i; + + foreach i in plObject_attributes + { + if IsClass(i,cClass) + { + return TRUE; + } + } + + return FALSE; + } + + GetObjectAttribute(cClass=&ShockStorm) + { + local i; + + foreach i in plObject_attributes + { + if IsClass(i,cClass) + { + return i; + } + } + + return $; + } end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/nomoveon/battler/monster.kod b/kod/object/active/holder/nomoveon/battler/monster.kod index d743f3cbf1..148d1e9ee5 100644 --- a/kod/object/active/holder/nomoveon/battler/monster.kod +++ b/kod/object/active/holder/nomoveon/battler/monster.kod @@ -354,6 +354,7 @@ classvars: vrTeach_quest_needed = monster_teach_quest_needed vrNothing_for_sale = Lm_nothing_for_sale vrMrcnt_too_costly = Lm_mrcnt_too_costly + vrMrcntCannotGive = Lm_mrcnt_cant_give vrMonster_healing = Lm_monster_healing vrMinion_trouble = minion_trouble @@ -4701,7 +4702,7 @@ messages: else { % Buyer can't accept the item for some reason. - Post(poOwner,@SomeoneSaid,#what=self,#string=Lm_mrcnt_cant_give, + Post(poOwner,@SomeoneSaid,#what=self,#string=vrMrcntCannotGive, #type=SAY_RESOURCE,#parm1=Send(oBuy,@GetDef), #parm2=Send(oBuy,@GetName)); Send(oBuy,@Delete); diff --git a/kod/object/active/holder/nomoveon/battler/monster/lostsoul.kod b/kod/object/active/holder/nomoveon/battler/monster/lostsoul.kod new file mode 100644 index 0000000000..3220571634 --- /dev/null +++ b/kod/object/active/holder/nomoveon/battler/monster/lostsoul.kod @@ -0,0 +1,1001 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LostSoul is Monster + +constants: + + include blakston.khd + + ANIM_CAST = 3 + + % Neutral action, our troops don't smile, etc. + NO_ACTION = 1 + + % What percent of the statues are soldiers? + SOLDIER_PERCENT = 20 + + % What percent of the time do we wake up when someone is near? + WAKEUP_CHANCE = 0 + + % How long are we dormant when someone tries to wake us up? + DORMANT_DURATION = 2 * 60 * 1000 % 2 minutes (in ms) + +resources: + + LostSoul_name_rsc = "lost soul" + LostSoul_active_name_rsc = "demented lost soul" + + LostSoul_desc_rsc = \ + "This is the spirit of some ancient %s.\n\n%r" + + LostSoul_dead_name_rsc = "dissipated lost soul" + + LostSoul_mason = "nomad" + LostSoul_farmer = "witch" + LostSoul_merchant = "merchant" + LostSoul_blacksmith = "sorcerer" + LostSoul_elder = "wanderer" + LostSoul_soldier = "bandit" + + LostSoul_damage = "~B~r%s%s is noticeably more damaged." + LostSoul_severe_damage = "~B~r%s%s is severely damaged." + + LostSoul_rose_drop = "My... beloved...!" + + LostSoul_icon_blank = blank.bgf + + LostSoul_male_icon_rsc = bta.bgf + LostSoul_female_icon_rsc = btb.bgf + + % a is male, b is female + LostSoul_legs_a_rsc = bfa.bgf + LostSoul_legs_b_rsc = bfb.bgf + + LostSoul_rightarm_a_rsc = bra.bgf + LostSoul_rightarm_b_rsc = brb.bgf + + LostSoul_leftarm_a_rsc = bla.bgf + LostSoul_leftarm_b_rsc = blb.bgf + + LostSoul_dead_male_icona_rsc = playerXa.bgf + LostSoul_dead_male_iconb_rsc = playerXb.bgf + LostSoul_dead_female_icona_rsc = heraXa.bgf + LostSoul_dead_female_iconb_rsc = heraXb.bgf + + LostSoul_head_male_icon = phax.bgf + LostSoul_head_female_icon = phkx.bgf + + % male + LostSoul_eyes_ax_icon = peax.bgf + LostSoul_eyes_bx_icon = pebx.bgf + LostSoul_eyes_cx_icon = pecx.bgf + LostSoul_eyes_dx_icon = pedx.bgf + + % female + LostSoul_eyes_kx_icon = pekx.bgf + LostSoul_eyes_lx_icon = pelx.bgf + LostSoul_eyes_mx_icon = pemx.bgf + + % male + LostSoul_mouth_ax_icon = pmax.bgf + LostSoul_mouth_bx_icon = pmbx.bgf + LostSoul_mouth_cx_icon = pmcx.bgf + + % female + LostSoul_mouth_kx_icon = pmkx.bgf + LostSoul_mouth_lx_icon = pmlx.bgf + LostSoul_mouth_mx_icon = pmmx.bgf + + % male + LostSoul_nose_ax_icon = pnax.bgf + LostSoul_nose_bx_icon = pnbx.bgf + LostSoul_nose_cx_icon = pncx.bgf + + % female + LostSoul_nose_kx_icon = pnkx.bgf + LostSoul_nose_lx_icon = pnlx.bgf + LostSoul_nose_mx_icon = pnmx.bgf + + % both + LostSoul_hair_cd_icon = ptcd.bgf + LostSoul_hair_bald_icon = blank.bgf + + % male + LostSoul_hair_ac_icon = ptac.bgf + LostSoul_hair_ba_icon = ptba.bgf + LostSoul_hair_ad_icon = ptad.bgf + LostSoul_hair_bb_icon = ptbb.bgf + LostSoul_hair_xa_icon = ptxa.bgf + + % female + LostSoul_hair_bc_icon = ptbc.bgf + LostSoul_hair_ca_icon = ptca.bgf + LostSoul_hair_db_icon = ptdb.bgf + LostSoul_hair_bd_icon = ptbd.bgf + LostSoul_hair_cb_icon = ptcb.bgf + LostSoul_hair_dc_icon = ptdc.bgf + LostSoul_hair_dr_icon = ptdr.bgf + LostSoul_hair_xb_icon = ptxb.bgf + + LostSoul_rightarm_gauntlet_male = bre.bgf + LostSoul_rightarm_gauntlet_female = brf.bgf + LostSoul_leftarm_gauntlet_male = ble.bgf + LostSoul_leftarm_gauntlet_female = blf.bgf + + LostSoul_sound_attack = xeoatk.wav + LostSoul_sound_aware = xeodie.wav + LostSoul_sound_death = xeo1awr.wav + +classvars: + + vrDesc = LostSoul_desc_rsc + vrDead_name = LostSoul_dead_name_rsc + + viTreasure_type = TID_STATUE + + viAttack_type = ATCK_WEAP_SLASH + + vrSound_hit = LostSoul_sound_attack + vrSound_miss = LostSoul_sound_attack + vrSound_aware = LostSoul_sound_aware + vrSound_death = LostSoul_sound_death + + viKarma = 0 + viDead_drawfx = DRAWFX_SECONDTRANS + + % percentage chance to be female + viChanceFemale = 50 + + % Our normal speed when active + viNormalSpeed = SPEED_AVERAGE + viCashmin = 150 + viCashmax = 500 + +properties: + + % Put these as properties so they can be set + vrName = LostSoul_name_rsc + + vrIcon = LostSoul_male_icon_rsc + vrDead_Icon = LostSoul_dead_male_icona_rsc + vrDead_Legs = LostSoul_dead_male_iconb_rsc + + viLevel = 200 + viDifficulty = 5 + viVisionDistance = 3 + % Attack range of 192, or 3 row/col. + viAttackRange = 192 + viGender = GENDER_MALE + + viSpeed = SPEED_NONE + + piAnimation = ANIM_NONE + + prLegs = LostSoul_legs_a_rsc + prRight_arm = LostSoul_rightarm_a_rsc + prLeft_arm = LostSoul_leftarm_a_rsc + + prHead = LostSoul_head_male_icon + prEyes = LostSoul_eyes_ax_icon + prMouth = LostSoul_mouth_ax_icon + prNose = LostSoul_nose_ax_icon + prToupee = LostSoul_hair_ac_icon + + % Original data, for replacing when we heal. + prOriginalHead = LostSoul_head_male_icon + prOriginalEyes = LostSoul_eyes_ax_icon + prOriginalMouth = LostSoul_mouth_ax_icon + prOriginalNose = LostSoul_nose_ax_icon + prOriginalLeft_arm = LostSoul_leftarm_a_rsc + + plUsing = $ + plOverlays = $ + poHair_Remove = $ + + piDrawFX = DRAWFX_SECONDTRANS + + % Is this a soldier? + pbIsSoldier = FALSE + + % Do we have a shield? + pbHasShield = FALSE + + % What are we an image of? + prProfession = LostSoul_elder + + % How long should we remain dormant, unable to be awoken by players? + ptRemainDormant = $ + + % Used to lock soul into a dormant state + pbLockedDormant = FALSE + +messages: + + Constructor(bIsSoldier=FALSE) + { + local lProfessions; + + plOverlays = [ ]; + plUsing = [ ]; + plResistances = [ ]; + + pbIsSoldier = bIsSoldier; + + Send(self,@SetGender); + Send(self,@SetFace); + Send(self,@SetEquipment); + Send(self,@SetHair); + + if pbIsSoldier + { + prProfession = LostSoul_soldier; + + % Give us a little boost to our stats + viLevel = viLevel + 15; + viDifficulty = viDifficulty + 2; + + % This makes the soliders not have karma + pbDontDispose = TRUE; + } + else + { + lProfessions = [ LostSoul_mason, LostSoul_farmer, + LostSoul_merchant, LostSoul_blacksmith, + LostSoul_elder + ]; + + prProfession = Nth(lProfessions,Random(1,5)); + } + + propagate; + } + + Constructed() + { + % Set the original data here, for replacing when we heal from damage. + prOriginalHead = prHead; + prOriginalEyes = prEyes; + prOriginalMouth = prMouth; + prOriginalNose = prNose; + prOriginalLeft_arm = prLeft_arm; + + propagate; + } + + Delete() + { + local oUsedItem; + + if ptRemainDormant <> $ + { + DeleteTimer(ptRemainDormant); + ptRemainDormant = $; + } + + foreach oUsedItem in plUsing + { + Send(oUsedItem,@Delete); + } + + plUsing = $; + plOverlays = $; + + propagate; + } + + %%% Infrastructure + + ShowDesc() + { + % Does not show status information, because it has none. + AddPacket(4,vrDesc, 4,prProfession, 4,monster_nothing); + + return; + } + + HitPointThresholdDamage() + "Shows physical damage instead of a message." + { + local i, iThreshold, iRandomDamage, rDamageReport, each_obj; + + iThreshold = piHit_points / (piMax_hit_points * 20); + + % General damage report + rDamageReport = LostSoul_damage; + + if iThreshold <= 1 + { + if prHead = LostSoul_icon_blank + { + return; + } + + prHead = LostSoul_icon_blank; + rDamageReport = LostSoul_severe_damage; + } + else + { + iRandomDamage = Random(1,4); + + if iRandomDamage = 1 + { + if prEyes = LostSoul_icon_blank + { + return; + } + + prEyes = LostSoul_icon_blank; + } + else if iRandomDamage = 2 + { + if prMouth = LostSoul_icon_blank + { + return; + } + + prMouth = LostSoul_icon_blank; + } + else if iRandomDamage = 3 + { + if prNose = LostSoul_icon_blank + { + return; + } + + prNose = LostSoul_icon_blank; + } + else + { + if prLeft_arm = LostSoul_icon_blank + { + return; + } + + prLeft_arm = LostSoul_icon_blank; + pbHasShield = FALSE; + } + } + + Send(poOwner,@SomethingChanged,#what=self); + + foreach i in Send(poOwner,@GetHolderActive) + { + each_obj=Send(poOwner,@HolderExtractObject,#data=i); + if IsClass(each_obj,&User) + AND Send(each_obj,@GetKillTarget) = self + { + Post(each_obj,@MsgSendUser,#what=self,#message_rsc=rDamageReport, + #parm1=Send(self,@GetCapDef),#parm2=Send(self,@GetName)); + } + } + + return; + } + + HitPointThresholdHeal() + "Healed a health point, fix one of our broken parts." + { + if prHead <> prOriginalHead + { + prHead = prOriginalHead; + } + else if prLeft_arm <> prOriginalLeft_arm + { + prLeft_arm = prOriginalLeft_arm; + } + else if prEyes <> prOriginalEyes + { + prEyes = prOriginalEyes; + } + else if prMouth <> prOriginalMouth + { + prMouth = prOriginalMouth; + } + else if prNose <> prOriginalNose + { + prNose = prOriginalNose; + } + + if poOwner <> $ + { + Send(poOwner,@SomethingChanged,#what=self); + } + + return; + } + + SpellResist(oSpell=$,who=$,iSpellpower=$) + { + % Ignore sight-based spells and Seduce + if IsClass(oSpell,&Blind) + OR IsClass(oSpell,&Dazzle) + OR IsClass(oSpell,&Seduce) + { + return TRUE; + } + + propagate; + } + + SomethingMoved(what = $,new_row = $,new_col = $) + { + local iDelay; + + % Only care about moving stuff if we're not already moving + % Only care about non-immortal players we can see and are "close" + if viSpeed = SPEED_NONE + AND NOT pbLockedDormant + AND what <> $ + AND IsClass(what,&Player) + AND NOT (IsClass(what,&DM) AND Send(what,@PlayerIsImmortal)) + AND Send(poOwner,@LineOfSight,#obj1=self,#obj2=what) + AND Send(self,@CanSee,#what=what) + { + if Random(1,100) <= WAKEUP_CHANCE + { + % Set our intruder as our target if we don't have another one. + % 25 hatred is pretty low. + if Send(self,@GetHatred) <= 25 + { + Send(self,@TargetSwitch,#what=what,#iHatred=25); + } + + % Someone walked close enough! Spring to life! + Send(self,@WakeUp); + } + else + { + % Don't wake up yet. Rest a bit longer. + pbLockedDormant = TRUE; + + iDelay = DORMANT_DURATION; + % Created by an admin? Probably an invasion. Don't sleep as long. + if pbDontDispose + { + iDelay = iDelay / 5; + } + + ptRemainDormant = CreateTimer(self,@EndDormantTimer,iDelay); + } + } + + propagate; + } + + EndDormantTimer() + { + ptRemainDormant = $; + pbLockedDormant = FALSE; + + return; + } + + TweakBehavior() + "Our default behavior it not be noticable as a monster." + { + if viSpeed = SPEED_NONE + { + piBehavior = AI_NOMOVE | AI_NOFIGHT | AI_NPC; + } + + return; + } + + %%% Dealing with waking up and going dormant + + WakeUp() + { + % Make ourselves mobile! + viSpeed = viNormalSpeed; + + piBehavior = piBehavior & ~(AI_NOMOVE | AI_NOFIGHT | AI_NPC); + if poOwner <> $ + { + Send(poOwner,@SomethingChanged,#what=self); + Send(self,@EnterStateChase,#target=poTarget,#actnow=TRUE); + } + + % Now, fix our name! + vrName = LostSoul_active_name_rsc; + + return; + } + + %%% Defense messages + + GetParryAbility(stroke_obj=$) + { + if (stroke_obj <> $) AND NOT Send(stroke_obj,@CanParry) + { + return 0; + } + + return (viDifficulty*10)/2; + } + + GetBlockAbility(stroke_obj=$) + { + if ((stroke_obj <> $) AND NOT Send(stroke_obj,@CanBlock)) + OR NOT pbHasShield + { + return 0; + } + + return (viDifficulty*10); + } + + %%% Graphics setup + + SetGender() + { + if random(1,100) < viChanceFemale + { + viGender = GENDER_FEMALE; + vrIcon = LostSoul_female_icon_rsc; + prHead = LostSoul_head_female_icon; + prEyes = LostSoul_eyes_kx_icon; + prMouth = LostSoul_mouth_kx_icon; + prNose = LostSoul_nose_kx_icon; + prToupee = LostSoul_hair_ca_icon; + prLegs = LostSoul_legs_b_rsc; + prRight_arm = LostSoul_rightarm_b_rsc; + prLeft_arm = LostSoul_leftarm_b_rsc; + vrDead_Icon = LostSoul_dead_female_icona_rsc; + vrDead_Legs = LostSoul_dead_female_iconb_rsc; + } + + return; + } + + SetHair() + { + local RandomNumber,Translation,HelmetHair; + + % Sets the hairdo, color. Has a chance of putting a helmet on instead. + + % Helmet? + if poHair_Remove <> $ + { + return; + } + + % Select a hairdo + if viGender = GENDER_MALE + { + RandomNumber = random(0,6); + % 0 keeps the "default" + if RandomNumber = 1 + { + prToupee = LostSoul_hair_cd_icon; + } + else if RandomNumber = 2 + { + prToupee = LostSoul_hair_bald_icon; + } + else if RandomNumber = 3 + { + prToupee = LostSoul_hair_ba_icon; + } + else if RandomNumber = 4 + { + prToupee = LostSoul_hair_ad_icon; + } + else if RandomNumber = 5 + { + prToupee = LostSoul_hair_bb_icon; + } + else + { + prToupee = LostSoul_hair_xa_icon; + } + } + else + { + % female + RandomNumber = random(0,8); + % 0 keeps the "default" + if RandomNumber = 1 + { + prToupee = LostSoul_hair_cd_icon; + } + else if RandomNumber = 2 + { + prToupee = LostSoul_hair_bc_icon; + } + else if RandomNumber = 3 + { + prToupee = LostSoul_hair_db_icon; + } + else if RandomNumber = 4 + { + prToupee = LostSoul_hair_bd_icon; + } + else if RandomNumber = 5 + { + prToupee = LostSoul_hair_cb_icon; + } + else if RandomNumber = 6 + { + prToupee = LostSoul_hair_dc_icon; + } + else if RandomNumber = 7 + { + prToupee = LostSoul_hair_dr_icon; + } + else + { + prToupee = LostSoul_hair_xb_icon; + } + } + + return; + } + + SetFace() + % Randomly sets (gender-appropriate) face elements. + { + local RandomNumber; + % NOTE: 0 always keeps the "default" (a) icon. + + if viGender = GENDER_MALE + { + RandomNumber = random(0,3); + if RandomNumber = 1 + { + prEyes = LostSoul_eyes_bx_icon; + } + else if RandomNumber = 2 + { + prEyes = LostSoul_eyes_cx_icon; + } + else + { + prEyes = LostSoul_eyes_dx_icon; + } + + RandomNumber = random(0,2); + if RandomNumber = 1 + { + prNose = LostSoul_nose_bx_icon; + } + else + { + prNose = LostSoul_nose_cx_icon; + } + + RandomNumber = random(0,2); + if RandomNumber = 1 + { + prMouth = LostSoul_mouth_bx_icon; + } + else + { + prMouth = LostSoul_mouth_cx_icon; + } + } + else + { + % female + + RandomNumber = random(0,2); + if RandomNumber = 1 + { + prEyes = LostSoul_eyes_lx_icon; + } + else + { + prEyes = LostSoul_eyes_mx_icon; + } + + RandomNumber = random(0,2); + if RandomNumber = 1 + { + prNose = LostSoul_nose_lx_icon; + } + else + { + prNose = LostSoul_nose_mx_icon; + } + + RandomNumber = random(0,2); + if RandomNumber = 1 + { + prMouth = LostSoul_mouth_lx_icon; + } + else + { + prMouth = LostSoul_mouth_mx_icon; + } + } + + return; + } + + SetEquipment() + { + + return; + } + + MonsterAttack(what = $) + { + % add in any attack animation calls + Send(self,@DoSlash); + + return; + } + + DoSlash() + { + piAnimation = PANM_WEAPON_ATTACK; + Send(poOwner,@SomethingChanged,#what=self); + piAnimation = ANIM_NONE; + + return; + } + + SendMoveAnimation() + { + % use standing torso + AddPacket(1,ANIMATE_NONE, 2,1); + + return; + } + + SendAnimation() + { + % All new data + if piAnimation = PANM_WEAPON_ATTACK + { + AddPacket(1,ANIMATE_ONCE, 4,300, 2,2, 2,4, 2,1); + + return; + } + + propagate; + } + + SendMoveOverlays() + { + local i, iOverlays, hotspot, iLeft_group, iRight_group; + + % Player has 7 standard overlays: right arm, left arm, legs, head, + % eyes, mouth, nose + iOverlays = 7; + + if poHair_remove = $ + { + iOverlays = iOverlays + 1; + } + + iLeft_group = 1; + iRight_group = 1; + + foreach i in plOverlays + { + % Bows can have more than 1. + iOverlays = iOverlays + Send(i,@GetNumberOverlays); + hotspot = Send(i,@GetOverlayHotspot); + if hotspot = HS_RIGHT_WEAPON + { + iRight_group = 17; + } + else if hotspot = HS_LEFT_WEAPON or hotspot = HS_BOTTOM_BOW + { + % it's really a shield, not weapon + iLeft_group = 7; + } + } + + AddPacket(1,iOverlays); + AddPacket(4,prLeft_arm, 1,HS_LEFT_HAND); + + if iLeft_group = 1 + { + AddPacket(1,ANIMATE_CYCLE, 4,200, 2,2, 2,3); + } + else + { + AddPacket(1,ANIMATE_NONE, 2,iLeft_group); + } + + AddPacket(4,prRight_arm, 1,HS_RIGHT_HAND); + + if iRight_group = 1 + { + Addpacket(1,ANIMATE_CYCLE, 4,200, 2,2, 2,3); + } + else + { + AddPacket(1,ANIMATE_NONE, 2,iRight_group); + } + + AddPacket(4,prLegs, 1,HS_LEGS, 1,ANIMATE_CYCLE, 4,100, 2,2, 2,5); + + AddPacket(4,prHead, 1,HS_HEAD, 1,ANIMATE_NONE, 2,1); + + AddPacket(4,prMouth, 1,HS_MOUTH, 1,ANIMATE_NONE, 2,NO_ACTION); + AddPacket(4,prEyes, 1,HS_EYES, 1,ANIMATE_NONE, 2,NO_ACTION); + AddPacket(4,prNose, 1,HS_NOSE, 1,ANIMATE_NONE, 2,1); + + if poHair_remove = $ + { + AddPacket(4,prToupee, 1,HS_TOUPEE); + AddPacket(1,ANIMATE_NONE, 2,1); + } + + foreach i in plOverlays + { + Send(i,@SendOverlayInformation,#iAnimation=$); + } + + return; + } + + SendOverlays() + { + local i, iOverlays, hotspot, iLeft_group, iRight_group; + + % Send overlay bitmap info to user. + + % Player has 7 standard overlays: right arm, left arm, legs, head, + % eyes, mouth, nose + iOverlays = 7; + + if poHair_remove = $ + { + iOverlays = iOverlays + 1; + } + + % Overlay info -- display correct group for animation & action + + iLeft_group = 1; + iRight_group = 1; + + foreach i in plOverlays + { + % Bows can have more than 1. + iOverlays = iOverlays + Send(i,@GetNumberOverlays, + #iAnimation=piAnimation); + hotspot = Send(i,@GetOverlayHotspot); + if hotspot = HS_RIGHT_WEAPON + { + iRight_group = 17; + } + else if hotspot = HS_LEFT_WEAPON or hotspot = HS_BOTTOM_BOW + { + % it's really a shield, not weapon + iLeft_group = 7; + } + } + + AddPacket(1, iOverlays); + + AddPacket(4,prLeft_arm,1,HS_LEFT_HAND); + + if piAnimation = ANIM_CAST + { + AddPacket(1,ANIMATE_ONCE, 4,300, 2,8, 2,8, 2,iLeft_group); + } + else if piAnimation = PANM_WEAPON_ATTACK + { + AddPacket(1,ANIMATE_ONCE, 4,300, 2,4, 2,6, 2,iLeft_group); + } + else + { + AddPacket(1,ANIMATE_NONE, 2,iLeft_group); + } + + AddPacket(4,prRight_arm,1,HS_RIGHT_HAND); + + if piAnimation = ANIM_NONE + { + AddPacket(1,ANIMATE_NONE, 2,iRight_group); + } + else if piAnimation = ANIM_CAST + { + AddPacket(1,ANIMATE_ONCE, 4,300, 2,11, 2,11, 2,iRight_group); + } + else if piAnimation = PANM_WEAPON_ATTACK + { + AddPacket(1,ANIMATE_ONCE, 4,300, 2,4, 2,6, 2,17); + } + + AddPacket(4,prLegs,1,HS_LEGS); + + if piAnimation = PANM_WEAPON_ATTACK + { + AddPacket(1,ANIMATE_ONCE, 4,900, 2,6, 2,6, 2,1); + } + else + { + AddPacket(1,ANIMATE_NONE, 2,1); + } + + AddPacket(4,prHead, 1,HS_HEAD, 1,ANIMATE_NONE, 2,1); + AddPacket(4,prMouth, 1,HS_MOUTH, 1,ANIMATE_NONE, 2,NO_ACTION); + AddPacket(4,prEyes, 1,HS_EYES, 1,ANIMATE_NONE, 2,NO_ACTION); + AddPacket(4,prNose, 1,HS_NOSE, 1,ANIMATE_NONE, 2,1); + + if poHair_remove = $ + { + AddPacket(4,prToupee, 1,HS_TOUPEE); + AddPacket(1,ANIMATE_NONE, 2,1); + } + + foreach i in plOverlays + { + Send(i,@SendOverlayInformation,#iAnimation=piAnimation); + } + + return; + } + + SetStatueArmor(NewArmor = $) + { + vrIcon = Send(NewArmor,@GetShirtIcon,#who=self); + plUsing = cons(NewArmor, plUsing); + + if poOwner <> $ + { + Send(poOwner,@SomethingChanged,#what=self); + } + + return; + } + + AddEquipmentObject(what = $) + "Adds Overlay to list if not duplicated." + { + local i; + + foreach i in plOverlays + { + if i = what + { + return; + } + } + + plUsing = Cons(what,plUsing); + plOverlays = Cons(what,plOverlays); + + if poOwner <> $ + { + Send(poOwner,@SomethingChanged,#what=self); + } + + return; + } + + % Override from monster superclass + CreateDeadBody(killer=$) + { + return Create(&DeadBody, + #victim=self, + #killer=killer, + #PlayerBodyOverlay=vrDead_Legs, + #DrawFX=viDead_drawfx); + } + + CreateTreasure(who=$,corpse=$) + { + + propagate; + } + + CanMorphTo() + { + return FALSE; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/nomoveon/battler/monster/makefile b/kod/object/active/holder/nomoveon/battler/monster/makefile index 844b015693..d3ead605ac 100644 --- a/kod/object/active/holder/nomoveon/battler/monster/makefile +++ b/kod/object/active/holder/nomoveon/battler/monster/makefile @@ -17,7 +17,8 @@ BOFS = ant.bof centip.bof cow.bof ent.bof fairy.bof \ stntroll.bof snowrat.bof lich.bof deadlich.bof duskrat.bof thrasher.bof \ nrthlwrm.bof bramble.bof eviltwin.bof dethspid.bof grdworm.bof human.bof \ xeochctl.bof dflypet.bof dangel.bof mollusk.bof specmum.bof lvstatue.bof \ - evilent.bof elephant.bof bunny.bof wolf.bof chupacabra.bof minotaur.bof + evilent.bof elephant.bof bunny.bof wolf.bof chupacabra.bof minotaur.bof \ + lostsoul.bof diff --git a/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn.kod b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn.kod new file mode 100644 index 0000000000..9a93294124 --- /dev/null +++ b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn.kod @@ -0,0 +1,22 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertTown is Towns + +constants: + + include blakston.khd + +resources: + +messages: + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/makefile b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/makefile new file mode 100644 index 0000000000..b37ebebc34 --- /dev/null +++ b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/makefile @@ -0,0 +1,10 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\deserttwn.bof +BOFS = nomadwiseman.bof xaerdunafterlife.bof + +!include $(KODDIR)\kod.mak diff --git a/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/nomadwiseman.kod b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/nomadwiseman.kod new file mode 100644 index 0000000000..04d5d55594 --- /dev/null +++ b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/nomadwiseman.kod @@ -0,0 +1,31 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +NomadWiseMan is DesertTown + +constants: + + include blakston.khd + +resources: + + nomadwiseman_name_rsc = "Korath" + nomadwiseman_icon_rsc = mrelder.bgf + nomadwiseman_desc_rsc = \ + "This wise man has seen many years in the desert." + +classvars: + + vrName = nomadwiseman_name_rsc + vrIcon = nomadwiseman_icon_rsc + vrDesc = nomadwiseman_desc_rsc + viOccupation = MOB_ROLE_ELDER + + viAttributes = \ + MOB_NOFIGHT | MOB_RANDOM | MOB_LISTEN | MOB_NOMOVE \ + | MOB_SELLER | MOB_NOQUEST | MOB_RECEIVE + +properties: + +messages: + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/xaerdunafterlife.kod b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/xaerdunafterlife.kod new file mode 100644 index 0000000000..392fc0e179 --- /dev/null +++ b/kod/object/active/holder/nomoveon/battler/monster/towns/deserttwn/xaerdunafterlife.kod @@ -0,0 +1,170 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +XaerdunAfterlife is DesertTown + +constants: + + include blakston.khd + +resources: + + xaerdun_name_rsc = "Xaerdun, Demon of Greed" + xaerdun_icon_rsc = xaerdun.bgf + xaerdun_desc_rsc = \ + "Remarkably straightforward for a Demon, Xaerdun eschews all traditional" + " codes of demonic conduct in favor of profit-seeking. Here, Xaerdun " + "keeps some part of himself in this form of existence, lying in wait " + "for those unfortunate souls that have recently died, " + "for he offers a product that few hapless mortals can refuse. " + "He will no doubt assist you - for a cost." + + demise_greeting = \ + "~s~IIt seems you have met a terrible fate. I'm sure you'd like to " + "live again. Most do. Fortunately for you, I specialize in all things " + "the living desire. Perhaps we can make a deal. Approach me, " + "once-mortal." + + initial_recovery_offer = \ + "~s~II'm uncertain of the quality of this new currency you possess, " + "but gold is gold. Perhaps you would like to recover your items " + "for one million of said coins? I know you are in a bad situation, " + "so I shall employ my powers to withdraw them from " + "whichever banks hold your currency." + + initial_resurrection_offer = \ + "~s~IHow much do you value the chance to live again? Perhaps five " + "hundred thousand of your coins?" + + random_musing_msg1 = \ + "~s~II know of things in other places, though my power may not always " + "extend to them. I have seen the lavish desires of a great man in " + "your land, a true hero, one Bei Naq. Surely, you must look up to him?" + random_musing_msg2 = \ + "~s~II am surprised that your people tolerate one of our kind in such " + "a position of power. You likely hope that Bei Naq shall slay the " + "Demon among you, yes?" + random_musing_msg3 = \ + "~s~IOne of your kind, a man named Roq, has seen us on occasion." + random_musing_msg4 = \ + "~s~II am likely the only demon who cares to know of your ranks, " + "if only so that I may make more profitable alliances. You had a king, " + "once, who allied with forces far darker than we. Perhaps you could " + "set up a meeting between myself and those forces? No? That's too bad." + random_musing_msg5 = \ + "~s~IThe followers of the one you call Jala came here quite some time " + "ago. I despise them, for they refuse to make deals. High and mighty " + "ideals make for very poor and very dead folk, don't they know? " + "Their greatest heroine still wanders this barren unlife simply " + "to spite me!" + random_musing_msg6 = \ + "~s~II wonder who I would speak with to possibly purchase your fabled " + "Lich Queen of Brax..." + random_musing_msg7 = \ + "~s~IYou've quite an interesting face. Is it for sale?" + random_musing_msg8 = \ + "~s~IHow long are you going to hang around here, once-mortal? Don't " + "your kind have an insatiable desire to adventure? Just pay me " + "and move on already." + random_musing_msg9 = \ + "~s~II can't seem to phase my lower half any further into this place. " + "It's quite drafty down there." + random_musing_msg10 = \ + "~s~IThis place was once quite beautiful. But you wouldn't want to know " + "about~n ~o~Bthat~n ~s~Icataclysm. Simply understanding what happened " + "to this existence you call an afterlife would likely cause your " + "brain-flesh to spontaneously incinerate. We can't have that. You " + "wouldn't be able to make deals anymore." + + riija_response = \ + "~r~BDo not speak the name of that vile Trickster here!" + qor_response = \ + "~s~IEvil is ~Iso~I bourgeoisie." + shal_response = \ + "~s~IWhat profit is there in doing good for its own sake?" + kraanan_response = \ + "~s~IWar is profitable. I like war. Do you know anyone who can put me " + "in touch with this Kraanan entity?" + faren_response = \ + "~s~ICan his magics pull gold from the earth?" + jala_response = \ + "~s~IUgh." + sorcery_response = \ + "~s~II have allied with powerful sorcerers at times. That was long ago, " + "however. Do any practitioners still live? If not, I suppose you might " + "find them wandering in this place, if they have not lost their minds " + "over the ages." + banditry_response = \ + "~s~ICrude tactics, but effective. Bandits are some of my favorite " + "trading partners." + witchery_response = \ + "~s~IYou wanna join the fighting elite?" + + Witchery_forget_potion_sale = \ + "~s~IYou choose to sever our alliance? So be it, %s%s is yours if you " + "wish to buy one. Beware though, the powers of the amnesiac are " + "permanent.~n~k" + Xaerdun_witchery_teach = \ + "~s~IThrough alliance with me, you can learn %s%s%s%s%s%s%s%s%s%s%s%s " + "at that level." + Xaerdun_nothing_for_sale = \ + "~s~II've nothing minor to sell to once-mortals right now." + Xaerdun_too_costly = \ + "~s~IYou can't afford %s%s! No sale!" + Xaerdun_cannot_give = \ + "~s~II'm unable to give you %s%s. Perhaps you carry too much?" + +classvars: + + vrName = xaerdun_name_rsc + vrIcon = xaerdun_icon_rsc + vrDesc = xaerdun_desc_rsc + viOccupation = MOB_ROLE_ELDER + + vrTeach_message = Xaerdun_witchery_teach + vrNothing_for_sale = Xaerdun_nothing_for_sale + vrMrcnt_too_costly = Xaerdun_too_costly + vrMrcntCannotGive = Xaerdun_cannot_give + + viAttributes = \ + MOB_NOFIGHT | MOB_LISTEN | MOB_RANDOM | MOB_RECEIVE \ + | MOB_TEACHER | MOB_COND_SELLER | MOB_NOMOVE | MOB_PERM_QUESTER + +properties: + +messages: + + UserEntered(who=$) + { + Post(who,@SomeoneSaid,#what=self,#string=demise_greeting, + #type=SAY_RESOURCE); + + propagate; + } + + InitCondSale() + { + local oObj; + + oObj=Create(&ForgetPotionXaerdunWitchery); + Send(Send(SYS,@GetLibrary),@AddToMobLib,#mob=self, + #triggers=[Send(oObj,@GetName)], + #action=[LIBACT_CONDITIONAL,oObj,25000,Witchery_forget_potion_sale]); + + return; + } + + SetForSale() + { + % Xaerdun sells levels 1-3 of his spells here. + % Players will have to find him in reality for levels 4-6. + plFor_Sale = [ + $,$, + [ + SID_LIGHT + ], + $]; + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/nomoveon/battler/monster/towns/makefile b/kod/object/active/holder/nomoveon/battler/monster/towns/makefile index 11ffde8834..a8abf55db4 100644 --- a/kod/object/active/holder/nomoveon/battler/monster/towns/makefile +++ b/kod/object/active/holder/nomoveon/battler/monster/towns/makefile @@ -6,6 +6,7 @@ DEPEND = ..\towns.bof BOFS = barlqtwn.bof crnthtwn.bof jasprtwn.bof marntwn.bof tostwn.bof hazartwn.bof \ - kocatwn.bof wanderer.bof goad.bof decoratr.bof ladyphen.bof + kocatwn.bof wanderer.bof goad.bof decoratr.bof ladyphen.bof \ + deserttwn.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/active/holder/nomoveon/battler/player.kod b/kod/object/active/holder/nomoveon/battler/player.kod index e921c09aa2..75bfcf882c 100644 --- a/kod/object/active/holder/nomoveon/battler/player.kod +++ b/kod/object/active/holder/nomoveon/battler/player.kod @@ -1357,6 +1357,11 @@ messages: piFlags = piFlags | PFLAG_NO_MAGIC; } + foreach i in plObject_attributes + { + Send(i,@SetSpellPlayerFlag,#who=self); + } + foreach i in plEnchantments { Send(Nth(i,2),@SetSpellPlayerFlag,#who=self); @@ -1693,7 +1698,7 @@ messages: NewOwner(what = $) { - local iBasePhaseTime; + local iBasePhaseTime, i; Send(self,@ResetGainFlags); @@ -1744,6 +1749,14 @@ messages: DeleteTimer(ptRescue); ptRescue = $; } + + foreach i in plEnchantments + { + if IsClass(Nth(i,2),&RegionEffect) + { + Send(Nth(i,2),@NewOwner,#who=self,#where=what); + } + } return; } @@ -3457,8 +3470,10 @@ messages: foreach i in plEnchantments { % Phase reactivates enchantments while it is still in plEnchantments + % Spectate and Region Effects do not naturally wear off over time. if IsClass(Nth(i,2),&Phase) OR IsClass(Nth(i,2),&Spectate) + OR IsClass(Nth(i,2),&RegionEffect) { continue; } diff --git a/kod/object/active/holder/nomoveon/battler/player/user.kod b/kod/object/active/holder/nomoveon/battler/player/user.kod index e92dc5cb0e..2fca50e50d 100644 --- a/kod/object/active/holder/nomoveon/battler/player/user.kod +++ b/kod/object/active/holder/nomoveon/battler/player/user.kod @@ -2695,6 +2695,11 @@ messages: { Send(self,@TeleportTo,#RID=RID_NEWB1,#bAdminPort=FALSE); } + else if iRoom >= RID_DESERT AND iRoom <= RID_DESERT_END + { + Send(self,@TeleportTo,#RID=RID_DESERTALDUNES,#bAdminPort=FALSE); + Post(self,@ApplyDeathPenalties); + } else { oNew_room = Send(SYS,@FindRoomByNum,#num=RID_UNDERWORLD); @@ -2717,6 +2722,14 @@ messages: piSave_Row = Send(oRoom,@GetTeleportRow); piSave_Col = Send(oRoom,@GetTeleportCol); } + else if iRoom >= RID_DESERT AND iRoom <= RID_DESERT_END + { + piSave_Room = RID_DESERTALDUNES; + oRoom = Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES); + piSave_Row = Send(oRoom,@GetTeleportRow); + piSave_Col = Send(oRoom,@GetTeleportCol); + Post(self,@ApplyDeathPenalties); + } else { Debug("Player died while offline: ",Send(self,@GetTrueName)); @@ -7768,7 +7781,7 @@ messages: SomethingShot(who=$, target=$, projectile=$, flags=PROJ_FLAG_NONE, iLightIntensity=0, iLightColor=0) { - local iValue; + local iValue, iProjectileLightColor; % This happened in the past. Ignore this, because Sending nil in a % packet is bad. @@ -7776,6 +7789,16 @@ messages: { propagate; } + + if poOwner <> $ + AND Send(poOwner,@LightingColorOverride) + { + iProjectileLightColor = Send(poOwner,@GetLightingColorOverride); + } + else + { + iProjectileLightColor = Send(projectile,@GetProjectileLightColor); + } AddPacket(1,BP_SHOOT,4,Send(projectile,@GetProjectileIcon)); Send(projectile,@SendProjectileAnimation); @@ -7796,7 +7819,7 @@ messages: % Flags, Intensity, color AddPacket(2,iValue, 1,Send(projectile,@GetProjectileLightIntensity), - 2,Send(projectile,@GetProjectileLightColor)); + 2,iProjectileLightColor); } else { @@ -7822,6 +7845,12 @@ messages: AddPacket(1,BP_RADIUS_SHOOT,4,Send(projectile,@GetProjectileIcon)); Send(projectile,@SendProjectileAnimation); + + if poOwner <> $ + AND Send(poOwner,@LightingColorOverride) + { + iLightColor = Send(poOwner,@GetLightingColorOverride); + } % Send shooter. AddPacket(4,who,1,speed,2,(flags & 0x000F),1,range,1,number); diff --git a/kod/object/active/holder/room.kod b/kod/object/active/holder/room.kod index 09e3756187..11c80fa6a8 100644 --- a/kod/object/active/holder/room.kod +++ b/kod/object/active/holder/room.kod @@ -79,6 +79,7 @@ resources: creaky_door_sound = doropen1.wav water_wading_sound = splash.wav + snow_walk_sound = snowstep.wav room_jungle_sound1 = jungle01.wav room_jungle_sound2 = jungle02.wav @@ -1494,7 +1495,7 @@ messages: ReqSpellCast(who = $, oSpell = $, lItems = $) { - local oTarget; + local oTarget, i; if (piRoom_flags & ROOM_NO_MAGIC) { @@ -1517,6 +1518,14 @@ messages: return FALSE; } + + foreach i in plObject_attributes + { + if NOT Send(i,@ReqSpellCast,#who=who,#oSpell=oSpell,#lItems=lItems) + { + return FALSE; + } + } % Must have Line of Sight if spell is single target if (Send(oSpell,@GetNumSpellTargets) = 1 @@ -2164,6 +2173,12 @@ messages: % first send room icons Send(self,@SendRoomIcons,#what=what); + + % next send special room events + foreach i in plObject_attributes + { + Send(what,@ShowAddEnchantment,#what=i,#type=ENCHANTMENT_ROOM); + } foreach i in plEnchantments { diff --git a/kod/object/active/holder/room/desert.kod b/kod/object/active/holder/room/desert.kod index 1291d51aa8..0bb86bfe61 100644 --- a/kod/object/active/holder/room/desert.kod +++ b/kod/object/active/holder/room/desert.kod @@ -8,24 +8,454 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - DesertRoom is Room +% The desert is very active with elemental energies. +% Each phase of the day has either a helpful or harmful storm or threat that is +% based on which Elemental Prisms have been defeated. +% The Lightning Prism begins defeated, so DAWN phase always has &ShockStorm. +% Storms are helpful, Threats are harmful. +% Both storms and threats alter the weather and music. +% Therefore, the desert changes constantly throughout each day. +% +% Storms and Threats are RoomObjectAttributes that come complete with +% a room icon. They're not spells, though, so they can't be discorded or +% altered by players except through coded triggered actions. +% For example, defeating the Prism of Fire removes the Falling, Lava, and Phase +% threats from the Abyssal Bore. +% +% Other desert systems: +% +% If you log off anywhere in the desert, you're booted to Waylay Oasis, +% or (if you log off before you even reach it) all the way back to mainland. +% +% A large part of the desert is instanced. Monsters and objects do not respawn. +% Instead, players must continually move on to the next instanced area, +% where monsters and objects are spawned upon entrance. +% +% The instanced desert naturally separates players so that they end up alone. +% Once they're alone, they can begin running across each other, but only +% one to one. This ensures 1v1 situations (if one is pking) or temporary 2 +% person alliances against +% very dangerous mobs. As soon as you have your fight with mobs and move on, +% the two of you split up again. +% +% The instanced desert screens also have a variety of objects +% and things of interest. +% +% === +% Chests: nomad bands have a chest in their encampment. The nomad leader +% has a key on him. These chests contain substantial unique rewards +% you can't get elsewhere that reflect the tribe they come from. +% It's tough being alone (or paired) and taking on content. +% These chests are to give players incentive to actually take on these +% nomad bands - because the nomads work together, the fights will be +% very difficult. Ex. one purges you, one blinds you, two fire arrows +% while the last one heals and buffs his allies. The rewards will +% likely be gear imbues of specific types that are unavailable elsewhere. +% For example, +max mana might only be available from a witch-heavy +% tribe, and nowhere else in the game. +% Nomad bands will also be part of the reputation system, so that +% players can make enemies of them (aggro if you come near) +% or friends (they heal you, buff you, and even give you supplies). +% The three tribes do not like each other, though, so going up in rep +% with one tribe means losing rep with the other two. This lets players +% befriend the tribe they're least effective against, or the tribe +% whose types of buffs and help they most want. +% +% If the Witchery school is enabled, gaining reputation with a tribe +% will allow players to buy spells (the higher the rep, the higher +% the level, assuming they qualify in %s). +% +% The three tribes' primary demons and attributes are: +% - Xaerdun (merchants, some honest / some not, thieves, greedy types) +% - Nequzon (witches, witch-worshippers, naturalists) +% - Iwodach (battle warlocks, prideful, value strength and knowledge) +% +% === +% Ruins: enter one of several specific dungeons. These dungeons are +% not instanced, and you can run across other players regularly in them. +% These areas are heavy on lore, puzzles, and traps. Some of these +% ruins have quest objects. Some just have awesome loot. The ruin +% dungeons do not contain monsters (the puzzles are incredibly hard +% and often time-of-day based so that one might need to spend lots of +% time within and risk other players coming by). The traps can and will +% kill people, too, if they screw up badly enough. If the Sorcery +% school is enabled, some of its skills can be found in these dungeons, +% either in ancient tomes or by examining the tattoos on mummified +% remains. +% +% === +% Altars: the decayed remains of ancient temples. These altars have +% had all identifying marks worn away by time, but the magic within them +% has maintained their core stone forms. It's impossible to know whether +% the altar is for one of the region's deities (good effect) or for one +% of the demons the tribes still have pacts with (bad effect), but either +% way, using one will make something interesting and random happen to +% the player. Examples include total loss of health regeneration for ten +% minutes, double loot drops for five minutes, a random XP gain, etc. +% And one very rare result that players will always hope for. +% +% === +% Bandits: lone or paired human NPCs who are not part of the reputation +% system. These bandits will be rare, but dire. They'll be balanced +% difficult enough that players will actually run away and complain +% most of the time. Bandits are out there to KILL. However, the instanced +% areas will only have one or the pair at a time, and slaying them +% will make everything 'bandit free' for quite some time (72 hours). +% There are several different solo and paired bandit types with their +% own personalities. Most of the bandits do not attack outlaws and +% murderers, and might actually help them attack white-named players. +% Once a certain type of bandit is chosen for the area, it will never +% change unless someone slays them. (e.g. if you get a really dangerous +% pair, someone will have to take care of them, or they'll spawn at +% random for the indefinite future). Bandits themselves have scarce +% loot, but the rewards offered by the tribes are Very Nice. Bring +% the bandits' heads to collect. The posted rewards also serve as +% notice for players as to which bandits to expect. For example, +% if a reward is up for Kerrenor & Celegorm, you'll know you're +% going to run across a Qor & Shal bandit pair. +% +% Turning in bandit heads will give you +rep with all three tribes. +% Reaching max rep with all three tribes will cause them to address +% you by names of honor, and none will attack you unless you +% attack them and lose rep. This honored situation would take quite +% a bit of bandit slaying, though, and also take a long time +% (since they only regenerate every 72 hours). +% +% If the Banditry school is enabled, some of its higher-level skills +% can be learned from bandits if you run across them and are +% an outlaw or murderer yourself and qualify in %s. +% (That is not to say they're not available elsewhere - but those +% will be at much higher costs & possibly requiring quests. +% The idea is to encourage players to actually think about going +% outlaw temporarily and spending time in the desert in order +% to meet up with the proper teachers - and in so doing, probably +% run across other players in the meantime and inevitably clash. +% Living the bandit life!) +% +% === +% Oases: Non-combat areas where players can quickly heal and recover. +% Intended for breaks (you can't log off there, and you can't return +% at will, since the desert is instanced). +% +% === +% Monsters: The desert has very strong monsters that wield elemental +% magics and respond to player actions. Ex. if you try to use +% Explosive Frost on a fire monster, it'll cast Resist Cold on itself. +% + constants: include blakston.khd resources: + desert_music = desertheat.mp3 + desert_music_snow = desertsnow.mp3 + desert_music_acid_rain = desertrain.mp3 + desert_music_static_shock = desertdawn.mp3 + classvars: viTerrain_type = TERRAIN_BADLANDS properties: + vrWading_Sound = water_wading_sound + + pbSnowGroundTexture = TRUE + prMusic = desert_music + + piPreviousPhase = 0 messages: + RecalcLightAndWeather() + { + local SysDayPhase, i; + + SysDayPhase = Send(SYS,@GetDayPhase); + + % We're crossing over into a new quarter. + if piPreviousPhase <> SysDayPhase + { + Send(self,@EndAllThreats); + Send(self,@StartThreat,#cClasses=Send(self,@GetPhaseThreats)); + + prMusic = Send(self,@GetPhaseMusic); + foreach i in plActive + { + if IsClass(First(i),&User) + { + Send(First(i),@SendRoomMusic,#music_rsc=prMusic); + } + } + } + + piPreviousPhase = SysDayPhase; + propagate; + } + + GetPhaseMusic() + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + return desert_music_static_shock; + case DAY_PHASE_DAY: + return desert_music; + case DAY_PHASE_DUSK: + return desert_music_acid_rain; + case DAY_PHASE_NIGHT: + return desert_music_snow; + } + return desert_music; + } + + GetPhaseThreats() + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + return [&ShockStorm]; + case DAY_PHASE_DAY: + if Send(Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER2), + @GetPrismOfFireDefeated) + { + return [&HeatStorm]; + } + return [&HeatThreat]; + case DAY_PHASE_DUSK: + return [&AcidThreat]; + case DAY_PHASE_NIGHT: + return [&ColdThreat]; + } + return [&ShockStorm]; + } + + StartThreat(cClasses=$,report=TRUE) + { + local i; + + % Don't start same threat if one already exists. + % Don't start threats with no players present. + if pbUser_in_room + AND cClasses <> $ + { + foreach i in cClasses + { + if NOT Send(self,@HasObjectAttribute,#cClass=i) + { + Create(i,#host_object=self,#iDuration=$,#report=report); + } + } + } + + return; + } + + EndThreat(cClass=&ShockStorm) + { + if Send(self,@HasObjectAttribute,#cClass=cClass) + { + Send(Send(self,@GetObjectAttribute,#cClass=cClass),@Delete); + } + return; + } + + EndAllThreats() + { + local i; + + foreach i in plObject_attributes + { + if IsClass(i,&RoomThreat) + OR IsClass(i,&RoomStorm) + { + Send(i,@Delete); + } + } + + return; + } + + FirstUserEntered() + { + Post(self,@StartThreat,#cClasses=Send(self,@GetPhaseThreats), + #report=FALSE); + + propagate; + } + + LastUserLeft() + { + Post(self,@EndAllThreats); + + propagate; + } + + StartSnow(bOnGround=TRUE) + { + if bOnGround + { + if pbSnowGroundTexture + { + % Turn any water to ice. Water tagged as ID 13. + % Water sidedefs tagged as ID 14. + Send(self,@ChangeTexture,#id=13,#new_texture=61017, + #flags=CTF_FLOOR); + % Stop vertically scrolling water from animating. + Send(self,@AnimateWall,#wall=14,#animation=ANIMATE_NONE, + #first_group=1,#second_group=1,#end_group=1); + % Change texture. + Send(self,@ChangeTexture,#id=14,#new_texture=61017, + #flags=CTF_BELOWWALL); + % Stop water scroll effect and remove depth. + Send(self,@SetSectorFlags,#sector=13,#depth=SF_DEPTH0, + #scrollSpeed=SCROLL_NONE); + + % Give snow some depth and walking sounds. + Send(self,@SetSectorFlags,#sector=0,#depth=SF_DEPTH1, + #scrollSpeed=SCROLL_NONE); + vrWading_Sound = snow_walk_sound; + } + } + + propagate; + } + + EndSnow(override=FALSE) + "Use override if rooms have manually placed textures." + { + if pbSnowGroundTexture + OR override + { + % Remove sludge water textures. + Send(self,@RemoveTextureChange,#id=13); + % Remove sludge water sides. + Send(self,@RemoveTextureChange,#id=14); + Send(self,@RemoveAnimateWallChange,#wall=14); + % Reset sector flag changes to sludge water. + Send(self,@RemoveSectorFlagChange,#id=13); + + % Remove snow depth and walking sounds. + Send(self,@RemoveSectorFlagChange,#id=0); + vrWading_Sound = water_wading_sound; + } + + propagate; + } + + StartRain() + { + % Acid rain darkens the sand and pock marks rock. + Send(self,@ChangeTexture,#id=0,#new_texture=09080,#flags=CTF_FLOOR); + % Acid rain makes the water gross. + Send(self,@ChangeTexture,#id=13,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=14,#new_texture=09363, + #flags=CTF_NORMALWALL); + Send(self,@SetSectorFlags,#sector=13,#depth=SF_DEPTH1, + #scrollSpeed=SCROLL_SLOW); + + propagate; + } + + EndRain() + { + % Remove gross sand. + Send(self,@RemoveTextureChange,#id=0); + % Remove sludge water textures. + Send(self,@RemoveTextureChange,#id=13); + % Remove sludge water sides. + Send(self,@RemoveTextureChange,#id=14); + % Reset sector flag changes to sludge water. + Send(self,@RemoveSectorFlagChange,#id=13); + + propagate; + } + + DoPhaseWeather() + "Called to simulate weather by some rooms." + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + % Some rooms rain instead of snow. + if Send(self,@CheckRoomFlag,#flag=ROOM_SNOWING) + OR Send(self,@CheckRoomFlag,#flag=ROOM_RAINING) + { + Send(self,@EndSnow); + Send(self,@EndRain); + } + break; + case DAY_PHASE_DAY: + break; + case DAY_PHASE_DUSK: + if NOT Send(self,@CheckRoomFlag,#flag=ROOM_RAINING) + { + Send(self,@StartRain); + } + break; + case DAY_PHASE_NIGHT: + if Send(self,@CheckRoomFlag,#flag=ROOM_RAINING) + { + Send(self,@EndRain); + } + if NOT Send(self,@CheckRoomFlag,#flag=ROOM_SNOWING) + { + Send(self,@StartSnow); + } + break; + } + return; + } + + Delete() + { + Send(self,@EndAllThreats); + propagate; + } + + ReqSomethingUse(what = $, use_item = $) + { + local i; + + foreach i in plObject_attributes + { + % This area gives special messages to inform players + % of threat avoidance. Can also be used to prevent some item use + % by threats. + if IsClass(i,&RoomThreat) + { + if NOT Send(i,@ReqSomethingUse,#what=what,#use_item=use_item) + { + return FALSE; + } + } + } + + propagate; + } + + SpellCast(who = $,oSpell = $) + { + local i; + + foreach i in plObject_attributes + { + % This area gives special messages to inform players + % of threat avoidance. + if IsClass(i,&RoomThreat) + { + Send(i,@SpellCast,#who=who,#oSpell=oSpell); + } + } + + propagate; + } + GetRegion() { return RID_DESERT; diff --git a/kod/object/active/holder/room/desert/desertbridge.kod b/kod/object/active/holder/room/desert/desertbridge.kod index 3cfe08d9fd..0f234a230f 100644 --- a/kod/object/active/holder/room/desert/desertbridge.kod +++ b/kod/object/active/holder/room/desert/desertbridge.kod @@ -10,6 +10,25 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DesertBridge is DesertRoom +% In all that remains of the once mighty Temple of Hristofalnakan, +% (it's a mouthful, the NPCs know) the Essence of Lightning has taken up +% residence on the sacred altar. It protects this area and Waylay Oasis +% from the effects of the other prisms, and offers its own protections +% and boosts in the form of &ShockStorm. Using the Essence of Lightning will +% transport an adventurer to the Prism of Lightning, an otherworldly plane +% that exists purely in the sky. However, an unknown great hero has already +% gone ahead and defeated the Avatar of Lightning - thus the presnce of the +% Essence here in the first place. Travel through the Prism of Lightning +% can land an adventurer on the next island over. Some dangers remain within, +% but the Avatar of Lightning is apparently dead for all time. How this was +% accomplished, none know. + +% Use of the Essence of Lightning flags an adventurer for the ability to learn +% Lightning Strike (the behind-teleportation lightning + hammer attack). + +% This area always has &ShockStorm. +% This area never has other weather effects. + constants: include blakston.khd @@ -39,20 +58,46 @@ properties: piRoom_num = RID_DESERTBRIDGE messages: - -% West Entrance: row 3, col 1, fine row 15, fine col 27, face east -% Coming from the east: row 18, col 182, fine row 49, fine col 59, face west - CreateStandardExits() { plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_WEST, RID_WAYLAYOASIS, 15, 15, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTDUNES, 5, 5, ROTATE_NONE ],plEdge_Exits); + plEdge_Exits = Cons([ LEAVE_WEST, RID_WAYLAYOASIS, 48, 125, ROTATE_NONE ],plEdge_Exits); propagate; } + CreateStandardObjects() + { + Send(self,@NewHold,#what=Create(&LightningPrism), + #new_row=17,#new_col=172,#fine_row=32,#fine_col=0); + Send(self,@NewHold,#what=Create(&DynamicLight), + #new_row=17,#new_col=171,#fine_row=32,#fine_col=63); + propagate; + } + + RecalcLightAndWeather() + { + local SysDayPhase; + + SysDayPhase = Send(SYS,@GetDayPhase); + + % We're crossing over into a new quarter. + % Collapsed Causeway does not get threats other than shock storms. + % It does simulate rain and snow, though. + if piPreviousPhase <> SysDayPhase + { + Send(self,@DoPhaseWeather); + } + + propagate; + } + + GetPhaseThreats() + { + return [&ShockStorm]; + } + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/desert/desertcaveraft.kod b/kod/object/active/holder/room/desert/desertcaveraft.kod new file mode 100644 index 0000000000..0c2b15c19f --- /dev/null +++ b/kod/object/active/holder/room/desert/desertcaveraft.kod @@ -0,0 +1,52 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertCaveRaft is DesertRoom + +constants: + + include blakston.khd + +resources: + + room_desertcaveraft = desertcaveraft.roo + room_name_desertcaveraft = "Underground River" + +classvars: + + vrName = room_name_desertcaveraft + + viTeleport_row = 18 + viTeleport_col = 5 + + vbDoAcidRain = FALSE + vbDoBlizzard = FALSE + vbDoHeat = FALSE + + viTerrain_type = TERRAIN_CAVES + +properties: + + piBaseLight = LIGHT_MIN + piOutside_factor = 0 + + prRoom = room_desertcaveraft + piRoom_num = RID_DESERTCAVERAFT + +messages: + + RecalcLightAndWeather() + { + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/desert/desertcliffaccess.kod b/kod/object/active/holder/room/desert/desertcliffaccess.kod index bc6f3857bd..22a8d9eab0 100644 --- a/kod/object/active/holder/room/desert/desertcliffaccess.kod +++ b/kod/object/active/holder/room/desert/desertcliffaccess.kod @@ -40,14 +40,53 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTCLIFFNOACCESS1, 70, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTCLIFFNOACCESS3, 10, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTPATH1, 10, 10, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTPATH1), + #new_row=10, + #new_col=10, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFNOACCESS1), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFNOACCESS3), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertcliffnoaccess1.kod b/kod/object/active/holder/room/desert/desertcliffnoaccess1.kod index 2040dd3e36..6456a45e63 100644 --- a/kod/object/active/holder/room/desert/desertcliffnoaccess1.kod +++ b/kod/object/active/holder/room/desert/desertcliffnoaccess1.kod @@ -40,13 +40,43 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTCLIFFNOACCESS2, 70, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTCLIFFACCESS, 10, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + if new_col < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFNOACCESS2), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Bound(Send(what,@GetCol),0,60), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFACCESS), + #new_row=3, + #new_col=Bound(Send(what,@GetCol),0,60), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertcliffnoaccess2.kod b/kod/object/active/holder/room/desert/desertcliffnoaccess2.kod index 89711eb048..475426116d 100644 --- a/kod/object/active/holder/room/desert/desertcliffnoaccess2.kod +++ b/kod/object/active/holder/room/desert/desertcliffnoaccess2.kod @@ -40,13 +40,43 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTCLIFFNOACCESS3, 70, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTCLIFFNOACCESS1, 10, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + if new_col < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFNOACCESS3), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Bound(Send(what,@GetCol),0,60), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFNOACCESS1), + #new_row=3, + #new_col=Bound(Send(what,@GetCol),0,60), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertcliffnoaccess3.kod b/kod/object/active/holder/room/desert/desertcliffnoaccess3.kod index c3f5132fdf..b30e8deb00 100644 --- a/kod/object/active/holder/room/desert/desertcliffnoaccess3.kod +++ b/kod/object/active/holder/room/desert/desertcliffnoaccess3.kod @@ -40,13 +40,43 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTCLIFFACCESS, 70, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTCLIFFNOACCESS2, 10, 30, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + if new_col < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFACCESS), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Bound(Send(what,@GetCol),0,60), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFNOACCESS2), + #new_row=3, + #new_col=Bound(Send(what,@GetCol),0,60), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertdunes.kod b/kod/object/active/holder/room/desert/desertdunes.kod index a332cfdd38..1aa40aeccc 100644 --- a/kod/object/active/holder/room/desert/desertdunes.kod +++ b/kod/object/active/holder/room/desert/desertdunes.kod @@ -19,7 +19,7 @@ resources: include desertdunes.lkod room_desertdunes = desertdunes.roo - room_name_desertdunes = "The Black Desert" + room_name_desertdunes = "Desolate Dunes" classvars: @@ -40,14 +40,55 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTSHORE1, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTCLIFFACCESS, 10, 39, ROTATE_NONE ],plEdge_Exits); + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTCLIFFACCESS), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE1), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertriver1.kod b/kod/object/active/holder/room/desert/desertriver1.kod index 6a9636cb25..66630d0b19 100644 --- a/kod/object/active/holder/room/desert/desertriver1.kod +++ b/kod/object/active/holder/room/desert/desertriver1.kod @@ -40,7 +40,22 @@ properties: messages: - + Constructed() + { + % Natural state of Sector Group 31 is flowing and depth. + % We hide this most of the time. + % Group 33 is similar, but outside, so gets snow sometimes. + Send(self,@SetSectorFlags,#sector=31,#depth=SF_DEPTH0, + #scrollSpeed=SCROLL_NONE); + Send(self,@SetSectorFlags,#sector=33,#depth=SF_DEPTH0, + #scrollSpeed=SCROLL_NONE); + % We also hide our waterfalls. + Send(self,@AnimateWall,#wall=32,#animation=ANIMATE_NONE, + #first_group=1,#second_group=1,#end_group=1); + + propagate; + } + CreateStandardExits() { plEdge_Exits = $; @@ -92,6 +107,83 @@ messages: propagate; } + StartSnow(bOnGround=TRUE) + { + if bOnGround + { + if pbSnowGroundTexture + { + % Add snow to floor regions that are also sometimes water. + Send(self,@ChangeTexture,#id=33,#new_texture=61015, + #flags=CTF_FLOOR); + } + } + + propagate; + } + + EndSnow(override=FALSE) + { + local i; + + if pbSnowGroundTexture + OR override + { + % Remove snow from sand/rain floors. + Send(self,@RemoveTextureChange,#id=33); + } + + propagate; + } + + StartRain() + { + % This area has special tunnels that overflow. + % ID 31 covers a group of sectors that are naturally flowing + % but most of the time we cover that up. + + % Reveal the sectors! + Send(self,@RemoveSectorFlagChange,#id=31); + Send(self,@RemoveSectorFlagChange,#id=33); + + % Make those sectors into gross water! + Send(self,@ChangeTexture,#id=31,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=33,#new_texture=09363, + #flags=CTF_FLOOR); + + % Gross acid rain pours into tunnels. + % Again, natural state is animated, and we are revealing that + % by removing the stoppage. + Send(self,@ChangeTexture,#id=32,#new_texture=09363, + #flags=CTF_BELOWWALL); + Send(self,@RemoveAnimateWallChange,#wall=32); + + propagate; + } + + EndRain() + { + % Stop water scroll effect and remove depth to hide flow behavior. + Send(self,@SetSectorFlags,#sector=31,#depth=SF_DEPTH0, + #scrollSpeed=SCROLL_NONE); + % Stop water scroll effect and remove depth to hide flow behavior. + Send(self,@SetSectorFlags,#sector=33,#depth=SF_DEPTH0, + #scrollSpeed=SCROLL_NONE); + + % Go back to normal textures, groups 31 and 33. + Send(self,@RemoveTextureChange,#id=31); + Send(self,@RemoveTextureChange,#id=33); + + % No more waterfall animation. + Send(self,@AnimateWall,#wall=32,#animation=ANIMATE_NONE, + #first_group=1,#second_group=1,#end_group=1); + % Remove sludge waterfalls. + Send(self,@RemoveTextureChange,#id=32); + + propagate; + } + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/desert/desertriver2.kod b/kod/object/active/holder/room/desert/desertriver2.kod index 85bf2fb4f1..1e056154a9 100644 --- a/kod/object/active/holder/room/desert/desertriver2.kod +++ b/kod/object/active/holder/room/desert/desertriver2.kod @@ -10,6 +10,86 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DesertRiver2 is DesertRoom +% The Abyssal Bore is where the Prism of Fire, an elemental pocket plane of +% fire, intersects with our world. It's incredibly dangerous here while the +% rift is active. This area has no weather effects from other elements due +% to the rift's presence. +% +% Players must use the secret paths from Waylay Oasis to approach the high +% entrance, and then jump from ledge to ledge. +% +% The Abyssal Bore shakes every so often, and then deals very substantial +% fire damage to players based on their height. The closer they are to the +% lava, the more damage they'll take. The damage is balanced around +% players stacking fire resistances. Right up above the lava, unmitigated +% damage (0% fire res) can be as high as 90 damage during the most active +% rift periods. This is accomplished through &LavaThreat object. +% +% New custom sounds support the quakes and damage. +% +% Randomly created passive fires in the lava sector add to visuals. +% Fog clouds denote where the rivers' waters make steam by hitting the lava. +% +% Players who fall into the lava (as tracked by &FallThreat) +% will straight-up die. As in, @Killed, with a +% custom message about a regretful choice of step. This is, as far as I know, +% the first true deathtrap in Meridian. I think the area and high-level +% danger focus of this entire region warrants it. A message warns players +% whenever they enter: a single misstep here can get you KILLED. +% If we don't implement corpse summoning, an NPC in Waylay Oasis will +% have to automatically do it for players who die this way. +% NPCs will also warn players multiple times about this place. +% +% In about 200 test runs, I managed to reach the prism without falling every +% time. So it's a danger for those who like to jump off things blithely, +% and will likely serve as their first brutal lesson about the desert's +% dangers. Look before you leap - what did you think would happen if you +% jumped 4000 units down into a quaking & flaming pit of lava? +% +% Players can't phase out here, but it's not really to add to the danger. +% It's so that attempts to phase out will direct them toward the next step: +% Casting phase in the proper spot when the rift is open will transport +% them to the Prism of Fire elemental dungeon. Players who log out will +% be kicked to Waylay Oasis (to employ the panicked feeling that actually +% closing the client engenders). Blink will also return players to +% the northern waterfall, but must be timed correctly and done with good +% spellpower to escape. We might kill a few people who get deep down +% and start taking massive damage and panic, but they'll learn (and +% remember the experience forever). +% +% Casting Phase in the Prism of Fire will return players to Waylay Oasis. +% +% When the Prism of Fire's boss is defeated, the Abyssal Bore fills with +% water and becomes Warmfire Lake. All that remains of the rift is light +% from beneath. The rising waters also give access to the southern river, +% allowing players to proceed further south. +% +% Defeating the Prism of Fire also has substantial effects on the region. +% During the desert's day phase, players will no longer take heat damage. +% Instead, areas will have &HeatStorm, which brings storms of magical +% energy that heal health and give a special short-lived fire buff. +% +% +% +% +% Structure: +% FirstUserEntered begins rumbling and random fire. +% LastUserLeft puts a stop to rumbling and random fire. +% +% Elements are handled in: +% CreateLights / DeleteLights (don't want too many or players will lag) +% CreateFogClouds / DeleteFogClouds +% CreateRandomFire +% CreateRandomFogClouds (for if it rains) +% +% Room's secondary state: +% Stops all fire threats, sounds +% Raises most terrain to 3000, depth 3 +% Changes most terrain textures to water +% Changes name of screen to Warmfire Lake +% Adds lots of water sounds +% Changes water-step sound from lava to water + constants: include blakston.khd @@ -20,13 +100,29 @@ resources: room_desertriver2 = desertriver2.roo room_name_desertriver2 = "Abyssal Bore" + room_name_defeated = "Warmfire Lake" + + waterfall_sound_into_lava = wfall2.wav + quake_sound = earthquake03.wav + + entrance_warning = \ + "~rSulfuric gasses taint the air. Vast heat radiates from the earth. " + "This is a place of fire and death, and you know instinctively " + "that ~Ba single misstep here could get you killed." + + prism_opened_msg = \ + "~r~BThe earth rumbles, and the lava below begins churning fiercely." + prism_closed_msg = \ + "~rThe ground trembles, and the heat from below ebbs slightly." + + burning_step_sound = frying.wav classvars: - vrName = room_name_desertriver2 + viTeleport_row = 8 + viTeleport_col = 18 - viTeleport_row = 4 - viTeleport_col = 7 + viTerrain_type = TERRAIN_MOUNTAIN | TERRAIN_LAVA properties: @@ -38,24 +134,132 @@ properties: prRoom = room_desertriver2 piRoom_num = RID_DESERTRIVER2 + ptFireElementsTimer = $ + vrWading_Sound = burning_step_sound + vrName = room_name_desertriver2 + + pbPrismOpen = FALSE + pbPrismOfFireDefeated = FALSE + messages: + + Constructed() + { + plLooping_sounds = Cons([ waterfall_sound_into_lava, 13, 18, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 47, 28, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 49, 29, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ quake_sound, 0, 0, 300, 100],plLooping_sounds); + plLooping_sounds = Cons([ room_lava_sound, 0, 0, 300, 100 ], + plLooping_sounds); + + propagate; + } - - CreateStandardExits() + CreateStandardObjects() { - plEdge_Exits = $; + Send(self,@CreateLights); + Send(self,@CreateFogClouds); propagate; } - SomethingMoved(what = $,new_row = $, new_col = $) + RecalcLightAndWeather() + { + local SysDayPhase, i; + + SysDayPhase = Send(SYS,@GetDayPhase); + + % We're crossing over into a new quarter. + if piPreviousPhase <> SysDayPhase + { + % Simulate weather. + % StartSnow redirects to StartRain. + % It never snows here, as the heat melts the rain. + Send(self,@DoPhaseWeather); + + if SysDayPhase = DAY_PHASE_DAY + AND NOT pbPrismOfFireDefeated + { + Send(self,@OpenPrismOfFireEntrance); + foreach i in plActive + { + if IsClass(First(i),&User) + { + Send(First(i),@MsgSendUser,#message_rsc=prism_opened_msg); + } + } + } + if SysDayPhase = DAY_PHASE_DUSK + and pbPrismOpen + { + Send(self,@ClosePrismOfFireEntrance); + foreach i in plActive + { + if IsClass(First(i),&User) + { + Send(First(i),@MsgSendUser,#message_rsc=prism_closed_msg); + } + } + } + } + + propagate; + } + + GetPhaseThreats() + { + if pbPrismOfFireDefeated + { + return [&HeatStorm]; + } + + return [&LavaThreat, &FallThreat, &PhaseThreat]; + } + + NewHold(what = $) + { + % Warn players of the danger. + if IsClass(what,&Player) + AND NOT pbPrismOfFireDefeated + { + Send(what,@MsgSendUser,#message_rsc=entrance_warning); + } + propagate; + } + + FirstUserEntered() + { + if ptFireElementsTimer = $ + AND NOT pbPrismOfFireDefeated + { + ptFireElementsTimer = CreateTimer(self,@CreateRandomFire,1000); + } + + propagate; + } + + LastUserLeft() + { + if ptFireElementsTimer <> $ + { + DeleteTimer(ptFireElementsTimer); + ptFireElementsTimer = $; + } + + propagate; + } + + SomethingMoved(what=$,new_row=0,new_col=0,fine_row=0,fine_col=0) { if new_row < 2 and (new_col < 15) { Send(SYS,@UtilGoNearSquare,#what=what, #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER1), #new_row=37,#new_col=6,#fine_row=52,#fine_col=1, - #new_angle=ANGLE_NORTH); + #new_angle=Send(what,@GetAngle)); return; } @@ -64,7 +268,7 @@ messages: Send(SYS,@UtilGoNearSquare,#what=what, #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER1), #new_row=37,#new_col=18,#fine_row=42,#fine_col=12, - #new_angle=ANGLE_NORTH_WEST); + #new_angle=Send(what,@GetAngle)); return; } @@ -72,6 +276,442 @@ messages: propagate; } + CreateStandardExits() + { + plEdge_Exits = $; + + propagate; + } + + Delete() + { + Send(self,@DeleteFogClouds); + Send(self,@DeleteLights); + Send(self,@DeleteFirewalls); + + if ptFireElementsTimer <> $ + { + DeleteTimer(ptFireElementsTimer); + ptFireElementsTimer = $; + } + + propagate; + } + + CreateLights(iBr=255) + { + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=47,#new_col=30,#fine_row=26,#fine_col=2); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=50,#new_col=24,#fine_row=25,#fine_col=17); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=48,#new_col=15,#fine_row=53,#fine_col=56); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=43,#new_col=9,#fine_row=30,#fine_col=30); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=35,#new_col=4,#fine_row=35,#fine_col=28); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=28,#new_col=3,#fine_row=62,#fine_col=51); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=22,#new_col=3,#fine_row=27,#fine_col=50); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=15,#new_col=4,#fine_row=20,#fine_col=33); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=11,#new_col=7,#fine_row=56,#fine_col=35); + + % Waterfall + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_FIRE, + #iIntensity=100),#new_row=13,#new_col=17,#fine_row=34,#fine_col=54); + % Prism Entrance itself + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_BRED, + #iIntensity=255),#new_row=32,#new_col=24,#fine_row=43,#fine_col=16); + + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=15,#new_col=31,#fine_row=40,#fine_col=9); +% Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, +% #iIntensity=iBr),#new_row=17,#new_col=39,#fine_row=5,#fine_col=38); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=20,#new_col=42,#fine_row=60,#fine_col=55); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=30,#new_col=45,#fine_row=31,#fine_col=7); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=17,#new_col=39,#fine_row=5,#fine_col=38); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=37,#new_col=47,#fine_row=27,#fine_col=8); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=41,#new_col=44,#fine_row=59,#fine_col=15); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=45,#new_col=35,#fine_row=37,#fine_col=54); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_FIRE, + #iIntensity=iBr),#new_row=46,#new_col=27,#fine_row=55,#fine_col=58); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_RED, + #iIntensity=iBr),#new_row=41,#new_col=19,#fine_row=39,#fine_col=63); + + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=32,#new_col=13,#fine_row=56,#fine_col=1); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=20,#new_col=16,#fine_row=61,#fine_col=58); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=21,#new_col=11,#fine_row=42,#fine_col=57); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=19,#new_col=21,#fine_row=53,#fine_col=33); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=23,#new_col=28,#fine_row=5,#fine_col=5); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=24,#new_col=35,#fine_row=44,#fine_col=62); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=35,#new_col=38,#fine_row=18,#fine_col=16); + Send(self,@NewHold,#what=Create(&DynamicLight,#iColor=LIGHT_ORANGE, + #iIntensity=iBr),#new_row=39,#new_col=30,#fine_row=34,#fine_col=1); + return; + } + + DeleteLights() + { + local i; + + foreach i in plPassive + { + if IsClass(First(i),&DynamicLight) + { + Send(First(i),@Delete); + } + } + return; + } + + CreateRandomFire() + { + local iRandomRow, iRandomCol, oElement, i, iQflags, iRflags, iHeightF, + iHeightFWD, iHeightC, iServerID, iRandomFineRow, iRandomFineCol; + + ptFireElementsTimer = $; + + iRandomRow = Random(0,piRows); + iRandomCol = Random(0,piCols); + iRandomFineRow = Random(0,63); + iRandomFineCol = Random(0,63); + + iQflags = LIQ_GET_SECTORINFO; + + if GetLocationInfoBSP( + prmRoom, iQflags, iRandomRow, iRandomCol, iRandomFineRow, + iRandomFineCol, *iRflags, *iHeightF, *iHeightFWD, + *iHeightC, *iServerID) + { + % only place in lava + if iServerID = 44 + { + Send(self,@NewHold,#what=Create(&PassiveWallofFire,#Duration=10), + #new_row=iRandomRow,#new_col=iRandomCol, + #fine_row=iRandomFineRow,#fine_col=iRandomFineCol); + } + } + + if Send(self,@CheckRoomFlag,#flag=ROOM_RAINING) + { + Send(self,@CreateRandomFogCloud); + Send(self,@CreateRandomFogCloud); + Send(self,@CreateRandomFogCloud); + } + + ptFireElementsTimer=CreateTimer(self,@CreateRandomFire,1000); + + return; + } + + CreateRandomFogCloud() + { + local iRandomRow, iRandomCol, oElement, i, iQflags, iRflags, iHeightF, + iHeightFWD, iHeightC, iServerID, iRandomFineRow, iRandomFineCol; + + iRandomRow = Random(0,piRows); + iRandomCol = Random(0,piCols); + iRandomFineRow = Random(0,63); + iRandomFineCol = Random(0,63); + + iQflags = LIQ_GET_SECTORINFO; + + if GetLocationInfoBSP( + prmRoom, iQflags, iRandomRow, iRandomCol, iRandomFineRow, + iRandomFineCol, *iRflags, *iHeightF, *iHeightFWD, + *iHeightC, *iServerID) + { + % only place in lava + if iServerID = 44 + { + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=10, + #Caster=self), + #new_row=iRandomRow,#new_col=iRandomCol, + #fine_row=iRandomFineRow,#fine_col=iRandomFineCol); + } + } + + return; + } + + CreateFogClouds() + { + % North waterfall + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=12, + #new_col=16,#fine_row=43,#fine_col=20); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=12, + #new_col=16,#fine_row=0,#fine_col=0); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=12, + #new_col=16,#fine_row=63,#fine_col=63); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=12, + #new_col=16,#fine_row=56,#fine_col=12); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=14, + #new_col=19,#fine_row=44,#fine_col=36); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=14, + #new_col=19,#fine_row=63,#fine_col=63); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=14, + #new_col=19,#fine_row=0,#fine_col=0); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=14, + #new_col=19,#fine_row=56,#fine_col=12); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=14, + #new_col=17,#fine_row=43,#fine_col=18); + + % South waterfall + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=46, + #new_col=26,#fine_row=57,#fine_col=31); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=47, + #new_col=27,#fine_row=62,#fine_col=21); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=46, + #new_col=28,#fine_row=27,#fine_col=1); + Send(self,@NewHold,#what=Create(&FogCloud,#Duration=$),#new_row=46, + #new_col=28,#fine_row=49,#fine_col=62); + + return; + } + + DeleteFogClouds() + { + local i; + + foreach i in plPassive + { + if IsClass(First(i),&FogCloud) + { + Send(First(i),@Delete); + } + } + + return; + } + + DeleteFirewalls() + { + local i; + + foreach i in plPassive + { + if IsClass(First(i),&PassiveWallOfFire) + { + Send(First(i),@Delete); + } + } + return; + } + + OpenPrismOfFireEntrance() + { + if NOT pbPrismOfFireDefeated + { + Send(self,@SetSector,#sector=46, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=40); + % Texture the portal.. + Send(self,@ChangeTexture,#id=46,#new_texture=07702, + #flags=CTF_FLOOR); + pbPrismOpen = TRUE; + } + return; + } + + ClosePrismOfFireEntrance() + { + if pbPrismOfFireDefeated + { + Send(self,@SetSector,#sector=46, + #animation=ANIMATE_FLOOR_LIFT,#height=3000,#speed=0); + } + else + { + Send(self,@RemoveTextureChange,#id=46); + Send(self,@SetSector,#sector=46, + #animation=ANIMATE_FLOOR_LIFT,#height=6,#speed=1); + } + pbPrismOpen = FALSE; + return; + } + + StartRain() + { + % Acid rain makes the water gross. + Send(self,@ChangeTexture,#id=48,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=49,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=90,#new_texture=09363, + #flags=CTF_BELOWWALL); + + if pbPrismOfFireDefeated + { + Send(self,@ChangeTexture,#id=44,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=45,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=46,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=47,#new_texture=09363, + #flags=CTF_FLOOR); + } + propagate; + } + + EndRain() + { + Send(self,@RemoveTextureChange,#id=48); + Send(self,@RemoveTextureChange,#id=49); + Send(self,@RemoveTextureChange,#id=90); + + if pbPrismOfFireDefeated + { + Send(self,@ChangeTexture,#id=44,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=45,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=46,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=47,#new_texture=08968, + #flags=CTF_FLOOR); + } + + propagate; + } + + StartSnow(bOnGround=TRUE) + { + % Never snows here. The heat melts it into rain. + Send(self,@StartRain); + return; + } + + EndSnow(override=FALSE) + { + Send(self,@EndRain); + return; + } + + PrismOfFireDefeated() + { + local i; + + if ptFireElementsTimer <> $ + { + DeleteTimer(ptFireElementsTimer); + ptFireElementsTimer = $; + } + + Send(self,@DeleteFogClouds); + Send(self,@DeleteLights); + Send(self,@DeleteFirewalls); + + % Texture the new lake + Send(self,@ChangeTexture,#id=44,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=45,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=46,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=47,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=48,#new_texture=08968, + #flags=CTF_FLOOR); + Send(self,@SetSector,#sector=44, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + Send(self,@SetSector,#sector=45, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + Send(self,@SetSector,#sector=46, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + Send(self,@SetSector,#sector=47, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + Send(self,@SetSector,#sector=48, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + Send(self,@SetSectorFlags,#sector=44,#depth=SF_DEPTH3, + #scrollSpeed=SCROLL_NONE); + Send(self,@SetSectorFlags,#sector=45,#depth=SF_DEPTH3, + #scrollSpeed=SCROLL_NONE); + Send(self,@SetSectorFlags,#sector=46,#depth=SF_DEPTH3, + #scrollSpeed=SCROLL_NONE); + Send(self,@SetSectorFlags,#sector=47,#depth=SF_DEPTH3, + #scrollSpeed=SCROLL_NONE); + Send(self,@SetSectorFlags,#sector=48,#depth=SF_DEPTH3, + #scrollSpeed=SCROLL_NONE); + Send(self,@SetSectorFlags,#sector=49,#depth=SF_DEPTH3, + #scrollSpeed=SCROLL_FAST); + Send(self,@SetSectorFlags,#sector=13,#depth=SF_DEPTH2, + #scrollSpeed=SCROLL_FAST); + Send(self,@SetSectorLight,#sector=46,#light_effect=80); + + Post(self,@SetSector,#sector=44, + #animation=ANIMATE_FLOOR_LIFT,#height=3000,#speed=100); + Post(self,@SetSector,#sector=45, + #animation=ANIMATE_FLOOR_LIFT,#height=3000,#speed=100); + Post(self,@SetSector,#sector=46, + #animation=ANIMATE_FLOOR_LIFT,#height=3000,#speed=100); + Post(self,@SetSector,#sector=47, + #animation=ANIMATE_FLOOR_LIFT,#height=3000,#speed=100); + Post(self,@SetSector,#sector=48, + #animation=ANIMATE_FLOOR_LIFT,#height=3000,#speed=100); + + vrWading_Sound = water_wading_sound; + Send(self,@RemoveLoopingSound,#sound_rsc=quake_sound); + Send(self,@RemoveLoopingSound,#sound_rsc=room_lava_sound); + + plLooping_sounds = Cons([ waterfall_sound_into_lava, 5, 5, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 10, 5, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 15, 10, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 25, 5, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 5, 50, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 5, 35, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 15, 25, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 25, 25, 180, 100 ], + plLooping_sounds); + plLooping_sounds = Cons([ waterfall_sound_into_lava, 50, 5, 180, 100 ], + plLooping_sounds); + + vrName = room_name_defeated; + Send(self,@EndAllThreats); + pbPrismOfFireDefeated = TRUE; + + return; + } + + GetPrismOfFireDefeated() + { + return pbPrismOfFireDefeated; + } + + SpecialPhaseConditionsSatisfied(sector=0) + { + % Special Phase conditions + if sector=46 + AND pbPrismOpen + { + return TRUE; + } + + return FALSE; + } + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/desert/desertsandbar.kod b/kod/object/active/holder/room/desert/desertsandbar.kod new file mode 100644 index 0000000000..57af074788 --- /dev/null +++ b/kod/object/active/holder/room/desert/desertsandbar.kod @@ -0,0 +1,103 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertSandbar is DesertRoom + +constants: + + include blakston.khd + +resources: + + room_desertsandbar = desertsandbar.roo + room_name_desertsandbar = "Lonely Sandbar" + + no_travel_into_sea_sandbar = \ + "Trying to head that direction seems foolhardy right now." + travel_into_sea_sandbar = \ + "You trek back across the ice toward the shore." + +classvars: + + vrName = room_name_desertsandbar + + viTeleport_row = 5 + viTeleport_col = 5 + +properties: + + piBaseLight = LIGHT_DARK + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_desertsandbar + piRoom_num = RID_DESERTSANDBAR + +messages: + + SomethingMoved(what = $,new_row = $, new_col = $) + { + if new_col > (Send(self,@GetRoomCols)-2) + { + if IsClass(what,&User) + { + if Send(self,@CheckRoomFlag,#flag=ROOM_SNOWING) + { + Send(what,@MsgSendUser,#message_rsc=travel_into_sea_sandbar); + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE3), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + } + else + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea_sandbar); + } + } + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea_sandbar); + } + return; + } + + if new_row < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea_sandbar); + } + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea_sandbar); + } + return; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/desert/desertshore1.kod b/kod/object/active/holder/room/desert/desertshore1.kod index acb02bb041..260c8d9205 100644 --- a/kod/object/active/holder/room/desert/desertshore1.kod +++ b/kod/object/active/holder/room/desert/desertshore1.kod @@ -21,6 +21,8 @@ resources: room_desertshore1 = desertshore1.roo room_name_desertshore1 = "Solitary Shoreline" + no_travel_into_sea = "There's nothing but death for you out there." + classvars: vrName = room_name_desertshore1 @@ -40,13 +42,52 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTSHORE2, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTSHORE3, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea); + } + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE2), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE3), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertshore2.kod b/kod/object/active/holder/room/desert/desertshore2.kod index 4836a4d8ea..d29770164d 100644 --- a/kod/object/active/holder/room/desert/desertshore2.kod +++ b/kod/object/active/holder/room/desert/desertshore2.kod @@ -21,6 +21,10 @@ resources: room_desertshore2 = desertshore2.roo room_name_desertshore2 = "Solitary Shoreline" + no_travel_into_sea2 = \ + "If something interesting once existed out there, it's certainly " + "been gone for a long time." + classvars: vrName = room_name_desertshore2 @@ -40,13 +44,52 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTSHORE3, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTSHORE1, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea2); + } + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE3), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE1), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertshore3.kod b/kod/object/active/holder/room/desert/desertshore3.kod index 1a6b117d77..867655d557 100644 --- a/kod/object/active/holder/room/desert/desertshore3.kod +++ b/kod/object/active/holder/room/desert/desertshore3.kod @@ -21,6 +21,11 @@ resources: room_desertshore3 = desertshore3.roo room_name_desertshore3 = "Solitary Shoreline" + no_travel_into_sea3 = \ + "The underwater terrain drops off sharply here, preventing progress." + travel_into_sea3 = \ + "You set out onto the oceanic ice." + classvars: vrName = room_name_desertshore3 @@ -40,13 +45,69 @@ properties: messages: - - CreateStandardExits() + SomethingMoved(what = $,new_row = $, new_col = $) { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_NORTH, RID_DESERTSHORE1, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_SOUTH, RID_DESERTSHORE2, 60, 102, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTDUNES, 60, 102, ROTATE_NONE ],plEdge_Exits); + local oTargetRoom; + + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTDUNES), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + if Send(self,@CheckRoomFlag,#flag=ROOM_SNOWING) + { + oTargetRoom = Send(SYS,@FindRoomByNum,#num=RID_DESERTSANDBAR); + Send(what,@MsgSendUser,#message_rsc=travel_into_sea3); + Send(SYS,@UtilGoNearSquare,#what=what, + #where=oTargetRoom, + #new_row=Send(what,@GetRow), + #new_col=Send(oTargetRoom,@GetRoomCols)-3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + } + else + { + Send(what,@MsgSendUser,#message_rsc=no_travel_into_sea3); + } + } + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE1), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTSHORE2), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } propagate; } diff --git a/kod/object/active/holder/room/desert/desertsteppes.kod b/kod/object/active/holder/room/desert/desertsteppes.kod new file mode 100644 index 0000000000..7c6633e08f --- /dev/null +++ b/kod/object/active/holder/room/desert/desertsteppes.kod @@ -0,0 +1,104 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertSteppes is DesertRoom + +% The Sandswept Steppes are dynamically generated. +% There is no actual 'Sandswept Steppe' room on its own. Instead, players +% who enter are placed in a newly generated instance based on certain rules. +% +% - Groups of players are separated at zone transitions. +% - Players may never meet up more than 1 on 1. (i.e. 2 people max in an area) +% - Layout, monsters, and objects are created upon entrance. +% - Previous rooms are destroyed the moment nobody is left. No backtracking. +% - Players appear in the center of each room to avoid zone-edge confusion. +% - Each room has 'coordinates'. The desert is not infinite in size. +% Players can be in two different instances that are both Lat 7 Longi 8, ex +% because players won't see these values. +% Going north or west of 1, or south or east of 10, = leaving this area. +% So it's 10 x 10. +% Can't escape everywhere, though, as the edge rooms will have walls. +% Exits only in certain places (exits may move). +% - Yells are heard everywhere in this area. + +constants: + + include blakston.khd + + % South wall sectors: 6, 15, 22, 39, 46, 63, 70, 87, 94, 111, 118, 135, 142, 159, 165, 182 + % Southeast wall sec: 110, 119, 120, 133, 132, 145, 146, 155, 153, 154, 152, 151 + % east wall sectors: 151-162 + +resources: + + room_desertsteppes = desertsteppes.roo + room_name_desertsteppes = "Sandswept Steppes" + +classvars: + + vrName = room_name_desertsteppes + + viTeleport_row = 60 + viTeleport_col = 102 + +properties: + + piBaseLight = LIGHT_DARK + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_desertsteppes + piRoom_num = $ + + piLatitude = 1 + piLongitude = 1 + +messages: + + Constructor(iLatitude=$,iLongitude=$) + { + piLatitude = iLatitude; + piLongitude = iLongitude; + propagate; + } + + SomethingMoved(what = $,new_row = $, new_col = $) + { + % GO EAST + if new_col > (Send(self,@GetRoomCols)-2) + { + return; + } + + % GO WEST + if new_col < 2 + { + return; + } + + % GO NORTH + if new_row < 2 + { + return; + } + + % GO SOUTH + if new_row > (Send(self,@GetRoomRows)-2) + { + return; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/desert/makefile b/kod/object/active/holder/room/desert/makefile index 15052a78dc..cda5885aac 100644 --- a/kod/object/active/holder/room/desert/makefile +++ b/kod/object/active/holder/room/desert/makefile @@ -6,9 +6,10 @@ DEPEND = ..\desert.bof -BOFS = desertpath1.bof desertdunes.bof desertcliffaccess.bof waylayoasis.bof \ +BOFS = desertpath1.bof desertdunes.bof desertcliffaccess.bof \ desertcliffnoaccess1.bof desertcliffnoaccess2.bof \ desertcliffnoaccess3.bof desertshore1.bof desertshore2.bof \ - desertshore3.bof desertriver1.bof desertriver2.bof desertbridge.bof + desertshore3.bof desertriver1.bof desertriver2.bof desertbridge.bof \ + desertcaveraft.bof desertsandbar.bof desertsteppes.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/active/holder/room/desert/waylayoasis.kod b/kod/object/active/holder/room/desert/waylayoasis.kod deleted file mode 100644 index cb6a713a80..0000000000 --- a/kod/object/active/holder/room/desert/waylayoasis.kod +++ /dev/null @@ -1,77 +0,0 @@ -% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. -% All rights reserved. -% -% This software is distributed under a license that is described in -% the LICENSE file that accompanies it. -% -% Meridian is a registered trademark. - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -WaylayOasis is DesertRoom - -constants: - - include blakston.khd - -resources: - - include waylayoasis.lkod - - room_waylayoasis = waylayoasis.roo - room_name_waylayoasis = "Waylay Oasis" - -classvars: - - vrName = room_name_waylayoasis - - viTeleport_row = 58 - viTeleport_col = 51 - -properties: - - piBaseLight = LIGHT_DARK - piOutside_factor = OUTDOORS_5 - - piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS - - prRoom = room_waylayoasis - piRoom_num = RID_WAYLAYOASIS - -messages: - - - CreateStandardExits() - { - plEdge_Exits = $; - plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTPATH1, 11, 58, ROTATE_NONE ],plEdge_Exits); - plEdge_Exits = Cons([ LEAVE_EAST, RID_DESERTBRIDGE, 4, 5, ANGLE_EAST ],plEdge_Exits); - - propagate; - } - - SomethingMoved(what = $,new_row = $, new_col = $) - { - if new_row > (Send(self,@GetRoomRows)-1) and (new_col < 30) - { - Send(SYS,@UtilGoNearSquare,#what=what, - #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER1), - #new_row=5,#new_col=7,#new_angle=ANGLE_SOUTH_EAST); - return; - } - - if new_row > (Send(self,@GetRoomRows)-1) and (new_col > 50) - { - Send(SYS,@UtilGoNearSquare,#what=what, - #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER1), - #new_row=3,#new_col=33,#fine_col=44,#new_angle=ANGLE_SOUTH); - - return; - } - - propagate; - } - -end -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - diff --git a/kod/object/active/holder/room/ghall.kod b/kod/object/active/holder/room/ghall.kod index d46b04dc7d..3a9ad916a9 100644 --- a/kod/object/active/holder/room/ghall.kod +++ b/kod/object/active/holder/room/ghall.kod @@ -1311,7 +1311,7 @@ messages: { return FALSE; } - + ReqSpellCast(oSpell=$,who=$) { % Cannot conveyance inside a hall. @@ -1328,5 +1328,10 @@ messages: propagate; } + FoyerDissipatesWallElements() + { + return TRUE; + } + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/ghall/makefile b/kod/object/active/holder/room/ghall/makefile index 82ee9a03ee..f09e3f825f 100644 --- a/kod/object/active/holder/room/ghall/makefile +++ b/kod/object/active/holder/room/ghall/makefile @@ -8,7 +8,7 @@ DEPEND = ..\ghall.bof BOFS = guildh1.bof guildh2.bof guildh3.bof guildh4.bof guildh5.bof guildh6.bof \ guildh7.bof guildh8.bof guildh9.bof guildh10.bof guildh11.bof guildh12.bof \ - guildh13.bof guildh14.bof guildh15.bof + guildh13.bof guildh14.bof guildh15.bof waylayoasis.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/active/holder/room/ghall/waylayoasis.kod b/kod/object/active/holder/room/ghall/waylayoasis.kod new file mode 100644 index 0000000000..048c098b09 --- /dev/null +++ b/kod/object/active/holder/room/ghall/waylayoasis.kod @@ -0,0 +1,731 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +WaylayOasis is GuildHall + +% Waylay Oasis is the central location for most desert activities. +% It's the region rescue spot, hosts a variety of NPCs, is safe from +% elemental storms and threats, and is also a guild hall. +% Special consideration has been made for certain hall features. Most players +% won't realize it's a hall until they're being attacked from the ledges. +% +% Those on the ledges of the hall can hit and be hit from outside, +% but area attacks like Earthquake will not cross. +% +% Wall spells ARE allowed in the 'foyer' (which is everywhere but the hall) +% so players can leave ambushes and blockages in the narrow passages. +% +% The water also changes depth with rain and snow, +% so fights here will often be physically different. +% +% Players are not safe when they rescue in. That's part of the desert's danger. +% Enemies may wait at Waylay Oasis' blink/rescue spot, but of course others +% will easily find those enemies, too. Meant to be hotly contested sometimes, +% and uneasy peaceful by mutual consent at other times. For example, +% all players benefit from the defeat of an Elemental Prism, so teaming up +% with enemies might happen (since the dungeons are crazy hard). +% +% +% Waylay Oasis is balanced at just the right spot between Prism energies +% to avoid storms and threats. Mostly because it would be annoying, +% but also because we want clean and non-laggy PvP. + +constants: + + include blakston.khd + + LIFT_DELAY = 10000 + ENTRANCE_SECTOR1 = 20 + ENTRANCE_SECTOR2 = 21 + + HALL_SECTORS_SOUTH = 22 + LEDGE_SECTORS = 23 + HALL_SECTORS_NORTH = 24 + CHEST_PASSAGE_SECTOR = 25 + + AREA_OUTSIDE = 0 + AREA_INSIDE = 1 + AREA_LEDGES = 2 + + InsideAreaSouthMinRow = 55 + InsideAreaSouthMinFineRow = 6 + InsideAreaSouthMaxRow = 79 + InsideAreaSouthMaxFineRow = 20 + InsideAreaSouthMinCol = 76 + InsideAreaSouthMinFineCol = 10 + InsideAreaSouthMaxCol = 107 + InsideAreaSouthMaxFineCol = 18 + + InsideAreaSouthHallwayMinRow = 50 + InsideAreaSouthHallwayMaxRow = 55 + InsideAreaSouthHallwayMinCol = 100 + InsideAreaSouthHallwayMaxCol = 62 + + InsideAreaNorthMinRow = 10 + InsideAreaNorthMinFineRow = 30 + InsideAreaNorthMaxRow = 38 + InsideAreaNorthMaxFineRow = 63 + InsideAreaNorthMinCol = 78 + InsideAreaNorthMinFineCol = 32 + InsideAreaNorthMaxCol = 106 + InsideAreaNorthMaxFineCol = 52 + +resources: + + include waylayoasis.lkod + + room_waylayoasis = waylayoasis.roo + room_name_waylayoasis = "Waylay Oasis" + + waylayoasis_music = desertheat.mp3 + waylayoasis_music_snow = desertsnow.mp3 + waylayoasis_music_acid_rain = desertrain.mp3 + waylayoasis_music_static_shock = desertdawn.mp3 + + waterfall_sound_oasis = wfall2.wav + +classvars: + + vrName = room_name_waylayoasis + + viTerrain_type = TERRAIN_BADLANDS | TERRAIN_TEMPLE + + viTeleport_row = 58 + viTeleport_col = 51 + + viInner_teleport_row = 68 + viInner_teleport_col = 97 + + viNews_row = 71 + viNews_col = 76 + viNews_finerow = 5 + viNews_finecol = 61 + + viFoyer_north = 1 + viFoyer_south = 1 + viFoyer_west = 1 + viFoyer_east = 1 + + viLever_row = 39 + viLever_col = 94 + viLever_fine_row = 11 + viLever_fine_col = 39 + + viQuality = 9 + + viSeconds_til_close = 8 + + viWeatherZone = WEATHER_ZONE_DESERT + +properties: + + vrWading_Sound = water_wading_sound + + piBaseLight = LIGHT_DARK + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_waylayoasis + piRoom_num = RID_WAYLAYOASIS + + ptSecret = $ + ptEntrance = $ + plInside_doors = $ + + prMusic = waylayoasis_music + + pbSnowGroundTexture = TRUE + piPreviousPhase = 0 + +messages: + + Constructor() + { + plGuild_doors = [ [68,106], [69,106], [70,106], [71,106], [72,106], + [68,111], [69,111], [70,111], [71,111], [72,111]]; + + plInside_doors = [ [68,106], [69,106], [70,106], [71,106], [72,106]]; + + % This is a box containing the guild door, used + % for removing players from it when it closes. + plEntrance = [ 6850, 7250, 10650, 11100 ]; + + propagate; + } + + Constructed() + { + plLooping_sounds = [[ waterfall_sound_oasis, 21, 40, 65, 100 ]]; + + propagate; + } + + CreateStandardExits() + { + plEdge_Exits = $; + plEdge_Exits = Cons([ LEAVE_WEST, RID_DESERTPATH1, 11, 58, ROTATE_NONE ],plEdge_Exits); + + propagate; + } + + RecalcLightAndWeather() + { + local SysDayPhase, i; + + SysDayPhase = Send(SYS,@GetDayPhase); + + % We're crossing over into a new quarter. + % Waylay Oasis does not experience storms and threats. + % It does simulate rain and snow, though. + if piPreviousPhase <> SysDayPhase + { + Send(self,@DoPhaseWeather); + prMusic = Send(self,@GetPhaseMusic); + foreach i in plActive + { + if IsClass(First(i),&User) + { + Send(First(i),@SendRoomMusic,#music_rsc=prMusic); + } + } + } + + piPreviousPhase = SysDayPhase; + propagate; + } + + DoPhaseWeather() + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + if Send(self,@CheckRoomFlag,#flag=ROOM_SNOWING) + { + Send(self,@EndSnow); + } + break; + case DAY_PHASE_DAY: + break; + case DAY_PHASE_DUSK: + if NOT Send(self,@CheckRoomFlag,#flag=ROOM_RAINING) + { + Send(self,@StartRain); + } + break; + case DAY_PHASE_NIGHT: + if Send(self,@CheckRoomFlag,#flag=ROOM_RAINING) + { + Send(self,@EndRain); + } + if NOT Send(self,@CheckRoomFlag,#flag=ROOM_SNOWING) + { + Send(self,@StartSnow); + } + break; + } + return; + } + + GetPhaseMusic() + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + return waylayoasis_music_static_shock; + case DAY_PHASE_DAY: + return waylayoasis_music; + case DAY_PHASE_DUSK: + return waylayoasis_music_acid_rain; + case DAY_PHASE_NIGHT: + return waylayoasis_music_snow; + } + return desert_music; + } + + SomethingTryGo(what=$, row=$, col = $) + { + local i; + + foreach i in plInside_doors + { + if row = First(i) + AND col = Nth(i,2) + { + Send(self,@OpenEntrancedoor); + + return TRUE; + } + } + + propagate; + } + + SomeoneSaid(what = $,type = $,string = $,parm1 = $,parm2 = $,parm3 = $, + parm4 = $, parm5 = $,parm6 = $,parm7 = $,parm8 = $) + "Conversation on ledges can be heard anywhere. " + "Also, chest only opens from inside areas (not ledges)." + { + local i, each_obj, iSpeakerSection, iEachObjSection; + + if what = $ + { + propagate; + } + + if IsClass(what,&Admin) AND stringEqual(string,"shatterlock") + { + Send(self,@OpenEntranceDoor); + + propagate; + } + + if type = SAY_YELL OR NOT IsClass(what,&Player) + { + propagate; + } + + iSpeakerSection = Send(self,@GetSection,#who=what, + #iRow=Send(what,@GetRow), + #iCol=Send(what,@GetCol), + #iFineRow=Send(what,@GetFineRow), + #iFineCol=Send(what,@GetFineCol)); + + foreach i in plActive + { + each_obj = Send(self,@HolderExtractObject,#data=i); + + if Send(self,@SayRangeCheck,#talker=what,#hearer=each_obj) + { + iEachObjSection = Send(self,@GetSection,#who=each_obj, + #iRow=Send(each_obj,@GetRow), + #iCol=Send(each_obj,@GetCol), + #iFineRow=Send(each_obj,@GetFineRow), + #iFineCol=Send(each_obj,@GetFineCol)); + + if iSpeakerSection = iEachObjSection + OR iSpeakerSection = AREA_LEDGES + { + Send(each_obj,@SomeoneSaid,#what=what,#type=type,#string=string, + #parm1=parm1,#parm2=parm2,#parm3=parm3,#parm4=parm4, + #parm5=parm5,#parm6=parm6,#parm7=parm7,#parm8=parm8); + } + else + { + Send(each_obj,@MsgSendUser,#message_rsc=guildhall_muffled); + } + } + } + + if poGuild_owner <> $ + AND StringEqual(string,Send(poGuild_owner,@GetPassword)) + AND iSpeakerSection = AREA_INSIDE + AND NOT pbSecretDoorOpen + AND type <> SAY_EMOTE + { + Send(self,@OpenSecretDoor); + } + + return; + } + + ValidEntry(what=$) + { + % If they can't open the door, make sure they're outside the foyer + if NOT Send(self,@CanEnter,#who=what) + AND NOT Send(self,@InFoyer,#who=what) + { + Send(self,@Teleport,#what=what,#foyer=TRUE); + } + + return; + } + + InFoyer(who=$,iRow=$,iCol=$,iFineRow=$,iFineCol=$) + { + if who <> $ + { + iRow = Send(who,@GetRow); + iCol = Send(who,@GetCol); + iFineRow = Send(who,@GetFineRow); + iFineCol = Send(who,@GetFineCol); + } + + if Send(self,@GetSection,#who=who,#iRow=iRow,#iCol=iCol, + #iFineRow=iFineRow,#iFineCol=iFineCol) = AREA_OUTSIDE + { + return TRUE; + } + + return FALSE; + } + + GetSection(who=$,iRow=$,iCol=$,iFineRow=$,iFineCol=$) + { + local iQflags, iRflags, iHeightF, iHeightFWD, iHeightC, iServerID; + + if iRow = $ + OR iCol = $ + OR iFineRow = $ + OR iFineCol = $ + { + return AREA_OUTSIDE; + } + + iQflags = LIQ_GET_SECTORINFO; + + if GetLocationInfoBSP( + prmRoom, iQflags, iRow, iCol, iFineRow, iFineCol, + *iRflags, *iHeightF, *iHeightFWD, *iHeightC, *iServerID) + { + if iServerID = CHEST_PASSAGE_SECTOR + OR iServerID = HALL_SECTORS_SOUTH + OR iServerID = HALL_SECTORS_NORTH + { + return AREA_INSIDE; + } + + if iServerID = LEDGE_SECTORS + { + return AREA_LEDGES; + } + } + + return AREA_OUTSIDE; + } + + ReqSomethingAttack(what = $,victim = $) + "No one may attack through the door if it's closed." + { + local iVictimSection, iAttackerSection; + + if (what = $) or (victim = $) + { + propagate; + } + + iVictimSection = Send(self,@GetSection,#who=victim, + #iRow=Send(victim,@GetRow), + #iCol=Send(victim,@GetCol), + #iFineRow=Send(victim,@GetFineRow), + #iFineCol=Send(victim,@GetFineCol)); + iAttackerSection = Send(self,@GetSection,#who=what, + #iRow=Send(what,@GetRow), + #iCol=Send(what,@GetCol), + #iFineRow=Send(what,@GetFineRow), + #iFineCol=Send(what,@GetFineCol)); + + % The ledges are not protected from direct attacks from outside. + % Ledges will not get hit by earthquakes and other area attacks, though, + % and vice versa (no quaking from ledges) + if iAttackerSection = AREA_OUTSIDE + { + if iVictimSection = AREA_LEDGES + { + return TRUE; + } + } + if iVictimSection = AREA_OUTSIDE + { + if iAttackerSection = AREA_LEDGES + { + return TRUE; + } + } + + propagate; + } + + SomethingMoved(what = $,new_row = $, new_col = $) + { + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTBRIDGE), + #new_row=3,#new_col=3,#fine_row=50,#fine_col=9, + #new_angle=ANGLE_EAST); + return; + } + + if new_row > (Send(self,@GetRoomRows)-1) and (new_col < 30) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER1), + #new_row=5,#new_col=7,#new_angle=ANGLE_SOUTH_EAST); + return; + } + + if new_row > (Send(self,@GetRoomRows)-1) and (new_col > 50) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTRIVER1), + #new_row=3,#new_col=33,#fine_col=44,#new_angle=ANGLE_SOUTH); + + return; + } + + propagate; + } + + CreateStandardObjects() + { + local i, o; + + foreach i in [ + + [ 32, 111, 46, 14, OO_BONES ] + + ] + { + o = create(&OrnamentalObject,#type=nth(i,5)); + send(self,@NewHold,#what=o, + #new_row=nth(i,1),#new_col=nth(i,2), + #fine_row=nth(i,3),#fine_col=nth(i,4)); + } + + % southeast hermit + Send(self,@NewHold,#what=Create(&Firepit,#iSoundRadius=0), + #new_row=52,#new_col=114,#fine_row=59,#fine_col=35); + Send(self,@NewHold,#what=Create(&NomadWiseMan), + #new_row=52,#new_col=115,#fine_row=33, #fine_col=52, + #new_angle=ANGLE_WEST); + + % northeast cave + Send(self,@NewHold,#what=Create(&Firepit,#iSoundRadius=0), + #new_row=31,#new_col=116,#fine_row=59,#fine_col=25); + Send(self,@NewHold,#what=Create(&Firepit,#iSoundRadius=0), + #new_row=36,#new_col=122,#fine_row=50,#fine_col=5); + Send(self,@NewHold,#what=Create(&NomadWiseMan), + #new_row=37,#new_col=123,#fine_row=42, #fine_col=49, + #new_angle=ANGLE_WEST); + Send(self,@NewHold,#what=Create(&NomadWiseMan), + #new_row=37,#new_col=119,#fine_row=46, #fine_col=44, + #new_angle=ANGLE_WEST); + + % north cave + Send(self,@NewHold,#what=Create(&Firepit,#iSoundRadius=0), + #new_row=14,#new_col=106,#fine_row=59,#fine_col=55); + Send(self,@NewHold,#what=Create(&NomadWiseMan), + #new_row=14,#new_col=107,#fine_row=7, #fine_col=22, + #new_angle=ANGLE_WEST); + + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=98, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=99, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=100, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=101, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=102, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=103, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=104, + #fine_row=15,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=28,#new_col=105, + #fine_row=15,#fine_col=7); + + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=98, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=99, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=100, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=101, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=102, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=103, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=104, + #fine_row=48,#fine_col=7); + Send(self,@NewHold,#what=Create(&Pillar),#new_row=24,#new_col=105, + #fine_row=48,#fine_col=7); + + Send(self,@NewHold,#what=Create(&Brazier),#new_row=67,#new_col=104, + #fine_row=32,#fine_col=32); + Send(self,@NewHold,#what=Create(&Brazier),#new_row=68,#new_col=96, + #fine_row=57,#fine_col=58); + Send(self,@NewHold,#what=Create(&Brazier),#new_row=71,#new_col=81, + #fine_row=22,#fine_col=40); + + Send(self,@NewHold,#what=Create(&Brazier),#new_row=19,#new_col=88, + #fine_row=37,#fine_col=35); + + Send(self,@NewHold,#what=Create(&Brazier),#new_row=28,#new_col=106, + #fine_row=15,#fine_col=19); + Send(self,@NewHold,#what=Create(&Brazier),#new_row=24,#new_col=106, + #fine_row=48,#fine_col=19); + + % Chest room + Send(self,@NewHold,#what=Create(&Brazier),#new_row=16,#new_col=79, + #fine_row=16,#fine_col=48); + Send(self,@NewHold,#what=Create(&Chest),#new_row=17,#new_col=80, + #fine_row=61,#fine_col=9,#new_angle=ANGLE_EAST); + Send(self,@NewHold,#what=Create(&Chest),#new_row=20,#new_col=80, + #fine_row=37,#fine_col=9,#new_angle=ANGLE_EAST); + Send(self,@NewHold,#what=Create(&Chest),#new_row=22,#new_col=80, + #fine_row=46,#fine_col=9,#new_angle=ANGLE_EAST); + + propagate; + } + + OpenEntranceDoor() + { + if ptEntrance = $ + { + Send(self,@TimeStampDoor); + Send(self,@SetSector,#sector=ENTRANCE_SECTOR2, + #animation=ANIMATE_CEILING_LIFT,#height=80,#speed=40); + ptEntrance = CreateTimer(self,@CloseEntranceDoor,LIFT_DELAY); + Send(self,@OpenEntranceDoorSound); + } + + return; + } + + CloseEntranceDoor() + { + Send(self,@SetSector,#sector=ENTRANCE_SECTOR2, + #animation=ANIMATE_CEILING_LIFT,#height=0,#speed=40); + Send(self,@CloseEntranceDoorSound); + + % We now propagate this to handle removing players from the door, + % and so the door can still be entered while it is closing. + propagate; + } + + OpenSecretDoor() + { + if ptSecret = $ + { + Send(self,@SetSector,#sector=CHEST_PASSAGE_SECTOR, + #animation=ANIMATE_FLOOR_LIFT,#height=290,#speed=16); + Send(self,@SetSector,#sector=CHEST_PASSAGE_SECTOR, + #animation=ANIMATE_FLOOR_LIFT,#height=290,#speed=16); + ptSecret = CreateTimer(self,@CloseSecretDoor,LIFT_DELAY); + Send(self,@OpenSecretDoorSound); + } + + return; + } + + CloseSecretDoor() + { + ptSecret = $; + Send(self,@SetSector,#sector=CHEST_PASSAGE_SECTOR, + #animation=ANIMATE_FLOOR_LIFT,#height=350,#speed=16); + Send(self,@SetSector,#sector=CHEST_PASSAGE_SECTOR, + #animation=ANIMATE_FLOOR_LIFT,#height=350,#speed=16); + Send(self,@CloseSecretDoorSound); + + return; + } + + FoyerDissipatesWallElements() + { + return FALSE; + } + + StartSnow(bOnGround=TRUE) + { + if bOnGround + { + if pbSnowGroundTexture + { + % Turn any water to ice. Water tagged as ID 13. + % Water sidedefs tagged as ID 14. + Send(self,@ChangeTexture,#id=13,#new_texture=61017, + #flags=CTF_FLOOR); + % Stop vertically scrolling water from animating. + Send(self,@AnimateWall,#wall=14,#animation=ANIMATE_NONE, + #first_group=1,#second_group=1,#end_group=1); + % Change texture. + Send(self,@ChangeTexture,#id=14,#new_texture=61017, + #flags=CTF_NORMALWALL); + % Stop water scroll effect and remove depth. + Send(self,@SetSectorFlags,#sector=13,#depth=SF_DEPTH0, + #scrollSpeed=SCROLL_NONE); + + + % Give snow some depth and walking sounds. + Send(self,@SetSectorFlags,#sector=0,#depth=SF_DEPTH1, + #scrollSpeed=SCROLL_NONE); + vrWading_Sound = snow_walk_sound; + % Remove waterfall sound. + Send(self,@RemoveLoopingSound,#sound_rsc=waterfall_sound_oasis); + } + } + + propagate; + } + + EndSnow(override=FALSE) + "Use override if rooms have manually placed textures." + { + if pbSnowGroundTexture + OR override + { + % Remove sludge water textures. + Send(self,@RemoveTextureChange,#id=13); + % Remove sludge water sides. + Send(self,@RemoveTextureChange,#id=14); + Send(self,@RemoveAnimateWallChange,#wall=14); + % Reset sector flag changes to sludge water. + Send(self,@RemoveSectorFlagChange,#id=13); + + % Remove snow depth and walking sounds. + Send(self,@RemoveSectorFlagChange,#id=0); + vrWading_Sound = water_wading_sound; + + % Begin waterfall sound. + Send(self,@AddLoopingSound, + #lSoundData=[waterfall_sound_oasis, 21, 40, 65, 100]); + } + + propagate; + } + + StartRain() + { + % Acid rain darkens the sand and pock marks rock. + Send(self,@ChangeTexture,#id=0,#new_texture=09080,#flags=CTF_FLOOR); + % Acid rain makes the water gross. + Send(self,@ChangeTexture,#id=13,#new_texture=09363, + #flags=CTF_FLOOR); + Send(self,@ChangeTexture,#id=14,#new_texture=09363, + #flags=CTF_NORMALWALL); + Send(self,@SetSectorFlags,#sector=13,#depth=SF_DEPTH1, + #scrollSpeed=SCROLL_SLOW); + propagate; + } + + EndRain() + { + % Remove gross sand. + Send(self,@RemoveTextureChange,#id=0); + % Remove sludge water textures. + Send(self,@RemoveTextureChange,#id=13); + % Remove sludge water sides. + Send(self,@RemoveTextureChange,#id=14); + % Reset sector flag changes to sludge water. + Send(self,@RemoveSectorFlagChange,#id=13); + propagate; + } + + GetRegion() + { + return RID_DESERT; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/desert/waylayoasis.lkod b/kod/object/active/holder/room/ghall/waylayoasis.lkod similarity index 100% rename from kod/object/active/holder/room/desert/waylayoasis.lkod rename to kod/object/active/holder/room/ghall/waylayoasis.lkod diff --git a/kod/object/active/holder/room/monsroom/desertafterlife.kod b/kod/object/active/holder/room/monsroom/desertafterlife.kod new file mode 100644 index 0000000000..7c1279df6f --- /dev/null +++ b/kod/object/active/holder/room/monsroom/desertafterlife.kod @@ -0,0 +1,225 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertAfterlife is MonsterRoom + +% The desert is extremely far away from Meridian. +% So far, in fact, that they do not have the same afterlife. +% +% This afterlife contains some interesting things: +% - The demon Xaerdun, who will recover your items and send you back for a fee. +% Being the demon of Greed, he charges exorbitant prices, but he's also +% addicted to making a sale. The longer a player waits, the less he'll +% charge. (His corpse recovery also is the only way to get your items out +% of certain deathtraps like the Abyssal Bore.) +% Subtly, Xaerdun's item recovery brings those items to you while you +% are still in the afterlife, enabling you to adventure therein. +% Xaerdun takes from your bank account(s). If a player has absolutely +% no money, he/she is trapped in the afterlife until someone defeats +% a certain dungeon (or if the dungeon is already defeated, resurrection +% is free). +% +% - The wastes contain strange undying monsters. +% +% - The Prism of Life (holy) to the east (sunrise). Defeating the Prism of +% Life frees all trapped souls, players and NPCs alike. +% +% - The Prism of Death (unholy) to the west (sunset). Plays an important +% role in restoring balance (in this case, the Arbiter of the Prism of +% Life seeks to give all living beings immortality, which would be a +% disaster. They are not immune to wounds, they just can't die). The +% Prism of Death and its Arbiter are not enemies; very helpful to players +% and they explain the situation at large and why the Prism of Life +% must be defeated. +% +% - The lost heroine who defeated the Lightning Prism wanders here, +% unable to rest or escape. Defeating the Prism of Life allows her to +% resurrect. + +constants: + + include blakston.khd + +resources: + + desert_afterlife_music = desertafterlife.mp3 + + cannot_teleport_in_afterlife = \ + "Lacking the proper physical and vital bonds, your " + "teleportation spell falters." + cannot_alter_death_in_afterlife = \ + "This type of death is beyond your ability to master." + cannot_eye_buff_in_afterlife = \ + "This place has neither Sun nor light. The manner in which you " + "are currently perceiving your surroundings is beyond the scope " + "of any vision spell." + cannot_blind_dazzle_in_afterlife = \ + "The manner in which your enemy is currently perceiving existence " + "is beyond the scope of any vision spell." + cannot_conveyance_in_afterlife = \ + "If you knew how to send items to your vault from this place, you would " + "be truly wise indeed." + +classvars: + + viTerrain_type = TERRAIN_BADLANDS + + % No pens or PKing in afterlife. Everyone's already dead! + viPermanent_flags = ROOM_SAFELOGOFF | ROOM_NO_PK + +properties: + + pbSnowGroundTexture = FALSE + prMusic = desert_afterlife_music + +messages: + + NewHoldObject(what = $) + { + local oGreyGhost; + + if IsClass(what,&User) + { + oGreyGhost = Send(SYS,@FindSpellByNum,#num=SID_GREY_GHOST); + + if oGreyGhost <> $ + AND NOT Send(what,@IsEnchanted,#what=oGreyGhost) + { + Post(oGreyGhost,@CastSpell,#who=self,#lTargets=[what], + #report=TRUE); + } + } + else + { + Send(what,@AddDrawingEffects,#value=DRAWFX_SECONDTRANS); + } + + propagate; + } + + LeaveHold(what = $) + { + local oGreyGhost; + + if IsClass(what,&User) + { + oGreyGhost = Send(SYS,@FindSpellByNum,#num=SID_GREY_GHOST); + + if oGreyGhost <> $ + AND Send(what,@IsEnchanted,#what=oGreyGhost) + { + Post(oGreyGhost,@CastSpell,#who=self,#lTargets=[what], + #report=TRUE); + } + } + else + { + Post(what,@RemoveDrawingEffects,#value=DRAWFX_SECONDTRANS); + if Send(what,@GetOwner) <> $ + { + Post(Send(what,@GetOwner),@SomethingChanged,#what=what); + } + } + + propagate; + } + + StartRain() + { + return; + } + + StartSnow() + { + return; + } + + GetRegion() + { + return RID_DESERTALDUNES; + } + + OverridesDeathFunction() + { + return TRUE; + } + + OverrideDeathFunction(who=$,what=$) + { + local oAfterlifeRoom; + + Send(who,@RemoveAllEnchantments,#report=FALSE); + Send(who,@SetHealth,#amount=Send(who,@GetMaxHealth)); + + oAfterlifeRoom = Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES); + Send(oAfterlifeRoom,@Teleport,#what=who); + return; + } + + ReqSpellCast(who = $, oSpell = $, lItems = $) + { + if IsClass(oSpell,&Rescue) + OR IsClass(oSpell,&Elusion) + { + Send(who,@MsgSendUser,#message_rsc=cannot_teleport_in_afterlife); + return FALSE; + } + + if IsClass(oSpell,&DeathRift) + OR IsClass(oSpell,&PortalOfLife) + OR IsClass(oSpell,&Animate) + OR IsClass(oSpell,&Defile) + { + Send(who,@MsgSendUser,#message_rsc=cannot_alter_death_in_afterlife); + return FALSE; + } + + if IsClass(oSpell,&NightVision) + OR IsClass(oSpell,&EagleEyes) + { + Send(who,@MsgSendUser,#message_rsc=cannot_eye_buff_in_afterlife); + return FALSE; + } + + if IsClass(oSpell,&Blind) + OR IsClass(oSpell,&Dazzle) + { + Send(who,@MsgSendUser,#message_rsc=cannot_blind_dazzle_in_afterlife); + return FALSE; + } + + if IsClass(oSpell,&Conveyance) + { + Send(who,@MsgSendUser,#message_rsc=cannot_conveyance_in_afterlife); + return FALSE; + } + + propagate; + } + + CanHavePlayerPortal() + { + return FALSE; + } + + LightingColorOverride() + { + % No colors if we can help it. + return TRUE; + } + + GetLightingColorOverride(what=$) + { + return LIGHT_WHITE; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes.kod b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes.kod new file mode 100644 index 0000000000..baac3a6455 --- /dev/null +++ b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes.kod @@ -0,0 +1,127 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertAfterlifeDunes is DesertAfterlife + +% This room is free of monsters. +% Players may walk east (brighter) toward the Prism of Life. +% They may also walk west (darker) toward the Prism of Death. +% The souls of centuries of dead nomads who could not afford a deal with +% Xaerdun wander here. They can be found all over the other afterlife rooms. + +constants: + + include blakston.khd + +resources: + + room_desertALdunes = desertaldunes.roo + room_name_desertALdunes = "Eternal Wastes" + + brighter_going_east_msg = \ + "It seems slightly brighter in this direction somehow." + darker_going_west_msg = \ + "It seems slightly darker in this direction somehow." + +classvars: + + vrName = room_name_desertALdunes + + viTeleport_row = 37 + viTeleport_col = 140 + +properties: + + piBaseLight = LIGHT_DARK + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_desertALdunes + piRoom_num = RID_DESERTALDUNES + + piGen_time = 30000 + piGen_percent = 80 + + piInit_count_min = 4 + piInit_count_max = 6 + + piMonster_count_max = 25 + +messages: + + CreateStandardObjects() + { + Send(self,@NewHold,#what=Create(&XaerdunAfterlife), + #new_row=56,#new_col=105,#fine_row=22, #fine_col=55, + #new_angle=ANGLE_WEST); + return; + } + + SomethingMoved(what = $,new_row = $, new_col = $) + { + if new_col > (Send(self,@GetRoomCols)-2) + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=brighter_going_east_msg); + } + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES2E), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes2e.kod b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes2e.kod new file mode 100644 index 0000000000..620ce897e6 --- /dev/null +++ b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes2e.kod @@ -0,0 +1,147 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertAfterlifeDunes2East is DesertAfterlife + +constants: + + include blakston.khd + +resources: + + room_desertALdunes2e = desertaldunes2.roo + room_name_desertALdunes2e = "Eternal Wastes" + +classvars: + + vrName = room_name_desertALdunes2e + + viTeleport_row = 37 + viTeleport_col = 140 + +properties: + + piBaseLight = LIGHT_NICE + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_desertALdunes2e + piRoom_num = RID_DESERTALDUNES2E + + piGen_time = 30000 + piGen_percent = 100 + + piInit_count_min = 25 + piInit_count_max = 25 + + piMonster_count_max = 25 + +messages: + + Constructed() + { + plMonsters = [ [&LostSoul, 100] ]; + % 120 by 204 + plGenerators = [ [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)] + ]; + + propagate; + } + + SomethingMoved(what = $,new_row = $, new_col = $) + { + if new_col > (Send(self,@GetRoomCols)-2) + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=brighter_going_east_msg); + } + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES3E), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=darker_going_west_msg); + } + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES2E), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES2E), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes3e.kod b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes3e.kod new file mode 100644 index 0000000000..475aa4b110 --- /dev/null +++ b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes3e.kod @@ -0,0 +1,147 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertAfterlifeDunes3East is DesertAfterlife + +constants: + + include blakston.khd + +resources: + + room_desertALdunes3e = desertaldunes2.roo + room_name_desertALdunes3e = "Eternal Wastes" + +classvars: + + vrName = room_name_desertALdunes3e + + viTeleport_row = 37 + viTeleport_col = 140 + +properties: + + piBaseLight = LIGHT_MAX + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_desertALdunes3e + piRoom_num = RID_DESERTALDUNES3E + + piGen_time = 30000 + piGen_percent = 100 + + piInit_count_min = 25 + piInit_count_max = 25 + + piMonster_count_max = 25 + +messages: + + Constructed() + { + plMonsters = [ [&LostSoul, 100] ]; + % 120 by 204 + plGenerators = [ [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)] + ]; + + propagate; + } + + SomethingMoved(what = $,new_row = $, new_col = $) + { + if new_col > (Send(self,@GetRoomCols)-2) + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=brighter_going_east_msg); + } + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES4E), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=darker_going_west_msg); + } + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES2E), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES3E), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES3E), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes4e.kod b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes4e.kod new file mode 100644 index 0000000000..e872529da0 --- /dev/null +++ b/kod/object/active/holder/room/monsroom/desertafterlife/desertALdunes4e.kod @@ -0,0 +1,143 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +DesertAfterlifeDunes4East is DesertAfterlife + +constants: + + include blakston.khd + +resources: + + room_desertALdunes4e = desertaldunes2.roo + room_name_desertALdunes4e = "Eternal Wastes" + +classvars: + + vrName = room_name_desertALdunes4e + + viTeleport_row = 37 + viTeleport_col = 140 + +properties: + + piBaseLight = LIGHT_MAX + piOutside_factor = OUTDOORS_5 + + piDirectional_percent = DIRECTIONAL_PERCENT_OUTDOORS + + prRoom = room_desertALdunes4e + piRoom_num = RID_DESERTALDUNES4E + + piGen_time = 30000 + piGen_percent = 100 + + piInit_count_min = 25 + piInit_count_max = 25 + + piMonster_count_max = 25 + +messages: + + Constructed() + { + plMonsters = [ [&LostSoul, 100] ]; + % 120 by 204 + plGenerators = [ [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)], + [ Random(3,117), Random(3,201)] + ]; + + propagate; + } + + SomethingMoved(what = $,new_row = $, new_col = $) + { + if new_col > (Send(self,@GetRoomCols)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES4E), + #new_row=Send(what,@GetRow), + #new_col=3, + #fine_row=Send(what,@GetFineRow), + #fine_col=0, + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_col < 2 + { + if IsClass(what,&User) + { + Send(what,@MsgSendUser,#message_rsc=darker_going_west_msg); + } + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES3E), + #new_row=Send(what,@GetRow), + #new_col=Send(self,@GetRoomCols)-3, + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row < 2 + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES4E), + #new_row=Send(self,@GetRoomRows)-3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + if new_row > (Send(self,@GetRoomRows)-2) + { + Send(SYS,@UtilGoNearSquare,#what=what, + #where=Send(SYS,@FindRoomByNum,#num=RID_DESERTALDUNES4E), + #new_row=3, + #new_col=Send(what,@GetCol), + #fine_row=0, + #fine_col=Send(what,@GetFineCol), + #new_angle=Send(what,@GetAngle)); + return; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + diff --git a/kod/object/active/holder/room/monsroom/desertafterlife/makefile b/kod/object/active/holder/room/monsroom/desertafterlife/makefile new file mode 100644 index 0000000000..5b50e20b6b --- /dev/null +++ b/kod/object/active/holder/room/monsroom/desertafterlife/makefile @@ -0,0 +1,12 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\desertafterlife.bof + +BOFS = desertaldunes.bof desertaldunes2e.bof desertaldunes3e.bof \ + desertaldunes4e.bof + +!include $(KODDIR)\kod.mak diff --git a/kod/object/active/holder/room/monsroom/elementaldungeon1.kod b/kod/object/active/holder/room/monsroom/elementaldungeon1.kod new file mode 100644 index 0000000000..fbb94e18a4 --- /dev/null +++ b/kod/object/active/holder/room/monsroom/elementaldungeon1.kod @@ -0,0 +1,96 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ElementalDungeon1 is MonsterRoom + +constants: + + include blakston.khd + +resources: + + room_name_elementaldungeon1 = "Prism of Fire" + room_elementaldungeon1 = elementaldungeon1.roo + elementaldungeon1_music = cave.mp3 + + fire_prism_phase_out = \ + "You phase out of the elemental plane of fire, and are drawn along " + "the natural curvature of the region's mana flows. Singed, but alive, " + "you find yourself at a familiar oasis." + +classvars: + + vrName = room_name_elementaldungeon1 + + viTeleport_row = 2 + viTeleport_col = 2 + + +properties: + + prRoom = room_elementaldungeon1 + piRoom_num = RID_ELEMENTAL_DUNGEON1 + + piBaseLight = LIGHT_MIN + piOutside_factor = 0 + + prMusic = elementaldungeon1_music + + piGen_time = 30000 + piGen_percent = 80 + + piInit_count_min = 4 + piInit_count_max = 6 + + piMonster_count_max = 25 + +messages: + + Constructed() + { + plMonsters = [ [&XeoFire, 100] ]; + + plGenerators = [ [22, 24], [61, 20], [47, 68], [15, 37], [12,61], + [31, 52], [64, 46], [33, 34], [35, 18] ]; + propagate; + } + + FirstUserEntered() + { + propagate; + } + + LastUserLeft() + { + propagate; + } + + ReqSpellCast(who = $, oSpell = $) + { + local iQflags, iRflags, iHeightF, iHeightFWD, iHeightC, iServerID; + + if IsClass(oSpell,&Phase) + { + if NOT IsClass(who,&Player) + { + propagate; + } + + Post(who,@MsgSendUser,#message_rsc=fire_prism_phase_out); + Post(Send(SYS,@FindRoomByNum,#num=RID_WAYLAYOASIS), + @Teleport,#what=who); + return FALSE; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/monsroom/elementaldungeon2.kod b/kod/object/active/holder/room/monsroom/elementaldungeon2.kod new file mode 100644 index 0000000000..1d7828ce1e --- /dev/null +++ b/kod/object/active/holder/room/monsroom/elementaldungeon2.kod @@ -0,0 +1,274 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ElementalDungeon2 is MonsterRoom + +% Platforms appear as players approach, disappear as they move. +% Falling boots you (the essence of lightning catches you). +% Air xeo'chicatls do spawn and attack, but the boss isdead. +% Items that drop fall and are gone forever. + +constants: + + include blakston.khd + + START_SECTOR = 1 + ESCAPE_SECTOR = 36 + +resources: + + room_name_elementaldungeon2 = "Prism of Lightning" + room_elementaldungeon2 = elementaldungeon2.roo + elementaldungeon2_music = cave.mp3 + + item_dropped_msg = \ + "The %s falls into endless eternities of air!" + +classvars: + + vrName = room_name_elementaldungeon2 + + viTeleport_row = 5 + viTeleport_col = 5 + + +properties: + + prRoom = room_elementaldungeon2 + piRoom_num = RID_ELEMENTAL_DUNGEON2 + + piBaseLight = LIGHT_MIN + piOutside_factor = 0 + + prMusic = elementaldungeon2_music + + piGen_time = 30000 + piGen_percent = 80 + + piInit_count_min = 4 + piInit_count_max = 6 + + piMonster_count_max = 25 + + piDropPlatformTime = 4000 + plDropPlatforms = $ + + piCheckForFallsTime = 250 + ptCheckForFallsTimer = $ + +messages: + + Constructed() + { + local iPlatformNum; + + plMonsters = [ [&XeoAir, 100] ]; + + plGenerators = [ [22, 24], [61, 20], [47, 68], [15, 37], [12,61], + [31, 52], [64, 46], [33, 34], [35, 18] ]; + + iPlatformNum = 2; + while iPlatformNum <= 35 + { + Send(self,@SetSector,#sector=iPlatformNum, + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + iPlatformNum++; + } + + propagate; + } + + DropPlatform(timer=$) + { + local i; + + foreach i in plDropPlatforms + { + if Nth(i,2) = timer + { + SetNth(i,2,$); + Send(self,@SetSector,#sector=First(i), + #animation=ANIMATE_FLOOR_LIFT,#height=0,#speed=0); + plDropPlatforms = DelListElem(plDropPlatforms,i); + } + } + return; + } + + FirstUserEntered() + { + ptCheckForFallsTimer = CreateTimer(self,@CheckForFalls, + piCheckForFallsTime); + propagate; + } + + LastUserLeft() + { + if ptCheckForFallsTimer <> $ + { + DeleteTimer(ptCheckForFallsTimer); + ptCheckForFallsTimer = $; + } + propagate; + } + + CheckForFalls() + { + local i, iQflags, iRflags, iHeightF, iHeightFWD, iHeightC, iServerID, + cur_row, cur_col, fine_row, fine_col; + + ptCheckForFallsTimer = $; + ptCheckForFallsTimer = CreateTimer(self,@CheckForFalls, + piCheckForFallsTime); + + foreach i in plActive + { + if IsClass(First(i),&User) + { + iQflags = LIQ_GET_SECTORINFO; + + cur_row = Send(First(i),@GetRow); + cur_col = Send(First(i),@GetCol); + fine_row = Send(First(i),@GetFineRow); + fine_col = Send(First(i),@GetFineCol); + if GetLocationInfoBSP( + prmRoom, iQflags, cur_row, cur_col, fine_row, fine_col, + *iRflags, *iHeightF, *iHeightFWD, *iHeightC, *iServerID) + { + if iServerID = 80 + OR Send(First(i),@GetHeightAtObject) = 0 + { + Send(self,@BootPlayer,#who=First(i)); + } + } + } + } + return; + } + + BootPlayer(who=$) + { + Send(who,@AdminGoToSafety); + return; + } + + Delete() + { + local i; + + foreach i in plDropPlatforms + { + DeleteTimer(Nth(i,2)); + SetNth(i,2,$); + plDropPlatforms = DelListElem(plDropPlatforms,i); + } + if ptCheckForFallsTimer <> $ + { + DeleteTimer(ptCheckForFallsTimer); + ptCheckForFallsTimer = $; + } + propagate; + } + + NewHold(what = $) + { + local i, each_obj; + + % Anything you drop is lost forever! + if IsClass(what,&Item) + { + foreach i in plActive + { + each_obj = Send(self,@HolderExtractObject,#data=i); + if IsClass(each_obj,&Player) + { + Send(each_obj,@MsgSendUser,#message_rsc=item_dropped_msg, + #parm1=Send(what,@GetName)); + } + } + Post(what,@Delete); + } + propagate; + } + + SomethingMoved(what=$,new_row=0,new_col=0,fine_row=0,fine_col=0) + { + local iQflags, iRflags, iHeightF, iHeightFWD, iHeightC, iServerID, i, + bFound; + + if NOT IsClass(what,&Player) + { + propagate; + } + + iQflags = LIQ_GET_SECTORINFO; + + if GetLocationInfoBSP( + prmRoom, iQflags, new_row, new_col, fine_row, fine_col, + *iRflags, *iHeightF, *iHeightFWD, *iHeightC, *iServerID) + { + if iServerID > 2 + { + Send(self,@SetSector,#sector=iServerID-1, + #animation=ANIMATE_FLOOR_LIFT,#height=5500,#speed=0); + bFound = FALSE; + foreach i in plDropPlatforms + { + if First(i) = iServerID-1 + { + bFound = TRUE; + } + } + + if NOT bFound + { + plDropPlatforms = Cons([iServerID-1, + CreateTimer(self,@DropPlatform,piDropPlatformTime)], + plDropPlatforms); + } + } + + if iServerID < 35 + { + Send(self,@SetSector,#sector=iServerID+1, + #animation=ANIMATE_FLOOR_LIFT,#height=5500,#speed=0); + bFound = FALSE; + foreach i in plDropPlatforms + { + if First(i) = iServerID+1 + { + bFound = TRUE; + } + } + + if NOT bFound + { + plDropPlatforms = Cons([iServerID+1, + CreateTimer(self,@DropPlatform,piDropPlatformTime)], + plDropPlatforms); + } + } + + if iServerID = 36 + { + Send(self,@ReachedExit,#who=what); + } + } + + propagate; + } + + ReachedExit(who=$) + { + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/monsroom/elementaldungeon3.kod b/kod/object/active/holder/room/monsroom/elementaldungeon3.kod new file mode 100644 index 0000000000..8da099c500 --- /dev/null +++ b/kod/object/active/holder/room/monsroom/elementaldungeon3.kod @@ -0,0 +1,80 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ElementalDungeon3 is MonsterRoom + +constants: + + include blakston.khd + +resources: + + room_name_elementaldungeon3 = "Prism of Ice" + room_elementaldungeon3 = elementaldungeon3.roo + elementaldungeon3_music = cave.mp3 + +classvars: + + vrName = room_name_elementaldungeon3 + + viTeleport_row = 3 + viTeleport_col = 3 + + +properties: + + prRoom = room_elementaldungeon3 + piRoom_num = RID_ELEMENTAL_DUNGEON3 + + piBaseLight = LIGHT_MIN + piOutside_factor = 0 + + prMusic = elementaldungeon3_music + + piGen_time = 30000 + piGen_percent = 80 + + piInit_count_min = 4 + piInit_count_max = 6 + + piMonster_count_max = 25 + +messages: + + Constructed() + { + plMonsters = [ [&XeoWater, 100] ]; + + plGenerators = [ [22, 24], [61, 20], [47, 68], [15, 37], [12,61], + [31, 52], [64, 46], [33, 34], [35, 18] ]; + propagate; + } + + FirstUserEntered() + { + propagate; + } + + LastUserLeft() + { + propagate; + } + + CreateStandardExits() + { + plEdge_Exits = $; + plEdge_Exits = Cons([LEAVE_EAST, RID_ELEMENTAL_DUNGEON1, 5, 5, + ROTATE_NONE], plEdge_exits); + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/active/holder/room/monsroom/makefile b/kod/object/active/holder/room/monsroom/makefile index b70d5f2865..ee777488aa 100644 --- a/kod/object/active/holder/room/monsroom/makefile +++ b/kod/object/active/holder/room/monsroom/makefile @@ -18,8 +18,9 @@ BOFS = badland1.bof a5.bof a6.bof c4.bof c6.bof castle1b.bof d4.bof d5.bof \ kcforest.bof orccav1x.bof orccav5x.bof lichmaze.bof necarea1.bof necarea2.bof \ necarea3.bof necare3a.bof necare3b.bof necarea4.bof necarea5.bof \ marcryp1.bof marcryp2.bof oldjas.bof oldmar.bof oldbarln.bof oldbarls.bof \ - marcry3a.bof castle2b.bof castle2c.bof throne2.bof survivalroom.bof - + marcry3a.bof castle2b.bof castle2c.bof throne2.bof survivalroom.bof \ + elementaldungeon1.bof elementaldungeon2.bof elementaldungeon3.bof \ + desertafterlife.bof # g7.bof h8.bof i2.bof i4.bof i5.bof \ # j4.bof j5.bof j6.bof j7.bof j8.bof j9.bof k3.bof \ diff --git a/kod/object/active/wallelem.kod b/kod/object/active/wallelem.kod index 9af4851c4f..b86bf7d928 100644 --- a/kod/object/active/wallelem.kod +++ b/kod/object/active/wallelem.kod @@ -331,7 +331,8 @@ messages: LocationCheck(where = $) { if (IsClass(where,&GuildHall) - AND Send(where,@InFoyer,#who=poCaster)) + AND Send(where,@InFoyer,#who=poCaster) + AND Send(where,@FoyerDissipatesWallElements)) OR (Send(where,@IsArena) AND NOT Send(where,@InPlay,#what=self,#bNonPlayerOkay=TRUE)) { diff --git a/kod/object/active/wallelem/wallfire.kod b/kod/object/active/wallelem/wallfire.kod index ab68bd7d7b..bf9d661c2a 100644 --- a/kod/object/active/wallelem/wallfire.kod +++ b/kod/object/active/wallelem/wallfire.kod @@ -236,6 +236,13 @@ messages: AddPacket(1,5); } + if Send(self,@GetOwner) <> $ + AND Send(Send(self,@GetOwner),@LightingColorOverride) + { + AddPacket(2,Send(Send(self,@GetOwner),@GetLightingColorOverride)); + return; + } + % Red color AddPacket(2,LIGHT_RED); diff --git a/kod/object/active/wallelem/wallltng.kod b/kod/object/active/wallelem/wallltng.kod index d08edf9b89..2510936f54 100644 --- a/kod/object/active/wallelem/wallltng.kod +++ b/kod/object/active/wallelem/wallltng.kod @@ -195,6 +195,14 @@ messages: AddPacket(2,(LIGHT_FLAG_ON | LIGHT_FLAG_DYNAMIC)); % 5 out of 255 intensity of light AddPacket(1,5); + + if Send(self,@GetOwner) <> $ + AND Send(Send(self,@GetOwner),@LightingColorOverride) + { + AddPacket(2,Send(Send(self,@GetOwner),@GetLightingColorOverride)); + return; + } + % Lightning color AddPacket(2,LIGHT_LIGHTNING); diff --git a/kod/object/item/passitem/pforget/forgetxwi.kod b/kod/object/item/passitem/pforget/forgetxwi.kod new file mode 100644 index 0000000000..6fe60d0183 --- /dev/null +++ b/kod/object/item/passitem/pforget/forgetxwi.kod @@ -0,0 +1,76 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ForgetPotionXaerdunWitchery is ForgetPotion + +constants: + + include blakston.khd + +resources: + + forgetpotionXaerdunWitchery_desc_rsc = \ + "Quaffing this elixir will break your alliance with Xaerdun, " + "and erase any knowledge of his granted powers from your mind." + +classvars: + + viForget_school = SS_WITCHERY + viForget_subschool = SS_WITCHERY_XAERDUN + vrDesc = forgetpotionXaerdunWitchery_desc_rsc + +properties: + +messages: + + QuaffTimer() + { + local i, lSpells, oSpell, iSpellnum; + + ptQuaff = $; + lSpells = Send(poApply_target,@GetSpellList); + piCount = 0; + + foreach i in lSpells + { + iSpellNum = send(poApply_target,@DecodeSpellNum,#compound=i); + oSpell = send(SYS,@FindSpellByNum,#num=iSpellNum); + + if Send(oSpell,@GetSchool) = viForget_school + + %%% ??? whats this? + %AND Send(oSpell,@GetSubSchool) = viForget_subschool + + AND Send(oSpell,@CanForget) + { + send(poApply_target,@RemoveSpell,#num=iSpellNum); + send(poApply_target,@MsgSendUser,#message_rsc=forgetpotion_forget_spell, + #parm1=send(oSpell,@GetName)); + piCount = piCount + 1; + } + } + + if piCount = 0 + { + Send(poApply_target,@MsgSendUser,#message_rsc=forgetpotion_nothing_happens); + } + else + { + send(self,@DoSideEffects); + send(poApply_target,@ToCliStats,#group=3); + } + + Send(self,@Delete); + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/item/passitem/pforget/makefile b/kod/object/item/passitem/pforget/makefile index fc658a8dad..6b502f5f7a 100644 --- a/kod/object/item/passitem/pforget/makefile +++ b/kod/object/item/passitem/pforget/makefile @@ -6,6 +6,6 @@ DEPEND = ..\pforget.bof BOFS = forgetsk.bof forgetfr.bof forgetsh.bof forgetkr.bof forgetqr.bof \ - forgetrj.bof forgetjl.bof + forgetrj.bof forgetjl.bof forgetxwi.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/passive/flikerer/dynlight.kod b/kod/object/passive/flikerer/dynlight.kod index d346173d4b..788d57bedf 100644 --- a/kod/object/passive/flikerer/dynlight.kod +++ b/kod/object/passive/flikerer/dynlight.kod @@ -152,5 +152,10 @@ messages: propagate; } + GetIntensity() + { + return piLightIntensity; + } + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/fogcloud.kod b/kod/object/passive/fogcloud.kod index aec8edda47..76aae8298d 100644 --- a/kod/object/passive/fogcloud.kod +++ b/kod/object/passive/fogcloud.kod @@ -43,6 +43,12 @@ messages: constructor(Caster=$,Duration=75) { local iDuration; + + if Duration=$ + { + propagate; + } + iDuration = Random(Duration-15,Duration+15); iDuration = iDuration * 750; iDuration = bound(iDuration,22500,150000); diff --git a/kod/object/passive/lightningprism.kod b/kod/object/passive/lightningprism.kod new file mode 100644 index 0000000000..4cd08537d4 --- /dev/null +++ b/kod/object/passive/lightningprism.kod @@ -0,0 +1,76 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LightningPrism is PassiveObject + +constants: + + include blakston.khd + +resources: + + lightningprism_name_rsc = "essence of lightning" + lightningprism_icon_rsc = lightningprismhuge.bgf + default_lightningprism_desc_rsc = \ + "This raw pulsing sphere of energy appears kin to Meridian's mana nodes," + " but there is clearly something otherworldly about it. It as if " + "the pure concept of lightning has been made real in the physical " + "plane. It is impossible to guess what might happen if you were to touch " + "it." + + lightningprism_not_in_range = \ + "You are not close enough to touch the essence of lightning." + lightningprism_touched = \ + "You touch the essence of lightning, and a massive burst of energy " + "tears through you!" + +classvars: + + vrName = lightningprism_name_rsc + + viObject_flags = OF_ACTIVATABLE + +properties: + + vrDesc = default_lightningprism_desc_rsc + vrIcon = lightningprism_icon_rsc + + piDrawEffectFlag = OF_FLICKERING + piRange = 3 + +messages: + + SendAnimation() + { + AddPacket(1,ANIMATE_CYCLE, 4,150, 2,1, 2,4); + + return; + } + + TryActivate(who=$) + "Return False only if you want user.kod to send its own error message to user." + { + if (abs(send(who,@getrow)-send(self,@getrow)) > piRange) + or (abs(send(who,@getcol)-send(self,@getcol)) > piRange) + { + Send(who,@MsgSendUser,#message_rsc=lightningprism_not_in_range); + return TRUE; + } + + Send(who,@MsgSendUser,#message_rsc=lightningprism_touched); + Post(Send(SYS,@FindRoomByNum,#num=RID_ELEMENTAL_DUNGEON2), + @Teleport,#what=who); + Post(who,@EffectSendUserDuration,#effect=EFFECT_WHITEOUT,#duration=1250); + + return TRUE; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/makefile b/kod/object/passive/makefile index 6bb62262f6..2d29fec1db 100644 --- a/kod/object/passive/makefile +++ b/kod/object/passive/makefile @@ -19,6 +19,7 @@ BOFS = tree.bof spell.bof boulder.bof news.bof trestype.bof arsign01.bof \ pltngwll.bof nectome.bof modlnode.bof wrmtrail.bof fogcloud.bof \ bell.bof dflycage.bof pillow.bof ghostitm.bof ventstem.bof shrinfog.bof \ psporec.bof evntsign.bof maillist.bof objectatt.bof roomflagicon.bof \ - heatshimmer.bof questtemplate.bof + qormastree.bof snowman.bof heatshimmer.bof questtemplate.bof \ + lightningprism.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/passive/objectatt.kod b/kod/object/passive/objectatt.kod index f47d79d9eb..9891a3b938 100644 --- a/kod/object/passive/objectatt.kod +++ b/kod/object/passive/objectatt.kod @@ -137,6 +137,11 @@ messages: return iModifier; } + ReqSpellCast() + { + return TRUE; + } + AppendDesc() { return; diff --git a/kod/object/passive/objectatt/battlerobjectatt.kod b/kod/object/passive/objectatt/battlerobjectatt.kod new file mode 100644 index 0000000000..7549272e1a --- /dev/null +++ b/kod/object/passive/objectatt/battlerobjectatt.kod @@ -0,0 +1,79 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +BattlerObjectAttribute is ObjectAttribute + +% These are special effects such as freeze, shock, burning. +% They are NOT spells! We do not want them interacting with spells. +% They would disrupt or alter things like Purge. + +constants: + + include blakston.khd + +resources: + + battlerobjectatt_name_rsc = "Special Battler Attribute" + battlerobjectatt_icon_rsc = light.bgf + battlerobjectatt_desc_rsc = "There is a special effect on this battler." + +classvars: + + vrName = battlerobjectatt_name_rsc + vrIcon = battlerobjectatt_icon_rsc + vrDesc = battlerobjectatt_desc_rsc + + viShow_enchantment_icon = 0x02 + +properties: + +messages: + + ReqAddToObject(host_object=$) + { + if NOT IsClass(host_object,&Battler) + { + return FALSE; + } + return TRUE; + } + + Constructed(host_object=$) + { + if IsClass(poHostObject,&User) + { + Send(poHostObject,@ShowUsersAddEnchantment,#what=self); + } + + propagate; + } + + ShowEnchantmentIcon(type = $) + { + return viShow_enchantment_icon & 0x02; + } + + SetSpellPlayerFlag(who=$) + { + return; + } + + Delete() + { + if IsClass(poHostObject,&User) + { + Send(poHostObject,@ShowUsersRemoveEnchantment,#what=self); + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/battlerobjectatt/battlerfrozen.kod b/kod/object/passive/objectatt/battlerobjectatt/battlerfrozen.kod new file mode 100644 index 0000000000..fa173aed6e --- /dev/null +++ b/kod/object/passive/objectatt/battlerobjectatt/battlerfrozen.kod @@ -0,0 +1,174 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +BattlerFrozen is BattlerObjectAttribute + +% Battler is frozen and cannot move. Blue tint, if possible. +% This is a non-spell status effect that is dealt as a result of +% certain heavy cold damage strikes. Its duration is dependent upon +% damage dealt, and is typically very short-lived. Whereas Hold +% can last a few seconds, BattlerFrozen is typically 1 second or less. +% +% Alternatively, something is indefinitely frozen as part of a storyline +% or content experience. + +constants: + + include blakston.khd + +resources: + + frozen_begin_msg = \ + "You have been frozen!" + frozen_end_msg = \ + "The ice cracks away, and you are able to move again." + frozen_cannot_do_action_msg = \ + "You can't move! You're frozen!" + +classvars: + + viEffectStartMsg = frozen_begin_msg + viEffectEndMsg = frozen_end_msg + viRefuseMsg = frozen_cannot_do_action_msg + +properties: + + % How long does the freeze last? (often variable through constructor) + piFreezeTime = 1000 + ptFreezeTimer = $ + + % What light and color do we apply to a frozen battler? + piIntensity = 40 + piColor = LIGHT_BBLUE + + pbPlayFreezeSound = FALSE + pbPlayUnfreezeSound = FALSE + + % Our light object. + poLight = $ + +messages: + + Constructor(iDuration=$,report=TRUE,host_object=$) + { + local i; + + % $ iDuration means indefinite + if iDuration <> $ + AND iDuration > 0 + { + ptFreezeTimer = CreateTimer(self,@EndFreeze,iDuration); + } + else if piFreezeTime <> $ + AND piFreezeTime > 0 + { + ptFreezeTimer = CreateTimer(self,@EndFreeze,piFreezeTime); + } + + propagate; + } + + Constructed() + { + Send(self,@Freeze,#who=poHostObject); + propagate; + } + + Freeze(who=$) + { + local oRoom; + + oRoom = Send(poHostObject,@GetOwner); + if oRoom <> $ + { + poLight = Create(&DynamicLight,#iIntensity=piIntensity, + #iColor=piColor); + + Send(oRoom,@NewHold,#what=poLight, + #new_row=Send(who,@GetRow), + #new_col=Send(who,@GetCol), + #fine_row=Send(who,@GetFineRow), + #fine_col=Send(who,@GetFineCol)); + } + + if IsClass(who,&User) + { + Send(who,@MsgSendUser,#message_rsc=frozen_begin_msg); + Send(who,@EffectSendUser,#what=self,#effect=EFFECT_PARALYZE_ON); + return; + } + + if IsClass(who,&Monster) + { + Send(who,@SetBehaviorFlag,#flag=AI_NOMOVE,#value=TRUE); + Send(who,@SetBehaviorFlag,#flag=AI_NOFIGHT,#value=TRUE); + } + return; + } + + Unfreeze(who=$) + { + if poLight <> $ + { + Send(poLight,@Delete); + } + poLight = $; + + if IsClass(who,&User) + { + % Don't override Hold / Phase. + if NOT Send(who,@IsEnchanted,#byClass=&Hold) + AND NOT Send(who,@IsEnchanted,#byClass=&DMHold) + AND NOT Send(who,@CheckPlayerFlag,#flag=PFLAG_PHASED) + { + Send(who,@EffectSendUser,#what=self,#effect=EFFECT_PARALYZE_OFF); + } + return; + } + + if IsClass(who,&Monster) + { + Send(who,@SetBehaviorFlag,#flag=AI_NOMOVE,#value=FALSE); + Send(who,@SetBehaviorFlag,#flag=AI_NOFIGHT,#value=FALSE); + } + return; + } + + SetSpellPlayerFlag(who=$) + { + Send(who,@SetPlayerFlag,#flag=PFLAG_NO_MOVE,#value=TRUE); + Send(who,@SetPlayerFlag,#flag=PFLAG_NO_FIGHT,#value=TRUE); + Send(who,@SetPlayerFlag,#flag=PFLAG_NO_MAGIC,#value=TRUE); + + return; + } + + EndFreeze() + { + ptFreezeTimer = $; + Send(self,@Delete); + return; + } + + Delete() + { + Send(self,@Unfreeze,#who=poHostObject); + + if ptFreezeTimer <> $ + { + DeleteTimer(ptFreezeTimer); + ptFreezeTimer = $; + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/battlerobjectatt/makefile b/kod/object/passive/objectatt/battlerobjectatt/makefile new file mode 100644 index 0000000000..9086333c28 --- /dev/null +++ b/kod/object/passive/objectatt/battlerobjectatt/makefile @@ -0,0 +1,10 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\battlerobjectatt.bof +BOFS = battlerfrozen.bof + +!include $(KODDIR)\kod.mak \ No newline at end of file diff --git a/kod/object/passive/objectatt/makefile b/kod/object/passive/objectatt/makefile index 65fea42f0c..86146f37c8 100644 --- a/kod/object/passive/objectatt/makefile +++ b/kod/object/passive/objectatt/makefile @@ -5,6 +5,9 @@ !include $(TOPDIR)\common.mak DEPEND = ..\objectatt.bof -BOFS = weaponobjectatt.bof defmodobjectatt.bof +BOFS = weaponobjectatt.bof \ + defmodobjectatt.bof \ + roomobjectatt.bof \ + battlerobjectatt.bof !include $(KODDIR)\kod.mak \ No newline at end of file diff --git a/kod/object/passive/objectatt/roomobjectatt.kod b/kod/object/passive/objectatt/roomobjectatt.kod new file mode 100644 index 0000000000..88ab420850 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt.kod @@ -0,0 +1,66 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RoomObjectAttribute is ObjectAttribute + +% These are special effects in a room, such as storms and general threats. +% They are NOT spells! We do not want them interacting with spells. +% They would disrupt or alter things like Discordance. + +constants: + + include blakston.khd + +resources: + + roomobjectatt_name_rsc = "Special Room Attribute" + roomobjectatt_icon_rsc = light.bgf + roomobjectatt_desc_rsc = "There is a special event happening in this room." + +classvars: + + vrName = roomobjectatt_name_rsc + vrIcon = roomobjectatt_icon_rsc + vrDesc = roomobjectatt_desc_rsc + + viShow_enchantment_icon = 0x01 + +properties: + +messages: + + ReqAddToObject(host_object=$) + { + if NOT IsClass(host_object,&Room) + { + return FALSE; + } + return TRUE; + } + + Constructed(host_object=$) + { + Send(poHostObject,@ShowUsersAddEnchantment,#what=self); + propagate; + } + + ShowEnchantmentIcon(type = $) + { + return viShow_enchantment_icon & 0x01; + } + + Delete() + { + Send(poHostObject,@ShowUsersRemoveEnchantment,#what=self); + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/makefile b/kod/object/passive/objectatt/roomobjectatt/makefile new file mode 100644 index 0000000000..8732f49a5f --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/makefile @@ -0,0 +1,10 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\roomobjectatt.bof +BOFS = roomstorm.bof roomthreat.bof + +!include $(KODDIR)\kod.mak \ No newline at end of file diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm.kod b/kod/object/passive/objectatt/roomobjectatt/roomstorm.kod new file mode 100644 index 0000000000..393c14bfff --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm.kod @@ -0,0 +1,284 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RoomStorm is RoomObjectAttribute + +% Storms create random temporary areas of glowing light. +% They apply an effect while a player is inside them. + +constants: + + include blakston.khd + + % In ms, how often we intensify lights and apply effects + INTENSIFY_TIME = 250 + +resources: + + default_storm_begin_msg = \ + "A storm of arcane energies overtakes the area!" + default_storm_end_msg = \ + "The storm of arcane energies passes." + default_bubble_sound = flamewind.wav + default_bubble_expire_sound = whoosh04.wav + +classvars: + + viStormBeginMsg = default_storm_begin_msg + viStormEndMsg = default_storm_end_msg + vBubbleSound = default_bubble_sound + vBubbleExpireSound = default_bubble_expire_sound + +properties: + + % What buff does the storm apply? What spellpower? + piSpellID = SID_BLESS + poSpell = $ + piSpellPower = 50 + + % How long does the storm last? (ms) + % Send iDuration to constructor to change + % $ means indefinite + piStormTime = 5 * 60 * 1000 + ptStormTimer = $ + + % How many magical bubbles do we have max at one time? + piBubblesMax = 5 + % How long does each bubble last? (ms) + piBubblesDuration = 10000 + % How big are the bubbles? + piBubblesRadiusFine = 5 * 64 + % How often do we spawn bubbles? + piBubblesTime = 5000 + ptBubblesTimer = $ + + % What is the start and end brightness of our bubbles? + piIntensityMin = 0 + piIntensityMax = 255 + % What color are our bubbles? + piColor = LIGHT_BWHITE + + % These are our bubbles. + % [light object, intensity timer, intensity increment, end timer] + plBubbles = $ + + pbPlayBubbleSound = FALSE + pbPlayBubbleExpireSound = FALSE + +messages: + + Constructor(iDuration=$,iPower=$,report=TRUE,host_object=$) + { + local i; + + % $ iDuration means indefinite + if iDuration <> $ + AND iDuration > 0 + { + ptStormTimer = CreateTimer(self,@EndStorm,iDuration); + } + else if piStormTime <> $ + AND piStormTime > 0 + { + ptStormTimer = CreateTimer(self,@EndStorm,piStormTime); + } + + if iPower <> $ + { + piSpellPower = iPower; + } + + poSpell = Send(SYS,@FindSpellByNum,#num=piSpellID); + + foreach i in Send(host_object,@GetHolderActive) + { + if IsClass(First(i),&User) + AND report + { + Send(First(i),@MsgSendUser,#message_rsc=viStormBeginMsg); + } + } + + ptBubblesTimer = CreateTimer(self,@CreateBubble,piBubblesTime); + propagate; + } + + CreateBubble(timer=$) + { + local i, oLight, iIncrement; + + ptBubblesTimer = $; + if Length(plBubbles) < piBubblesMax + { + oLight = Create(&DynamicLight,#iIntensity=piIntensityMin, + #iColor=piColor); + iIncrement = Bound((piIntensityMax - piIntensityMin),0,255) / + Bound((piBubblesDuration / INTENSIFY_TIME),1,$); + + Send(poHostObject,@NewHold,#what=oLight, + #new_row=Random(1,Send(poHostObject,@GetRoomRows)-1), + #new_col=Random(1,Send(poHostObject,@GetRoomCols)-1)); + + if pbPlayBubbleSound + { + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + { + Send(First(i),@WaveSendUser,#wave_rsc=vBubbleSound, + #source_obj=oLight); + } + } + } + + plBubbles = Cons([oLight, + CreateTimer(self,@BrightenBubble,INTENSIFY_TIME), + iIncrement, + CreateTimer(self,@EndBubble,piBubblesDuration)], + plBubbles); + } + ptBubblesTimer = CreateTimer(self,@CreateBubble,piBubblesTime); + return; + } + + BrightenBubble(timer=$) + { + local i; + + foreach i in plBubbles + { + if Nth(i,2) = timer + { + SetNth(i,2,$); + + Send(First(i),@SetLight, + #iIntensity=Bound(Send(First(i),@GetIntensity)+Nth(i,3),0,255)); + + Send(self,@ApplyBubbleEffects,#what=First(i)); + + SetNth(i,2,CreateTimer(self,@BrightenBubble,INTENSIFY_TIME)); + } + } + return; + } + + ApplyBubbleEffects(what=$) + { + local i; + + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + { + if Send(First(i),@SquaredFineDistanceTo3D,#what=what) + <= piBubblesRadiusFine*piBubblesRadiusFine + { + Send(self,@ApplyDirectEffect,#who=First(i),#what=what); + } + } + } + return; + } + + ApplyDirectEffect(who=$,what=$) + { + if poSpell <> $ + AND NOT Send(who,@IsEnchanted,#what=poSpell) + { + Send(poSpell,@CastSpell,#who=what,#lTargets=[who], + #iSpellPower=piSpellPower); + } + return; + } + + EndBubble(timer=$) + { + local i, n; + + foreach i in plBubbles + { + if Nth(i,4) = timer + { + if pbPlayBubbleExpireSound + { + foreach n in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(n),&User) + { + Send(First(n),@WaveSendUser, + #wave_rsc=vBubbleExpireSound, + #row=Send(First(i),@GetRow), + #col=Send(First(i),@GetCol)); + } + } + } + Send(First(i),@Delete); + SetNth(i,1,$); + DeleteTimer(Nth(i,2)); + SetNth(i,2,$); + SetNth(i,3,$); + SetNth(i,4,$); + plBubbles = DelListElem(plBubbles,i); + } + } + + return; + } + + EndStorm() + { + local i; + + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + { + Send(First(i),@MsgSendUser,#message_rsc=viStormEndMsg); + } + } + + ptStormTimer = $; + + Send(self,@Delete); + return; + } + + Delete() + { + local i; + + if ptStormTimer <> $ + { + DeleteTimer(ptStormTimer); + ptStormTimer = $; + } + if ptBubblesTimer <> $ + { + DeleteTimer(ptBubblesTimer); + ptBubblesTimer = $; + } + + foreach i in plBubbles + { + Send(First(i),@Delete); + SetNth(i,1,$); + DeleteTimer(Nth(i,2)); + SetNth(i,2,$); + SetNth(i,3,$); + DeleteTimer(Nth(i,4)); + SetNth(i,4,$); + plBubbles = DelListElem(plBubbles,i); + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm/acidstorm.kod b/kod/object/passive/objectatt/roomobjectatt/roomstorm/acidstorm.kod new file mode 100644 index 0000000000..6170cf1ac1 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm/acidstorm.kod @@ -0,0 +1,74 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +AcidStorm is RoomStorm + +% Test storm that applies a new acid buff. + +constants: + + include blakston.khd + +resources: + + acid_storm_begin_msg = \ + "Curious acidities overwhelm the area." + acid_storm_end_msg = \ + "The acidic energies depart the area." + +classvars: + + viStormBeginMsg = acid_storm_begin_msg + viStormEndMsg = acid_storm_end_msg + +properties: + + % What buff does the storm apply? What spellpower? + piSpellID = SID_ACID_STORM_BUFF + piSpellPower = 50 + + % How long does the storm last? (ms) + % Send iDuration to constructor to change + % $ means indefinite + piStormTime = 3 * 60 * 1000 + ptStormTimer = $ + + % How many magical bubbles do we have max at one time? + piBubblesMax = 15 + % How long does each bubble last? (ms) + piBubblesDuration = 6000 + % How big are the bubbles? + piBubblesRadiusFine = 5 * 64 + % How often do we spawn bubbles? + piBubblesTime = 1000 + ptBubblesTimer = $ + + % What is the start and end brightness of our bubbles? + piIntensityMin = 0 + piIntensityMax = 255 + % What color are our bubbles? + piColor = LIGHT_BGREEN + +messages: + + Constructed() + { + Send(poHostObject,@StartRain); + propagate; + } + + Delete() + { + Send(poHostObject,@EndRain); + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm/coldstorm.kod b/kod/object/passive/objectatt/roomobjectatt/roomstorm/coldstorm.kod new file mode 100644 index 0000000000..c83fbf5ba1 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm/coldstorm.kod @@ -0,0 +1,96 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ColdStorm is RoomStorm + +% When the Prism of Ice is defeated, this storm occurs instead of cold +% damage during the desert's night phase. It gives a cold resistance buff +% and also heals vigor. + +constants: + + include blakston.khd + +resources: + + cold_storm_name_rsc = "freezing winds" + cold_storm_icon_rsc = iexfrost.bgf + cold_storm_desc_rsc = \ + "Icy winds and bursts of cold erupt from seemingly nowhere. Strangely, " + "the effects don't seem to be dangerous..." + + cold_storm_begin_msg = \ + "Freezing breezes blow through the area." + cold_storm_end_msg = \ + "The chill winds depart the area." + +classvars: + + vrName = cold_storm_name_rsc + vrIcon = cold_storm_icon_rsc + vrDesc = cold_storm_desc_rsc + + viStormBeginMsg = cold_storm_begin_msg + viStormEndMsg = cold_storm_end_msg + +properties: + + % What buff does the storm apply? What spellpower? + piSpellID = SID_COLD_STORM_BUFF + piSpellPower = 50 + + % How long does the storm last? (ms) + % Send iDuration to constructor to change + % $ means indefinite + piStormTime = 3 * 60 * 1000 + ptStormTimer = $ + + % How many magical bubbles do we have max at one time? + piBubblesMax = 15 + % How long does each bubble last? (ms) (coordinate with any sounds used) + piBubblesDuration = 7000 + % How big are the bubbles? + piBubblesRadiusFine = 5 * 64 + % How often do we spawn bubbles? + piBubblesTime = 1000 + ptBubblesTimer = $ + + % What is the start and end brightness of our bubbles? + piIntensityMin = 0 + piIntensityMax = 255 + % What color are our bubbles? + piColor = LIGHT_BBLUE + + pbPlayBubbleSound = TRUE + pbPlayBubbleExpireSound = TRUE + +messages: + + Constructed() + { + Send(poHostObject,@StartSnow); + propagate; + } + + Delete() + { + Send(poHostObject,@EndSnow); + propagate; + } + + ApplyDirectEffect(who=$) + { + Send(who,@AddExertion,#amount=-10000); + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm/gortstorm.kod b/kod/object/passive/objectatt/roomobjectatt/roomstorm/gortstorm.kod new file mode 100644 index 0000000000..f2cfb9aa22 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm/gortstorm.kod @@ -0,0 +1,62 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GortStorm is RoomStorm + +% Test storm that applies Armor of Gort. + +constants: + + include blakston.khd + +resources: + + gort_storm_begin_msg = \ + "Kraanan blesses the area with protective energies." + gort_storm_end_msg = \ + "Kraanan's favor passes from the area." + +classvars: + + viStormBeginMsg = gort_storm_begin_msg + viStormEndMsg = gort_storm_end_msg + +properties: + + % What buff does the storm apply? What spellpower? + piSpellID = SID_ARMOR_OF_GORT + piSpellPower = 50 + + % How long does the storm last? (ms) + % Send iDuration to constructor to change + % $ means indefinite + piStormTime = 3 * 60 * 1000 + ptStormTimer = $ + + % How many magical bubbles do we have max at one time? + piBubblesMax = 15 + % How long does each bubble last? (ms) + piBubblesDuration = 6000 + % How big are the bubbles? + piBubblesRadiusFine = 5 * 64 + % How often do we spawn bubbles? + piBubblesTime = 1000 + ptBubblesTimer = $ + + % What is the start and end brightness of our bubbles? + piIntensityMin = 0 + piIntensityMax = 255 + % What color are our bubbles? + piColor = LIGHT_BBLUE + +messages: + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm/heatstorm.kod b/kod/object/passive/objectatt/roomobjectatt/roomstorm/heatstorm.kod new file mode 100644 index 0000000000..4839511365 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm/heatstorm.kod @@ -0,0 +1,95 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +HeatStorm is RoomStorm + +% When the Prism of Fire is defeated, this storm occurs instead of heat +% damage during the desert's day phase. It heals and gives a fire res buff. + +constants: + + include blakston.khd + +resources: + + heat_storm_name_rsc = "searing storm" + heat_storm_icon_rsc = iwalfire.bgf + heat_storm_desc_rsc = \ + "Heated winds and spouts of flame burst from elsewhere. Strangely, " + "the effects don't seem to be dangerous..." + + heat_storm_begin_msg = \ + "A searing wind blows through the area." + heat_storm_end_msg = \ + "The heated winds depart the area." + +classvars: + + vrName = heat_storm_name_rsc + vrIcon = heat_storm_icon_rsc + vrDesc = heat_storm_desc_rsc + + viStormBeginMsg = heat_storm_begin_msg + viStormEndMsg = heat_storm_end_msg + +properties: + + % What buff does the storm apply? What spellpower? + piSpellID = SID_HEAT_STORM_BUFF + piSpellPower = 50 + + % How long does the storm last? (ms) + % Send iDuration to constructor to change + % $ means indefinite + piStormTime = 3 * 60 * 1000 + ptStormTimer = $ + + % How many magical bubbles do we have max at one time? + piBubblesMax = 15 + % How long does each bubble last? (ms) (coordinate with any sounds used) + piBubblesDuration = 7000 + % How big are the bubbles? + piBubblesRadiusFine = 5 * 64 + % How often do we spawn bubbles? + piBubblesTime = 1000 + ptBubblesTimer = $ + + % What is the start and end brightness of our bubbles? + piIntensityMin = 0 + piIntensityMax = 255 + % What color are our bubbles? + piColor = LIGHT_BRED + + pbPlayBubbleSound = TRUE + pbPlayBubbleExpireSound = TRUE + +messages: + + ApplyDirectEffect(who=$) + { + % Can supercharge health, but only sometimes. Volatile. + % Done this way so that players notice the health effects. + % It's likely they'll first encounter this storm with full health, + % so they'd have no idea otherwise. + + if Random(1,3) = 1 + { + Send(who,@GainHealth,#amount=1); + } + else + { + Send(who,@GainHealthNormal,#amount=1); + } + + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm/makefile b/kod/object/passive/objectatt/roomobjectatt/roomstorm/makefile new file mode 100644 index 0000000000..3769758acf --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm/makefile @@ -0,0 +1,10 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\roomstorm.bof +BOFS = gortstorm.bof acidstorm.bof shockstorm.bof heatstorm.bof coldstorm.bof + +!include $(KODDIR)\kod.mak \ No newline at end of file diff --git a/kod/object/passive/objectatt/roomobjectatt/roomstorm/shockstorm.kod b/kod/object/passive/objectatt/roomobjectatt/roomstorm/shockstorm.kod new file mode 100644 index 0000000000..6822df9c6f --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomstorm/shockstorm.kod @@ -0,0 +1,101 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ShockStorm is RoomStorm + +% Because the Prism of Lightning has been defeated already, this storm +% rages during the morning phase in Biskalane. It applies shock resistance +% and restores mana. + +constants: + + include blakston.khd + +resources: + + shock_storm_name_rsc = "static storm" + shock_storm_icon_rsc = iwalllit.bgf + shock_storm_desc_rsc = \ + "Electrical energies arc and soar through this room in a storm of " + "crackling mana made corporeal. Strangely, the effects don't seem to be " + "dangerous..." + + shock_storm_begin_msg = \ + "The hairs on the back of your neck stand up as static energies course " + "through the area." + shock_storm_end_msg = \ + "The static energies depart the area." + +classvars: + + vrName = shock_storm_name_rsc + vrIcon = shock_storm_icon_rsc + vrDesc = shock_storm_desc_rsc + + viStormBeginMsg = shock_storm_begin_msg + viStormEndMsg = shock_storm_end_msg + +properties: + + % What buff does the storm apply? What spellpower? + piSpellID = SID_SHOCK_STORM_BUFF + piSpellPower = 50 + + % How long does the storm last? (ms) + % Send iDuration to constructor to change + % $ means indefinite + piStormTime = 3 * 60 * 1000 + ptStormTimer = $ + + % How many magical bubbles do we have max at one time? + piBubblesMax = 15 + % How long does each bubble last? (ms) (coordinate with any sounds used) + piBubblesDuration = 7000 + % How big are the bubbles? + piBubblesRadiusFine = 5 * 64 + % How often do we spawn bubbles? + piBubblesTime = 1000 + ptBubblesTimer = $ + + % What is the start and end brightness of our bubbles? + piIntensityMin = 0 + piIntensityMax = 255 + % What color are our bubbles? + piColor = LIGHT_BWHITE + + pbPlayBubbleSound = TRUE + pbPlayBubbleExpireSound = TRUE + +messages: + + ApplyDirectEffect(who=$) + { + local bCap; + + % Can supercharge mana, but also caps it sometimes. Volatile. + % Done this way so that players notice the mana effects. + % It's likely they'll first encounter this storm with full mana, + % so they'd have no idea otherwise. + + if Random(1,20) = 1 + { + bCap = TRUE; + } + else + { + bCap = FALSE; + } + + Send(who,@GainMana,#amount=1,#bCapped=bCap); + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat.kod new file mode 100644 index 0000000000..e71264cc24 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat.kod @@ -0,0 +1,268 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RoomThreat is RoomObjectAttribute + +% Room threats do damage periodically according to certain conditions. +% For example, a blizzard might deal a player cold damage unless they are +% wearing something warm or have a resist cold ring. + +constants: + + include blakston.khd + +resources: + + default_threat_begin_msg = \ + "A threat overtakes the area." + default_threat_end_msg = \ + "The looming threat passes." + default_threat_damage_msg = \ + "Somehow, a threat damages you somewhere on your person for a generic " + "amount of damage! You feel less healthy than before!" + default_good_item_use_msg = \ + "Congratulations! You used an item this threat cares about!" + default_good_spell_use_msg = \ + "That spell was a smart idea. You're smart. I like you. I mean, the " + "threat likes you. Or hates you less, as it were. You get the idea." + default_death_msg = \ + "~B~U~k[###]~n ~B~v%s died to a generic threat! What a loser!" + +classvars: + + viThreatBeginMsg = default_threat_begin_msg + viThreatEndMsg = default_threat_end_msg + viThreatDamageMsg = default_threat_damage_msg + + viGoodItemUseMsg = default_good_item_use_msg + viGoodSpellUseMsg = default_good_spell_use_msg + + viDeathMsg = default_death_msg + +properties: + + % What type of damage do we do? + piDamageTypeAttack = 0 + piDamageTypeSpell = 0 + + % How much damage do we do? + piDamageMin = 6 + piDamageMax = 14 + + % How often do we deal damage? + pbDealDamage = TRUE + piDamageTime = 3000 + ptDamageTimer = $ + + % How long does the threat last? + piThreatTime = 10 * 60 * 1000 + ptThreatTimer = $ + + % Do we report the damage? + piReport = FALSE + + % Inform players they've done well in avoiding the threat. + plSpecialItemClasses = $ + + % We check for item use at ReqSomethingUse, so it's possible we'll + % fail to equip if something is cursed. See Constructor in subclasses. + plSpecialExemptItemClasses = $ + + % Special spell messages on a successful spell cast + plSpecialSpellClasses = $ + +messages: + + Constructor(iDuration=0,iPower=$,report=TRUE,host_object=$) + { + local i; + + % $ iDuration means indefinite + if iDuration = $ + { + ptThreatTimer = $; + } + else if iDuration > 0 + { + ptThreatTimer = CreateTimer(self,@EndThreat,iDuration); + } + else if piThreatTime <> $ + AND piThreatTime > 0 + { + ptThreatTimer = CreateTimer(self,@EndThreat,piThreatTime); + } + + foreach i in Send(host_object,@GetHolderActive) + { + if IsClass(First(i),&User) + AND report + { + Send(First(i),@MsgSendUser,#message_rsc=viThreatBeginMsg); + } + } + +% Example special items lists. Don't set here or they'll overwrite. +% plSpecialItemClasses = [&Robe, &ColdRing]; +% plSpecialExemptItemClasses = [&RingOfLethargy]; +% plSpecialSpellClasses = [&ResistCold]; + + if pbDealDamage + { + ptDamageTimer = CreateTimer(self,@DealDamage,piDamageTime); + } + + propagate; + } + + DealDamage(timer=$) + { + local i, iDamageResult; + + ptDamageTimer = $; + + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + AND Send(self,@ReqDealDamage,#who=First(i)) + { + Send(First(i),@MsgSendUser,#message_rsc=viThreatDamageMsg); + + % Some threats apply other effects. + Send(self,@ApplyDirectEffect,#who=First(i)); + + iDamageResult = Send(First(i),@AssessDamage,#what=self, + #damage=Random(piDamageMin,piDamageMax), + #atype=piDamageTypeAttack, + #aspell=piDamageTypeSpell,#report=piReport); + + % We killed someone! + if iDamageResult = $ + { + Send(First(i),@Killed); + Send(self,@SendDeathMessage,#who=First(i)); + } + } + } + + % Some threats change the time dynamically. + Send(self,@AlterDamageTime); + ptDamageTimer = CreateTimer(self,@DealDamage,piDamageTime); + return; + } + + ReqDealDamage(who=$) + { + % Alter this to create exemptions to the threat. + return TRUE; + } + + ApplyDirectEffect(who=$) + { + return; + } + + AlterDamageTime() + { + % Modify this in a subclass to change threat time dynamically. + return; + } + + EndThreat() + { + + ptThreatTimer = $; + + Send(self,@Delete); + return; + } + + Delete() + { + local i; + + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + { + Send(First(i),@MsgSendUser,#message_rsc=viThreatEndMsg); + } + } + + if ptThreatTimer <> $ + { + DeleteTimer(ptThreatTimer); + ptThreatTimer = $; + } + if ptDamageTimer <> $ + { + DeleteTimer(ptDamageTimer); + ptDamageTimer = $; + } + propagate; + } + + ReqSomethingUse(what = $, use_item = $) + { + local i, bFoundExempt; + + if IsClass(what,&Player) + { + foreach i in plSpecialExemptItemClasses + { + if Send(what,@IsUsingA,#class=i) + { + return TRUE; + } + } + + foreach i in plSpecialItemClasses + { + if IsClass(use_item,i) + { + Post(what,@MsgSendUser,#message_rsc=viGoodItemUseMsg); + return TRUE; + } + } + } + + return TRUE; + } + + SpellCast(who = $,oSpell = $) + { + local i; + + foreach i in plSpecialSpellClasses + { + if IsClass(oSpell,i) + { + Post(who,@MsgSendUser,#message_rsc=viGoodSpellUseMsg); + } + } + + propagate; + } + + SendDeathMessage(who=$) + { + local i; + + % Threats have custom death messages, for funsies. + foreach i in Send(SYS,@GetUsersLoggedOn) + { + Send(i,@MsgSendUser,#message_rsc=viDeathMsg, + #parm1=Send(who,@GetName)); + } + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/acidthreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat/acidthreat.kod new file mode 100644 index 0000000000..11ce04f38e --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/acidthreat.kod @@ -0,0 +1,130 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +AcidThreat is RoomThreat + +% Acid rain deals damage if players aren't wearing a helmet. +% Also exempted if resist acid / ring. +% Damage is small but very rapid to simulate ongoing rain drops. + +constants: + + include blakston.khd + +resources: + + acid_threat_name_rsc = "elemental acid" + acid_threat_icon_rsc = isplacid.bgf + acid_threat_desc_rsc = \ + "Acid rains down from the skies. The only protections against this " + "kind of burning agony are helmets or the appropriate resistance magics." + + acid_threat_begin_msg = \ + "Horrendously noxious rain begins to fall." + acid_threat_end_msg = \ + "The noxious rain suddenly crystalizes as a cold flash point is reached." + acid_threat_damage_msg = \ + "Without a helmet to slough it away, the acidic rain burns " + "your exposed skin." + + acid_good_item_use_msg = \ + "You sigh with relief as the acid ceases to burn." + acid_good_spell_use_msg = \ + "The chalkiness on your skin provides immediate relief from the " + "precipitating acid." + + acid_death_msg = \ + "~B~U~k[###]~n ~B~v%s melted under the onslaught of acid rain." + +classvars: + + vrName = acid_threat_name_rsc + vrIcon = acid_threat_icon_rsc + vrDesc = acid_threat_desc_rsc + + viThreatBeginMsg = acid_threat_begin_msg + viThreatEndMsg = acid_threat_end_msg + viThreatDamageMsg = acid_threat_damage_msg + viGoodItemUseMsg = acid_good_item_use_msg + viGoodSpellUseMsg = acid_good_spell_use_msg + + viDeathMsg = acid_death_msg + +properties: + + % What type of damage do we do? + piDamageTypeAttack = 0 + piDamageTypeSpell = ATCK_SPELL_ACID + + % How much damage do we do? + piDamageMin = 1 + piDamageMax = 3 + + % How often do we deal damage? + piDamageTime = 1000 + ptDamageTimer = $ + + % How long does the threat last? + piThreatTime = 10 * 60 * 1000 + ptThreatTimer = $ + + % Do we report the damage? + piReport = FALSE + + % This threat drains vigor. + piVigorDrop = 10000 + +messages: + + Constructor() + { +% plSpecialItemClasses = [&AcidRing,&SimpleHelm]; +% plSpecialExemptItemClasses = [&RingOfLethargy]; +% plSpecialSpellClasses = [&ResistAcid]; + + propagate; + } + + Constructed() + { + Send(poHostObject,@StartRain); + propagate; + } + + ReqDealDamage(who=$) + { + % A helmet protects players. + % Magic spirit helms count, too. + % But don't use &Helmet class because that includes circlets & masks. + if Send(who,@IsUsingA,#class=&SimpleHelm) + OR Send(who,@IsUsingA,#class=&Helm) +% OR Send(who,@IsEnchanted,#byClass=&ResistAcid) +% OR Send(who,@IsUsingA,#class=&AcidRing) + { + return FALSE; + } + + return TRUE; + } + + ApplyDirectEffect(who=$) + { + Send(who,@AddExertion,#amount=piVigorDrop); + propagate; + } + + Delete() + { + Send(poHostObject,@EndRain); + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/coldthreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat/coldthreat.kod new file mode 100644 index 0000000000..2202c48a75 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/coldthreat.kod @@ -0,0 +1,133 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ColdThreat is RoomThreat + +% Blizzards deal damage if players aren't wearing robes / resist ring / buff. +% Their damage time also changes randomly to reflect the storm's ebb and flow. + +constants: + + include blakston.khd + +resources: + + cold_threat_name_rsc = "elemental blizzard" + cold_threat_icon_rsc = iexfrost.bgf + cold_threat_desc_rsc = \ + "The cold here is unnatural and brutal. The only protections against " + "this kind of danger are warm robes or the appropriate resistance " + "magics." + + cold_threat_begin_msg = \ + "Snow begins whipping across the sands as the last of the sun's heat " + "fades." + cold_threat_end_msg = \ + "The desert's burgeoning heat melts away the chill night's snow." + cold_threat_damage_msg = \ + "The biting cold gnaws at your bones." + + cold_good_item_use_msg = \ + "The icy breezes no longer bother you." + cold_good_spell_use_msg = \ + "Faren's magical fire battles away the gnawing desert chill." + + cold_death_msg = \ + "~B~U~k[###]~n ~B~v%s froze to death in a blizzard." + +classvars: + + vrName = cold_threat_name_rsc + vrIcon = cold_threat_icon_rsc + vrDesc = cold_threat_desc_rsc + + viThreatBeginMsg = cold_threat_begin_msg + viThreatEndMsg = cold_threat_end_msg + viThreatDamageMsg = cold_threat_damage_msg + viGoodItemUseMsg = cold_good_item_use_msg + viGoodSpellUseMsg = cold_good_spell_use_msg + + viDeathMsg = cold_death_msg + +properties: + + % What type of damage do we do? + piDamageTypeAttack = 0 + piDamageTypeSpell = ATCK_SPELL_COLD + + % How much damage do we do? + piDamageMin = 6 + piDamageMax = 14 + + % How often do we deal damage? + piDamageTime = 3000 + ptDamageTimer = $ + + % How long does the threat last? + piThreatTime = 10 * 60 * 1000 + ptThreatTimer = $ + + % Do we report the damage? + piReport = FALSE + + % This threat drains vigor. + piVigorDrop = 50000 + +messages: + + Constructor() + { +% plSpecialItemClasses = [&Robe, &ColdRing]; +% plSpecialExemptItemClasses = [&RingOfLethargy]; +% plSpecialSpellClasses = [&ResistCold]; + + propagate; + } + + Constructed() + { + Send(poHostObject,@StartSnow); + propagate; + } + + ReqDealDamage(who=$) + { + % Robes are warm enough to protect players. + if Send(who,@IsUsingA,#class=&Robe) +% OR Send(who,@IsEnchanted,#byClass=&ResistCold) +% OR Send(who,@IsUsingA,#class=&ColdRing) + { + return FALSE; + } + + return TRUE; + } + + AlterDamageTime() + { + piDamageTime = Bound(piDamageTime + + Random(-100,100),500,6000); + propagate; + } + + ApplyDirectEffect(who=$) + { + Send(who,@AddExertion,#amount=piVigorDrop); + propagate; + } + + Delete() + { + Send(poHostObject,@EndSnow); + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/fallthreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat/fallthreat.kod new file mode 100644 index 0000000000..272caf286c --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/fallthreat.kod @@ -0,0 +1,190 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +FallThreat is RoomThreat + +% This threat kills players that reach 0 height or below for >1500 ms. +% Used for good old fashioned deathtraps. + +constants: + + include blakston.khd + + DAMAGE_MIN_TIME = 6000 + DAMAGE_MAX_TIME = 12000 + + LOW_DAMAGE = 3000 + MEDIUM_DAMAGE = 6000 + HIGH_DAMAGE = 9000 + +resources: + + fall_threat_name_rsc = "lethal fall" + fall_threat_icon_rsc = irthquak.bgf + fall_threat_desc_rsc = \ + "If you fall here, you will most certainly die." + + fall_threat_begin_msg = \ + "You must watch your step. A fall here would be deadly." + fall_threat_end_msg = \ + "You sigh with relief as the threat of falling to your death fades." + + fell_msg = \ + "As you fall to your doom, you have time only to regret your " + "choice of step." + + fell_death_msg = \ + "~B~U~k[###]~n ~B~v%s fell to %s doom." + +classvars: + + vrName = fall_threat_name_rsc + vrIcon = fall_threat_icon_rsc + vrDesc = fall_threat_desc_rsc + + viThreatBeginMsg = fall_threat_begin_msg + viThreatEndMsg = fall_threat_end_msg + + viDeathMsg = fell_death_msg + +properties: + + % In this case, we check those at 0 or below height every piDamageTime. + % Gives players 2-4 seconds to escape a fall / do small jumps. + % If you are in plFallDanger and the check finds you again, you die. + piDamageTime = 2000 + plFallDanger = $ + + % Extra quick checks to remove players from the list if they make landfall. + % This is to avoid situations where a check finds a player, he runs across + % safe ground for < 2 seconds, then hits open air - and dies instantly. + piRemoveTime = 250 + ptRemoveTimer = $ + +messages: + + Constructed() + { + ptRemoveTimer = CreateTimer(self,@RemoveSafePlayers,piRemoveTime); + propagate; + } + + DealDamage(timer=$) + { + local i; + + ptDamageTimer = $; + + % If you're on this list already and still at height 0 or below, + % you die. + % The two-check method allows for small jumps. + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + { + if Send(First(i),@GetHeightAtObject) <= 0 + { + if plFallDanger <> $ + AND FindListElem(plFallDanger,First(i)) <> 0 + { + Send(self,@PlayerFell,#who=First(i)); + plFallDanger = DelListElem(plFallDanger,First(i)); + } + else + { + plFallDanger = Cons(First(i),plFallDanger); + } + } + else + { + % Not at height 0. Take them off of the list. + if plFallDanger <> $ + AND FindListElem(plFallDanger,First(i)) <> 0 + { + plFallDanger = DelListElem(plFallDanger,First(i)); + } + } + } + } + + % Some threats change the time dynamically. + Send(self,@AlterDamageTime); + ptDamageTimer = CreateTimer(self,@DealDamage,piDamageTime); + return; + } + + PlayerFell(who=$) + { + Send(who,@MsgSendUser,#message_rsc=fell_msg); + Send(who,@Killed); + Send(self,@SendDeathMessage,#who=who); + return; + } + + RemoveSafePlayers() + { + local i; + + ptRemoveTimer = $; + + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&User) + { + if Send(First(i),@GetHeightAtObject) > 0 + { + % Not at height 0. Take them off of the list. + if plFallDanger <> $ + AND FindListElem(plFallDanger,First(i)) <> 0 + { + plFallDanger = DelListElem(plFallDanger,First(i)); + } + } + } + } + + ptRemoveTimer = CreateTimer(self,@RemoveSafePlayers,piRemoveTime); + return; + } + + Delete() + { + local i; + + foreach i in plFallDanger + { + plFallDanger = DelListElem(plFallDanger,i); + } + + if ptRemoveTimer <> $ + { + DeleteTimer(ptRemoveTimer); + ptRemoveTimer = $; + } + + propagate; + } + + SendDeathMessage(who=$) + { + local i; + + % Need an extra parameter for HisHer + foreach i in Send(SYS,@GetUsersLoggedOn) + { + Send(i,@MsgSendUser,#message_rsc=viDeathMsg,#parm1=Send(who,@GetName), + #parm2=Send(who,@GetHisHer)); + } + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/heatthreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat/heatthreat.kod new file mode 100644 index 0000000000..4937a5ac1d --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/heatthreat.kod @@ -0,0 +1,113 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +HeatThreat is RoomThreat + +% Heat deals damage if players are wearing body armor of any type. +% Exempt if not wearing any, or resist fire ring / buff. + +constants: + + include blakston.khd + +resources: + + heat_threat_name_rsc = "elemental heat" + heat_threat_icon_rsc = iblfire.bgf + heat_threat_desc_rsc = \ + "The heat here is unnatural and vicious. The only protections against " + "this kind of danger are dressing down or the appropriate resistance " + "magics." + + heat_threat_begin_msg = \ + "The sun seems to bear down on you with particular ferocity." + heat_threat_end_msg = \ + "The sun passes behind ominous clouds." + heat_threat_damage_msg = \ + "The intense heat makes you sweat profusely under your heavy gear." + + heat_good_item_use_msg = \ + "And, out here, the sun might just be the most dangerous enemy of all." + heat_good_spell_use_msg = \ + "Faren's chill banishes the sun's heat." + + heat_death_msg = \ + "~B~U~k[###]~n ~B~v%s succumbed to the heat of the sun." + +classvars: + + vrName = heat_threat_name_rsc + vrIcon = heat_threat_icon_rsc + vrDesc = heat_threat_desc_rsc + + viThreatBeginMsg = heat_threat_begin_msg + viThreatEndMsg = heat_threat_end_msg + viThreatDamageMsg = heat_threat_damage_msg + viGoodItemUseMsg = heat_good_item_use_msg + viGoodSpellUseMsg = heat_good_spell_use_msg + + viDeathMsg = heat_death_msg + +properties: + + % What type of damage do we do? + piDamageTypeAttack = 0 + piDamageTypeSpell = ATCK_SPELL_FIRE + + % How much damage do we do? + piDamageMin = 8 + piDamageMax = 17 + + % How often do we deal damage? + piDamageTime = 5000 + ptDamageTimer = $ + + % How long does the threat last? + piThreatTime = 10 * 60 * 1000 + ptThreatTimer = $ + + % Do we report the damage? + piReport = FALSE + + % This threat drains vigor. + piVigorDrop = 30000 + +messages: + + Constructor() + { +% plSpecialItemClasses = [&FireRing]; +% plSpecialExemptItemClasses = [&RingOfLethargy]; +% plSpecialSpellClasses = [&ResistFire]; + + propagate; + } + + ReqDealDamage(who=$) + { + % If they take off their armor, they'll be fine. + if NOT Send(who,@IsUsingA,#class=&Armor) +% OR Send(who,@IsEnchanted,#byClass=&ResistFire) +% OR Send(who,@IsUsingA,#class=&FireRing) + { + return FALSE; + } + + return TRUE; + } + + ApplyDirectEffect(who=$) + { + Send(who,@AddExertion,#amount=piVigorDrop); + propagate; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/lavathreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat/lavathreat.kod new file mode 100644 index 0000000000..d7f7bc216f --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/lavathreat.kod @@ -0,0 +1,256 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +LavaThreat is RoomThreat + +% Lava deals damage based on height. The higher the player is, the less +% damage they're dealt. +% This threat also rumbles the area. + +constants: + + include blakston.khd + + DAMAGE_MIN_TIME = 6000 + DAMAGE_MAX_TIME = 12000 + + LOW_DAMAGE = 3000 + MEDIUM_DAMAGE = 6000 + HIGH_DAMAGE = 9000 + + LOW_VIGOR_DRAIN = 50000 + MEDIUM_VIGOR_DRAIN = 100000 + HIGH_VIGOR_DRAIN = 200000 + +resources: + + lava_threat_name_rsc = "molten lava" + lava_threat_icon_rsc = iwalfire.bgf + lava_threat_desc_rsc = \ + "The earth bubbles over and its molten innards flow freely here. " + "The deeper you go, the deadlier the heat will be. Resistance to fire " + "will prolong your life, but nothing can remain alive here for long." + + lava_threat_begin_msg = \ + "Terrible earthen fires begin to burn through the area." + lava_threat_end_msg = \ + "The molten lava here cools somewhat." + lava_threat_damage_msg = \ + "~rThe rising heat singes your skin and hurts your lungs." + + lava_weak_damage_msg = \ + "~rThe rising heat singes your skin and hurts your lungs." + lava_painful_damage_msg = \ + "~rThe oppressive heat sears your limbs!" + lava_deadly_damage_msg = \ + "~rVolcanic heat melts your skin!" + lava_catastrophic_damage_msg = \ + "~B~rYou are cooking from the inside out!~B" + + lava_death_msg = \ + "~B~U~k[###]~n ~B~v%s was cooked alive by the heat of molten lava." + + flame_sound = fire02.wav + +classvars: + + vrName = lava_threat_name_rsc + vrIcon = lava_threat_icon_rsc + vrDesc = lava_threat_desc_rsc + + viThreatBeginMsg = lava_threat_begin_msg + viThreatEndMsg = lava_threat_end_msg + viThreatDamageMsg = lava_threat_damage_msg + + viDeathMsg = lava_death_msg + +properties: + + % What type of damage do we do? + piDamageTypeAttack = 0 + piDamageTypeSpell = ATCK_SPELL_FIRE + + % How often do we deal damage? + % This threat randomizes 6000 to 12000. + piDamageTime = 6000 + ptDamageTimer = $ + + % What height room are we dealing with here? + % Send a height at creation if this isn't Abyssal Bore. + piRoomMaxHeight = 4000 + + % If this isn't Abyssal Bore, use iDamage sent at creation. + piLavaDamageMax = 50000 + + % If this isn't Abyssal Bore, use rumble duration sent at creation. + piRumbleDuration = 3000 + + % This threat drains vigor, but it usually changes based on height. + piVigorDrop = 50000 + +messages: + + Constructor(iHeight=4000,iDamage=50000,iRumbleDuration=3000) + { + piRoomMaxHeight = iHeight; + piLavaDamageMax = iDamage; + piRumbleDuration = iRumbleDuration; + propagate; + } + + DealDamage(timer=$) + { + local i, iDamageResult; + + ptDamageTimer = $; + + % Shake the room. + Send(self,@DoLavaRumble); + + foreach i in Send(poHostObject,@GetHolderActive) + { + if IsClass(First(i),&Player) + { + Send(self,@DealLavaHeatDamage,#who=First(i)); + } + } + + % Some threats change the time dynamically. + Send(self,@AlterDamageTime); + ptDamageTimer = CreateTimer(self,@DealDamage,piDamageTime); + return; + } + + AlterDamageTime() + { + piDamageTime = Random(DAMAGE_MIN_TIME,DAMAGE_MAX_TIME); + return; + } + + DoLavaRumble() + { + local iDuration; + + % Abyssal Bore is primary home to this threat. + if IsClass(poHostObject,&DesertRiver2) + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + iDuration = 2000; + break; + case DAY_PHASE_DAY: + % The heat threat is much worse while the rift is active. + iDuration = 3000; + break; + case DAY_PHASE_DUSK: + iDuration = 2000; + break; + case DAY_PHASE_NIGHT: + % The rift is weaker at night, as the Prism of Ice is dominant. + iDuration = 1000; + break; + } + } + else + { + iDuration = piRumbleDuration; + } + + Send(poHostObject,@Rumble,#duration=iDuration); + + return; + } + + DealLavaHeatDamage(who=$) + { + local iHeight, iInitialDamage, iDamage, iDamageResult, i, + iInitialVigorDrain, iVigorDrain; + + iHeight = Send(who,@GetHeightAtObject); + + % Abyssal Bore is the primary home to this lava threat. + if IsClass(poHostObject,&DesertRiver2) + { + switch(Send(SYS,@GetDayPhase)) + { + case DAY_PHASE_DAWN: + iInitialDamage = MEDIUM_DAMAGE; + iInitialVigorDrain = MEDIUM_VIGOR_DRAIN; + break; + case DAY_PHASE_DAY: + % The heat threat is much worse while the rift is active. + iInitialDamage = HIGH_DAMAGE; + iInitialVigorDrain = HIGH_VIGOR_DRAIN; + break; + case DAY_PHASE_DUSK: + iInitialDamage = MEDIUM_DAMAGE; + iInitialVigorDrain = MEDIUM_VIGOR_DRAIN; + break; + case DAY_PHASE_NIGHT: + % The rift is weaker at night, as the Prism of Ice is dominant. + iInitialDamage = LOW_DAMAGE; + iInitialVigorDrain = LOW_VIGOR_DRAIN; + break; + } + } + else + { + iInitialDamage = piLavaDamageMax; + iInitialVigorDrain = piVigorDrop; + } + + iDamage = iInitialDamage; + iVigorDrain = iInitialVigorDrain; + + if piRoomMaxHeight > 0 + { + iDamage = Bound(iDamage - + ((iDamage * ((iHeight*100)/piRoomMaxHeight))/100),1,$); + iVigorDrain = Bound(iVigorDrain - + ((iVigorDrain * ((iHeight*100)/piRoomMaxHeight))/100),1,$); + } + + if iDamage >= (iInitialDamage * 3 / 4) + { + Send(who,@MsgSendUser,#message_rsc=lava_catastrophic_damage_msg); + } + else if iDamage >= (iInitialDamage * 2 / 4) + { + Send(who,@MsgSendUser,#message_rsc=lava_deadly_damage_msg); + } + else if iDamage >= (iInitialDamage / 4) + { + Send(who,@MsgSendUser,#message_rsc=lava_painful_damage_msg); + } + else + { + Send(who,@MsgSendUser,#message_rsc=lava_weak_damage_msg); + } + + Send(who,@WaveSendUser,#wave_rsc=flame_sound); + + Send(who,@AddExertion,#amount=iVigorDrain); + iDamageResult = Send(who,@AssessDamage,#what=self, + #damage=iDamage, + #aspell=ATCK_SPELL_FIRE,#report=FALSE, + #precision=TRUE); + + if iDamageResult = $ + { + Send(who,@Killed); + Send(self,@SendDeathMessage,#who=who); + } + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/makefile b/kod/object/passive/objectatt/roomobjectatt/roomthreat/makefile new file mode 100644 index 0000000000..f849625cc6 --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/makefile @@ -0,0 +1,11 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\roomthreat.bof +BOFS = coldthreat.bof heatthreat.bof acidthreat.bof lavathreat.bof \ + fallthreat.bof phasethreat.bof + +!include $(KODDIR)\kod.mak \ No newline at end of file diff --git a/kod/object/passive/objectatt/roomobjectatt/roomthreat/phasethreat.kod b/kod/object/passive/objectatt/roomobjectatt/roomthreat/phasethreat.kod new file mode 100644 index 0000000000..778e71527d --- /dev/null +++ b/kod/object/passive/objectatt/roomobjectatt/roomthreat/phasethreat.kod @@ -0,0 +1,96 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +PhaseThreat is RoomThreat + +% Players cannot phase out here except under special conditions. + +constants: + + include blakston.khd + +resources: + + phase_threat_name_rsc = "reality disturbance" + phase_threat_icon_rsc = iphase.bgf + phase_threat_desc_rsc = \ + "The fabric of reality has been torn asunder. Phasing is possible only " + "under certain conditions." + + phase_threat_begin_msg = \ + "Reality begins to fluctuate chaotically." + phase_threat_end_msg = \ + "The fluctuations in the fabric of reality here fade." + + cannot_phase_here = \ + "The fabric of reality here rages chaotically around the epicenter of a " + "rupture. You cannot risk phasing out unless you can manage to reach " + "the convergence itself." + phase_success = \ + "You phase out of this plane of existence - and directly into another!" + +classvars: + + vrName = phase_threat_name_rsc + vrIcon = phase_threat_icon_rsc + vrDesc = phase_threat_desc_rsc + + viThreatBeginMsg = phase_threat_begin_msg + viThreatEndMsg = phase_threat_end_msg + +properties: + + pbDealDamage = FALSE + +messages: + + ReqSpellCast(who = $, oSpell = $) + { + local iQflags, iRflags, iHeightF, iHeightFWD, iHeightC, iServerID; + + if IsClass(oSpell,&Phase) + { + if NOT IsClass(who,&Player) + { + return TRUE; + } + + iQflags = LIQ_GET_SECTORINFO; + + if GetLocationInfoBSP( + Send(poHostObject,@GetRoomData), iQflags, Send(who,@GetRow), + Send(who,@GetCol), + Send(who,@GetFineRow), + Send(who,@GetFineCol), + *iRflags, *iHeightF, *iHeightFWD, *iHeightC, *iServerID) + { + if Send(poHostObject,@SpecialPhaseConditionsSatisfied, + #sector=iServerID) + { + Post(who,@MsgSendUser,#message_rsc=phase_success); + Post(Send(SYS,@FindRoomByNum,#num=RID_ELEMENTAL_DUNGEON1), + @Teleport,#what=who); + Post(who,@EffectSendUserDuration,#effect=EFFECT_WHITEOUT, + #duration=1250); + return FALSE; + } + else + { + Send(who,@MsgSendUser,#message_rsc=cannot_phase_here); + return FALSE; + } + } + } + + return TRUE; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/debuff/hold.kod b/kod/object/passive/spell/debuff/hold.kod index 4738148270..6838945aec 100644 --- a/kod/object/passive/spell/debuff/hold.kod +++ b/kod/object/passive/spell/debuff/hold.kod @@ -222,13 +222,14 @@ messages: { if IsClass(who,&Player) { - % Don't override phasing hold. + % Don't override phasing hold, dm hold, or frozen. if NOT Send(who,@CheckPlayerFlag,#flag=PFLAG_PHASED) + AND NOT Send(who,@IsEnchanted,#byClass=&DMHold) + AND NOT Send(who,@HasObjectAttribute,#cClass=&BattlerFrozen) { Send(who,@EffectSendUser,#what=self,#effect=EFFECT_PARALYZE_OFF); } - Send(who,@EffectSendUser,#what=self,#effect=EFFECT_PARALYZE_OFF); if state { Send(who,@MsgSendUser,#message_rsc=vrEnchantment_Off); diff --git a/kod/object/passive/spell/dmspell/dmhold.kod b/kod/object/passive/spell/dmspell/dmhold.kod index 89f1ab2604..29395f700b 100644 --- a/kod/object/passive/spell/dmspell/dmhold.kod +++ b/kod/object/passive/spell/dmspell/dmhold.kod @@ -148,7 +148,14 @@ messages: { if IsClass(who,&Player) { - Send(who, @EffectSendUser, #what=self, #effect=EFFECT_PARALYZE_OFF); + % Don't override hold / frozen / phase. + + if NOT Send(who,@IsEnchanted,#byClass=&Hold) + AND NOT Send(who,@HasObjectAttribute,#cClass=&BattlerFrozen) + AND NOT Send(who,@CheckPlayerFlag,#flag=PFLAG_PHASED) + { + Send(who,@EffectSendUser,#what=self,#effect=EFFECT_PARALYZE_OFF); + } if report { diff --git a/kod/object/passive/spell/makefile b/kod/object/passive/spell/makefile index 6fc6a2ac8b..59e0ad35db 100644 --- a/kod/object/passive/spell/makefile +++ b/kod/object/passive/spell/makefile @@ -58,6 +58,7 @@ BOFS = animate.bof \ sweep.bof \ teleportspell.bof \ utility.bof \ - walspell.bof + walspell.bof \ + regioneffect.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/passive/spell/persench/resist/acidstormbuff.kod b/kod/object/passive/spell/persench/resist/acidstormbuff.kod new file mode 100644 index 0000000000..1db43d7cf5 --- /dev/null +++ b/kod/object/passive/spell/persench/resist/acidstormbuff.kod @@ -0,0 +1,75 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +AcidStormBuff is ResistanceEnchantment + +constants: + + include blakston.khd + +resources: + + AcidStormBuff_name_rsc = "acidic energies" + AcidStormBuff_icon_rsc = iresacid.bgf + AcidStormBuff_desc_rsc = \ + "Strange green energies race around you, protecting you from acid." + + AcidStormBuff_already_enchanted = \ + "You are already resistant to acid spells." + + AcidStormBuff_on = \ + "Eerie green energies race around your body." + AcidStormBuff_off = \ + "The eerie green energies depart." + + AcidStormBuff_sound = shalille.wav + +classvars: + + vrName = AcidStormBuff_name_rsc + vrIcon = AcidStormBuff_icon_rsc + vrDesc = AcidStormBuff_desc_rsc + vrSucceed_wav = AcidStormBuff_sound + + viSpell_num = SID_ACID_STORM_BUFF + viSchool = SS_SHALILLE + viSpell_level = 2 + viMana = 9 + + viPersonal_ench = True + viChance_To_Increase = 30 + + viFlash = FLASH_GOOD_SELF + + viResistanceType = -ATCK_SPELL_ACID + vrAlreadyEnchanted = AcidStormBuff_already_enchanted + vrEnchantment_On = AcidStormBuff_on + vrEnchantment_Off = AcidStormBuff_off + +properties: + +messages: + + GetResistanceStrength(iSpellpower = 0) + { + %% strength varies from 1 to 99 + return iSpellPower; + } + + GetDuration(iSpellPower = 0) + { + local iDuration; %%% 6 to 10 seconds + iDuration = 6000 + (iSpellPower+1)*40; + return iDuration; + } + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/persench/resist/coldstormbuff.kod b/kod/object/passive/spell/persench/resist/coldstormbuff.kod new file mode 100644 index 0000000000..acd4a083d1 --- /dev/null +++ b/kod/object/passive/spell/persench/resist/coldstormbuff.kod @@ -0,0 +1,76 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ColdStormBuff is ResistanceEnchantment + +constants: + + include blakston.khd + +resources: + + ColdStormBuff_name_rsc = "chilling cold" + ColdStormBuff_icon_rsc = irescold.bgf + ColdStormBuff_desc_rsc = \ + "Strange freezing energies race around you, protecting you from cold." + + ColdStormBuff_already_enchanted = \ + "You are already resistant to cold spells." + + ColdStormBuff_on = \ + "Freezing energies empower your body." + ColdStormBuff_off = \ + "The freezing strength leaves your body." + + ColdStormBuff_sound = faren.wav + +classvars: + + vrName = ColdStormBuff_name_rsc + vrIcon = ColdStormBuff_icon_rsc + vrDesc = ColdStormBuff_desc_rsc + vrSucceed_wav = ColdStormBuff_sound + + viSpell_num = SID_COLD_STORM_BUFF + viSchool = SS_FAREN + viSpell_level = 2 + viMana = 9 + + viPersonal_ench = True + viChance_To_Increase = 30 + viMeditate_ratio = 0 + + viFlash = FLASH_GOOD_SELF + + viResistanceType = -ATCK_SPELL_COLD + vrAlreadyEnchanted = ColdStormBuff_already_enchanted + vrEnchantment_On = ColdStormBuff_on + vrEnchantment_Off = ColdStormBuff_off + +properties: + +messages: + + GetResistanceStrength(iSpellpower = 0) + { + %% strength varies from 1 to 99 + return iSpellPower; + } + + GetDuration(iSpellPower = 0) + { + local iDuration; %%% 60 to 120 seconds + iDuration = 60000 + (iSpellPower+1)*600; + return iDuration; + } + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/persench/resist/heatstormbuff.kod b/kod/object/passive/spell/persench/resist/heatstormbuff.kod new file mode 100644 index 0000000000..f494220019 --- /dev/null +++ b/kod/object/passive/spell/persench/resist/heatstormbuff.kod @@ -0,0 +1,76 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +HeatStormBuff is ResistanceEnchantment + +constants: + + include blakston.khd + +resources: + + HeatStormBuff_name_rsc = "searing heat" + HeatStormBuff_icon_rsc = iresfire.bgf + HeatStormBuff_desc_rsc = \ + "Strange searing energies race around you, protecting you from fire." + + HeatStormBuff_already_enchanted = \ + "You are already resistant to fire spells." + + HeatStormBuff_on = \ + "Fiery energies race around your body." + HeatStormBuff_off = \ + "The fiery energies leave your body." + + HeatStormBuff_sound = faren.wav + +classvars: + + vrName = HeatStormBuff_name_rsc + vrIcon = HeatStormBuff_icon_rsc + vrDesc = HeatStormBuff_desc_rsc + vrSucceed_wav = HeatStormBuff_sound + + viSpell_num = SID_HEAT_STORM_BUFF + viSchool = SS_FAREN + viSpell_level = 2 + viMana = 9 + + viPersonal_ench = True + viChance_To_Increase = 30 + viMeditate_ratio = 0 + + viFlash = FLASH_GOOD_SELF + + viResistanceType = -ATCK_SPELL_FIRE + vrAlreadyEnchanted = HeatStormBuff_already_enchanted + vrEnchantment_On = HeatStormBuff_on + vrEnchantment_Off = HeatStormBuff_off + +properties: + +messages: + + GetResistanceStrength(iSpellpower = 0) + { + %% strength varies from 1 to 99 + return iSpellPower; + } + + GetDuration(iSpellPower = 0) + { + local iDuration; %%% 60 to 120 seconds + iDuration = 60000 + (iSpellPower+1)*600; + return iDuration; + } + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/persench/resist/makefile b/kod/object/passive/spell/persench/resist/makefile index e74d375572..4b5bcc4c6e 100644 --- a/kod/object/passive/spell/persench/resist/makefile +++ b/kod/object/passive/spell/persench/resist/makefile @@ -6,6 +6,7 @@ DEPEND = ..\resist.bof BOFS = resfire.bof resshock.bof rescold.bof resgood.bof resmagic.bof \ - resevil.bof resacid.bof + resevil.bof resacid.bof acidstormbuff.bof shockstormbuff.bof \ + coldstormbuff.bof heatstormbuff.bof !include $(KODDIR)\kod.mak diff --git a/kod/object/passive/spell/persench/resist/shockstormbuff.kod b/kod/object/passive/spell/persench/resist/shockstormbuff.kod new file mode 100644 index 0000000000..73417b5c47 --- /dev/null +++ b/kod/object/passive/spell/persench/resist/shockstormbuff.kod @@ -0,0 +1,76 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +ShockStormBuff is ResistanceEnchantment + +constants: + + include blakston.khd + +resources: + + ShockStormBuff_name_rsc = "static energies" + ShockStormBuff_icon_rsc = iresshok.bgf + ShockStormBuff_desc_rsc = \ + "Strange electrical energies race around you, protecting you from shock." + + ShockStormBuff_already_enchanted = \ + "You are already resistant to shock spells." + + ShockStormBuff_on = \ + "Static electrical energies race around your body." + ShockStormBuff_off = \ + "The static energies leave your body." + + ShockStormBuff_sound = faren.wav + +classvars: + + vrName = ShockStormBuff_name_rsc + vrIcon = ShockStormBuff_icon_rsc + vrDesc = ShockStormBuff_desc_rsc + vrSucceed_wav = ShockStormBuff_sound + + viSpell_num = SID_SHOCK_STORM_BUFF + viSchool = SS_FAREN + viSpell_level = 2 + viMana = 9 + + viPersonal_ench = True + viChance_To_Increase = 30 + viMeditate_ratio = 0 + + viFlash = FLASH_GOOD_SELF + + viResistanceType = -ATCK_SPELL_SHOCK + vrAlreadyEnchanted = ShockStormBuff_already_enchanted + vrEnchantment_On = ShockStormBuff_on + vrEnchantment_Off = ShockStormBuff_off + +properties: + +messages: + + GetResistanceStrength(iSpellpower = 0) + { + %% strength varies from 1 to 99 + return iSpellPower; + } + + GetDuration(iSpellPower = 0) + { + local iDuration; %%% 60 to 120 seconds + iDuration = 60000 + (iSpellPower+1)*600; + return iDuration; + } + +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/regioneffect.kod b/kod/object/passive/spell/regioneffect.kod new file mode 100644 index 0000000000..f3065b6f72 --- /dev/null +++ b/kod/object/passive/spell/regioneffect.kod @@ -0,0 +1,176 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +RegionEffect is Spell + +% These use the spell system, but are not really spells. +% For example, GreyGhost marks a person as grayscale and anon. +% Used by content for immersion. + +constants: + + include blakston.khd + +resources: + + default_system_effect_on = "You've become affected by a system effect." + default_system_effect_off = "You're no longer affected by a system effect." + +classvars: + + applied_on_first = default_system_effect_on + applied_off_first = default_system_effect_off + + applied_in_rooms_of_class = &MonsterRoom + +properties: + +messages: + + ShowDesc() + { + AddPacket(4,vrDesc); + + return; + } + + GetNumSpellTargets() + { + return 1; + } + + PlaySpellSound(room_obj = $,what = $) + { + return; + } + + CanPayCosts(who = $) + { + % Can always cast this. + return TRUE; + } + + SuccessChance() + { + return TRUE; + } + + % Informed by player.kod NewOwner. + % Necessary in case of offline teleportation. + NewOwner(who=$,where=$) + { + if where = $ + OR NOT IsClass(where,applied_in_rooms_of_class) + { + Post(who,@RemoveEnchantment,#what=self); + } + + return; + } + + CastSpell(who=$,lTargets=$,report=TRUE) + { + local each_obj, i, oRoom, oTarget; + + if lTargets = $ + { + return; + } + + oTarget = First(lTargets); + + if oTarget = $ + OR NOT IsClass(oTarget,&Player) + { + return; + } + + % Usually called by a Post after user LeaveHolds from rooms that + % apply these. That way we'll have our new owner by then. + % If the player has the RegionEffect and is no longer + % in our type of room, remove ourself. + if Send(oTarget,@IsEnchanted,#what=self) + AND Send(oTarget,@GetOwner) <> $ + AND NOT IsClass(Send(oTarget,@GetOwner),applied_in_rooms_of_class) + { + Send(oTarget,@RemoveEnchantment,#what=self); + + return; + } + + % If we're still enchanted, player is still in region. Do nothing. + if Send(oTarget,@IsEnchanted,#what=self) + { + return; + } + + oRoom = Send(oTarget,@GetOwner); + if oRoom <> $ + AND report + { + foreach i in Send(oRoom,@GetHolderActive) + { + each_obj = First(i); + if each_obj = oTarget + { + Send(oTarget,@MsgSendUser,#message_rsc=applied_on_first); + + continue; + } + } + } + + Send(oTarget,@StartEnchantment,#what=self,#time=$); + + propagate; + } + + EndEnchantment(who = $,report = TRUE,state = $) + { + local each_obj, i, oRoom; + + if who = $ + { + return; + } + + Send(who,@EffectSendUserXlat,#xlat=0); + + oRoom = Send(who,@GetOwner); + if oRoom <> $ + AND report + { + foreach i in Send(oRoom,@GetHolderActive) + { + each_obj = First(i); + if each_obj = who + { + Send(who,@MsgSendUser,#message_rsc=applied_off_first); + + continue; + } + } + } + + return; + } + + CanBeRemovedByPlayer() + { + return FALSE; + } + + GetStateValue() + { + return $; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/regioneffect/greyghost.kod b/kod/object/passive/spell/regioneffect/greyghost.kod new file mode 100644 index 0000000000..85afe41ac3 --- /dev/null +++ b/kod/object/passive/spell/regioneffect/greyghost.kod @@ -0,0 +1,59 @@ +% Meridian 59, Copyright 1994-2012 Andrew Kirmse and Chris Kirmse. +% All rights reserved. +% +% This software is distributed under a license that is described in +% the LICENSE file that accompanies it. +% +% Meridian is a registered trademark. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +GreyGhost is RegionEffect + +constants: + + include blakston.khd + +resources: + + greyghost_name_rsc = "dead" + greyghost_icon_rsc = ispectate.bgf + greyghost_desc_rsc = \ + "You feel strange - and grey. This condition seems to be different than " + "the forms you're used to, but it is most certainly Death!" + greyghost_on_first = \ + "You feel strange... lifeless... grey..." + greyghost_off_first = \ + "You feel the spark of life burgeoning within you." + +classvars: + + vrName = greyghost_name_rsc + vrIcon = greyghost_icon_rsc + vrDesc = greyghost_desc_rsc + + applied_on_first = greyghost_on_first + applied_off_first = greyghost_off_first + + applied_in_rooms_of_class = &DesertAfterlife + + viSpell_num = SID_GREY_GHOST + +properties: + +messages: + + SetSpellPlayerFlag(who = $) + { + if NOT Send(who,@CheckPlayerFlag,#flag=PFLAG_PHASED) + AND NOT Send(who,@CheckPlayerFlag,#flag=PFLAG_SPECTATOR) + { + Send(who,@SetPlayerDrawFX,#drawfx=DRAWFX_SECONDTRANS); + Send(who,@SetPlayerFlag,#flag=PFLAG_ANONYMOUS,#value=TRUE); + } + + return; + } + +end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/kod/object/passive/spell/regioneffect/makefile b/kod/object/passive/spell/regioneffect/makefile new file mode 100644 index 0000000000..b4e36ac92a --- /dev/null +++ b/kod/object/passive/spell/regioneffect/makefile @@ -0,0 +1,11 @@ +# +# Makefile for compiling the Blakod +# + +!include $(TOPDIR)\common.mak + +DEPEND = ..\regioneffect.bof + +BOFS = greyghost.bof + +!include $(KODDIR)\kod.mak diff --git a/kod/object/passive/spell/teleportspell/rescue.kod b/kod/object/passive/spell/teleportspell/rescue.kod index 59d1017262..e71fe2d50a 100644 --- a/kod/object/passive/spell/teleportspell/rescue.kod +++ b/kod/object/passive/spell/teleportspell/rescue.kod @@ -167,6 +167,15 @@ messages: return; } + + % If we're in the desert, teleport to Waylay Oasis. + if Send(oCurrentRoom,@GetRegion) = RID_DESERT + { + oTargetRoom = Send(SYS,@FindRoomByNum,#num=RID_WAYLAYOASIS); + Post(oTargetRoom,@Teleport,#what=who); + + return; + } } % Default: Go to our home room. diff --git a/kod/object/passive/spell/utility/phase.kod b/kod/object/passive/spell/utility/phase.kod index 7feb2a5461..b5f1520a6d 100644 --- a/kod/object/passive/spell/utility/phase.kod +++ b/kod/object/passive/spell/utility/phase.kod @@ -145,6 +145,7 @@ messages: } if IsClass(oRoom,&GuildHall) + AND NOT Send(oRoom,@InFoyer,#who=who) { Send(who,@MsgSendUser,#message_rsc=no_phasing_in_guildhall_msg); @@ -364,9 +365,10 @@ messages: Send(who,@SetPlayerFlag,#flag=PFLAG_PHASED,#value=FALSE); Send(who,@FreezePhaseTimer); - % Don't override Hold. + % Don't override Hold / Frozen. if NOT Send(who,@IsEnchanted,#byClass=&Hold) AND NOT Send(who,@IsEnchanted,#byClass=&DMHold) + AND NOT Send(who,@HasObjectAttribute,#cClass=&BattlerFrozen) { Send(who,@EffectSendUser,#what=self,#effect=EFFECT_PARALYZE_OFF); } diff --git a/kod/util/library.kod b/kod/util/library.kod index b35881d53f..2aa5f0c469 100644 --- a/kod/util/library.kod +++ b/kod/util/library.kod @@ -1759,6 +1759,11 @@ messages: %Priestesses + plSpeechLib = Cons( + [&XaerdunAfterlife, [ + [[LT_jala], 0, [LIBACT_MOOD, 2], 3] + ]], plSpeechLib); + plSpeechLib = Cons( [&kraananpriestess, [ [[LT_threecon], 0, [LIBACT_MOOD, -2], 1] diff --git a/kod/util/system.kod b/kod/util/system.kod index aa5dd35d88..a08b35a3e1 100644 --- a/kod/util/system.kod +++ b/kod/util/system.kod @@ -2608,6 +2608,15 @@ messages: Send(self,@CreateOneRoomIfNew,#num=RID_DESERTRIVER1,#class=&DesertRiver1); Send(self,@CreateOneRoomIfNew,#num=RID_DESERTRIVER2,#class=&DesertRiver2); Send(self,@CreateOneRoomIfNew,#num=RID_DESERTBRIDGE,#class=&DesertBridge); + Send(self,@CreateOneRoomIfNew,#num=RID_DESERTCAVERAFT,#class=&DesertCaveRaft); + Send(self,@CreateOneRoomIfNew,#num=RID_ELEMENTAL_DUNGEON1,#class=&ElementalDungeon1); + Send(self,@CreateOneRoomIfNew,#num=RID_ELEMENTAL_DUNGEON2,#class=&ElementalDungeon2); + Send(self,@CreateOneRoomIfNew,#num=RID_ELEMENTAL_DUNGEON3,#class=&ElementalDungeon3); + Send(self,@CreateOneRoomIfNew,#num=RID_DESERTSANDBAR,#class=&DesertSandbar); + Send(self,@CreateOneRoomIfNew,#num=RID_DESERTALDUNES,#class=&DesertAfterlifeDunes); + Send(self,@CreateOneRoomIfNew,#num=RID_DESERTALDUNES2E,#class=&DesertAfterlifeDunes2East); + Send(self,@CreateOneRoomIfNew,#num=RID_DESERTALDUNES3E,#class=&DesertAfterlifeDunes3East); + Send(self,@CreateOneRoomIfNew,#num=RID_DESERTALDUNES4E,#class=&DesertAfterlifeDunes4East); if poRentableRoomMaintenance <> $ % After a clean build, for example. @@ -3378,6 +3387,13 @@ messages: Send(self,@CreateOneSpellIfNew,#num=SID_LOADOUT,#class=&Loadout); Send(self,@CreateOneSpellIfNew,#num=SID_CONVEYANCE,#class=&Conveyance); Send(self,@CreateOneSpellIfNew,#num=SID_PHASE,#class=&Phase); + + Send(self,@CreateOneSpellIfNew,#num=SID_ACID_STORM_BUFF,#class=&AcidStormBuff); + Send(self,@CreateOneSpellIfNew,#num=SID_SHOCK_STORM_BUFF,#class=&ShockStormBuff); + Send(self,@CreateOneSpellIfNew,#num=SID_HEAT_STORM_BUFF,#class=&HeatStormBuff); + Send(self,@CreateOneSpellIfNew,#num=SID_COLD_STORM_BUFF,#class=&ColdStormBuff); + + Send(self,@CreateOneSpellIfNew,#num=SID_GREY_GHOST,#class=&GreyGhost); % Trance may be searched for frequently, leave at end of list Send(self,@CreateOneSpellIfNew,#num=SID_TRANCE,#class=&Trance); @@ -5378,6 +5394,8 @@ messages: plMonsterTemplates = cons(create(&IceElementalChampion),plMonsterTemplates); plMonsterTemplates = cons(create(&NeruElemental),plMonsterTemplates); plMonsterTemplates = cons(create(&NeruElementalChampion),plMonsterTemplates); + + plMonsterTemplates = cons(create(&LostSoul),plMonsterTemplates); return; } @@ -5578,6 +5596,7 @@ messages: plItemTemplates = cons(create(&ForgetPotionSkills),plItemTemplates); plItemTemplates = cons(create(&ForgetPotionRiija),plItemTemplates); plItemTemplates = cons(create(&ForgetPotionJala),plItemTemplates); + plItemTemplates = cons(create(&ForgetPotionXaerdunWitchery),plItemTemplates); plItemTemplates = cons(create(&IllusionaryFormPotion),plItemTemplates); plItemTemplates = cons(create(&PurifyPotion),plItemTemplates); diff --git a/resource/audio/mp3/desertafterlife.mp3 b/resource/audio/mp3/desertafterlife.mp3 new file mode 100644 index 0000000000..4c63f105da Binary files /dev/null and b/resource/audio/mp3/desertafterlife.mp3 differ diff --git a/resource/audio/mp3/desertdawn.mp3 b/resource/audio/mp3/desertdawn.mp3 new file mode 100644 index 0000000000..271962b10c Binary files /dev/null and b/resource/audio/mp3/desertdawn.mp3 differ diff --git a/resource/audio/mp3/desertheat.mp3 b/resource/audio/mp3/desertheat.mp3 new file mode 100644 index 0000000000..a33bfb48d8 Binary files /dev/null and b/resource/audio/mp3/desertheat.mp3 differ diff --git a/resource/audio/mp3/desertrain.mp3 b/resource/audio/mp3/desertrain.mp3 new file mode 100644 index 0000000000..8f1dbd3b4a Binary files /dev/null and b/resource/audio/mp3/desertrain.mp3 differ diff --git a/resource/audio/mp3/desertsnow.mp3 b/resource/audio/mp3/desertsnow.mp3 new file mode 100644 index 0000000000..7c7cddbbde Binary files /dev/null and b/resource/audio/mp3/desertsnow.mp3 differ diff --git a/resource/audio/wav/earthquake03.wav b/resource/audio/wav/earthquake03.wav new file mode 100644 index 0000000000..7918c763dd Binary files /dev/null and b/resource/audio/wav/earthquake03.wav differ diff --git a/resource/audio/wav/fire02.wav b/resource/audio/wav/fire02.wav new file mode 100644 index 0000000000..db82bcaf4b Binary files /dev/null and b/resource/audio/wav/fire02.wav differ diff --git a/resource/audio/wav/flamewind.wav b/resource/audio/wav/flamewind.wav new file mode 100644 index 0000000000..22855403fc Binary files /dev/null and b/resource/audio/wav/flamewind.wav differ diff --git a/resource/audio/wav/snowstep.wav b/resource/audio/wav/snowstep.wav new file mode 100644 index 0000000000..5a01045af0 Binary files /dev/null and b/resource/audio/wav/snowstep.wav differ diff --git a/resource/audio/wav/whoosh04.wav b/resource/audio/wav/whoosh04.wav new file mode 100644 index 0000000000..935a085890 Binary files /dev/null and b/resource/audio/wav/whoosh04.wav differ diff --git a/resource/audio/wav/zap01.wav b/resource/audio/wav/zap01.wav new file mode 100644 index 0000000000..78129d8190 Binary files /dev/null and b/resource/audio/wav/zap01.wav differ diff --git a/resource/graphics/gar/Xaerdun.bgf b/resource/graphics/gar/Xaerdun.bgf new file mode 100644 index 0000000000..77b7cd8968 Binary files /dev/null and b/resource/graphics/gar/Xaerdun.bgf differ diff --git a/resource/graphics/gar/grd19081.bgf b/resource/graphics/gar/grd19081.bgf new file mode 100644 index 0000000000..35c50215b6 Binary files /dev/null and b/resource/graphics/gar/grd19081.bgf differ diff --git a/resource/graphics/gar/lightningprism.bgf b/resource/graphics/gar/lightningprism.bgf new file mode 100644 index 0000000000..c8d9414d9e Binary files /dev/null and b/resource/graphics/gar/lightningprism.bgf differ diff --git a/resource/graphics/gar/lightningprism.bmp b/resource/graphics/gar/lightningprism.bmp new file mode 100644 index 0000000000..7a7b2c5853 Binary files /dev/null and b/resource/graphics/gar/lightningprism.bmp differ diff --git a/resource/graphics/gar/lightningprismbig.bgf b/resource/graphics/gar/lightningprismbig.bgf new file mode 100644 index 0000000000..6ece472b98 Binary files /dev/null and b/resource/graphics/gar/lightningprismbig.bgf differ diff --git a/resource/graphics/gar/lightningprismhuge.bgf b/resource/graphics/gar/lightningprismhuge.bgf new file mode 100644 index 0000000000..f32192aa78 Binary files /dev/null and b/resource/graphics/gar/lightningprismhuge.bgf differ diff --git a/resource/graphics/gar/makefile b/resource/graphics/gar/makefile index b694a8a295..db780d42f8 100644 --- a/resource/graphics/gar/makefile +++ b/resource/graphics/gar/makefile @@ -8,7 +8,9 @@ SOURCEDIR = . OUTDIR = $(CLIENTRUNDIR)\resource OUTDIRSUB = ..\$(OUTDIR) -BGFS = igreyinvis.bgf isitting.bgf itransferitem.bgf iexplosion.bgf irift.bgf +BGFS = igreyinvis.bgf isitting.bgf itransferitem.bgf iexplosion.bgf irift.bgf \ + lightningprism.bgf lightningprismbig.bgf lightningprismhuge.bgf \ + grd19081.bgf xaerdun.bgf DIRS = diff --git a/resource/graphics/makefile b/resource/graphics/makefile index 14eef48506..fc16562259 100644 --- a/resource/graphics/makefile +++ b/resource/graphics/makefile @@ -11,7 +11,7 @@ OUTDIR = $(TOPDIR)\resource\graphics DIRS = keen\ mark \ diggie \ - delerium \ + delerium \ gar \ lod diff --git a/resource/rooms/desertALdunes.roo b/resource/rooms/desertALdunes.roo new file mode 100644 index 0000000000..7ee729d869 Binary files /dev/null and b/resource/rooms/desertALdunes.roo differ diff --git a/resource/rooms/desertaldunes2.roo b/resource/rooms/desertaldunes2.roo new file mode 100644 index 0000000000..fb02ca850c Binary files /dev/null and b/resource/rooms/desertaldunes2.roo differ diff --git a/resource/rooms/desertbridge.roo b/resource/rooms/desertbridge.roo index 8161f909ad..46d9225364 100644 Binary files a/resource/rooms/desertbridge.roo and b/resource/rooms/desertbridge.roo differ diff --git a/resource/rooms/desertbridge2.roo b/resource/rooms/desertbridge2.roo new file mode 100644 index 0000000000..b4aeba4317 Binary files /dev/null and b/resource/rooms/desertbridge2.roo differ diff --git a/resource/rooms/desertcaveraft.roo b/resource/rooms/desertcaveraft.roo new file mode 100644 index 0000000000..08248452bb Binary files /dev/null and b/resource/rooms/desertcaveraft.roo differ diff --git a/resource/rooms/desertcliffnoaccess3.roo b/resource/rooms/desertcliffnoaccess3.roo index a87bdbde50..6fc66efe7b 100644 Binary files a/resource/rooms/desertcliffnoaccess3.roo and b/resource/rooms/desertcliffnoaccess3.roo differ diff --git a/resource/rooms/desertriver1.roo b/resource/rooms/desertriver1.roo index c6e3dc0b19..5429ee55c9 100644 Binary files a/resource/rooms/desertriver1.roo and b/resource/rooms/desertriver1.roo differ diff --git a/resource/rooms/desertriver2.roo b/resource/rooms/desertriver2.roo index 58ac962767..6e22889a57 100644 Binary files a/resource/rooms/desertriver2.roo and b/resource/rooms/desertriver2.roo differ diff --git a/resource/rooms/desertsandbar.roo b/resource/rooms/desertsandbar.roo new file mode 100644 index 0000000000..84e62e800d Binary files /dev/null and b/resource/rooms/desertsandbar.roo differ diff --git a/resource/rooms/desertshore1.roo b/resource/rooms/desertshore1.roo index cdace9bc83..d93a5ca73d 100644 Binary files a/resource/rooms/desertshore1.roo and b/resource/rooms/desertshore1.roo differ diff --git a/resource/rooms/desertshore2.roo b/resource/rooms/desertshore2.roo index 8cd53281b8..faac180545 100644 Binary files a/resource/rooms/desertshore2.roo and b/resource/rooms/desertshore2.roo differ diff --git a/resource/rooms/desertshore3.roo b/resource/rooms/desertshore3.roo index f7a85bc94a..3ff6dff8ae 100644 Binary files a/resource/rooms/desertshore3.roo and b/resource/rooms/desertshore3.roo differ diff --git a/resource/rooms/desertsteppes.roo b/resource/rooms/desertsteppes.roo new file mode 100644 index 0000000000..534c4ce95d Binary files /dev/null and b/resource/rooms/desertsteppes.roo differ diff --git a/resource/rooms/elementaldungeon1.roo b/resource/rooms/elementaldungeon1.roo new file mode 100644 index 0000000000..cb93e0f7a7 Binary files /dev/null and b/resource/rooms/elementaldungeon1.roo differ diff --git a/resource/rooms/elementaldungeon2.roo b/resource/rooms/elementaldungeon2.roo new file mode 100644 index 0000000000..3fa75927eb Binary files /dev/null and b/resource/rooms/elementaldungeon2.roo differ diff --git a/resource/rooms/elementaldungeon3.roo b/resource/rooms/elementaldungeon3.roo new file mode 100644 index 0000000000..9d913c7928 Binary files /dev/null and b/resource/rooms/elementaldungeon3.roo differ diff --git a/resource/rooms/waylayoasis.roo b/resource/rooms/waylayoasis.roo index f57dbbb42f..7e2d498912 100644 Binary files a/resource/rooms/waylayoasis.roo and b/resource/rooms/waylayoasis.roo differ diff --git a/roomedit/wip/blackdesert.roo b/roomedit/wip/blackdesert.roo new file mode 100644 index 0000000000..50cfaf78e0 Binary files /dev/null and b/roomedit/wip/blackdesert.roo differ diff --git a/roomedit/wip/blackdeserta.roo b/roomedit/wip/blackdeserta.roo new file mode 100644 index 0000000000..c169cc2588 Binary files /dev/null and b/roomedit/wip/blackdeserta.roo differ diff --git a/roomedit/wip/blackdesertb.roo b/roomedit/wip/blackdesertb.roo new file mode 100644 index 0000000000..e57eb3cd3e Binary files /dev/null and b/roomedit/wip/blackdesertb.roo differ diff --git a/roomedit/wip/blackdesertc.roo b/roomedit/wip/blackdesertc.roo new file mode 100644 index 0000000000..ed0202a9c7 Binary files /dev/null and b/roomedit/wip/blackdesertc.roo differ diff --git a/roomedit/wip/blackdesertd.roo b/roomedit/wip/blackdesertd.roo new file mode 100644 index 0000000000..0e1a9f7063 Binary files /dev/null and b/roomedit/wip/blackdesertd.roo differ diff --git a/roomedit/wip/blackdesertlarge.roo b/roomedit/wip/blackdesertlarge.roo new file mode 100644 index 0000000000..9a4421012c Binary files /dev/null and b/roomedit/wip/blackdesertlarge.roo differ diff --git a/roomedit/wip/blackdesertnoslopes.roo b/roomedit/wip/blackdesertnoslopes.roo new file mode 100644 index 0000000000..4598946794 Binary files /dev/null and b/roomedit/wip/blackdesertnoslopes.roo differ diff --git a/roomedit/wip/blackdesertnoslopes2.roo b/roomedit/wip/blackdesertnoslopes2.roo new file mode 100644 index 0000000000..20ca292b1e Binary files /dev/null and b/roomedit/wip/blackdesertnoslopes2.roo differ diff --git a/roomedit/wip/sandstormwastes.roo b/roomedit/wip/sandstormwastes.roo new file mode 100644 index 0000000000..3a5cb454f8 Binary files /dev/null and b/roomedit/wip/sandstormwastes.roo differ