static void TryToBreed(gamebook.Scenario SC, critters.Critter C) { /* A breeding monster will reproduce if: */ /* - there are less than the max number of monsters on board */ /* - there is a free spot somewhere close to the breeder */ if (critters.NumberOfCritters(SC.CList) < rpgtext.CHART_MaxMonsters) { /* Determine a direction in which to generate the new */ /* monster. The direction can't be "5". */ int D = rpgdice.Random(8) + 1; if (D > 4) { D += 1; } int X = C.M.x + texmaps.VecDir[D - 1, 0]; int Y = C.M.y + texmaps.VecDir[D - 1, 1]; /* Do the checks to make sure this spot is good for adding */ /* a monster. */ if (texmaps.OnTheMap(X, Y) && texmaps.TerrPass[SC.gb.map[X - 1, Y - 1].terr - 1] > 0) { if (!texmodel.ModelPresent(SC.gb.mog, X, Y)) { critters.AddCritter(ref SC.CList, SC.gb, C.crit, X, Y); } } } }
static void ActChaos(gamebook.Scenario SC, critters.Critter C) { /*The critter is gonna be acting chaotically right now.*/ /*Determine a direction to move in. We don't want dir 5*/ /*to be a valid choice.*/ int D = rpgdice.Random(8) + 1; if (D > 4) { D += 1; } int t = 1; while (texmaps.TerrPass[SC.gb.map[C.M.x + texmaps.VecDir[D - 1, 0] - 1, C.M.y + texmaps.VecDir[D - 1, 1] - 1].terr - 1] < 1 && t <= 3) { D = rpgdice.Random(8) + 1; if (D > 4) { D += 1; } t += 1; } texmaps.WalkReport WR = WalkCritter(SC, texmaps.VecDir[D - 1, 0], texmaps.VecDir[D - 1, 1]); if (SC.CAct == null) { return; } if (WR.m != null) { /*Chaotic critters usually won't attack others of*/ /*their own kind... usually. As a lazy way of*/ /*checking this and making alliances, assume that any*/ /*two models using the same letter to represent them*/ /*are friendly.*/ if (WR.m.kind == critters.MKIND_Critter && WR.m.gfx == C.M.gfx && rpgdice.Random(100) != 23) { if (texmaps.TileLOS(SC.gb.POV, C.M.x, C.M.y) && texmaps.OnTheScreen(SC.gb, C.M.x, C.M.y)) { rpgtext.DCGameMessage(critters.MonMan[C.crit - 1].name + " growls."); } } else if (WR.m.kind == dcchars.MKIND_Character || WR.m.kind == critters.MKIND_Critter) { if (rpgdice.Random(8) != 5) { C.Target = WR.m; } CritterAttack(SC, C, WR.m.x, WR.m.y); } } }
static void SlimeDoNothing(gamebook.Scenario SC, critters.Critter C) { if (rpgdice.Random(64) == 9) { if (texmaps.TileLOS(SC.gb.POV, C.M.x, C.M.y) && texmaps.OnTheScreen(SC.gb, C.M.x, C.M.y)) { rpgtext.DCGameMessage(critters.MonMan[C.crit - 1].name + " " + SlimeAct[rpgdice.Random(5)]); } } }
static int TacRange(critters.Critter C) { /*Given critter C, determine its effective combat range.*/ if (C.Eqp != null && C.Eqp.ikind == dcitems.IKIND_Gun) { return(dcitems.CGuns[C.Eqp.icode - 1].RNG); } return(critters.MonMan[C.crit - 1].Range); }
static bool StatAttack(gamebook.Scenario SC, spells.SpellDesc S) { //{Affect every enemy model within range with a given} //{status change condition.} bool itworked = false; //{Determine the accuracy of the attack, also its Value and} //{Duration. Use defaults if these values can't be found.} int V = rpgdice.RollStep(spells.AAVal(S.ATT, spells.AA_Value)); if (V < 5) { V = 5; } for (int X = SC.PC.m.x - S.p1; X <= SC.PC.m.x + S.p1; ++X) { for (int Y = SC.PC.m.y - S.p1; Y <= SC.PC.m.y + S.p1; ++Y) { if (texmodel.ModelPresent(SC.gb.mog, X, Y)) { critters.Critter C = critters.LocateCritter(texmodel.FindModelXY(SC.gb.mlist, X, Y), SC.CList); if (C != null) { if (rpgdice.RollStep(S.p2) > rpgdice.RollStep(critters.MonMan[C.crit - 1].Mystic)) { if (critters.SetCritterStatus(C, S.step, V)) { texmaps.MapSplat(SC.gb, '*', S.c, X, Y, false); itworked = true; } } } } } } if (itworked) { rpgtext.DCAppendMessage("Done."); texfx.Delay(); texmaps.DisplayMap(SC.gb); //{Give the PC a few points for successfully using} //{this spell.} gamebook.DoleExperience(SC, rpgdice.Random(3)); } else { rpgtext.DCAppendMessage("Failed!"); } return(true); }
static void CritterAttack(gamebook.Scenario SC, critters.Critter C, int TX, int TY) { /*Critter C wants to attack whatever is in square TX,TY.*/ /*Fill out the Attack Request.*/ dccombat.AttackRequest AR = new dccombat.AttackRequest(); AR.Attacker = C.M; AR.TX = TX; AR.TY = TY; AR.DF = gamebook.DF_Physical; AR.C = Crt.Color.LightRed; /*Fill out the rest of the data dep}ant upon what equipment*/ /*the creature is using.*/ if (C.Eqp != null && C.Eqp.ikind == dcitems.IKIND_Gun) { AR.HitRoll = critters.MonMan[C.crit - 1].HitRoll + dcitems.CGuns[C.Eqp.icode - 1].ACC; AR.Damage = dcitems.CGuns[C.Eqp.icode - 1].DMG; AR.Range = dcitems.CGuns[C.Eqp.icode - 1].RNG; AR.ATT = dcitems.CGuns[C.Eqp.icode - 1].ATT; if (AR.ATT.Contains(spells.AA_LineAttack)) { AR.Desc = "fires " + dcitems.ItemNameShort(C.Eqp); } else { AR.Desc = "fires " + dcitems.ItemNameShort(C.Eqp) + " at"; } } else if (C.Eqp != null && C.Eqp.ikind == dcitems.IKIND_Wep) { AR.HitRoll = critters.MonMan[C.crit - 1].HitRoll + dcitems.CWep[C.Eqp.icode - 1].ACC; AR.Damage = critters.MonMan[C.crit - 1].Damage + dcitems.CWep[C.Eqp.icode - 1].DMG; AR.Range = -1; AR.Desc = "swings " + dcitems.ItemNameShort(C.Eqp) + " at"; AR.ATT = dcitems.CWep[C.Eqp.icode - 1].ATT; } else { AR.HitRoll = critters.MonMan[C.crit - 1].HitRoll; AR.Damage = critters.MonMan[C.crit - 1].Damage; AR.Range = critters.MonMan[C.crit - 1].Range; AR.Desc = critters.MonMan[C.crit - 1].ADesc; AR.ATT = critters.MonMan[C.crit - 1].AtAt; } /*Process the attack. If a fatality is inflicted on the*/ /*critter's target, the Excommunicate static void will reset*/ /*the target field to null.*/ dccombat.ProcessAttack(SC, AR); }
static void ActHalfHunter(gamebook.Scenario SC, critters.Critter C) { /*This one is easy. Make a random roll, then branch to*/ /*a different static void.*/ if (rpgdice.Random(2) == 1) { ActPCHunter(SC, C); } else { ActChaos(SC, C); }; }
static bool CritterActive(critters.Critter C) { /*Return TRUE if a given critter is capable of acting*/ /*right now, FALSE if it is for any reason incapacitated.*/ if (plotbase.NAttValue(C.SF, statusfx.NAG_StatusChange, statusfx.SEF_Paralysis) != 0) { return(false); } else if (plotbase.NAttValue(C.SF, statusfx.NAG_StatusChange, statusfx.SEF_Sleep) != 0) { return(false); } return(true); }
static void ActGuardian(gamebook.Scenario SC, critters.Critter C) { /*This critter is the guardian of a room.*/ /*The guardian may try to acquire a target, or may remain in*/ /*standby mode.*/ if (rpgdice.Random(10) == 1) { /*Try to acquire a target.*/ for (int X = C.M.x - critters.MonMan[C.crit - 1].Sense; X <= C.M.x + critters.MonMan[C.crit - 1].Sense; ++X) { for (int Y = C.M.y - critters.MonMan[C.crit - 1].Sense; Y <= C.M.y + critters.MonMan[C.crit - 1].Sense; ++Y) { if (texmodel.ModelPresent(SC.gb.mog, X, Y)) { texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, X, Y); if (M.kind == dcchars.MKIND_Character) { ActPCHunter(SC, C); } else if (M.kind == critters.MKIND_Critter) { if (M.gfx != C.M.gfx && rpgdice.Random(3) == 1 && texmaps.CalcObscurement(C.M, M, SC.gb) > -1) { C.Target = M; } } } } } } if (C.Target != null) { ActAgressive(SC, C); } if (rpgdice.Random(5) == 3) { ActChaos(SC, C); } else if (rpgdice.Random(3) == 2) { ActPassive(SC); } /*Else, just sit there and do nothing.*/ }
static int RollDefenses(gamebook.Scenario SC, AttackRequest AR, int TX, int TY) { //{Roll the defenses for whatever is in location TX,TY} int DefRoll = 0; if (SC.gb.mog.IsSet(TX, TY)) { //{the target is a model. Yay! Determine if it's a} //{critter, the PC, or something else, then look up} //{its defense value.} int DfSt = gamebook.ModelDefenseStep(SC, texmodel.FindModelXY(SC.gb.mlist, TX, TY), AR.DF); //{Do the defense roll. Note that unlike most rolls,} //{defense rolls have a minimum value.} DefRoll = rpgdice.RollStep(DfSt); if (DefRoll < DfSt) { DefRoll = DfSt; } //{While we're here, might as well do something else} //{altogether. If a critter is attacked, it switches its} //{TARGET to whatever model attacked it.} if (texmodel.FindModelXY(SC.gb.mlist, TX, TY).kind == critters.MKIND_Critter) { critters.Critter C = critters.LocateCritter(texmodel.FindModelXY(SC.gb.mlist, TX, TY), SC.CList); if (C.M != AR.Attacker) { C.Target = AR.Attacker; if (AR.Attacker.kind == dcchars.MKIND_Character) { SC.PC.target = texmodel.FindModelXY(SC.gb.mlist, TX, TY); AlertOthers(SC, C, AR.Damage); } } } } else { //{ There's no model present, meaning that we're} //{ firing at a map tile.It's not likely to dodge.} DefRoll = 0; } return(DefRoll); }
static void ActSlimy(gamebook.Scenario SC, critters.Critter C) { /*The big thing about a slime is that it never moves.*/ /*It just sits there, and attacks whatever is within reach.*/ /*Slimes give prefrence to attacking the PC. If the PC*/ /*isn't nearby, it may attack other targets rpgdice.rng.Nextly.*/ /*I just noticed something. For lower-order organisms,*/ /*slimes sure is pretty complicated. Their behavior static void*/ /*is the biggest one so far. Maybe that's just because I*/ /*like them...*/ /*If the slime has a target, then attack it.*/ if (C.Target != null) { SlimeAttack(SC, C); } /*If the slime has no target, try to get a lock on the PC.*/ if (GetLockOnPC(SC, C)) { C.Target = SC.PC.m; SlimeAttack(SC, C); } /*The slime hasn't got a target. Just lash out at anything nearby!*/ int D = rpgdice.Random(8) + 1; if (D > 4) { D += 1; } if (texmodel.ModelPresent(SC.gb.mog, C.M.x + texmaps.VecDir[D - 1, 0], C.M.y + texmaps.VecDir[D - 1, 1])) { /*Aha! There's a model here! Thwack it! Uhh... unless it's another slime, of course.*/ texmodel.Model M = texmodel.FindModelXY(SC.gb.mlist, C.M.x + texmaps.VecDir[D - 1, 0], C.M.y + texmaps.VecDir[D - 1, 1]); if (M.gfx != C.M.gfx && M.kind == critters.MKIND_Critter) { CritterAttack(SC, C, C.M.x + texmaps.VecDir[D - 1, 0], C.M.y + texmaps.VecDir[D - 1, 1]); } } else { SlimeDoNothing(SC, C); } }
public static void UpdateMonsterMemory(Scenario SC, critters.Critter C) { //{This monster has apparently just walked into the player's} //{view. Update the player's Monster Memory, and maybe print} //{the monster's introductory text.} if (plotbase.NAttValue(SC.NA, plotbase.NAG_MonsterMemory, C.crit) < 100) { plotbase.AddNAtt(ref SC.NA, plotbase.NAG_MonsterMemory, C.crit, 1); if (plotbase.NAttValue(SC.NA, plotbase.NAG_MonsterMemory, C.crit) == 1) { SetTrigger(SC, PLT_SeeNewCritter, C.crit); if (critters.MonMan[C.crit - 1].IntroText != null) { rpgtext.DCGameMessage(critters.MonMan[C.crit - 1].IntroText, true); texfx.ModelFlash(SC.gb, C.M); rpgtext.GamePause(); } } } C.Spotted = true; }
//{*** MODEL LOOKUP FUNCTIONS ***} public static void Excommunicate(Scenario SC, texmodel.Model M) { //{This isn't a lookup function, but it seemed appropriate} //{to place it here. Model M and the thing it belongs to are} //{about to be removed from play. Remove all mention of this} //{model from game memory.} //{Remove all mention of this model from the Target lists} //{of various monsters.} critters.Critter CT = SC.CList; while (CT != null) { if (CT.Target == M) { CT.Target = null; } CT = CT.next; } //{Clear the PC's target.} if (SC.PC.target == M) { SC.PC.target = null; } //{Clear the active critter, if this is the active critters.} if (SC.CAct != null && SC.CAct.M == M) { SC.CAct = null; } //{If the next critter to act is the one who was killed,} //{move that pointer to the next critter in line.} if (SC.CA2 != null && SC.CA2.M == M) { SC.CA2 = SC.CA2.next; } }
static void SlimeAttack(gamebook.Scenario SC, critters.Critter C) { /*The Slime wants to attack something. Of course,*/ /*given the fact that slimes can't move, this might*/ /*not be possible.*/ /*Slimes with ranged attacks may attack anyhow.*/ if (TacRange(C) > -1 && texmaps.Range(C.M, C.Target) <= critters.MonMan[C.crit - 1].Sense && texmaps.CalcObscurement(C.M, C.Target, SC.gb) > -1) { CritterAttack(SC, C, C.Target.x, C.Target.y); } else if (Math.Abs(C.M.x - C.Target.x) <= 1 && Math.Abs(C.M.y - C.Target.y) <= 1) { CritterAttack(SC, C, C.Target.x, C.Target.y); } else { SlimeDoNothing(SC, C); } }
public static void CritterDeath(gamebook.Scenario SC, critters.Critter C, bool KilledByPC) { //{A critter has died. Deal with it.} if (C == null) { rpgtext.DCGameMessage("SHAZBOT - The attemptes killing of a nonexistant critters."); return; } //{Critters will only drop random treasure if they are killed} //{by the PC.} if (KilledByPC) { if (critters.MonMan[C.crit - 1].TType > 0) { int N = 0; while (N < critters.MonMan[C.crit - 1].TNum && rpgdice.Random(100) < critters.MonMan[C.crit - 1].TDrop) { N += 1; dcitems.DCItem I = charts.GenerateItem(SC, critters.MonMan[C.crit - 1].TType); dcitems.PlaceDCItem(SC.gb, SC.ig, I, C.M.x, C.M.y); } } } if (C.Eqp != null && rpgdice.Random(100) < CDropEqp) { //{The critter dropped whatever it was carrying.} dcitems.PlaceDCItem(SC.gb, SC.ig, C.Eqp, C.M.x, C.M.y); //{Set the Eqp field to Nil, or else the RemoveCritter procedure} //{will delete the item. And mess up our map.} C.Eqp = null; } gamebook.Excommunicate(SC, C.M); critters.RemoveCritter(C, ref SC.CList, SC.gb); }
static void ProcessAlertCritters(ref string Event, gamebook.Scenario SC) { /*Alert all critters of the given type to*/ /*the PC's presence.*/ /* Find out what sort of critter to alert. */ string CType = texutil.ExtractWord(ref Event); /* If the parameter was supplied, go on to alert those critters. */ if (CType != "") { critters.Critter CTemp = SC.CList; while (CTemp != null) { if (CTemp.M.gfx == CType[0]) { CTemp.Target = SC.PC.m; } CTemp = CTemp.next; } } }
static void AlertOthers(gamebook.Scenario SC, critters.Critter C, int DMG) { //{The PC has just attacked critter C. All other critters} //{of this type now have a chance to target the PC for} //{retribution.} critters.Critter CTemp = SC.CList; while (CTemp != null) { if (CTemp.M.gfx == C.M.gfx && CTemp.Target == null && texmaps.Range(CTemp.M, C.M) < 25) { //{This critter is a contemporary of the one} //{which was attacked.} //{Check to see whether the shot was noticed.} if (CTemp.AIType != critters.AIT_Passive && rpgdice.RollStep(critters.MonMan[CTemp.crit - 1].Sense) > rpgdice.RollStep(dcchars.PCStealth(SC.PC) - DMG)) { CTemp.Target = SC.PC.m; } } CTemp = CTemp.next; } }
static bool GetLockOnPC(gamebook.Scenario SC, critters.Critter C) { /*The critter in question is trying to get a "lock" on*/ /*the PC. Make a Sense roll against the PC's Stealth*/ /*skill.*/ /*If the player is out of range, we can't get a lock.*/ if (texmaps.Range(C.M, SC.PC.m) > critters.MonMan[C.crit - 1].Sense * 2) { return(false); } int O = texmaps.CalcObscurement(C.M, SC.PC.m, SC.gb); if (O > -1) { if (rpgdice.RollStep(dcchars.PCStealth(SC.PC)) < rpgdice.RollStep(critters.MonMan[C.crit - 1].Sense) - O) { return(true); } } return(false); }
static void ActPCHunter(gamebook.Scenario SC, critters.Critter C) { /*The critter is gonna attempt to hunt down and destroy*/ /*the player character, to the exclusion of all other*/ /*targets. If the PC is not in sight, either track him*/ /*(if within tracking range) or act passively.*/ /*Determine whether or not the monster can get a 'lock'*/ /*on the player. To do this, we use the monster's Sense*/ /*rating versus the player's Stealth rating.*/ if (GetLockOnPC(SC, C)) { C.Target = SC.PC.m; } if (C.Target != null) { ActAgressive(SC, C); } else { ActPassive(SC); } }
static void PlayScene(gamebook.Scenario SC) { /* This procedure holds the actual game loop. */ /* Note that at the } of this procedure, the scenario is */ /* deallocated. */ Crt.ClrScr(); texmaps.UpdatePOV(SC.gb.POV, SC.gb); texmaps.ApplyPOV(SC.gb.POV, SC.gb); texmaps.DisplayMap(SC.gb); gamebook.PCStatLine(SC); SC.PC.lastCmd = ' '; rpgtext.DCGameMessage("Welcome to the game."); do { SC.ComTime += 1; /* Set time triggers here. */ if (SC.ComTime % 720 == 0) { gamebook.SetTrigger(SC, "HOUR"); } else if (SC.ComTime % 120 == 0) { gamebook.SetTrigger(SC, "10MIN"); } else if (SC.ComTime % 12 == 0) { gamebook.SetTrigger(SC, "MINUTE"); } /*Update the PC's Status List.*/ statusfx.UpdateStatusList(ref SC.PC.SF); /*Check the PCs food status. A check is performed*/ /*every 10 minutes.*/ if (SC.ComTime % 120 == 81) { if (SC.PC.carbs > -10) { SC.PC.carbs -= 1; } if (SC.PC.carbs < 0) { rpgtext.DCGameMessage("You are starving!"); } else if (SC.PC.carbs < 10) { rpgtext.DCGameMessage("You are hungry."); } gamebook.PCStatLine(SC); } /* Check for PC regeneration. A check is performed every minute. */ /* The PC does _not_ regenerate while poisoned. Ouch. */ if (SC.ComTime % 12 == 0) { /*See if the PC gets any HPs back this click...*/ if (SC.PC.HP < SC.PC.HPMax && plotbase.NAttValue(SC.PC.SF, statusfx.NAG_StatusChange, statusfx.SEF_Poison) == 0) { if (gamebook.NumberOfActions(SC.ComTime / 12, dcchars.PCRegeneration(SC.PC)) > 0) { SC.PC.HP += gamebook.NumberOfActions(SC.ComTime / 12, dcchars.PCRegeneration(SC.PC)); /*If the PC is starving and injured, perminant damage to health may result.*/ if (SC.PC.carbs < 0 && rpgdice.Random(Math.Abs(SC.PC.carbs)) > rpgdice.Random(SC.PC.stat[8 - 1]) && SC.PC.HPMax > 10) { SC.PC.HPMax -= 1; rpgtext.DCGameMessage("You feel seriously ill."); } if (SC.PC.HP > SC.PC.HPMax) { SC.PC.HP = SC.PC.HPMax; } gamebook.PCStatLine(SC); } } /*Check for PC MP restoration.*/ if (SC.PC.MP < SC.PC.MPMax) { SC.PC.MP += gamebook.NumberOfActions(SC.ComTime / 12, dcchars.PCRestoration(SC.PC)); if (SC.PC.MP > SC.PC.MPMax) { SC.PC.MP = SC.PC.MPMax; } gamebook.PCStatLine(SC); } } /*Check for random monsters every 5 minutes.*/ if (SC.ComTime % rpgtext.PLAY_MonsterTime == 0) { charts.WanderingCritters(SC); } /* Check for spontaneous identification of items every hour. */ if (SC.ComTime % 720 == 553) { pcaction.ScanUnknownInv(SC); } /*If the player gets an action this second, use it.*/ for (int t = 1; t <= gamebook.NumberOfActions(SC.ComTime, dcchars.PCMoveSpeed(SC.PC)); ++t) { DoPCAction(SC); /* If QUIT was the command, or if the PC is dead, */ /* break this loop. */ if (SC.PC.lastCmd == rpgtext.KMap[26].key || SC.PC.HP <= 0) { break; } plotline.HandleTriggers(SC); SC.gb.POV.range = dcchars.PCVisionRange(SC.PC); } /* If a QUIT request wan't recieved, handle clouds and critters. */ if (SC.PC.lastCmd != rpgtext.KMap[26].key) { /*Cloud handling. Happens every 4 seconds.*/ if (SC.ComTime % 4 == 1) { cbrain.BrownianMotion(SC); } /*Critter handling*/ critters.Critter Cr = SC.CList; while (Cr != null) { /*Save the position of the next critter,*/ /*since the critter we're processing might*/ /*accidentally kill itself during its move.*/ SC.CA2 = Cr.next; for (int t = 1; t <= gamebook.NumberOfActions(SC.ComTime, critters.MonMan[Cr.crit - 1].Speed); ++t) { cbrain.CritterAction(SC, ref Cr); if (Cr == null) { break; } } Cr = SC.CA2; if (SC.PC.HP < 1) { Cr = null; } } } }while (SC.PC.lastCmd != rpgtext.KMap[26].key && SC.PC.HP > 0); if (SC.PC.HP < 1) { rpgtext.DCGameMessage("Game Over."); rpgtext.GamePause(); string fname = "savegame/" + SC.PC.name + ".txt"; if (rpgtext.PLAY_DangerOn) { File.Delete(fname); } } }
static bool DamageCritter(gamebook.Scenario SC, critters.Critter C, int MOS, AttackRequest AR, int DMG, ref AttackReport Rep) { //{Damages a critters. Returns TRUE if the critter has been} //{destroyed; returns FALSE if it is still functional.} //var // OriginalDamage,E,N,V,Armor: int; // snuffedit: bool; //{Save the original damage} int OriginalDamage = DMG; //{Scale damage for elemental types.} //{First, determine what element is being invoked.} int E = spells.AAVal(AR.ATT, spells.AA_Element); DMG = critters.ScaleCritterDamage(C, DMG, E); //{Scale damage for critter slaying attribute.} E = spells.AAVal(AR.ATT, spells.AA_Slaying); if (E > 0 && critters.MonMan[C.crit - 1].CT.Contains(critters.CTMan[E - 1])) { if (DMG < OriginalDamage) { DMG = OriginalDamage; } DMG *= 2 + rpgdice.Random(3); } //{Reduce damage for armor, and increase it for condition.} if (DMG > 0) { //{If a critter is asleep, then it will take double damage} //{from a successful hit and be woken up if it survives.} if (plotbase.NAttValue(C.SF, statusfx.NAG_StatusChange, statusfx.SEF_Sleep) != 0) { DMG *= 2; plotbase.SetNAtt(ref C.SF, statusfx.NAG_StatusChange, statusfx.SEF_Sleep, 0); } //{Calculate Armor rating.} int Armor = critters.MonMan[C.crit - 1].Armor; //{Reduce for MOS} if (MOS > 0) { if (MOS > 4) { MOS = 4; } Armor = (Armor * (4 - MOS)) / 4; if (texmaps.TileLOS(SC.gb.POV, C.M.x, C.M.y)) { rpgtext.DCAppendMessage("Critical Hit!"); } } //{Reduce for Armor-Piercing} if (AR.ATT.Contains(spells.AA_ArmorDoubling)) { Armor *= 2; } else if (AR.ATT.Contains(spells.AA_ArmorPiercing)) { Armor = (Armor + 1) / 2; } DMG -= Armor; if (DMG < 1) { DMG = 1; } } C.HP -= DMG; bool snuffedit = true; if (C.HP > 0) { //{ The target is still alive. See what else needs to be done.} snuffedit = false; //{ There may be status change effects here.} E = spells.AAVal(AR.ATT, spells.AA_StatusChange); if (E != 0) { int N = spells.AAVal(AR.ATT, spells.AA_HitRoll); if (N == 0 || rpgdice.RollStep(N) > rpgdice.RollStep(critters.MonMan[C.crit - 1].Mystic)) { int v = rpgdice.RollStep(spells.AAVal(AR.ATT, spells.AA_Value)); if (v < 5) { v = 5; } if (critters.SetCritterStatus(C, -E, v) && texmaps.TileLOS(SC.gb.POV, C.M.x, C.M.y)) { rpgtext.DCAppendMessage(statusfx.NegSFName[E - 1] + "!"); } } } } else { snuffedit = true; //{ Add the XPV of the critter to the Attack Report's XPV field.} Rep.XPV = Rep.XPV + critters.MonMan[C.crit - 1].XPV; CritterDeath(SC, C, AR.Attacker == SC.PC.m); } return(snuffedit); }
/*This unit handles critter behavior and stuff.*/ public static void CritterAction(gamebook.Scenario SC, ref critters.Critter C) { if (C.HP < 0) { Crt.Write("ERROR- We caught a dead critter acting..!\n"); do { rpgtext.RPGKey(); } while (true); } /*Critter C is about to perform some sort of action. Yay!*/ /*Decide what it's gonna do based on its AI type.*/ SC.CAct = C; if (CritterActive(C)) { if (critters.MonMan[C.crit - 1].CT.Contains(critters.XCT_Breeder) && rpgdice.Random(15) == 1) { TryToBreed(SC, C); } else if (C.Target != null && C.AIType != critters.AIT_Slime) { ActAgressive(SC, C); } else { switch (C.AIType) { case critters.AIT_Passive: ActPassive(SC); break; case critters.AIT_PCHunter: ActPCHunter(SC, C); break; case critters.AIT_Chaos: ActChaos(SC, C); break; case critters.AIT_Guardian: ActGuardian(SC, C); break; case critters.AIT_Slime: ActSlimy(SC, C); break; case critters.AIT_HalfHunter: ActHalfHunter(SC, C); break; } } } /* Protect against the creature already being dead here. */ if (SC.CAct != null) { /*Update the critter's status.*/ if (plotbase.NAttValue(C.SF, statusfx.NAG_StatusChange, statusfx.SEF_Poison) != 0) { C.HP -= rpgdice.Random(6); } statusfx.UpdateStatusList(ref C.SF); if (C.HP < 0) { dccombat.CritterDeath(SC, C, false); } } C = SC.CAct; SC.CAct = null; }
static void ActAgressive(gamebook.Scenario SC, critters.Critter C) { /*This critter apparently has a target. Try to get as*/ /*close to it as possible.*/ /*Check, first of all, that we have a target.*/ if (C.Target == null) { ActPassive(SC); return; } /*Next, on a random whim, check to make sure the target is*/ /*still visible.*/ if (rpgdice.Random(3) == 1 && C.Target == SC.PC.m && !C.Spotted) { int O = texmaps.CalcObscurement(C.M, C.Target, SC.gb); if (O == -1 || O > critters.MonMan[C.crit - 1].Sense) { C.Target = null; ActPassive(SC); return; } } else if (rpgdice.Random(10) == 3) { if (C.HP >= critters.MonMan[C.crit - 1].MaxHP) { int O = texmaps.CalcObscurement(C.M, C.Target, SC.gb); if (O == -1 || O > critters.MonMan[C.crit - 1].Sense) { C.Target = null; ActPassive(SC); return; } } } /*Check to see whether or not our critter is gonna try a missile attack.*/ if (TacRange(C) > 0 && rpgdice.Random(2) == 1 && texmaps.CalcObscurement(C.M, C.Target, SC.gb) > -1) { /* In addition to the above qualifiers, the critter will */ /* only use a missile attack if its target is within visual */ /* range or if it can be seen by the PC. */ if (texmaps.Range(C.M, C.Target) < critters.MonMan[C.crit - 1].Sense || texmaps.TileLOS(SC.gb.POV, C.M.x, C.M.x)) { CritterAttack(SC, C, C.Target.x, C.Target.y); return; } } /*We aren't gonna try a missile attack.*/ /*Move towards the target.*/ int DX = 0; int DY = 0; if (C.Target.x < C.M.x) { DX = -1; } else if (C.Target.x > C.M.x) { DX = 1; } if (C.Target.y < C.M.y) { DY = -1; } else if (C.Target.y > C.M.y) { DY = 1; } /*Check to see if we're in attack range.*/ if (C.M.x + DX != C.Target.x || C.M.y + DY != C.Target.y) { /*Check for obstructions*/ if (!MoveOK(SC, C.M.x + DX, C.M.y + DY)) { if (MoveOK(SC, C.M.x + DX, C.M.y)) { DY = 0; } else if (MoveOK(SC, C.M.x, C.M.y + DY)) { DX = 0; } else if (rpgdice.Random(2) == 1) { ActPassive(SC); return; } } } texmaps.WalkReport WR = WalkCritter(SC, DX, DY); if (SC.CAct == null) { return; } if (WR.m != null) { /*The critter has walked into a model. Decide*/ /*whether or not to attack.*/ /*If the model is in the same square as the critter's target,*/ /*it will be attacked. Before I just used to compare the*/ /*target model with WR.M, but since adding clouds to the game*/ /*I have to compare the position of WR.M to the position of*/ /*the int}ed target. This is important just in case the*/ /*critter is attempting to attack a hallucination-inducing*/ /*cloud, and instead blunders into one of its own buddies...*/ if (WR.m.x == C.Target.x && WR.m.y == C.Target.y) { CritterAttack(SC, C, WR.m.x, WR.m.y); } } }
public static void WanderingCritters(gamebook.Scenario SC) { //{Add some random monsters to the map, if appropriate.} //{Decide how many random generations we're gonna perform.} int N = critters.NumberOfCritters(SC.CList); if (N >= MaxMonsters) { return; } int Gen = 0; if (critters.NumberOfCritters(SC.CList) < MaxMonsters / 2) { Gen = rpgtext.CHART_NumGenerations + rpgdice.Random(rpgtext.CHART_NumGenerations); } else { Gen = rpgdice.Random(rpgtext.CHART_NumGenerations) + 1; } while (Gen > 0) { Gen -= 1; //{Check to see if there is any room for more monsters.} //{The more monsters we have, the less likely we are to add more.} if (critters.NumberOfCritters(SC.CList) < rpgdice.Random(MaxMonsters / 2) + (MaxMonsters / 2) + 1) { //{Roll on the random monster chart.} //{ First decide what chart to use. Either pick a chart } //{ based on PC level, or use the "signature chart" for } //{ the currrent location. } int Chart; if (rpgdice.Random(3) != 1) { //{ Pick chart based on level. } Chart = (rpgdice.Random(SC.PC.lvl) / 3) + 1; } else { //{ The Signature Charts start at 1 and go down. } Chart = 2 - SC.Loc_Number; } //{ Range check the selected chart... } if (Chart > NumWChart) { Chart = NumWChart; } else if (Chart < 1) { Chart = 1; } //{ Roll the Entry and Number. } int E = rpgdice.Random(NumWCT); N = rpgdice.Random(WanderChart[Chart - 1, E, 1]) + 1; //{Decide upon a nice place to put our critters.} //{Select an origin spot - the generated critters will be centered here.} int X0 = rpgdice.Random(texmodel.XMax) + 1; int Y0 = rpgdice.Random(texmodel.YMax) + 1; //{We're gonna give up if we can't find an appropriate} //{tile after 10,000 tries.} int tries = 0; while (texmaps.TerrPass[SC.gb.map[X0 - 1, Y0 - 1].terr - 1] < 1 && tries < 10000) { X0 += 1; tries += 0; if (X0 > texmodel.XMax) { Y0 += 1; X0 = 1; } if (Y0 > texmodel.YMax) { Y0 = 1; } } for (int t = 1; t <= N; t++) { //{Starting position for the swarm is the origin determined earlier.} int X = X0; int Y = Y0; //{Check to see if this is an appropriate spot.} tries = 0; while (tries < 10 && !GoodSpot(SC, X, Y)) { tries += 1; X += rpgdice.Random(3) - rpgdice.Random(3); if (X < 1) { X = 1; } else if (X > texmodel.XMax) { X = texmodel.XMax; } Y += rpgdice.Random(3) - rpgdice.Random(3); if (Y < 1) { Y = 1; } else if (Y > texmodel.YMax) { Y = texmodel.YMax; } } //{If we have a good spot, render the monster.} //{Otherwise, just forget it.} if (GoodSpot(SC, X, Y)) { critters.Critter C = critters.AddCritter(ref SC.CList, SC.gb, WanderChart[Chart - 1, E, 0], X, Y); //{Check to see whether the monster is equipped with a weapon.} if (C != null && critters.MonMan[C.crit - 1].EType > 0 && rpgdice.Random(100) < critters.MonMan[C.crit - 1].EChance) { C.Eqp = GenerateItem(SC, critters.MonMan[C.crit - 1].EType); } } } } } }