pax_global_header00006660000000000000000000000064132107402610014506gustar00rootroot0000000000000052 comment=415dee8d8730ef1ed8adfd741b1a2b2fa201c2e7 gearhead-2-0.701/000077500000000000000000000000001321074026100134145ustar00rootroot00000000000000gearhead-2-0.701/.gitignore000066400000000000000000000006141321074026100154050ustar00rootroot00000000000000*.py[cod] *.o *.ppu savegame/ arena gharena gearhead2 cosplay xxran *.exe *.dll gearhead cosplay2 rnpc # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject gearhead-2-0.701/ability.pp000066400000000000000000000704141321074026100154200ustar00rootroot00000000000000unit ability; { This unit handles character and mecha abilities. } { Mostly, it's used for obtaining and rolling skill } { totals and stuff. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses ui4gh,rpgdice,texutil,gears,gearutil,movement,ghintrinsic; const XPA_AttackHit = 2; XPA_PerMOS = 3; XPA_DestroyMaster = 75; XPA_DestroyThing = 1; XPA_AvoidAttack = 3; XPA_GoodRepairJob = 4; XPA_GoodChat = 1; XPA_SK_Critical = 2; XPA_SK_Basic = 1; { XP for just using a combat skill. } XPA_SK_UseRepair = 3; { PERSONAL COMMUNICATION CAPABILITIES } PCC_Memo = NAS_Memo; { Can view adventure memos } PCC_EMail = NAS_EMail; { Can receive emails from NPCs } PCC_Phone = NAS_Phone; { Can send phone calls in local area } PCC_News = NAS_News; { Can view internet global news } NAG_EpisodeData = -4; NAS_UID = 0; NAS_Target = 1; NAS_ATarget = 3; { Absolute Target } NAS_PrevDamage = 4; { Previous damage rating. } NAS_InitRecharge = 6; { Initiative Recharge for NPCs } { Orders are filed under NAG_EpisodeData } NAS_Orders = 2; NumAITypes = 5; NAV_SeekAndDestroy = 0; NAV_GotoSpot = 1; NAV_SeekEdge = 2; NAV_Passive = 3; NAV_RunAway = 4; NAV_Follow = 5; NAS_ContinuousOrders = 7; { reminder variable for the aibrain unit, } { so it can remember what a particular model is doing. } NAS_ChatterRecharge = 8; { Chatter Recharge for NPCs } { These refer to things that have happened to corpses/wreckage } NAS_Ransacked = 11; NAS_Gutted = 9; NAS_Flayed = 10; NAS_Temporary = 13; { If nonzero, this item should be deleted by DelinkJjang. } NAS_SurrenderStatus = 14; { This counter tells whether or not an NPC has surrendered. } NAV_NowSurrendered = 1; { NPC is currently surrendered. } NAV_ReAttack = 2; { NPC surrendered once, but now will attack again. } NAS_TauntResistance = 15; { After being taunted too much, one begins to build up } { a resistance to it. See the VerbalAttack procedure } { for more information. } NAS_EncounterVisibility = 16; { Timer for encounter visibility. } NAS_WeaponUpgrades = 17; { Number of times this weapon has been upgraded. } { Used by the mecha customizer. } NAS_SpecialActionRecharge = 18; { NPCs will only use special systems once a minute or so. } AI_Type_Label: Array [0..NumAITypes] of String = ( 'SD','GO','EDGE','PASS','RUN','FOL' ); var Skill_Roll_History: SAttPtr; Function LocatePilot( Mecha: GearPtr ): GearPtr; Function GearOperational( Mek: GearPtr ): Boolean; Function GearActive( Mek: GearPtr ): Boolean; function SkillValue( Master: GearPtr; Skill,Stat: Integer ): Integer; function ReactionTime( Master: GearPtr ): Integer; function PilotName( Part: GearPtr ): String; Function MonsterThreatLevel( M: GearPtr ): Integer; Procedure DoleExperience( Mek: GearPtr; XPV: LongInt ); Procedure DoleExperience( Mek,Target: GearPtr; XPV: LongInt ); Function DoleSkillExperience( Mek: GearPtr; Skill,XPV: LongInt ): Boolean; procedure ExpandCharacter( PC: GearPtr ); procedure ResizeCharacter( PC: GearPtr ); Function MappingRange( Mek: GearPtr; Scale: Integer ): Integer; Procedure AddMoraleDMG( PC: GearPtr; M: Integer ); Procedure AddReputation( PC: GearPtr; R,V: Integer ); Procedure AddStaminaDown( PC: GearPtr; Strain: Integer ); Procedure AddMentalDown( PC: GearPtr; Strain: Integer ); Function CurrentMental( PC: GearPtr ): Integer; Function CurrentStamina( PC: GearPtr ): Integer; Function HasPCommCapability( PC: GearPtr; C: Integer ): Boolean; Function HasTalent( PC: GearPtr; T: Integer ): Boolean; Function HasIntrinsic( PC: GearPtr; I: Integer; CasualUse: Boolean ): Boolean; Function IsEnviroSealed( PC: GearPtr ): Boolean; Function PartyPetSlots( PC: GearPtr ): Integer; Function SkillRank( PC: GearPtr; Skill: Integer ): Integer; Function HasSkill( PC: GearPtr; Skill: Integer ): Boolean; Procedure SkillComment( const Msg: String ); Procedure SkillCommentDivider; Function Calculate_Threat_Points( Level,Percent: Integer ): LongInt; implementation uses ghchars,ghmodule,ghholder,ghsensor,ghmecha; Function LocatePilot( Mecha: GearPtr ): GearPtr; { Locate the pilot of this mecha. If no pilot may be found, } { return Nil. } var CPit, Pilot: GearPtr; { Pointers to the Cockpit and Pilot } begin { Error Check - make sure we have a valid mecha here. } if ( Mecha = Nil ) then Exit( Nil ); if not IsMasterGear( Mecha ) then Mecha := FindMaster( Mecha ); if Mecha = Nil then begin Pilot := Nil; end else if Mecha^.G = GG_Character then begin { Just return this character, since we can't find a mecha. } Pilot := Mecha; end else begin { This is probably a mecha. } { Locate the cockpit. If no cockpit may be found, return Nil. } CPit := SeekGear( Mecha , GG_Cockpit , 0 , False ); if CPit = Nil then Exit( Nil ); { Locate the pilot. } Pilot := CPit^.SubCom; while ( Pilot <> Nil ) and ( Pilot^.G <> GG_Character ) do begin Pilot := Pilot^.Next; end; end; LocatePilot := Pilot; end; Function GearOperational( Mek: GearPtr ): Boolean; { A gear is operational if it is capable of action. } { Mecha need a pilot in order to be operational. } { Other gears are operational if they aren't destroyed. } var MO: Boolean; { This func used to be called MekOperational, so MO } begin { Error Check } if ( Mek = Nil ) then begin MO := False end else if Mek^.G = GG_Mecha then begin MO := NotDestroyed( Mek ) and NotDestroyed( LocatePilot( Mek ) ); end else MO := NotDestroyed( Mek ); GearOperational := MO; end; Function GearActive( Mek: GearPtr ): Boolean; { ACTIVE means that a given gear is capable of self-controlled action. } { Generally only master gears may be active. } begin if Mek = Nil then GearActive := False else if Mek^.G = GG_Character then GearActive := GearOperational( Mek ) and ( NAttValue( Mek^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) <> NAV_NowSurrendered ) else if IsMasterGear( Mek ) then GearActive := GearOperational( Mek ) else GearActive := False; end; Function SkillRank( PC: GearPtr; Skill: Integer ): Integer; { Return the PC's rank in this skill. } begin { Make sure we're dealing with the real PC here. } PC := LocatePilot( PC ); SkillRank := CharaSkillRank( PC , Skill ); end; function SkillValue( Master: GearPtr; Skill,Stat: Integer ): Integer; { Find MASTER's skill roll value. This is the } { skill rank + the attribute value + any modifiers } { that might apply (maneuver class, etc). } function UnitSkillValue( M: GearPtr ): Integer; { Return the skill value of this unit. } { M points to the list of unit models. } var MSkill,BigSkill,TSkill: Integer; begin { Check through every mek on the board. } BigSkill := 0; TSkill := 0; while m <> Nil do begin if M^.G = GG_Character then begin MSkill := SkillValue( M , Skill , Stat ); if MSkill > BigSkill then BigSkill := MSkill; if MSkill >= 5 then TSkill := TSkill + ( MSkill div 5 ); end; m := m^.Next; end; UnitSkillValue := BigSkill + TSkill - ( BigSkill div 5 ); end; var C,Tool: GearPtr; {Ptr to the controling character / skill bank.} SkRk,StRk: Integer; {Skill Rank, Stat Rank. } SkMod,Morale: Integer; {Skill Roll Modifier. } it: Integer; begin { Error check- make sure that we're actually dealing } { with a master gear and not a ham sandwich or anything. } if ( Master = Nil ) or Not ( IsMasterGear( Master ) or ( Master^.G = GG_Adventure ) ) then Exit( 0 ); { Error check- make sure we have valid skill and stat numbers. } if (Skill < 1) or (Skill > NumSkill) then Exit( 0 ); if ( Stat < 1 ) or ( Stat > num_character_stats ) then Exit( 0 ); { Skill Roll Modifier starts out at 0. } SkMod := 0; if Master^.G = GG_Character then begin { Since this is a character, just grab the needed } { ranks from the gear's stats and attributes. } SkRk := CharaSkillRank( Master , Skill ); StRk := CStat( Master , Stat ); C := Master; { If the skill isn't known at all, there's a penalty. } if ( SkRk < 1 ) then SkMod := -2; { Check for tools. } SkMod := SkMod + ToolBonus( Master , Skill ); { If this is a combat skill, check for RAGE. } if ( Skill <= 10 ) and ( NAttValue( Master^.NA , NAG_Talent , NAS_Rage ) <> 0 ) then begin Morale := NAttValue( Master^.NA , NAG_Condition , NAS_MoraleDamage ); if Morale > 0 then begin SkRk := SkRk + Morale div 20; end else if Morale < -20 then begin SkRk := SkRk - 2; end; end; end else if Master^.G = GG_Adventure then begin { This must be an arena unit. } { We don't need the additional checks below; just calculate } { the skill value and return it. } Exit( UnitSkillValue( Master^.SubCom ) ); end else if Master^.G = GG_Mecha then begin { As of this implementation, mecha are assumed to } { have a single pilot. } C := LocatePilot( Master ); if C = Nil then Exit( 0 ); SkRk := SkillValue( C , Skill , Stat ) + ModifiersSkillBonus( Master , Skill ); StRk := 0; { If this mecha has reflex control, there may be a +1 bonus to this skill. } if HasMechaTrait( Master , MT_ReflexSystem ) and ( Skill < 4 ) then begin if CharaSkillRank( C , Skill + 3 ) >= CharaSkillRank( C , Skill ) then Inc( SkRk ); end; if SkillMan[Skill].MekSys = MS_Maneuver then begin SkMod := SkMod + MechaManeuver( Master ); end else if SkillMan[Skill].MekSys = MS_Targeting then begin SkMod := SkMod + MechaTargeting( Master ); end else if SkillMan[Skill].MekSys = MS_Sensor then begin SkMod := SkMod + MechaSensorRating( Master ); end; end else begin { Props are easy. Return the basic skill rank. } StRk := 0; SkRk := NAttValue( Master^.NA , NAG_Skill , Skill); C := Master; end; { The final value equals the Skill Rank plus } { stat modifier plus skill roll modifier. } it := ( ( StRk + 1 ) div 2 ) + SkRk + SkMod; if it < 1 then it := 1; { Last minute check- if the character is dead, they don't get } { to roll dice. } if Destroyed( C ) then it := 0; SkillValue := it; end; function ReactionTime( Master: GearPtr ): Integer; { Determine the reaction time for this character/mecha. } const { Even the slowest people will react at this base speed. } Minimum_Initiative = 10; { To prevent huge differences in reaction time, all times } { get modified by a baseline. } Baseline_Initiative = 15; var I,RT: Integer; begin { Determine the Initiative skill value for this character. } if Master^.G = GG_Prop then begin I := SkillValue( Master , NAS_Initiative , STAT_Speed ) + 1; if I < 1 then I := 1; end else begin Master := LocatePilot( Master ); if Master <> Nil then begin I := CStat( Master , STAT_Speed ) + CharaSkillRank( Master , NAS_Initiative ); if ( I < Minimum_Initiative ) then I := Minimum_Initiative; end else I := 1; end; RT := ( ClicksPerRound * 10 ) div ( I + Baseline_Initiative ); if RT > ClicksPerRound then RT := ClicksPerRound else if RT < 2 then RT := 2; ReactionTime := RT; end; function PilotName( Part: GearPtr ): String; { Locate the name of the pilot of this thing; } { provide the best substitute if no controller can be found. } var M: GearPtr; name: String; begin M := Part; if not IsMasterGear( Part ) then M := FindMaster( Part ); if M = Nil then begin if Part = Nil then name := 'Nothing' else name := GearName( Part ); end else if M^.G = GG_Mecha then begin Part := LocatePilot( M ); if Part = Nil then name := GearName( M ) else name := GearName( Part ); end else begin name := GearName( M ); end; PilotName := Name; end; Function MonsterThreatLevel( M: GearPtr ): Integer; { Return the threat level of this monster. This is used for generating random monsters } { and also for assigning XP from kills. } begin if M = Nil then begin MonsterThreatLevel := 0; end else if ( M^.G = GG_Character ) or ( M^.G = GG_Prop ) then begin MonsterThreatLevel := NAttValue( M^.NA , NAG_GearOps , NAS_MonsterTV ); end else begin MonsterThreatLevel := 0; end; end; Procedure DoleExperience( Mek: GearPtr; XPV: LongInt ); { Give XPV experience points to whoever is behind the wheel of } { master unit Mek. } var P: GearPtr; { The pilot, in theory. } begin P := LocatePilot( Mek ); if P <> Nil then begin AddNAtt( P^.NA , NAG_Experience , NAS_TotalXP , XPV ); if XPV > Random(25) then AddMoraleDmg( P , -1 ); end; end; Procedure DoleExperience( Mek,Target: GearPtr; XPV: LongInt ); { Give XPV experience points to whoever is behind the wheel of } { master unit Mek. Scale the experience points by the relative } { values of Mek and Target. } var MPV,TPV,MonPV: LongInt; { Mek PV, Target PV } XP2: Int64; { To prevent arithmetic overflows. } begin MPV := GearValue( Mek ); if MPV < 1 then MPV := 1; if Target <> Nil then begin TPV := GearValue( Target ); { Monsters might benefit from an upward-adjusted TPV based on } { their difficulcy rating. } if MonsterThreatLevel( Target ) > 0 then begin MonPV := MonsterThreatLevel( Target ) * MonsterThreatLevel( Target ) * 10 - MonsterThreatLevel( Target ) * 100; if MonPV > TPV then TPV := MonPV; end; XP2 := ( XPV * TPV * ( Target^.Scale + 1 ) ) div MPV; XPV := XP2; end; if XPV < 1 then XPV := 1; DoleExperience( Mek , XPV ); end; Function DoleSkillExperience( Mek: GearPtr; Skill,XPV: LongInt ): Boolean; { Give XPV experience points to whoever is behind the wheel of } { master unit Mek. Apply these XP directly to SKILL. } { Return TRUE if this results in a skill increase, or FALSE if not. } var P: GearPtr; { The pilot, in theory. } SkLvl: Integer; it,DidIncrease: Boolean; begin P := LocatePilot( Mek ); it := False; { Assume FALSE unless shown otherwise. } if P <> Nil then begin AddNAtt( P^.NA , NAG_Experience , NAS_Skill_XP_Base + Skill , XPV ); { Check to see if enough skill-specific XPs have been earned to advance the skill. } repeat SkLvl := NAttValue( P^.NA , NAG_Skill , Skill ); { Assume FALSE unless proven TRUE. } DidIncrease := False; { Hidden skills can always improve from natural experience, since that's the } { only way to improve them. } if ( NATTValue( P^.NA , NAG_Experience , NAS_Skill_XP_Base + Skill ) >= SkillAdvCost( Nil , SkLvl ) ) and ( ( NAttValue( P^.NA , NAG_Skill , Skill ) > 0 ) or SkillMan[ Skill ].Hidden or Direct_Skill_Learning ) then begin { Set IT to true, advance the skill, and decrease the } { number of skill-specific XPs the character has. } it := True; DidIncrease := True; AddNAtt( P^.NA , NAG_Experience , NAS_Skill_XP_Base + Skill , -SkillAdvCost( Nil , SkLvl ) ); AddNAtt( P^.NA , NAG_Skill , Skill , 1 ); end; until not DidIncrease; end; { Return the boolean value. } DoleSkillExperience := it; end; procedure ExpandCharacter( PC: GearPtr ); { Create a body for a currently disembodied character gear. } var M,H: GearPtr; { Module , Hand } { PROCEDURES BLOCK } Procedure InsertLimb( N: Integer ); begin M := AddGear( PC^.SubCom , PC ); M^.G := GG_Module; M^.S := N; M^.V := MasterSize( M ); M^.Stat[STAT_Resizable] := 1; InitGear( M ); end; begin if PC^.SubCom = Nil then begin InsertLimb( GS_Head ); InsertLimb( GS_Body ); InsertLimb( GS_Arm ); SetSAtt( M^.SA , 'NAME <' + MsgString( 'EXPAND_RightArm' ) + '>' ); H := AddGear( M^.SubCom , M ); H^.G := GG_Holder; H^.S := GS_Hand; SetSAtt( H^.SA , 'NAME <' + MsgString( 'EXPAND_RightHand' ) + '>' ); InitGear( H ); InsertLimb( GS_Arm ); SetSAtt( M^.SA , 'NAME <' + MsgString( 'EXPAND_LeftArm' ) + '>' ); H := AddGear( M^.SubCom , M ); H^.G := GG_Holder; H^.S := GS_Hand; SetSAtt( H^.SA , 'NAME <' + MsgString( 'EXPAND_LeftHand' ) + '>' ); InitGear( H ); InsertLimb( GS_Leg ); SetSAtt( M^.SA , 'NAME <' + MsgString( 'EXPAND_RightLeg' ) + '>' ); InsertLimb( GS_Leg ); SetSAtt( M^.SA , 'NAME <' + MsgString( 'EXPAND_LeftLeg' ) + '>' ); end; end; Procedure ResizeCharacter ( PC: GearPtr ); { Adjust the size of the limbs of a human character to } { reflect an altered body stat.} var Module : GearPtr; begin if PC^.G = GG_Character then begin Module := PC^.SubCom; While Module <> Nil do begin if (Module^.G = GG_Module) and (Module^.Stat[STAT_Resizable] = 1) then Module^.V := MasterSize (PC); Module := Module^.Next; end; end; end; Function MappingRange( Mek: GearPtr; Scale: Integer ): Integer; { Determine how far this mek can see new map tiles. } { This is determined by two things- first, the mek's sensor } { rating, and secondly the pilot's Perception stat. } var Sensor,Pilot: GearPtr; it,t: Integer; begin it := 0; Sensor := SeekActiveIntrinsic( Mek , GG_Sensor , GS_MainSensor ); if Sensor <> Nil then begin it := it + Sensor^.V; end; Pilot := LocatePilot( Mek ); if Pilot <> Nil then begin it := it + ( CStat( Pilot , STAT_Perception ) div 3 ); end; { Adjust the mapping range for scale. } if Mek^.Scale > Scale then begin for t := ( Scale + 1 ) to Mek^.Scale do it := it * 2; end else if Mek^.Scale < ( Scale + 1 ) then begin for t := 1 to ( Scale - Mek^.Scale - 1 ) do it := it div 2; if it < 1 then it := 1; end; MappingRange := it; end; Procedure AddMoraleDMG( PC: GearPtr; M: Integer ); { Add some morale to the PC, keeping it withing the normal } { range of +100 (miserable) to -100 (ecstatic). } var CL: Integer; { Current Level } begin if PC^.G <> GG_Character then PC := LocatePilot( PC ); if ( PC <> Nil ) and ( PC^.G = GG_Character ) then begin CL := NAttValue( PC^.NA , NAG_Condition , NAS_MoraleDamage ); { If it's positive morale damage and CL is negative, } { make a RESISTANCE roll to avoid losing mood. } if ( M > 1 ) and ( CL < 0 ) then begin if RollStep( SkillValue( PC , NAS_Toughness , STAT_Ego ) ) < ( M + 1 ) then begin CL := CL div 2; end; end; if Abs( CL + M ) > 100 then begin SetNATt( PC^.NA , NAG_Condition , NAS_MoraleDamage , 100 * Sgn( CL ) ); end else begin SetNATt( PC^.NA , NAG_Condition , NAS_MoraleDamage , CL + M ); end; end; end; Procedure AddReputation( PC: GearPtr; R,V: Integer ); { Add a certain amount to reputation R, keeping in mind that } { the allowable range is -100..+100. } const MaxPositiveHeroism = 100; var CL,PosHero: Integer; { Current Level, Positive Heroism } begin if PC^.G <> GG_Character then PC := LocatePilot( PC ); R := Abs( R ); if ( PC <> Nil ) and ( PC^.G = GG_Character ) then begin CL := NAttValue( PC^.NA , NAG_CHarDescription , -R ); { Increasing a favored reputation improves morale, } { while decreasing a reputation abuses it. } if R = -NAS_Renowned then begin { Gaining renown always improves mood, losing it always } { abuses mood. } if V > 0 then begin AddMoraleDmg( PC , -MORALE_RepSmall ); end else if V < 0 then begin AddMoraleDmg( PC , MORALE_RepSmall ); end; end else if Sgn( CL ) = Sgn( V ) then begin if Abs( V ) = 1 then begin AddMoraleDmg( PC , -MORALE_RepSmall ); end else begin AddMoraleDmg( PC , -MORALE_RepBig ); end; end else if Sgn( CL ) = -Sgn( V ) then begin if Abs( V ) = 1 then begin AddMoraleDmg( PC , MORALE_RepSmall ); end else begin AddMoraleDmg( PC , MORALE_RepBig ); end; end; { Any act of major villainy or comission of crimes } { (greater than a -1 change) will completely wipe } { out any heroic or lawful reputation that } { this character may have had. } { Acts of positive heroism may be limited. You can only gain } { up to 100 positive heroic points, so it's not possible to } { farm heroic points. } if ( R <= 2 ) and ( V < -1 ) and ( CL > 0 ) then begin CL := 0; end else if ( R = -NAS_Heroic ) and ( V > 0 ) then begin PosHero := NAttValue( PC^.NA , NAG_CHarDescription , NAS_PositiveHeroism ); AddNAtt( PC^.NA , NAG_CHarDescription , NAS_PositiveHeroism , V ); if ( PosHero + V ) > MaxPositiveHeroism then begin V := MaxPositiveHeroism - PosHero; if V < 0 then V := 0; end; end; if Abs( CL + V ) > 100 then begin SetNATt( PC^.NA , NAG_CharDescription , -R , 100 * Sgn( CL ) ); end else begin SetNATt( PC^.NA , NAG_CharDescription , -R , CL + V ); end; end; end; Procedure AddStaminaDown( PC: GearPtr; Strain: Integer ); { Apply stamina drain to the PC. } begin { Begin with a battery of error checks. } if ( PC <> Nil ) and ( Strain > 0 ) then begin if PC^.G <> GG_Character then PC := LocatePilot( PC ); if ( PC <> Nil ) then begin if ( CurrentStamina( PC ) > 0 ) then begin AddNAtt( PC^.NA , NAG_Condition , NAS_StaminaDown , Strain ); { Using SP trains athletics. } DoleSkillExperience( PC , NAS_Athletics , Strain * 2 ); end else begin AddMoraleDmg( PC , Strain ); end; end; end; end; Procedure AddMentalDown( PC: GearPtr; Strain: Integer ); { Apply mental drain to the PC. } begin { Begin with a battery of error checks. } if ( PC <> Nil ) and ( Strain > 0 ) then begin if PC^.G <> GG_Character then PC := LocatePilot( PC ); if ( PC <> Nil ) then begin if ( CurrentMental( PC ) > 0 ) then begin AddNAtt( PC^.NA , NAG_Condition , NAS_MentalDown , Strain ); { Using MP trains concentration. } DoleSkillExperience( PC , NAS_Concentration , Strain * 2 ); end else begin AddMoraleDMG( PC , Strain ); end; end; end; end; Function CurrentMental( PC: GearPtr ): Integer; { Return how many mental points this character currently has. } var it: Integer; begin PC := LocatePilot( PC ); if PC <> Nil then begin it := CharMental( PC ) - NAttValue( PC^.NA , NAG_Condition , NAS_MentalDown ); if it < 0 then it := 0; CurrentMental := it; end else begin CurrentMental := 0; end; end; Function CurrentStamina( PC: GearPtr ): Integer; { Return how many stamina points this character currently has. } var it: Integer; begin PC := LocatePilot( PC ); if PC <> Nil then begin it := CharStamina( PC ) - NAttValue( PC^.NA , NAG_Condition , NAS_StaminaDown ); if it < 0 then it := 0; CurrentStamina := it; end else begin CurrentStamina := 0; end; end; Function HasPCommCapability( PC: GearPtr; C: Integer ): Boolean; { Return TRUE if the listed PC has the requested Personal } { Communications Capability. } begin HasPCommCapability := HasIntrinsic( PC , C , True ); end; Function HasTalent( PC: GearPtr; T: Integer ): Boolean; { Return TRUE if PC has the listed talent, FALSE otherwise. } var it: Boolean; begin if ( PC <> Nil ) and ( PC^.G = GG_Adventure ) then begin PC := PC^.SubCom; it := False; while ( PC <> Nil ) and not it do begin if PC^.G = GG_Character then it := HasTalent( PC , T ); PC := PC^.Next; end; end else begin PC := LocatePilot( PC ); it := ( PC <> Nil ) and ( NAttValue( PC^.NA , NAG_Talent , T ) <> 0 ); end; HasTalent := it; end; Function HasIntrinsic( PC: GearPtr; I: Integer; CasualUse: Boolean ): Boolean; { Return TRUE if the PC has the listed intrinsic, or FALSE otherwise. } { If this is casual use, search the general inventory. Otherwise } { just search the subcomponents. } Function IntrinsicFoundAlongTrack( Part: GearPtr ): Boolean; { Return TRUE if the intrinsic is found along this track. } var WasFound: Boolean; begin { Begin by assuming FALSE. } WasFound := False; { Search along the track until we run out of parts or find } { the intrinsic. } while ( Part <> Nil ) and not WasFound do begin if NotDestroyed( Part ) then begin WasFound := NAttValue( Part^.NA , NAG_Intrinsic , I ) <> 0; if not WasFound then WasFound := IntrinsicFoundAlongTrack( Part^.SubCom ); if not WasFound then WasFound := IntrinsicFoundAlongTrack( Part^.InvCom ); end; Part := Part^.Next; end; IntrinsicFoundAlongTrack := WasFound; end; var it: Boolean; begin { Start with an error check- if this isn't a regular intrinsic, return FALSE. } if ( PC = Nil ) or ( I < 1 ) or ( I > NumIntrinsic ) then Exit( False ); it := NAttValue( PC^.NA , NAG_Intrinsic , I ) <> 0; if not it then it := IntrinsicFoundAlongTrack( PC^.SubCom ); if CasualUse and not it then it := IntrinsicFoundAlongTrack( PC^.InvCom ); HasIntrinsic := it; end; Function IsEnviroSealed( PC: GearPtr ): Boolean; { Return TRUE if the PC is environmentally sealed, or FALSE otherwise. } { The PC counts as environmentally sealed if either 1) the main chara gear } { has the sealed intrinsic, or 2) all modules individually have the } { sealed intrinsic. } { Oh, 3) Mecha and other non-characters automatically count as sealed. } var M: GearPtr; it: Boolean; begin if ( NAttValue( PC^.NA , NAG_Intrinsic , NAS_EnviroSealed ) <> 0 ) or ( PC^.G <> GG_Character ) then begin IsEnviroSealed := true; end else begin { Check all the limbs for sealing. } M := PC^.SubCom; { Assume TRUE unless shown FALSE. } it := True; { If a single limb is found that isn't sealed, then the entirety isn't sealed. } while M <> Nil do begin if ( M^.G = GG_Module ) and NotDestroyed( M ) then begin if not HasIntrinsic( M , NAS_EnviroSealed , True ) then it := False; end; M := M^.Next; end; IsEnviroSealed := it; end; end; Function PartyPetSlots( PC: GearPtr ): Integer; { Return however many pets the PC can have. } begin PC := LocatePilot( PC ); if PC = Nil then Exit( 0 ); PartyPetSlots := 1 + CStat( PC , STAT_Charm ) div 4; end; Function HasSkill( PC: GearPtr; Skill: Integer ): Boolean; { Return TRUE if the PC has the listed skill, or FALSE otherwise. } var it: Boolean; begin if ( PC <> Nil ) and ( PC^.G = GG_Adventure ) then begin PC := PC^.SubCom; it := False; while ( PC <> Nil ) and not it do begin if PC^.G = GG_Character then it := HasSkill( PC , Skill ); PC := PC^.Next; end; end else begin { Make sure we're dealing with the real PC here. } PC := LocatePilot( PC ); if PC <> Nil then begin it := ( NAttValue( PC^.NA , NAG_Skill , Skill ) > 0 ) or HasTalent( PC , NAS_JackOfAll ); end else begin it := False; end; end; HasSkill := it; end; Procedure SkillComment( const Msg: String ); { Add a comment to the skill roll history. } var SA: SAttPtr; begin if NumSAtts( Skill_Roll_History ) >= 100 then begin SA := Skill_Roll_History; RemoveSAtt( Skill_Roll_History , SA ); end; StoreSAtt( Skill_Roll_History , Msg ); end; Procedure SkillCommentDivider; { Draw a nice divider in the skill roll history list. } begin SkillComment( '--' ); end; Function Calculate_Threat_Points( Level,Percent: Integer ): LongInt; { Calculate an appropriate threat value, based upon the modified } { renown level and the % scale factor. } var it: LongInt; begin if Level < 0 then Level := 0 else if Level > 300 then Level := 300; { This formula looks strange; it was created using some expected values and then } { deriving an equation to fit. } { For low level encounters, use a linear equation. } if Level < 31 then begin it := Level * 10000 div 30; { Higher on, switch to the quadratic. } end else begin it := 20 * Level * Level - 900 * Level + 19040; end; { Modify for the percent requested. } it := it * Percent; Calculate_Threat_Points := it; end; initialization Skill_Roll_History := Nil; finalization DisposeSATt( Skill_Roll_History ); end. gearhead-2-0.701/action.pp000066400000000000000000001620021321074026100152330ustar00rootroot00000000000000unit action; { This is the *ACTION* unit! It handles two kinds of action- } { gears moving across the gameboard, and gears taking damage + } { potentially blowing up. This might seem like two strange things } { to combine in a single unit, but believe me it makes sense. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale; Type DamageRec = Record EjectRoll,EjectOK,PilotDied,MechaDestroyed: Boolean; DamageDone: LongInt; end; const TRIGGER_NumberOfUnits = 'NU'; TRIGGER_UnitEliminated = 'TD'; TRIGGER_NPCEliminated = 'FAINT'; TRIGGER_NPCSurrendered = 'SURRENDER'; TRIGGER_TeamMovement = 'TMOVE'; TRIGGER_NPCMovement = 'MOVE'; TRIGGER_PCAttack = 'PCATTACK'; { *** ENACT MOVEMENT RESULTS *** } EMR_Blocked = -2; { May indicate a charge... } EMR_Crash = 2; { If either of these are returned from movement, } { you should do a check to resolve crashes and charges. } SA_Explosion = 'EXPLOSION'; var Destroyed_Parts_List: SAttPtr; Function Confused( Mek: GearPtr ): Boolean; Procedure GiveSkillRollXPAward( Pilot: GearPtr; Skill,SkRoll,SkTar: Integer ); Function SkillRoll( GB: GameBoardPtr; PC: GearPtr; Skill,Stat,SkTar,SkMod: Integer; CasualUse,GainXP: Boolean ): Integer; Function CheckLOS( GB: GameBoardPtr; Observer,Target: GearPtr ): Boolean; Procedure VisionCheck( GB: GameBoardPtr; Mek: GearPtr ); Function DamageGear( gb: GameBoardPtr; O_Part,O_Weapon: GearPtr; O_DMG,O_MOS,O_NumHits: Integer; const AtAt: String ): DamageRec; Function DamageGear( gb: GameBoardPtr; Part: GearPtr; DMG: Integer): DamageRec; procedure Crash( gb: GameBoardPtr; Mek: GearPtr ); Procedure PrepAction( GB: GameBoardPtr; Mek: GearPtr; Action: Integer ); Procedure SetMoveMode( GB: GameBoardPtr; Mek: GearPtr; MM: Integer ); Function EnactMovement( GB: GameBoardPtr; Mek: GearPtr ): Integer; Procedure DoDrift( GB: GameBoardPtr; Mek: GearPtr ); Procedure WaitAMinute( GB: GameBoardPtr; Mek: GearPtr; D: Integer ); implementation uses ability,gearutil,ghchars,ghmodule,ghweapon,movement,rpgdice,texutil, math,ui4gh,ghintrinsic,ghsensor,ghprop,ghsupport, {$IFDEF ASCII} vidgfx; {$ELSE} sdlgfx; {$ENDIF} const EjectDamage = 10; { The damage step to roll during an ejection attempt. } MissileConcussion = 3; MeleeConcussion = 3; ModuleConcussion = 7; DamagePilotChance = 75; { % chance to damage pilot by concussion. } DamageInventoryChance = 25; { % chance to damage inventory gears by concussion. } Stealth_Per_Scale = 4; { If scale different from map, get stealth bonus/penalty } Function Confused( Mek: GearPtr ): Boolean; { Return true if either the pilot or the mecha is either } { HAYWIRE or STONED. } var Pilot: GearPtr; begin if Mek^.G = GG_Mecha then begin Pilot := LocatePilot( Mek ); end else begin Pilot := Nil; end; Confused := HasStatus( Mek , NAS_Haywire ) or HasStatus( Mek , NAS_Stoned ) or HasStatus( Pilot , NAS_Haywire ) or HasStatus( Pilot , NAS_Stoned ); end; Procedure GiveSkillRollXPAward( Pilot: GearPtr; Skill,SkRoll,SkTar: Integer ); { Give a skill XP award. The size of the award is going to depend on the } { relation betwee SkRoll and SkTar. } const { Basic_Skill_Award_Multiplier: Array [1..NumSkill] of Byte = ( 1,1,1,1,1, 2,2,2,2,2, 1,1,1,5,1, 1,1,1,5,1, 1,1,1,1,1, 1,5,3,3,1, 1,3,1,3,1, 2,3,1,1,10, 7,1,1 );} MaxTarLevel = 12; Skill_Award_By_Level: Array [1..12] of Integer = ( 1,2,3,4,5, 8,13,21,34,55, 89,144 ); var TarLevel: Integer; { Just how tough was this target? } SkRank: Integer; { Skill rank. } begin Pilot := LocatePilot( Pilot ); if ( Pilot <> Nil ) and ( Skill >= 1 ) and ( Skill <= NumSkill ) and ( SkRoll >= SkTar ) then begin { Determine the TarLevel. This will tell how much XP to give. } TarLevel := ( SkTar + 2 ) div 3; if TarLevel < 1 then TarLevel := 1 else if TarLevel > MaxTarLevel then TarLevel := MaxTarLevel; { Determine the Pilot's skill rank. This will be needed later. } SkRank := NAttValue( Pilot^.NA , NAG_Skill , Skill ); { The expereince award can't exceed one level beyond the skill rank. } if TarLevel > ( SkRank + 1 ) then TarLevel := SkRank + 1; if ( SkTar > SkRank ) and ( SkRank > 0 ) then begin DoleSkillExperience( Pilot , Skill , Skill_Award_By_Level[ TarLevel ] * 2 ); DoleExperience( Pilot , Skill_Award_By_Level[ TarLevel ] ); end else if ( SkTar > ( SkRank div 2 ) ) then begin DoleSkillExperience( Pilot , Skill , Skill_Award_By_Level[ TarLevel ] ); if SkRank > 0 then DoleExperience( Pilot , 1 ); end; end; end; Function SkillRoll( GB: GameBoardPtr; PC: GearPtr; Skill,Stat,SkTar,SkMod: Integer; CasualUse,GainXP: Boolean ): Integer; { Attempt to make a skill roll. } { Skill = The skill to use. } { SkTar = The target number to try and beat. } { SkMod = The modifier to the skill roll. } { CasualUse = TRUE if the PC can use things in his backpack and use the team's } { help, or FALSE otherwise. } var msg: String; SkRank,SkRoll: Integer; Pilot: GearPtr; begin { Determine the base skill value, and initialize the message. } msg := PilotName( PC ) + ' rolls ' + MSgString( 'SKILLNAME_' + BStr( Skill ) ) + '+' + MSgString( 'STATNAME_' + BStr( Stat ) ) + ' '; if CasualUse and ( GB <> Nil ) then begin SkRank := TeamSkill( GB , NAV_DefPlayerTeam , Skill , Stat ); end else begin SkRank := SkillValue( PC , Skill , Stat ); end; msg := msg + '[' + BStr( SkRank ); if SkMod <> 0 then msg := msg + SgnStr( SkMod ); msg := msg + ']'; if SkTar > 0 then begin msg := msg + ' vs ' + BStr( SkTar ) + ':'; end else begin msg := msg + ':'; SkTar := SkRank div 2; end; { Adjust the skill value by the modifier. } SkRank := SkRank + SkMod; if SkRank < 1 then SkRank := 1; { Make the die roll. } SkRoll := RollStep( SkRank ); msg := msg + BStr( SkRoll ); { Store the message. } { NOTE: Awareness only gets stored if it's successful. } { Otherwise the player could check the skill roll history and easily see } { that there's something hidden nearby. } if ( Skill <> NAS_Awareness ) or ( SkRoll > SkTar ) then begin SkillComment( Msg ); end; { If the roll was a success, give some skill XP. } Pilot := LocatePilot( PC ); if GainXP and ( Pilot <> Nil ) then begin GiveSkillRollXPAward( Pilot , Skill , SkRoll , SkTar ); end; { Return the result. } SkillRoll := SkRoll; end; Function CheckLOS( GB: GameBoardPtr; Observer,Target: GearPtr ): Boolean; { Do the line-of-sight roll to see if Observer notices Target. } Function TargetStealth: Integer; { Return the modified stealth rating of this target. } var T: Integer; begin T := MechaStealthRating( Target ); if Target^.Scale > GB^.Scale then begin T := T - ( Target^.Scale - GB^.Scale ) * Stealth_Per_Scale; end else if Target^.Scale < GB^.Scale then begin T := T + ( GB^.Scale - Target^.Scale ) * Stealth_Per_Scale; end; if ( Target^.G = GG_MetaTerrain ) and ( Target^.S = GS_MetaEncounter ) then begin T := T + Range( GB , Observer , Target ); if T < 5 then T := 5; end; TargetStealth := T; end; Function CasualUseAwareness: Boolean; { Can use the team skill level if the map scale is bigger than 2. } begin CasualUseAwareness := ( GB^.Scale > 2 ); end; var O,T,Roll: Integer; P1,P2: Point; it: Boolean; begin { Calculate the obscurement. } O := CalcObscurement( Observer , Target , gb ); { Encounters can only be spotted within a limited range, } { based on the observer's awareness skill. } if ( Target^.G = GG_MetaTerrain ) and ( Target^.S = GS_MetaEncounter ) then begin if Range( GB , Observer , Target ) > ( ( SkillValue( Observer , NAS_Awareness , STAT_Perception ) + 2 ) div 2 ) then O := -1; end; { If there's nothing standing between the target and the spotter, } { visibility is mostly guaranteed. } if ( O = 0 ) and ( Target^.G <> GG_MetaTerrain ) then begin { If the target has stealth and is outside of the spotter's front arc, } { then it gets a STEALTH roll. } if ( NAttValue( Target^.NA , NAG_Action , NAS_MoveAction ) <> NAV_FullSpeed ) and HasSkill( Target , NAS_Stealth ) then begin { Get the position of the spotter and the target. } P1 := GearCurrentLocation( Observer ); P2 := GearCurrentLocation( Target ); if not ArcCheck( P1.X , P1.Y , NAttValue( Observer^.NA , NAG_Location , NAS_D ) , P2.X , P2.Y , ARC_F180 ) then begin { Make the awareness roll. } T := TargetStealth; Roll := SkillRoll( GB , Observer , NAS_Awareness , STAT_Perception , T , 0 , CasualUseAwareness , AreEnemies( GB , Observer , Target ) ); if SkillRoll( GB , Target , NAS_Stealth , STAT_Speed , Roll , -5 , False , AreEnemies( GB , Observer , Target ) ) > Roll then begin it := False; end else begin it := True; end; end else begin { Within the front arc and with no cover, Stealth is of no help. } it := True; end; end else begin it := True; end; end else if O = -1 then begin it := False; end else begin { Make the awareness roll. } T := O + TargetStealth; Roll := SkillRoll( GB , Observer , NAS_Awareness , STAT_Perception , T , 0 , CasualUseAwareness , AreEnemies( GB , Observer , Target ) ); if Roll > T then begin { The target might get a STEALTH save now. } if ( NAttValue( Target^.NA , NAG_Action , NAS_MoveAction ) <> NAV_FullSpeed ) and HasSkill( Target , NAS_Stealth ) then begin if SkillRoll( GB , Target , NAS_Stealth , STAT_Speed , Roll , 5 , False , AreEnemies( GB , Observer , Target ) ) > Roll then begin it := False; end else begin it := True; end; end else begin it := True; end; end else it := False; end; { If the spotter is BLINDED, there's a 2/3 chance that the LOS check will fail. } if HasStatus( Observer , NAS_Blinded ) and ( Random( 3 ) <> 1 ) then it := False; CheckLOS := it; end; Procedure VisionCheck( GB: GameBoardPtr; Mek: GearPtr ); { Perform a sensor check for MEK. It might spot hidden enemy } { units; it may get spotted by enemy units itself. } var M2: GearPtr; Team: Integer; begin if ( Mek = Nil ) or ( not GearOperational( Mek ) ) or ( not OnTheMap( GB , Mek ) ) then exit; Team := NAttValue( Mek^.NA , NAG_Location , NAS_Team ); { Start by assuming that the mek will be hidden after this. } { Strip all of its visibility tokens. } StripNAtt( Mek , NAG_Visibility ); M2 := GB^.Meks; while M2 <> Nil do begin { We are only interested in this other mek if it's an } { enemy of the one we're checking. } if OnTheMap( GB , M2 ) then begin if IsMasterGear( Mek ) and not MekCanSeeTarget( gb , Mek , M2 ) then begin { If this enemy mecha has not yet been spotted, } { there's a chance it will become visible. } if IsMasterGear( M2 ) and CheckLOS( GB , Mek , M2 ) then begin { M2 has just been spotted. } RevealMek( GB , M2 , Mek ); end else if ( Team = NAV_DefPlayerTeam ) and ( M2^.G = GG_MetaTerrain ) and ( M2^.S = GS_MetaEncounter ) and ( M2^.Stat[ STAT_MetaVisibility ] >= 0 ) then begin if CheckLOS( GB , Mek , M2 ) then begin RevealMek( GB , M2 , Mek ); end; end; end; { There is also a chance that M2 might spot Mek. } if IsMasterGear( M2 ) and GearOperational( M2 ) and not MekCanSeeTarget( GB , M2 , Mek ) then begin if CheckLOS( GB , M2 , Mek ) then RevealMek( GB , Mek , M2 ); end; end; M2 := M2^.Next; end; if ( Team = NAV_DefPlayerTeam ) or ( Team = NAV_LancemateTeam ) then begin CheckVisibleArea( GB , Mek ); end; Screen_Needs_Redraw := True; end; Function DamageGear( gb: GameBoardPtr; O_Part,O_Weapon: GearPtr; O_DMG,O_MOS,O_NumHits: Integer; const AtAt: String ): DamageRec; { Damage the provided gear. } var DR: DamageRec; DAMAGE_OverKill,DAMAGE_Iterations: LongInt; { Procedures block. } Function TakeDamage( Part: GearPtr; DMG: LongInt ): Boolean; { Store the listed amount of damage in PART. Return TRUE if part } { was operational at the start & is still operational, FALSE otherwise. } { The main reason for having this procedure is to record triggers } { when something is destroyed. } var Ok_At_Start: Boolean; GoUp: GearPtr; { A counter that will be used to check all of PART's parents. } Team: Integer; begin Ok_At_Start := GearOperational( Part ); AddNAtt(Part^.NA,NAG_Damage,NAS_StrucDamage,DMG); { If PART was destroyed by this damage, there may be triggers that } { need generating. Get to work. } if Ok_At_Start and ( not GearOperational( Part ) ) then begin { PART and all of its parents up to root need to be } { checked for triggers. } GoUp := Part; while GoUp <> Nil do begin { If GoUp is destroyed, and it has a UID, generate } { a TD* trigger. } if ( NAttValue( GoUp^.NA , NAG_EpisodeData , NAS_UID ) <> 0 ) and ( Not GearOperational( GoUp ) ) then begin SetTrigger( GB , TRIGGER_UnitEliminated + BStr( NAttValue( GoUp^.NA , NAG_EpisodeData , NAS_UID ) ) ); end; { If GoUp is destroyed and it had a CID, generate } { a UTD* trigger. } if ( NAttValue( GoUp^.NA , NAG_Personal , NAS_CID ) <> 0 ) and ( Not GearOperational( GoUp ) ) then begin SetTrigger( GB , TRIGGER_NPCEliminated + BStr( NAttValue( GoUp^.NA , NAG_Personal , NAS_CID ) ) ); end; { If this is a root level gear, generate a NU* trigger. } if ( GoUp^.Parent = Nil ) and IsMasterGear( GoUp ) then begin Team := NAttValue( GoUp^.NA , NAG_Location , NAS_Team ); SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( Team ) ); end; { Move up one more level. } GoUp := GoUp^.Parent; end; { Add this gear to the list of destroyed parts. } { Only do this for non-master gears which aren't at } { root level. } if ( Part^.Parent <> Nil ) and not IsMasterGear( Part ) then StoreSAtt( Destroyed_Parts_List , GearName( Part ) ); end else if OK_At_Start and ( Part^.G = GG_Character ) then begin { Taking damage causes the afflicted to feel worse for wear. } AddMoraleDmg( Part , DMG ); end; TakeDamage := Ok_At_Start and NotDestroyed( Part ); end; Procedure EjectionCheck( Part: GearPtr ); { The parent has just been destroyed. Check to see whether or not } { there is a pilot inside of it, then roll to escape or be blasted } { to bits. } { ASSERT: PART is a subcomponent of its parent. } var P2: GearPtr; Master: GearPtr; ERoll,EMod,Team: Integer; SafeEject: Boolean; begin { To start with, find the team for this unit, since we might not be } { able to after the pilot ejects. } if Part <> Nil then Team := NAttValue( FindRoot( Part )^.NA , NAG_Location , NAS_Team ); { Determine if this is an honorable duel- if so, the chance of being killed } { is greatly reduced. } SafeEject := ( GB^.Scene <> Nil ) and AStringHasBString( SAttValue( GB^.Scene^.SA , 'SPECIAL' ) , 'ARENA' ); while ( Part <> Nil ) do begin P2 := Part^.Next; if NotDestroyed( Part ) then begin if Part^.G = GG_Character then begin { This character must either eject or die. } { Actually, eject, suffer damage, or die. } DR.EjectRoll := True; EMod := 5; { First, determine what sort of module the pilot } { is in. HEAD = +3 bonus to eject. } Master := Part^.Parent; while ( Master <> Nil ) and ( Master^.G <> GG_Module ) do begin Master := Master^.Parent; end; if Master <> Nil then begin { We've found the module the cockpit is in. } if Master^.S = GS_Head then EMod := EMod - 3; end else begin { We can't find a module, all the way back to root. } { Try to handle things gracefully... } Master := Part^.Parent; end; { Find the root-level master of this part. } while Master^.Parent <> Nil do begin Master := Master^.Parent; end; { Do the Skill Roll - SPEED + DODGE SKILL } ERoll := SkillRoll( GB , Part , NAS_Dodge , STAT_Speed , EMod , 0 , False, True ); if SafeEject then begin ERoll := ERoll + 20; end; if ERoll < ( EMod * 2 ) then begin { The character will eject, but takes some damage. } TakeDamage( Part , RollDamage( EjectDamage , Part^.Scale ) ); end; if ( ERoll > EMod ) and not PartHasIntrinsic( Part , NAS_Integral ) then begin { Delink the chaacter, then attach as a sibling of the master gear. } DelinkGear( Part^.Parent^.SubCom , Part ); SetNAtt( Part^.NA , NAG_Location , NAS_X , 0 ); SetNAtt( Part^.NA , NAG_Location , NAS_Y , 0 ); Part^.Next := Master^.Next; Master^.Next := Part; DR.EjectOK := True; end else begin { The character has not managed to eject successfully. } { He's toast. } TakeDamage( Part , GearMaxDamage( Part ) ); end; if Destroyed( Part ) then begin DR.PilotDied := True; end; { If an ejection has occurred, or the pilot has died trying, } { better set a NUMBER OF UNITS trigger. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( Team ) ); end else begin { Check the sub components for characters. } EjectionCheck( Part^.SubCom ); end; end; Part := P2; end; end; Procedure AmmoExplosion( Part: GearPtr ); { How should an ammo explosion work? Well, roll damage for the } { ammo and add it to the OVERKILL history variable. } var PercentFull: Integer; M: GearPtr; begin { Only installed ammo can explode. This may seem silly, and it } { probably is, but otherwise carrying replacement clips is } { asking for certain death. } if IsExternalPart( FindRoot( Part ) , Part ) then exit; { First calculate the number of shots in the magazine. } { If it is empty, no ammo explosion will take place. } if Part^.Stat[ STAT_AmmoPresent ] > 0 then PercentFull := ( ( Part^.Stat[ STAT_AmmoPresent ] - NAttValue( Part^.NA , NAG_WeaponModifier , NAS_AmmoSpent ) ) * 100 ) div Part^.Stat[ STAT_AmmoPresent ] else PercentFull := 100; if PercentFull > 0 then begin M := FindModule( Part ); if ( M = Nil ) or ( M^.S <> GS_Storage ) then begin DAMAGE_OverKill := DAMAGE_OverKill + RollDamage( Part^.V * PercentFull div 50 , Part^.Scale ); end; end; end; Procedure CriticalEngineFailure( Part: GearPtr ); { So sad; this mecha is set to go boom. } begin Part := FindRoot( Part ); if ( Part <> Nil ) and ( Part^.G = GG_Mecha ) then SetNAtt( Part^.NA , NAG_Action , NAS_WillExplode , 1 ); end; Procedure ApplyDamage( Part: GearPtr; DMG: LongInt); { Add to the damage total of this part, } { then check for special effects such as eject rolls, ammo } { explosions, and whatever else has been implemented. } var OK_at_Start: Boolean; { Was the part OK before damage was applied? } M: GearPtr; begin {ERROR CHECK - If we are attempting to damage a storage} {module or other -1HP type, don't do anything.} if GearMaxDamage(Part) > 0 then begin OK_At_Start := NotDestroyed( Part ); { Calculate overkill. } if GearCurrentDamage( Part ) < DMG then begin { Damage dealt to storage modules doesn't carry through to } { overkill. The module can be blown off without affecting } { the structural integrity of the whole. } M := FindModule( Part ); if ( M = Nil ) or ( M^.S <> GS_Storage ) then begin DAMAGE_OverKill := DAMAGE_OverKill + DMG - GearCurrentDamage( Part ); end; DMG := GearCurrentDamage( Part ); end; TakeDamage( Part , DMG ); if OK_At_Start and Destroyed( Part ) then begin { The part started out OK, but it's been } { destroyed. Check for special effects. } if (Part^.G = GG_Module ) or ( Part^.G = GG_Cockpit ) then begin EjectionCheck( Part^.SubCom ); end else if Part^.G = GG_Ammo then begin AmmoExplosion( Part ); end else if ( Part^.G = GG_Support ) and ( Part^.S = GS_Engine ) then begin { If the engine is destroyed, there's a chance for a catastrophic failure. } if Part^.Stat[ STAT_EngineSubType ] = EST_HighPerformance then begin CriticalEngineFailure( Part ); end else if Part^.Stat[ STAT_EngineSubType ] <> 0 then begin if Random( 4 ) = 1 then CriticalEngineFailure( Part ); end else if Random( 100 ) = 1 then begin CriticalEngineFailure( Part ); end; end; end; end; end; Function IsBeamAttack: Boolean; { Return TRUE if this is a beam attack. How to tell? } { If it's a beamgun, an emelee, or HYPER. } begin if HasAttackAttribute( AtAt , AA_Hyper ) then begin IsBeamAttack := TRUE; end else if ( O_Weapon <> Nil ) and ( O_Weapon^.G = GG_Weapon ) then begin IsBeamAttack := ( O_Weapon^.S = GS_BeamGun ) or ( O_Weapon^.S = GS_EMelee ); end else IsBeamAttack := False; end; Procedure StagedPenetration( Part: GearPtr; var DMG: Longint; var MOS , Scale: Integer ); {This procedure applies armor damage to Part.} { Variables DMG and MOS will be affected by this procedure. } var XA,PMaster: GearPtr; MAP: Integer; {The maximum number of armor points to lose.} AAP: Integer; {The actual number that will be lost.} AType: Integer; { What kind of armor does this part have? } Armor: LongInt; { Initial armor value of the part. } IsHardened: Boolean; begin { First, check InvComponents for external armor. } if ( Part <> Nil ) and ( not IsMasterGear( Part ) ) then begin XA := Part^.InvCom; while ( XA <> Nil ) do begin if XA^.G = GG_ExArmor then StagedPenetration( XA , Dmg , MOS , Scale ); XA := XA^.Next; end; end; { Damage can be affected by the armor type. } AType := NAttValue( Part^.NA , NAG_GearOps , NAS_ArmorType ); if AType = NAV_Hardened then begin { Hardened armor is hardened. That is all. } IsHardened := True; end else if ( AType = NAV_AntiBeam ) and IsBeamAttack then begin { Anti-Beam Armor reduces beam damage by half, and counts as } { hardened against it. } Dmg := Dmg div 2; IsHardened := True; end else begin IsHardened := False; end; { Locate the master of this part, which we'll need in order } { to check status conditions. } PMaster := FindMaster( Part ); { Only do armor damage to parts which have armor. } if ( GearMaxArmor( Part ) > 0 ) then begin Armor := GearCurrentArmor( Part ); { Reduce armor protection if the target is rusty. } if HasStatus( PMaster , NAS_Rust ) then ARMOR := ARMOR div 2; { Next, apply damage to the armor itself. } { Calculate the maximum armor damage possible. } { This is determined by the scale of the attack. } MAP := 2; if Scale > 0 then for AAP := 1 to Scale do MAP := MAP * 5; { If the part is MetaTerrain, the maximum armor } { penetration may well be reduced. If an attack can't } { get through the armor, it can't do any damage at all. } { This is to prevent the PC from knocking down doors with } { a lot of low-power attacks. } if ( Part^.G = GG_MetaTerrain ) and ( DMG < Armor ) then begin MAP := 0; end; { Decide upon actual armor damage. } { This will be MUCH greater if the target is rusty. } if HasStatus( PMaster , NAS_Rust ) then begin AAP := DMG div 2; end else begin AAP := Random( DMG div 2 + 1 ); if AAP > MAP then AAP := MAP; end; { BRUTAL attacks double their armor penetration. } if HasAttackAttribute( AtAt , AA_Brutal ) then begin AAP := AAP * 2; if AAP < 3 then AAP := 3; end; { HARDENED armor takes only half damage. } if IsHardened then begin AAP := AAP div 2; end; { Record the current armor value, then record the armor damage. } if AAP > GearCurrentArmor( Part ) then AAP := GearCurrentArmor( Part ); { Nonlethal attacks don't affect armor. } if not HasAttackAttribute( AtAt , AA_NonLethal ) then AddNAtt(Part^.NA,NAG_Damage,NAS_ArmorDamage,AAP); end else begin { It's possible that this part has no armor at all. } { Cover that possibility here. } Armor := 0; end; { Adjust armor for MOS, then reduce MOS. } if ( MOS > 0 ) and ( Armor > 0 ) then begin if MOS < 4 then Armor := ( Armor * ( 4 - MOS ) ) div 4 else Armor := 0; { MOS := MOS - 4;} MOS := MOS - 2; end; { Reduce the DMG variable by the current armor level. } DMG := DMG - Armor; if DMG < 0 then DMG := 0; end; Function SelectRandomModule( LList: GearPtr ): GearPtr; { Select a module from LList at random. If no module is found, } { return NIL. } var M: GearPtr; N: Integer; begin { First, count the number of modules present. } N := 0; M := LList; while M <> Nil do begin if M^.G = GG_Module then Inc( N ); M := M^.Next; end; { Next, select one of the modules randomly. } if N > 0 then begin N := Random( N ); M := LList; while ( M <> Nil ) and ( N > -1 ) do begin if M^.G = GG_Module then Dec( N ); if N <> -1 then M := M^.Next; end; end; SelectRandomModule := M; end; Function REALDamageGear( Part: GearPtr; DMG: LongInt; MOS,Scale: Integer ): LongInt; {This is where the REAL damage process starts.} var XA: GearPtr; N: Integer; begin { Increment the ITERATIONS value. } Inc( DAMAGE_Iterations ); { Do all damage thingies, unless we want to ignore damage. } if Not HasAttackAttribute( AtAt, AA_ArmorIgnore ) then begin { If this is the first iteration, reduce the amount of damage done } { by all armor values up to root level. This is so aimed shots at } { sensors/etc won't ignore the armor of the module they're located in. } { Note that only sub components get upwards armor protection- } { externally mounted inv components don't. } if ( DAMAGE_Iterations = 1 ) and IsSubCom( Part ) then begin XA := Part^.Parent; while XA <> Nil do begin StagedPenetration( XA , DMG , MOS , Scale ); XA := XA^.Parent; end; end; { If this is a character, apply staged penetration randomly } { to the armor of one limb. } if ( Part^.G = GG_Character ) then begin XA := SelectRandomModule( Part^.SubCom ); if XA <> Nil then StagedPenetration( XA , dmg , MOS , Scale ); end; {Reduce the amount of damage by the armor rating of} {this gear, and do damage to the armor.} StagedPenetration( Part , dmg , MOS , Scale ); end; if DMG > 0 then begin {If the damage made it through the armor, apply it to} {whatever's on the inside.} { Increase damage by excess margin of success. } if ( MOS > 0 ) and ( GearMaxDamage(Part) <> -1 ) then begin { Each extra point of MOS will increase damage } { by 20%. } DMG := ( DMG * ( 5 + MOS ) ) div 5; MOS := 0; end; {Depending upon what the part we are damaging is, we} {might apply damage here or pass it on to a subcomponent.} N := NumActiveGears(Part^.SubCom); if N > 0 then begin {There are subcomponents. Either damage this} {part directly, or pass damage on to a subcom.} if (GearMaxDamage(Part) = -1) or ( Random(100) = 23 ) then begin {Damage a subcomponent. Time for recursion.} DMG := REALDamageGear( FindActiveGear(Part^.SubCom,Random(N)+1), DMG , MOS , Scale ); end else if (Random(3) = 1) then begin { Apply half the damage to this component, } { and half the damage to its children. } ApplyDamage( Part,DMG div 2); { Recalculate the number of active subcomponents, as it may have changed. } N := NumActiveGears(Part^.SubCom); if N > 0 then begin DMG := ( DMG div 2 ) + REALDamageGear( FindActiveGear(Part^.SubCom,Random(N)+1), (DMG+1) div 2 , MOS , Scale ); end else begin { Apply all damage against this part. } ApplyDamage( Part , ( DMG + 1 ) div 2 ); end; end else begin ApplyDamage( Part , DMG ); end; end else begin {There are no subcomponents. Damage this} {module directly.} ApplyDamage( Part , DMG ); end; end else if DMG < 0 then begin { We don't want this procedure reporting Damage less than } { zero, because that's silly. } DMG := 0; end; REALDamageGear := DMG; end; Function NonlethalDamageGear( Part: GearPtr; DMG: LongInt; MOS,Scale: Integer ): LongInt; {This is where the REAL damage process starts, for nonlethal damage.} var XA: GearPtr; begin { Reduce the damage based on the target's armor value. } StagedPenetration( Part , DMG , MOS , Scale ); { We know this is a character, so reduce by the armor value of one randomly } { selected limb. } if ( Part^.G = GG_Character ) then begin XA := SelectRandomGear( Part^.SubCom ); if XA <> Nil then StagedPenetration( XA , dmg , MOS , Scale ); end; if DMG > 0 then begin {If the damage made it through the armor, apply it to} {whatever's on the inside.} { Increase damage by excess margin of success. } if ( MOS > 0 ) then begin { Each extra point of MOS will increase damage } { by 25%. } DMG := ( DMG * ( 4 + MOS ) ) div 4; MOS := 0; end; AddStaminaDown( Part , DMG ); end else if DMG < 0 then begin { We don't want this procedure reporting Damage less than } { zero, because that's silly. } DMG := 0; end; NonlethalDamageGear := DMG; end; Function ConcussionDamageAmount( Part , Weapon: GearPtr; Dmg , Scale: Integer ): Integer; { Determine the amount of concussive damage this attack could } { potentially apply to the soft bits of the mecha. } var it,MS: Integer; begin { Base concussion chance is equal to the damage class of } { the weapon. } it := Dmg; { Missiles and Melee Weapons do more concussion than normal. } if ( Weapon <> Nil ) then begin if Weapon^.G = GG_Weapon then begin if ( Weapon^.S = GS_Missile ) then begin it := it + MissileConcussion; end else if ( Weapon^.S = GS_Melee ) then begin it := it + MeleeConcussion; end; end else if Weapon^.G = GG_Module then begin it := it + ModuleConcussion; end; end; { If the weapon scale is greater than the target scale, } { more concussion is done. } if Scale > Part^.Scale then it := it * ( Scale - Part^.Scale + 1 ) else if Scale < Part^.Scale then it := it div ( Part^.Scale - Scale + 3 ); { Determine the master size of the target. } MS := MasterSize( Part ); if MS < 1 then MS := 1; ConcussionDamageAmount := it div MS; end; Function ApplyConcussion( Part: GearPtr; CDC: Integer; AutoDamage: Boolean ): Integer; { Concussion damage is force from the impact which is passed } { on to the soft parts of a mecha- i.e. its pilot. It can also } { be passed on to inventory items, since these are outside } { of the armor's protection. } { Return the amount of damage done. } Function ACNow: Integer; { Apply the concussion damage to PART now. } { Return the amount of damage done. } var D: Integer; begin D := Random( CDC + 1 ); if ( D > 0 ) and NotDestroyed( Part ) then ApplyDamage( Part , D ); ACNow := D; end; var P2: GearPtr; Total: Integer; begin { Initialize TOTAL to 0. } Total := 0; { If this part is succeptable to concussion damage, } { apply the damage. } if ( Part^.G = GG_Character ) and ( Part^.Parent <> Nil ) and ( Random( 100 ) < DamagePilotChance ) then begin Total := Total + ACNow; end else if AutoDamage and ( Random( 100 ) < DamageInventoryChance ) then begin Total := Total + ACNow; end; { Check all sub- and inv- components of this part. } { Automatically damage the inventory components. } P2 := Part^.SubCom; while P2 <> Nil do begin Total := Total + ApplyConcussion( P2 , CDC , False ); P2 := P2^.Next; end; P2 := Part^.InvCom; while P2 <> Nil do begin Total := Total + ApplyConcussion( P2 , CDC , True ); P2 := P2^.Next; end; ApplyConcussion := Total; end; var P2: GearPtr; Total,T,Scale,TPDmg0: LongInt; TMaster,TPilot: GearPtr; TMasterOK,TPilotOK,OKatStart: Boolean; MobileAtStart: Boolean; begin { Initialize History Variables. } DR.EjectRoll := False; DR.EjectOK := False; DR.PilotDied := False; DR.MechaDestroyed := False; DR.DamageDone := 0; DAMAGE_OverKill := 0; DAMAGE_Iterations := 0; DisposeSAtt( Destroyed_Parts_List ); TMaster := FindRoot( O_Part ); TPilot := LocatePilot( TMaster ); TMasterOK := ( TMaster <> TPilot ) and NotDestroyed( TMaster ); TPilotOK := NoTDestroyed( TPilot ); OKatStart := TMasterOK or TPilotOK; if TPilot <> Nil then TPDmg0 := AmountOfDamage( TPilot , False ); MobileAtStart := CurrentMoveRate( GB^.Scene , TMaster ) > 0; { Make sure at least one hit will be caused. } if O_NumHits < 1 then O_NumHits := 1; { Reset total damage done to 0. } Total := 0; { Determine the scale of the attack - this info is needed for } { rolling damage. If no weapon was used, this was probably a } { crash or other self-inflicted injury. Use the target's own } { scale against it. } if O_Weapon = Nil then Scale := O_Part^.Scale else begin Scale := O_Weapon^.Scale; { Area effect weapons deal scatter damage. } { Scatter weapons do full damage against props and metaterrain. } if HasAreaEffect( AtAt ) and ( O_Part^.G <> GG_MetaTerrain ) and ( O_Part^.G <> GG_Prop ) then begin O_NumHits := O_NumHits * O_DMG; O_DMG := 1; end; end; { Call the REAL procedure. } { If this is a nonlethal attack and the target is a character and the target also } { has some stamina remaining, use the special NonLethal attack procedure. } if HasATtackAttribute( AtAt , AA_NonLethal ) and ( TMaster^.G = GG_Character ) and ( CurrentStamina( TMaster ) > 0 ) then begin if NAttValue( TMaster^.NA , NAG_GearOps , NAS_Material ) = NAV_Meat then begin for T := 1 to O_NumHits do begin Total := Total + NonlethalDamageGear( TMaster , RollDamage(O_DMG,Scale) , O_MOS , Scale ); end; end else begin { Against something not made of meat, a nonlethal attack does but } { a single point of damage. } Total := REALDamageGear( O_Part , 1 , O_MOS , Scale ); end; end else if ( O_Weapon <> Nil ) and HasAttackAttribute( AtAt , AA_Hyper ) then begin { If the root part has a damage score, it gets hit. } if ( GearMaxDamage( O_Part ) > -1 ) then begin for T := 1 to O_NumHits do begin Total := Total + REALDamageGear( O_Part , RollDamage(O_DMG,Scale) , O_MOS , Scale ); end; end; { Each subcomponent then gets hit individually. } for T := 1 to O_NumHits do begin P2 := O_Part^.SubCom; while P2 <> Nil do begin Total := Total + REALDamageGear( P2 , RollDamage(O_DMG,Scale) , O_MOS , Scale ); P2 := P2^.Next; end; end; end else begin { Normal damage. } for T := 1 to O_NumHits do begin Total := Total + REALDamageGear( O_Part , RollDamage(O_DMG,Scale) , O_MOS , Scale ); end; end; { Do concussion damage as appropriate. } T := ConcussionDamageAmount( O_Part , O_Weapon , O_Dmg , Scale ); if ( T > 0 ) then Total := Total + ApplyConcussion( O_Part , T , False ); { Do overkill damage to the root torso. } if DAMAGE_OverKill > 0 then begin O_Part := FindRoot( O_Part ); if ( O_Part^.G = GG_Mecha ) and ( O_Part^.SubCom <> Nil ) then begin { For mecha, overkill damage goes directly to the torso. } O_Part := O_Part^.SubCom; while ( O_Part <> Nil ) and ( O_Part^.S <> GS_Body ) do O_Part := O_Part^.Next; if O_Part <> Nil then begin Total := Total + REALDamageGear( O_Part , DAMAGE_OverKill , O_MOS div 2 , Scale ); end; end else begin { Apply damage directly to the part. } Total := Total + REALDamageGear( O_Part , DAMAGE_OverKill , 0 , Scale ); end; end; DR.DamageDone := Total; { A surrendered gear that takes damage will un-surrender. } if ( Total > 0 ) and ( NAttValue( TMaster^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) = NAV_NowSurrendered ) then begin SetNAtt( TMaster^.NA , NAG_EpisodeData , NAS_SurrenderStatus , NAV_ReAttack ); AddNAtt( TMaster^.NA , NAG_StatusEffect , NAS_Enraged , 10 + Random( 20 ) ); end; DR.PilotDied := TPilotOK and Destroyed( TPilot ); DR.MechaDestroyed := TMasterOK and not GearOperational( TMaster ); { Check for explosions here. } if OkAtStart and Destroyed( TMaster ) then begin if ( SAttValue( TMaster^.SA , SA_Explosion ) <> '' ) then begin SetNAtt( TMaster^.NA , NAG_Action , NAS_WillExplode , 1 ); end; if NAttValue( TMaster^.NA , NAG_GearOps , NAS_CorpseOp ) = NAV_NoCorpse then begin SetNAtt( TMaster^.NA , NAG_Action , NAS_WillDisappear , 1 ); end; end; { Give experience for Vitality here. } if ( TPilot <> Nil ) and NotDestroyed( TPilot ) then begin { Determine the change in damage status for the pilot. } TPDmg0 := AmountOfDamage( TPilot , False ) - TPDmg0; { Taking damage trains vitality... } { As long as the character survives, that is. } if TPDmg0 > 0 then begin { Give an amount of experience equal to the amount of damage } { taken squared, but no more than enough to raise Vitality by one } { level. } TPDmg0 := TPDmg0 * TPDmg0 + 4; T := SkillAdvCost( Nil , NAttValue( TPilot^.NA , NAG_Skill , NAS_Vitality ) ); if TPDmg0 > T then TPDmg0 := T; DoleSkillExperience( TPilot , NAS_Vitality , TPDmg0 ); end; end; if MobileAtStart and ( CurrentMoveRate( GB^.Scene , TMaster ) = 0 ) and NotDestroyed( TMaster ) then begin Crash( GB , TMaster ); end; DamageGear := DR; end; Function DamageGear( gb: GameBoardPtr; Part: GearPtr; DMG: Integer): DamageRec; { Apply damage without a Margin Of Success. } begin DamageGear := DamageGear( gb , Part , Nil , DMG , 0 , 1 , '' ); end; procedure Crash( gb: GameBoardPtr; Mek: GearPtr ); { This mek has just become incapable of moving. Crash it. } var MM,MA,DMG: Integer; { Move Mode and Move Action } MT: LongInt; P: Point; begin { Make sure we have the root gear. } Mek := FindRoot( Mek ); P := GearCurrentLocation( Mek ); { Determine both the move mode and the move action for this mek. } MM := NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ); MA := NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ); { Pass on appropriate info to the damage procedure. } { The amount of damage done and the number of hits depends upon } { the move mode and move action. } if ( MM > 0 ) and ( MM <= NumMoveMode ) then begin { The movemode this mek has is legal. } Case MM of MM_Walk: DMG := 1; MM_Roll: DMG := 1; MM_Skim: DMG := 2; MM_Fly: DMG := 5; else DMG := 1; end; end else begin { The movemode this mek has isn't legal. } DMG := 1; end; if MA = NAV_FullSpeed then DMG := DMG * 5 else if MA = NAV_NormSpeed then DMG := DMG * 3 else DMG := DMG * 2; { Note that mecha only take damage from a crash in space if they're blocked. } if MoveBlocked( Mek , GB ) or ( TileTerrain( GB , P.X , P.Y ) <> TERRAIN_SPACE ) then begin SetNAtt( Mek^.NA , NAG_Action , NAS_WillCrash , DMG ); SetNAtt( Mek^.NA, NAG_Action , NAS_DriftSpeed , 0 ); end; MT := NAttValue( Mek^.NA , NAG_Action , NAS_MoveETA ); SetNAtt( Mek^.NA, NAG_Action , NAS_MoveAction , NAV_Stop ); SetNAtt( Mek^.NA, NAG_Action , NAS_TimeLimit , 0 ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , MT + 1000 ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ClicksPerRound ); end; Procedure DoActionSetup( GB: GameBoardPtr; Mek: GearPtr; Action: Integer ); { Perpare all of the mek's data structures for the action } { being undertaken. } begin if ( Action = NAV_Stop ) or ( Action = NAV_Hover ) or ( CPHMoveRate( GB^.Scene , Mek , GB^.Scale ) = 0 ) then begin if NAttValue( Mek^.NA , NAG_Action , NAS_DriftSpeed ) > 0 then begin { Stopping in space takes more time than just stopping elsewhere. } SetNAtt( Mek^.NA , NAG_Action , NAS_MoveAction , Action ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , Max( CalcMoveTime( Mek , GB ) , ReactionTime( Mek ) ) + GB^.ComTime + 1 ); SetNAtt( Mek^.NA , NAG_Action , NAS_DriftSpeed , 0 ); end else if NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ) = NAV_Stop then begin { The mek is already stopped. Wait one round before calling again. } SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ( ClicksPerRound div 5 ) ); end else begin { The mek is currently moving but it wants to stop. } if ( Action <> NAV_Stop ) and ( Action <> NAV_Hover ) then Action := NAV_Stop; SetNAtt( Mek^.NA , NAG_Action , NAS_MoveAction , Action ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + 1 ); end; { Reset the jumping time limit. } SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , 0 ); end else if ( Action = NAV_NormSpeed ) or ( Action = NAV_Reverse ) then begin { Move foreword. } if NAttValue( Mek^.NA , NAG_Action, NAS_MoveAction ) <> Action then begin SetNAtt( Mek^.NA , NAG_Action , NAS_MoveACtion , Action ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , CalcMoveTime( Mek , GB ) + GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , CalcMoveTime( Mek , GB ) + GB^.ComTime ); end else begin SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , NAttValue( Mek^.NA , NAG_Action , NAS_MoveETA ) + 1 ); end; { If jumping, set the jump time limit. } if ( NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ) = MM_Fly ) and ( JumpTime( GB^.Scene , Mek ) > 0 ) then begin { If this jump is just starting, set the time limit and recharge time now. } if NAttValue( Mek^.NA , NAG_Action , NAS_TimeLimit ) = 0 then begin SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , GB^.ComTime + JumpTime( GB^.Scene , Mek ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_JumpRecharge , GB^.ComTime + Jump_Recharge_Time ); end; end else begin { Reset the jumping time limit. } SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , 0 ); end; end else if Action = NAV_FullSpeed then begin { Move foreword, quickly. } if NAttValue( Mek^.NA , NAG_Action, NAS_MoveAction ) <> NAV_FullSpeed then begin SetNAtt( Mek^.NA , NAG_Action , NAS_MoveACtion , NAV_FullSpeed ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , CalcMoveTime( Mek , GB ) + GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , CalcMoveTime( Mek , GB ) + GB^.ComTime ); end else begin SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , Max( NAttValue( Mek^.NA , NAG_Action , NAS_MoveETA ) + 1 , ReactionTime( Mek ) div 2 + GB^.ComTime ) ); end; { Reset the jumping time limit. } SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , 0 ); end else if Action = NAV_TurnLeft then begin SetNAtt( Mek^.NA , NAG_Action , NAS_MoveACtion , NAV_TurnLeft ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , CalcMoveTime( Mek , GB ) + GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , Max( CalcMoveTime( Mek , GB ) , ReactionTime( Mek ) div 2 ) + GB^.ComTime ); { Reset the jumping time limit. } SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , 0 ); end else if Action = NAV_TurnRight then begin SetNAtt( Mek^.NA , NAG_Action , NAS_MoveACtion , NAV_TurnRight ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , CalcMoveTime( Mek , GB ) + GB^.ComTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , Max( CalcMoveTime( Mek , GB ) , ReactionTime( Mek ) div 2 ) + GB^.ComTime ); { Reset the jumping time limit. } SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , 0 ); end; end; Procedure PrepAction( GB: GameBoardPtr; Mek: GearPtr; Action: Integer ); { Given an action, prepare all of the mek's values for it. } begin if MoveLegal( GB^.Scene , Mek , Action , GB^.ComTime ) or ( CurrentMoveRate( GB^.Scene , Mek ) = 0 ) then begin DoActionSetup( GB , Mek , Action ); end else begin if Action = NAV_Stop then begin if MoveLegal( GB^.Scene , Mek , NAV_NormSpeed , GB^.ComTime ) then begin DoActionSetup( GB , Mek , NAV_NormSpeed ); end else begin Crash( GB , Mek ); end; end else begin if MoveLegal( GB^.Scene , Mek , NAV_Stop , GB^.ComTime ) then begin DoActionSetup( GB , Mek , NAV_Stop ); end else begin Crash( GB , Mek ); end; end; end; end; Procedure SetMoveMode( GB: GameBoardPtr; Mek: GearPtr; MM: Integer ); { Set the requested movement mode. If this sets the mode to Jump, } { the mecha will come to a halt. } begin SetNAtt( Mek^.NA , NAG_Action , NAS_MoveMode , MM); if ( MM = MM_Fly ) and ( JumpTime( GB^.Scene , Mek ) > 0 ) then SetNAtt( Mek^.NA , NAG_Action , NAS_MoveAction , NAV_Stop ); end; Procedure DoMoveTile( Mek: GearPtr; GB: GameBoardPtr ); { This mek is about to move foreword. Process the movement. } { Also, check for other meks in the target hex, and do a } { charge if necessary. } { If the mek moves off the map, it has fled the game. } var P: Point; begin { Find out the gear's destination. } P := GearDestination( Mek ); { If the destination isn't on the map, and this model isn't a player model, and it is in control } { of its actions, and it's not running away, disallow the move. } if ( not OnTheMap( GB , P.X , P.Y ) ) and ( not Confused( Mek ) ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) <> NAV_DefPlayerTeam ) and ( NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Orders ) <> NAV_RunAway ) then begin { Set ETA for next move. } SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , GB^.ComTime + ReactionTime( Mek ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); Exit; end; { Set the mek's position to its new value. } SetNAtt( Mek^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( Mek^.NA , NAG_Location , NAS_Y , P.Y ); { If moving in microgravity, set the drift values. } { Drift only happens if the model is at the same scale or larger than the map. } if OnTheMap( GB , P.X , P.Y ) and ( GB^.Scene <> Nil ) and ( NAttValue( GB^.Scene^.NA , NAG_EnvironmentData , NAS_Gravity ) = NAV_Microgravity ) and ( Mek^.Scale >= GB^.Scale ) then begin SetNAtt( Mek^.Na , NAG_Action , NAS_DriftVector , NAttValue( Mek^.NA , NAG_Location , NAS_D ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_DriftSpeed , GB^.ComTime - NAttValue( Mek^.NA , NAG_Action , NAS_MoveStart ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_DriftETA , GB^.ComTime + NAttValue( Mek^.NA , NAG_Action , NAS_DriftSpeed ) ); end; { If moving at top speed, set stamina drain. } if ( Mek^.G = GG_Character ) and ( NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ) = NAV_FullSpeed ) then begin AddStaminaDown( Mek , 1 ); end; { Set ETA for next move. } SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , GB^.ComTime + CalcMoveTime( Mek , GB ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveStart , GB^.ComTime ); end; Procedure DoTurn( Mek: GearPtr; GB: GameBoardPtr ); { The mek is turning. Make it so, Mister Laforge. } var cmd: Integer; {The exact command issued.} D: Integer; {The direction of the mek.} CallTime,DriftSpeed: LongInt; begin { Determine whether the mek is turning left or right. } cmd := NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ); { Determine the direction the mek is currently facing. } D := NAttValue( Mek^.NA , NAG_Location , NAS_D ); if cmd = NAV_TurnLeft then begin D := D - 1; if D < 0 then D := 7; end else begin D := D + 1; if D > 7 then D := 0; end; { Set the direction to the modified value. } SetNAtt( Mek^.NA , NAG_Location , NAS_D , D ); { Set the mek's movement to Stop, and call the action selector. } if MoveLegal( GB^.Scene , Mek , NAV_Hover , GB^.ComTime ) then begin PrepAction( GB , Mek , NAV_Hover ); end else begin { The stop called after a turn will not affect the calltime or } { the drift vector. } CallTime := NAttValue( Mek^.NA , NAG_Action , NAS_CallTime ); DriftSpeed := NAttValue( Mek^.NA , NAG_Action , NAS_DriftSpeed ); PrepAction( GB , Mek , NAV_Stop ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , CallTime ); SetNAtt( Mek^.NA , NAG_Action , NAS_DriftSpeed , DriftSpeed ); end; end; Function CrashTarget( Alt0,Alt1,Order: Integer ): Integer; { Return the target number to avoid a crash if a mecha is } { traveling from Alt0 to Alt1 with movement order Order. } var it: Integer; begin if Alt0 <= ( Alt1 + 1 ) then begin it := 7; end else begin it := ( Alt0 - ALt1 - 1 ) * 8 + 4; end; if Order <> NAV_FullSpeed then it := it - 5; CrashTarget := it; end; Procedure MaybeCharge( GB: GameBoardPtr; Mek: GearPtr ); { This mecha is blocked by something. If it's another mecha, maybe } { do a charge attack. } var D: Point; Target: GearPtr; begin { If the mek exists, it's bigger than SF:0, and it's moved at least one tile, } { then it might be ready for a charge attack. } if ( Mek <> Nil ) and ( Mek^.Scale > 0 ) and ( NAttValue( Mek^.NA , NAG_Action , NAS_TilesInARow ) > 0 ) then begin D := GearDestination( Mek ); Target := FindBlockerXYZ( GB , D.X , D.Y , MekAltitude( GB , Mek ) ); if ( Target <> Nil ) and AreEnemies( GB , Mek , Target ) then begin SetNAtt( Mek^.NA , NAG_Action , NAS_WillCharge , NAttValue( Target^.NA , NAG_EpisodeData , NAS_UID ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_ChargeSpeed , Speedometer( GB^.Scene , Mek ) ); end; end; end; Function EnactMovement( GB: GameBoardPtr; Mek: GearPtr ): Integer; { The time has come for this mech to move. } { This procedure checks to see what kind of movement is } { taking place, decides whether the move should be } { cancelled or delayed due to systems damage, then branches } { to the appropriate procedures. } { It returns 1 if the move was successful and the display } { should be updated, 0 if no event took place, and -1 if } { the mek in question crashed or was otherwise damaged. } var ETA,Spd,StartTime,Order,Alt0,ALt1,SkRoll: LongInt; NeedRedraw: Integer; Pilot: GearPtr; begin { Note that this call to MoveThatMek might result in } { no movement at all. It could be a wait call- an ETA } { is set even if the mek's movemode is Inactive, or its } { order is Stop. } NeedRedraw := 0; { Locate all the important values for this mek. } ETA := NAttValue( Mek^.NA , NAG_Action , NAS_MoveETA ); StartTime := NAttValue( Mek^.NA , NAG_Action , NAS_MoveStart ); Order := NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ); Spd := CalcMoveTime( Mek , GB ); if Order = NAV_Stop then begin { The mek isn't going anywhere. This is a wait call. } { Set the ETA to not call this procedure again for 1000 clicks. } { The mek might not have an activated move mode for whatever reason. } if NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ) = 0 then GearUp( Mek ); SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , ETA + 1000 ); { Clear the TilesInARow counter. } SetNAtt( Mek^.NA , NAG_Action , NAS_TilesInARow , 0 ); end else if ( NAttValue( Mek^.NA , NAG_Action , NAS_TimeLimit ) > 0 ) and ( NAttValue( Mek^.NA , NAG_Action , NAS_TimeLimit ) < GB^.ComTime ) then begin { If the mek was jumping and overshot the time limit, } { make it crash now. } if NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ) = MM_Fly then begin Crash( GB , Mek ); NeedRedraw := EMR_Crash; SetNAtt( Mek^.NA , NAG_Action , NAS_TilesInARow , 0 ); end else begin { If the time limit was overshot but the mek isn't } { jumping, clear it now. } SetNAtt( Mek^.NA , NAG_Action , NAS_TimeLimit , 0 ); SetNAtt( Mek^.NA , NAG_Action , NAS_TilesInARow , 0 ); end; end else if MoveBlocked( Mek , GB ) then begin { If the mecha is capable of stopping in time, then } { stop. Otherwise it will crash into the obstacle. } if MoveLegal( GB^.Scene , Mek , NAV_Stop , GB^.ComTime ) then begin NeedRedraw := EMR_Blocked; MaybeCharge( GB , Mek ); PrepAction( GB , Mek , NAV_Stop ); end else begin MaybeCharge( GB , Mek ); Crash( GB , Mek ); NeedRedraw := EMR_Crash; end; end else if Spd = 0 then begin { Movement mode has been disabled, or the mek } { is blocked. In any case, this could be crash material. } Crash( GB , Mek ); NeedRedraw := EMR_Crash; SetNAtt( Mek^.NA , NAG_Action , NAS_TilesInARow , 0 ); end else if ( Mek^.G = GG_Character ) and ( Order = NAV_FullSpeed ) and ( CurrentStamina( Mek ) <= 0 ) then begin PrepAction( GB , Mek , NAV_NormSpeed ); end else if (StartTime + Spd) <= ETA then begin { Everything is proceeding according to schedule. } { Actually process the movement. } { Store the initial altitude, to see if the mecha will } { require a piloting check to avoid crashing at the end. } Alt0 := MekAltitude( GB , Mek ); case Order of NAV_NormSpeed,NAV_FullSpeed,NAV_Reverse: begin DoMoveTile( Mek , GB ); if Order <> NAV_Reverse then AddNAtt( Mek^.NA , NAG_Action , NAS_TilesInARow , 1 ); SetTrigger( GB , TRIGGER_TeamMovement + BStr( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) ) ); Pilot := LocatePilot( Mek ); if ( Pilot <> Nil ) and ( NAttValue( Pilot^.NA , NAG_Personal , NAS_CID ) <> 0 ) then SetTrigger( GB , TRIGGER_NPCMovement + BStr( NAttValue( Pilot^.NA , NAG_Personal , NAS_CID ) ) ); end; NAV_TurnLeft,NAV_TurnRight: begin DoTurn( Mek , GB ); SetNAtt( Mek^.NA , NAG_Action , NAS_TilesInARow , 0 ); end; end; Alt1 := MekAltitude( GB , Mek ); if ( Alt1 < ( Alt0 - 1 ) ) and ( NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ) <> MM_Fly ) then begin if Mek^.G = GG_Mecha then begin SkRoll := RollStep( SkillValue( Mek , NAS_MechaPiloting , STAT_Speed ) ); end else begin SkRoll := RollStep( SkillValue( Mek , NAS_Dodge , STAT_Speed ) ); end; if SkRoll < CrashTarget( Alt0 , Alt1 , Order ) then begin Crash( GB , Mek ); NeedRedraw := EMR_Crash; end else begin NeedRedraw := 1; end; end else begin NeedRedraw := 1; end; end else begin { The mek has been delayed by damage, but not } { immobilized. Set new ETA. } SetNAtt( Mek^.NA , NAG_Action , NAS_MoveETA , StartTime + Spd ); end; EnactMovement := NeedRedraw; end; Procedure DoDrift( GB: GameBoardPtr; Mek: GearPtr ); { This mecha is apparently in space, and it's going to drift. Do that now. } var P1,P2: Point; Pilot: GearPtr; DD,MO: Integer; begin DD := NAttValue( Mek^.NA , NAG_Action , NAS_DriftVector ); MO := NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ); if ( DD <> NAttValue( Mek^.NA , NAG_Location , NAS_D ) ) or ( ( MO <> NAV_FullSpeed ) and ( MO <> NAV_NormSpeed ) ) then begin P1 := GearCurrentLocation( Mek ); P2.X := P1.X + AngDir[ DD , 1 ]; P2.Y := P1.Y + AngDir[ DD , 2 ]; if MovementBlocked( Mek , GB , P1.X , P1.Y , P2.X , P2.Y ) then begin SetNAtt( Mek^.NA , NAG_Action , NAS_DriftSpeed , 0 ); end else if OnTheMap( GB , P2.X , P2.Y ) then begin SetNAtt( Mek^.NA , NAG_Location , NAS_X , P2.X ); SetNAtt( Mek^.NA , NAG_Location , NAS_Y , P2.Y ); SetNAtt( Mek^.NA , NAG_Action , NAS_DriftETA , GB^.ComTime + NAttValue( Mek^.NA , NAG_Action , NAS_DriftSpeed ) ); { If this tile is "stable", stop drifting. } if ( GB^.Scene = Nil ) or ( NAttValue( GB^.Scene^.NA , NAG_EnvironmentData , NAS_Gravity ) <> NAV_Microgravity ) then SetNAtt( Mek^.NA , NAG_Action , NAS_DriftSpeed , 0 ); SetTrigger( GB , TRIGGER_TeamMovement + BStr( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) ) ); Pilot := LocatePilot( Mek ); if ( Pilot <> Nil ) and ( NAttValue( Pilot^.NA , NAG_Personal , NAS_CID ) <> 0 ) then SetTrigger( GB , TRIGGER_NPCMovement + BStr( NAttValue( Pilot^.NA , NAG_Personal , NAS_CID ) ) ); end; end; end; Procedure WaitAMinute( GB: GameBoardPtr; Mek: GearPtr; D: Integer ); { Force MEK to wait a short time, stopped if possible. } var NextCall: LongInt; begin Mek := FindRoot( Mek ); NextCall := NAttValue( Mek^.NA , NAG_Action , NAS_CallTime ); if ( GB <> Nil ) and ( NextCall < GB^.ComTime ) then NextCall := GB^.ComTime; NextCall := NextCall + D; if GB <> Nil then begin PrepAction( GB , Mek , NAV_Stop ); end; SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , NextCall ); end; initialization Destroyed_Parts_List := Nil; finalization DisposeSAtt( Destroyed_Parts_List ); end. gearhead-2-0.701/aibrain.pp000066400000000000000000002143511321074026100153700ustar00rootroot00000000000000unit aibrain; { This unit handles behavior for the various enemy units in } { Gearhead Arena. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale; procedure ShiftGears( GB: GameBoardPtr; Mek: GearPtr; DoOutput: Boolean ); Function CanSpeakWithTarget( GB: GameBoardPtr; PC,NPC: GearPtr ): Boolean; Procedure ClearHotMaps; Function SelectBestWeapon( GB: GameBoardPtr; Mek,Target: GearPtr ): GearPtr; Procedure GetAIInput( Mek: GearPtr; GB: GameBoardPtr ); Procedure ConfusedInput( Mek: GearPtr; GB: GameBoardPtr ); Procedure BrownianMotion( GB: GameBoardPtr ); Procedure HandleEncounters( GB: GameBoardPtr ); Procedure LancemateUsefulAction( Mek: GearPtr; GB: GameBoardPtr ); Function MOVE_MODEL_TOWARDS_SPOT( Mek: GearPtr; GB: GameBoardPtr; GX,GY: Integer ): Boolean; implementation uses ability,action,arenacfe,effects,movement,gearutil,specialsys,ghswag, ghchars,ghmodule,ghweapon,gearparser,ghprop,interact,rpgdice,skilluse, texutil,ui4gh,grabgear,arenascript,menugear,ghsupport,backpack, {$IFDEF ASCII} vidgfx,vidmap; {$ELSE} sdlmap,sdlgfx; {$ENDIF} const HMMM_EncounterGround = -1; Hot_Terr: Array [-1..NumMoveMode,1..NumTerr] of SmallInt = ( ( 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, { ENCOUNTER: GROUND } 2, 2,-1, 2, 2, 2, 2, 2, 2,-1, 2, 2, 2, 2, 2, -1, 2 ), ( 2, 3, 4,-1, 2, 2, 3, 3, 4, 5, { CHARA WALK } 3,-1,-1, 2, 2, 2, -1,-1, 2,-1, 2, 4, -1,-1,-1, -1,-1 ), ( 2, 3, 4, 5, 2, 2, 3, 3, 4, 5, { MECHA WALK } 3,-1,-1, 2, 2, 2, 5, 5, 2,-1, 2, 4, -1,-1,-1, -1,-1 ), ( 2,-1,-1,-1, 3, 1, 4, 3, 4, 5, { ROLL } 4,-1,-1, 2, 2, 2, 5, 5, 2,-1, 2, -1, -1,-1,-1, -1,-1 ), ( 2, 3, 4, 2, 2, 2, 3, 3, 4, 5, { SKIM } 2,-1,-1, 2, 2, 2, 2, 2, 2,-1, 2, 4, -1,-1,-1, -1,-1 ), ( 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, { FLY } 2, 2,-1, 2, 2, 2, 2, 2, 2,-1, 2, 2, -1, 2, 2, -1, 2 ), (-1,-1,-1,-1,-1, -1,-1,-1,-1,-1, { SPACE FLIGHT } -1,-1,-1, -1,-1,-1, -1,-1, -1,-1,-1, -1, 2,-1,-1, -1,-1 ) ); NumFFMap = 10; ORD_SeekAnyEdge = -1; ORD_SeekEnemy = 0; ORD_SeekSingleModel = 1; ORD_SeekSpot = 2; ORD_SeekEdge = 3; ORD_SeekTeam = 4; Hot_Map_Validity = 10; CORD_Flirt = 1; CORD_Chat = 2; var { The HOTMAP is used for pathfinding and general tactical } { decision-making. } { HOTMAP shows what squares the NPC will want to move into. } { COLDMAP shows what squares the NPC won't want to move into. } MapUpdate: Array [1..NumFFMap] of LongInt; MapTeam: Array [1..NumFFMap] of Integer; MapOrder: Array [1..NumFFMap] of Integer; MapMoveMode: Array [1..NumFFMap] of Integer; HotMap: Array [1..NumFFMap , 1..MaxMapWidth , 1..MaxMapWidth ] of Integer; ColdMap: Array [1..NumFFMap , 1..MaxMapWidth , 1..MaxMapWidth ] of Integer; procedure ShiftGears( GB: GameBoardPtr; Mek: GearPtr; DoOutput: Boolean ); { Set the mek's MoveMode attribute to the next } { active movemode that this mek has. } var P: Point; Terr,MM,CMM: Integer; begin { Initialize values. } CMM := NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ); MM := CMM mod NumMoveMode + 1; { Determine the terrain of the tile the mek is pointing at. The shifted movemode } { must be legal there. } P := GearCurrentLocation( Mek ); P.X := P.X + AngDir[ NAttValue( Mek^.NA , NAG_Location , NAS_D ) ,1]; P.Y := P.Y + AngDir[ NAttValue( Mek^.NA , NAG_Location , NAS_D ) ,2]; { If it turns out that the model's facing the edge of the map, use the current tile } { instead. } if not OnTheMap( GB , P.X , P.Y ) then P := GearCurrentLocation( Mek ); Terr := TileTerrain( GB , P.X , P.Y ); { Check each movemode in turn until we find one the mek is capable of that is also } { legal for the tile in front. } while ( MM <> CMM ) and ( ( BaseMoveRate( GB^.Scene , Mek , MM ) <= 0 ) or IsBlockingTerrainForMM( GB , Mek , Terr , MM ) ) do begin MM := MM mod NumMoveMode + 1; end; { If a legal move mode was found, switch to that. } if ( MM <> 0 ) and ( MM <> CMM ) then begin SetMoveMode( GB , Mek , MM ); if DoOutput then DialogMsg( ReplaceHash( MsgString( 'ShiftGears' ) , MsgString( 'MOVEMODENAME_' + BStr( MM ) ) ) ); end else begin if DoOutput then DialogMsg( MsgString( 'SHIFTGEARS_FAIL' ) ); end; end; Function CanSpeakWithTarget( GB: GameBoardPtr; PC,NPC: GearPtr ): Boolean; { Return TRUE if the PC can speak with the target without breaking out the } { telephone, or FALSE otherwise. } begin CanSpeakWithTarget := ( Range( GB , FindRoot( PC ) , FindRoot( NPC ) ) < 6 ); end; Function HotTileBlocksLOS( GB: GameBoardPtr; X , Y , MM: Integer ): Boolean; { Return TRUE if the given tile should block AI movement. } { BUGS: X,Y must lie on the map; MM must be in range 0..NumMoveMode. } begin HotTileBlocksLOS := TileBlocksLOS( GB , X , Y , 5 ) or ( Hot_Terr[ MM , TileTerrain( GB , X , Y ) ] < 0 ); end; Procedure HotMapFloodFill( GB: GameBoardPtr; N,MM: Integer ); { Given a map that's had the hot spots filled in, use the } { flood fill algorithm to determine paths etc. } var X,Y: Integer; Flag: Boolean; DV,DH,DD,DP: Integer; { Distance Vertical, Horizontal, and Diagnol. } begin flag := True; while flag do begin flag := False; for y := 2 to (GB^.MAP_Height-1) do begin for x := 2 to (GB^.Map_Width-1) do begin if not HotTileBlocksLOS( GB , X , Y , MM ) then begin dH := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + HotMap[ N , X - 1 , Y ]; dV := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + HotMap[ N , X , Y - 1 ]; if DV < DH then DH := DV; DD := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + 1 + HotMap[ N , X - 1 , Y - 1]; if DD < DH then DH := DD; DP := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + 1 + HotMap[ N , X + 1 , Y - 1]; if DP < DH then DH := DP; if DH < HotMap[ N , X , Y ] then begin HotMap[ N , X , Y ] := DH; flag := True; end; end; end; end; for y := ( GB^.Map_Height - 1 ) downto 2 do begin for x := ( GB^.Map_Width - 1 ) downto 2 do begin if not HotTileBlocksLOS( GB , X , Y , MM ) then begin dH := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + HotMap[ N , X + 1 , Y ]; dV := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + HotMap[ N , X , Y + 1 ]; if DV < DH then DH := DV; DD := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + 1 + HotMap[ N , X + 1 , Y + 1]; if DD < DH then DH := DD; DP := Hot_Terr[ MM , TileTerrain( GB,X,Y) ] + 1 + HotMap[ N , X - 1 , Y + 1]; if DP < DH then DH := DP; if DH < HotMap[ N , X , Y ] then begin HotMap[ N , X , Y ] := DH; flag := True; end; end; end; end; end; end; Procedure Calc_FollowMap( GB: GameBoardPtr; N,UID,MM: Integer ); { Unlike the SEEK AND DESTROY map, this map should have only } { one hot spot. } var M: GearPtr; P: Point; begin M := GB^.Meks; while M <> Nil do begin P := GearCurrentLocation( M ); if IsMasterGear( M ) and OnTheMap( GB , P.X , P.Y ) and GearOperational( M ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end else if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_Pass ] <= -100 ) and NotDestroyed( M ) and OnTheMap( GB , P.X , P.Y ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end; if NAttValue( M^.NA , NAG_EpisodeData , NAS_UID ) = UID then begin P := GearCurrentLocation( M ); if OnTheMap( GB , P.X , P.Y ) then begin HotMap[ N , P.X , P.Y ] := 0; HotMapFloodFill( GB , N , MM ); end; end; M := M^.Next; end; end; Procedure Calc_SpotMap( GB: GameBoardPtr; N,PDat,MM: Integer ); { Calculate a map seeking the described point. } var M: GearPtr; P: Point; begin { Set the position for all blocking gears on the coldmap. } M := GB^.Meks; while M <> Nil do begin P := GearCurrentLocation( M ); if IsMasterGear( M ) and OnTheMap( GB , P.X , P.Y ) and GearOperational( M ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end else if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_Pass ] <= -100 ) and NotDestroyed( M ) and OnTheMap( GB , P.X , P.Y ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end; M := M^.Next; end; { Set the hot spot. } P.X := PDat mod ( GB^.Map_Width + 1 ); P.Y := PDat div ( GB^.Map_Width + 1 ); if OnTheMap( GB , P.X , P.Y ) then HotMap[ N , P.X , P.Y ] := 0; { Increase the timer for this map, since we're dealing with a } { stationary position, so the data should remain good for a } { nice long time. } MapUpdate[ N ] := GB^.ComTime + 100; HotMapFloodFill( GB , N , MM ); end; Procedure Calc_EdgeMap( GB: GameBoardPtr; N,PDat,MM: Integer ); { Calculate a map seeking the described edges. } var M: GearPtr; P: Point; X,Y: Integer; begin { Set the position for all blocking gears on the coldmap. } M := GB^.Meks; while M <> Nil do begin P := GearCurrentLocation( M ); if IsMasterGear( M ) and OnTheMap( GB , P.X , P.Y ) and GearOperational( M ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end else if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_Pass ] <= -100 ) and NotDestroyed( M ) and OnTheMap( GB , P.X , P.Y ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end; M := M^.Next; end; { Set the hot spots. } for X := 1 to GB^.MAP_Width do begin if AngDir[ PDat , 2 ] = -1 then HotMap[ N , X , 1 ] := 0 else if AngDir[ PDat , 2 ] = 1 then HotMap[ N , X , GB^.MAP_Height ] := 0; end; for Y := 1 to GB^.MAP_Height do begin if AngDir[ PDat , 1 ] = -1 then HotMap[ N , 1, Y ] := 0 else if AngDir[ PDat , 1 ] = 1 then HotMap[ N , GB^.MAP_Width , Y ] := 0; end; { Increase the timer for this map, since we're dealing with a } { stationary position, so the data should remain good for a } { nice long time. } MapUpdate[ N ] := GB^.ComTime + 100; HotMapFloodFill( GB , N , MM ); end; Procedure Calc_RetreatMap( GB: GameBoardPtr; N,PDat,MM: Integer ); { Calculate a map seeking the edges. } var M: GearPtr; P: Point; X,Y: Integer; begin { Set the position for all blocking gears on the coldmap. } M := GB^.Meks; while M <> Nil do begin P := GearCurrentLocation( M ); if IsMasterGear( M ) and OnTheMap( GB , P.X , P.Y ) and GearOperational( M ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end else if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_Pass ] <= -100 ) and NotDestroyed( M ) and OnTheMap( GB , P.X , P.Y ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end; M := M^.Next; end; { Set the hot spots. } for X := 1 to GB^.MAP_Width do begin HotMap[ N , X , 1 ] := 0; HotMap[ N , X , GB^.MAP_Height ] := 0; end; for Y := 1 to GB^.MAP_Height do begin HotMap[ N , 1, Y ] := 0; HotMap[ N , GB^.MAP_Width , Y ] := 0; end; { Increase the timer for this map, since we're dealing with a } { stationary position, so the data should remain good for a } { nice long time. } MapUpdate[ N ] := GB^.ComTime + 100; HotMapFloodFill( GB , N , MM ); end; Procedure Calc_SDMap( GB: GameBoardPtr; N,Team,MM: Integer ); { Calculate a SEEK AND DESTROY hotmap for a member of TEAM using } { movement mode MM. } var M: GearPtr; Flag: Boolean; P: Point; begin { Set the position for all enemies of the listed team to 0 on hotmap. } { Set the position for all blocking gears on the coldmap. } M := GB^.Meks; flag := True; while M <> Nil do begin P := GearCurrentLocation( M ); if IsMasterGear( M ) and OnTheMap( GB , P.X , P.Y ) and GearOperational( M ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); if TeamCanSeeTarget( GB , Team , M ) and AreEnemies( GB , Team , NAttValue( M^.NA , NAG_Location , NAS_Team ) ) then begin HotMap[ N , P.X , P.Y ] := 0; Flag := False; end; end else if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_Pass ] <= -100 ) and NotDestroyed( M ) and OnTheMap( GB , P.X , P.Y ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end; M := M^.Next; end; { If no visible enemies found, no point in continuing with the } { algorithm. } if Flag then Exit; HotMapFloodFill( GB , N , MM ); end; Procedure Calc_TeamMap( GB: GameBoardPtr; N,Team,MM: Integer ); { Calculate a FOLLOW TEAM hotmap for a member of TEAM using } { movement mode MM. } var M: GearPtr; Flag: Boolean; P: Point; begin { Set the position for all enemies of the listed team to 0 on hotmap. } { Set the position for all blocking gears on the coldmap. } M := GB^.Meks; flag := True; while M <> Nil do begin P := GearCurrentLocation( M ); if IsMasterGear( M ) and OnTheMap( GB , P.X , P.Y ) and GearOperational( M ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = Team ) then begin HotMap[ N , P.X , P.Y ] := 0; Flag := False; end; end else if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_Pass ] <= -100 ) and NotDestroyed( M ) and OnTheMap( GB , P.X , P.Y ) then begin Inc( ColdMap[ N , P.X , P.Y ] ); end; M := M^.Next; end; { If no visible enemies found, no point in continuing with the } { algorithm. } if Flag then Exit; HotMapFloodFill( GB , N , MM ); end; Procedure CalculateHotMap( GB: GameBoardPtr; N,Team,MM,O: Integer ); { Calculate a hot map, choosing type based on the ORDER given. } var X,Y: Integer; begin MapUpdate[n] := GB^.ComTime + Hot_Map_Validity; MapTeam[n] := Team; MapMoveMode[n] := MM; MapOrder[n] := O; { Clear out the entire map, first. } For X := 1 to MaxMapWidth do begin For Y := 1 to MaxMapWidth do begin HotMap[ N , X , Y ] := 9999; ColdMap[ N , X , Y ] := 0; end; end; UpdateShadowMap( GB ); if O = ORD_SeekSingleModel then begin Calc_FollowMap( GB , N , Team , MM ); end else if O = ORD_SeekSpot then begin Calc_SpotMap( GB , N , Team , MM ); end else if O = ORD_SeekEdge then begin Calc_EdgeMap( GB , N , Team , MM ); end else if O = ORD_SeekTeam then begin Calc_TeamMap( GB , N , Team , MM ); end else if O = ORD_SeekAnyEdge then begin Calc_RetreatMap( GB , N , Team , MM ); end else begin Calc_SDMap( GB , N , Team , MM ); end; end; Procedure ClearHotMaps; { Clear all the currently processed floodfill maps. } var t: Integer; begin for t := 1 to NumFFMap do MapUpdate[ T ] := -1; end; Function GetHotMap( GB: GameBoardPtr; Team,MM,O: Integer ): Integer; { Locate an appropriate HotMap for the data provided. } var RepMap,GoodMap,T: Integer; begin RepMap := 1; GoodMap := 0; for T := 1 to NumFFMap do begin if ( MapUpdate[ t ] >= GB^.ComTime ) and ( MapTeam[ t ] = Team ) and ( MapMoveMode[ t ] = MM ) and ( MapOrder[ t ] = O ) then begin GoodMap := T; end; if MapUpdate[ t ] < MapUpdate[ RepMap ] then RepMap := T; end; if GoodMap <> 0 then begin GetHotMap := GoodMap; end else begin CalculateHotMap( GB , RepMap , Team , MM , O ); GetHotMap := RepMap; end; end; procedure AIAttacker( GB: GameBoardPtr; Mek,Weapon,Target: GearPtr ); { MEK wants to fire WEP at TAR. } var AttSkillVal, DefSkillVal: Integer; { Used to calculate the chances of hitting. } { Thanks to Peter Cordes } AtOp: Integer; T2,T3: GearPtr; begin { Calculate AttSkillVal, DefSkillVal, and set AtOp to 0. } DefSkillVal := BasicDefenseValue( FindRoot( Target ) ); AttSkillVal := SkillValue( Mek , AttackSkillNeeded( Weapon ), AttackStatNeeded( Weapon ) ) + CalcTotalModifiers( gb , Weapon , Target , 0 , WeaponAttackAttributes( Weapon ) ); AtOp := 0; { If the odds of hitting are good enough, the attacker may try } { to make a called shot. } if not NoCalledShots(WeaponAttackAttributes(Weapon),0) and (AttSkillVal-DefSkillVal > 4+Random(10)) and (Random(3) <> 1) and ((Weapon^.S <> GS_Ballistic) or (Weapon^.S <> GS_BeamGun) or (Random(6)+Random(6) > Weapon^.Stat[STAT_BurstValue])) then begin T2 := Target^.SubCom; if WeaponDC( Weapon ) > 10 then begin { If using a powerful weapon, do aimed shot at the torso. } while ( T2 <> Nil ) and (( T2^.G <> GG_Module ) or ( T2^.S <> GS_Body )) do T2 := T2^.Next; end else if T2 <> Nil then begin { If using a less powerful weapon, do aimed shot at the part with the lowest armor. } T3 := Nil; while T2 <> Nil do begin if T3 = Nil then T3 := T2 else if GearCurrentArmor( T2 ) < GearCurrentArmor( T3 ) then T3 := T2; T2 := T2^.Next; end; T2 := T3; end; if T2 <> Nil then Target := T2; end else if ( Weapon^.G = GG_Weapon ) then begin { If not making a called shot, the attacker will take } { advantage of rapid fire if possible. } if ( Weapon^.S = GS_Ballistic ) and ( Weapon^.Stat[ STAT_BurstValue ] > 0 ) then begin AtOp := Weapon^.Stat[ STAT_BurstValue ]; end else if ( Weapon^.S = GS_BeamGun ) and ( Weapon^.Stat[ STAT_BurstValue ] > 0 ) then begin AtOp := Weapon^.Stat[ STAT_BurstValue ]; end else if Weapon^.S = GS_Missile then begin AtOp := Random( 10 ); end; end; AttackerFrontEnd( GB , Mek , Weapon , Target , AtOp ); { Set the initiative recharge counter. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_InitRecharge , GB^.ComTime + ReactionTime( Mek ) ); end; procedure SelectMoveMode( Mek: GearPtr; GB: GameBoardPtr ); { Set the mek's MoveMode attribute to the highest } { active movemode that this mek has. } var T,MM,MaxSpeed: Integer; P: Point; begin MM := 0; MaxSpeed := 0; P := GearCurrentLocation( Mek ); for T := 1 to NumMoveMode do begin if ( BaseMoveRate( GB^.Scene , Mek , T ) > MaxSpeed ) and MoveLegal( GB^.Scene , Mek , T , NAV_NormSpeed , GB^.Comtime ) and TerrMan[ TileTerrain( GB , P.X , P.Y ) ].MMPass[t] then begin if not ( ( GB <> Nil ) and ( GB^.Scale > Mek^.Scale ) and ( T = MM_Fly ) and ( JumpTime( GB^.Scene , Mek ) > 0 ) ) then begin MM := T; MaxSpeed := BaseMoveRate( GB^.Scene , Mek , T ); end; end; end; if MM <> 0 then SetMoveMode( GB , Mek , MM); end; Function SelectBestWeapon( GB: GameBoardPtr; Mek,Target: GearPtr ): GearPtr; { Select the best weapon for attacking TARGET with. } var weapon: GearPtr; BestWeight: LongInt; function SafeToFire( Part: GearPtr ): Boolean; { Return TRUE if firing this weapon won't cause a huge problem, } { or FALSE otherwise. } var Found: Boolean; R: Integer; P: Point; M2: GearPtr; begin { First off, dumb things won't care if it's safe to fire or not. } if ( Mek^.G = GG_Character ) and ( Mek^.Stat[ STAT_Knowledge ] < RollStep( 5 ) ) then begin Exit( True ); end; { Check to see if this is a blast weapon. } if HasAttackAttribute( WeaponAttackAttributes( Part ) , AA_BlastAttack ) then begin { If there's an ally within the blast radius, this weapon isn't safe. } P := GearCurrentLocation( Target ); M2 := GB^.Meks; R := BlastRadius( GB , Part , WeaponAttackAttributes( Part ) ); Found := False; while M2 <> Nil do begin if AreAllies( GB , Mek , M2 ) and ( Range( M2 , P.X , P.Y ) < R ) then Found := True; M2 := M2^.Next; end; SafeToFire := Not Found; end else begin { The blastattack check is the only one I'm doing yet. } SafeToFire := True; end; end; function WGoodness( Part, Target: GearPtr ): Integer; { This is the heuristic SeekBigWeapon uses } var WG,AS,AM,BV,AAV : Integer; AttSkillVal, DefSkillVal : Integer; AtAt: String; begin { Can your lancemates size up the abilities of a defender in a } { mecha, very far away, in heavy cover? Sure they can! } { Just look at Piloting or Dodge, not talents, parrying, ... } DefSkillVal := BasicDefenseValue( FindRoot( Target ) ); { NOTE: the actual attack code does RollStep(SkillValue) + } { modifiers, so this is an approximation } { Missiles will have BustValue = 0. Don't fire } { until you can see the whites of their eyes. } AS := SkillValue( FindRoot(Part) , AttackSkillNeeded( Part ) , AttackStatNeeded( Part ) ); AM := CalcTotalModifiers( gb , Part , Target , Part^.Stat[ STAT_BurstValue ] , WeaponAttackAttributes( Part ) ); AttSkillVal := AS + AM + 5 - DefSkillVal; if AttSkillVal < 0 then AttSkillVal := 0; if ( Part^.G = GG_Weapon ) and (( Part^.S = GS_Ballistic ) or ( Part^.S = GS_BeamGun )) then begin BV := 1 + Part^.Stat[ STAT_BurstValue ]; end else if ( Part^.G = GG_Weapon ) and ( Part^.S = GS_Missile ) then begin BV := 2; end else begin BV := 3; end; { Apply special modifiers to BV. } AtAt := WeaponAttackAttributes( Part ); AAV := AttackAttributeValue( AtAt ) - 10; if AAV < 0 then AAV := 0; WG := WeaponDC( Part ) * BV + AAV + 2 * AttSkillVal; for AS := 1 to Part^.Scale do WG := WG * 3; { Debugging message DialogMsg( PilotName( Mek ) + '/' + GearName( Part ) + ': ' + BStr( WG ) ); DialogMsg( '- - DC: ' + BStr( WeaponDC( Part ) ) + ', BV:' + BStr( BV ) + ', AtAt:' + BStr( AAV ) + ', AtSkill:' + BStr( AttSkillVal ) ); } WGoodness := WG; end; procedure SeekBigWeapon( Part: GearPtr ); { Seek a weapon which is capable of hitting target. } { Select the best weapon, weighted by accuracy and damage } { Things we don't do: } { * scale factors } { * avoid wasting ammo/recharge time on easy-to-kill targets } { (it would be worth working on the target selection } { algorithm if you were going to do anything about this. } { Maybe find your best weapon and look for targets to } { fire it at. This could help a lot with LINE weapons. } { Or even find a weight for every weapon/target pair...) } var Weight : Integer; begin while ( Part <> Nil ) do begin if ( Part^.G = GG_Module ) or ( Part^.G = GG_Weapon ) then begin if ReadyToFire( GB , Mek , Part ) and RangeArcCheck( GB , Mek , Part , Target ) and SafeToFire( Part ) then begin if Weapon = Nil then begin Weapon := Part; BestWeight := WGoodness(Part, Target); end else begin Weight := WGoodness(Part, Target); { BestWeight is a variable in the parent procedure. I don't know how else to do this in Pascal... } if Weight > BestWeight then begin Weapon := Part; BestWeight := Weight; end; end; end; end; if ( Part^.SubCom <> Nil ) then SeekBigWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekBigWeapon( Part^.InvCom ); Part := Part^.Next; end; end; begin Weapon := Nil; BestWeight := -10000; SeekBigWeapon( Mek^.SubCom ); SeekBigWeapon( Mek^.InvCom ); SelectBestWeapon := Weapon; end; Function SelectRandomUsable( Mek: GearPtr ): GearPtr; { Select a random usable gear from this mecha. } var ShoppingList: NAttPtr; N: Integer; Procedure CheckAlongPath( LList: GearPtr ); { Check along this path for usable gears. Don't check the inventory } { or along paths which have been destroyed. } begin while LList <> Nil do begin if NotDestroyed( LList ) then begin if LList^.G = GG_Usable then begin Inc( N ); SetNAtt( ShoppingList , N , 0 , FindGearIndex( Mek , LList ) ); end; CheckAlongPath( LList^.SubCom ); end; LList := LList^.Next; end; end; var Part: GearPtr; begin ShoppingList := Nil; N := 0; CheckAlongPath( Mek^.SubCom ); Part := Nil; if N > 0 then begin N := NAttValue( ShoppingList , Random( N ) + 1 , 0 ); Part := LocateGearByNumber( Mek , N ); end; DisposeNAtt( ShoppingList ); SelectRandomUsable := Part; end; Procedure AttemptSpecialAction( GB: GameBoardPtr; Mek: GearPtr ); { The enemy can't attack just now. See if there's anything else } { worth doing. } var Part: GearPtr; begin if NAttValue( Mek^.NA , NAG_EpisodeData , NAS_SpecialActionRecharge ) > GB^.ComTime then Exit; { Select one of this mecha's special systems at random. } Part := SelectRandomUsable( Mek ); if Part <> Nil then begin if Part^.S = GS_Transformation then begin if AIShouldTransform( GB , Mek , Part ) then begin DoTransformation( GB , Mek , Part , True ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_SpecialActionRecharge , GB^.ComTime + 150 + Random( 50 ) ); end; end else if Part^.S = GS_LongRangeScanner then begin if AIShouldLRScan( GB , Mek , Part ) then begin DoLongRangeScan( GB , Mek , Part ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_SpecialActionRecharge , GB^.ComTime + 150 + Random( 50 ) ); end; end; end; end; Procedure AttackTargetOfOppurtunity( GB: GameBoardPtr; Mek: GearPtr ); { Look for the most oppurtune target to attack. } var weapon: GearPtr; TL,Target: GearPtr; CouldAttack: Boolean; { *** PROCEDURES BLOCK *** } procedure SeekFarWeapon( Part: GearPtr ); { Find the weapon with the longest current range. } begin while ( Part <> Nil ) do begin if ( Part^.G = GG_Module ) or ( Part^.G = GG_Weapon ) then begin if ReadyToFire( GB , Mek , Part ) then begin if Weapon = Nil then Weapon := Part else if WeaponRange( GB , Part , RANGE_Long ) > WeaponRange( GB , Weapon , RANGE_Long ) then Weapon := Part; end; end; if ( Part^.SubCom <> Nil ) then SeekFarWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekFarWeapon( Part^.InvCom ); Part := Part^.Next; end; end; begin { First, check to make sure that the mecha hasn't attacked } { too recently. } if NAttValue( Mek^.NA , NAG_EpisodeData , NAS_InitRecharge ) > GB^.ComTime then Exit; { Assume FALSE unless proven TRUE. } CouldAttack := False; { Start by finding a good weapon to fire with. } { Preference will be given to the weapon with the longest range. } Weapon := Nil; SeekFarWeapon( Mek^.SubCom ); SeekFarWeapon( Mek^.InvCom ); if Weapon <> Nil then begin { Next, see if we have an appropriate target. } Target := Nil; TL := gb^.meks; while TL <> Nil do begin if AreEnemies( GB , Mek , TL ) and OnTheMap( GB , TL ) and RangeArcCheck( GB , Mek , Weapon , TL ) and GearActive( TL ) and MekCanSeeTarget( GB , Mek , TL ) then begin if Target = Nil then Target := TL else if Range( gb , Target , Mek ) > Range( gb , TL , Mek ) then Target := TL; end; TL := TL^.Next; end; { If a target was found, fire at that target with the } { biggest weapon in the arsenal. } if Target <> Nil then begin Weapon := SelectBestWeapon( GB , Mek , Target ); if Weapon <> Nil then begin AIAttacker( GB , Mek , Weapon , Target ); CouldAttack := True; end; end; end; { If we couldn't attack, attempt some other combat action, maybe. } if not CouldAttack then AttemptSpecialAction( GB , Mek ); end; Procedure Wander( Mek: GearPtr; GB: GameBoardPtr ); { Just wander about. This procedure is often called when } { conventional pathfinding methods have failed. It is also } { used as the "SEEK" part of "SEEK AND DESTROY". } var P: Point; CD: Integer; begin { Determine current facing and position. } P := GearCurrentLocation( Mek ); CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); { If the current direction of travel doesn't lead off the map, } { and isn't blocked, just move foreword. } if OnTheMap( GB , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ) and ( Random( 5 ) <> 1 ) and not MoveBlocked( Mek , GB ) then begin if Mek^.G = GG_Mecha then begin PrepAction( GB , Mek , NAV_FullSpeed ); end else begin PrepAction( GB , Mek , NAV_NormSpeed ); end; end else begin if Random( 2 ) = 1 then begin PrepAction( GB , Mek , NAV_TurnRight ); end else begin PrepAction( GB , Mek , NAV_TurnLeft ); end; end; end; Function XMoveTowardsGoal( GB: GameBoardPtr; Mek: GearPtr; HM,OptMax,OptMin: Integer ): Boolean; { Using hotmap N as a guide, attempt to move towards the goal. } { OPTMAX and OPTMIN describe the maximum and minumum hotmap values the mek will } { try to reach. If its current location has a hot value lower than OptMax, it } { will stay in that spot. If its current location is lower than OptMin, it will } { attempt to move to a location with a higher hot value. } Function ThisMoveIsOkay( MAct,X,Y: Integer; P: Point ): Boolean; { This function combines two checks: Whether or not the target tile is } { unblocked, and whether or not the specified moveaction is legal. } begin ThisMoveIsOkay := MoveLegal( GB^.Scene , Mek , MAct , GB^.ComTime ) and not MovementBlocked( Mek , GB , P.X , P.Y , X , Y ); end; var P,P2: Point; T,D,Best,CD: Integer; begin P := GearCurrentLocation( Mek ); { If our current direction of travel is bringing us closer to the } { target, keep moving in that direction, even if it isn't the } { optimal path. } CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); if HotMap[ HM , P.X ,P.Y ] < OptMin then begin { Too close to the target- move farther away. } P2.X := P.X + AngDir[ ( CD + 4 ) mod 8 , 1 ]; P2.Y := P.Y + AngDir[ ( CD + 4 ) mod 8 , 2 ]; if OnTheMap( GB , P2.X , P2.Y ) and ( HotMap[ HM , P2.X , P2.Y ] > HotMap[ HM , P.X , P.Y ] ) and ThisMoveIsOkay( NAV_Reverse , P2.X , P2.Y , P ) then begin PrepAction( GB , Mek , NAV_Reverse ); XMoveTowardsGoal := True; end else if OnTheMap( GB , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ) and ( HotMap[ HM , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ] > HotMap[ HM , P.X , P.Y ] ) and ThisMoveIsOkay( NAV_NormSpeed , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] , P ) then begin if Mek^.G = GG_Character then begin PrepAction( GB , Mek , NAV_NormSpeed ); end else begin PrepAction( GB , Mek , NAV_FullSpeed ); end; XMoveTowardsGoal := True; end else begin D := -1; Best := HotMap[ HM , P.X , P.Y ]; for t := 0 to 7 do begin if OnTheMap( GB , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ) then begin if ( HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ] > Best ) and ( ColdMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ] < 1 ) and not MovementBlocked( Mek , GB , P.X , P.Y , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ) then begin Best := HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ]; D := T; end; end; end; if D <> -1 then begin if CD <> D then begin Best := NAV_TurnRight; for t := 1 to 4 do begin if (( CD + T ) mod 8 ) = D then Best := NAV_TurnRight else if (( CD + 8 - T ) mod 8 ) = D then Best := NAV_TurnLeft; end; PrepAction( GB , Mek , Best ); end else begin PrepAction( GB , Mek , NAV_FullSpeed ); end; XMoveTowardsGoal := True; end else begin { If we don't have a good place to go, } { try changing move modes to the lowest. } if Random( 2 ) = 1 then begin ShiftGears( GB , Mek , False ); end; XMoveTowardsGoal := False; end; end; end else if ( HotMap[ HM , P.X ,P.Y ] < OptMax ) and ( ( Random( 15 ) < HotMap[ HM , P.X ,P.Y ] ) or IsInCover( GB , Mek ) ) then begin { Within optimal range. Turn to hopefully face a target. } D := -1; Best := HotMap[ HM , P.X , P.Y ]; for t := 0 to 7 do begin if OnTheMap( GB , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ) then begin if ( HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ] < Best ) then begin Best := HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ]; D := T; end; end; end; { If a good direction was found, turn to face it. } { Otherwise assume that we're already facing a good direction. } if D <> -1 then begin if CD <> D then begin Best := NAV_TurnRight; for t := 1 to 4 do begin if (( CD + T ) mod 8 ) = D then Best := NAV_TurnRight else if (( CD + 8 - T ) mod 8 ) = D then Best := NAV_TurnLeft; end; PrepAction( GB , Mek , Best ); end else begin WaitAMinute( GB , Mek , ReactionTime( Mek ) ); end; end else begin { If we don't have a good place to go, } { try changing move modes to the lowest. } if Random( 2 ) = 1 then begin ShiftGears( GB , Mek , False ); end; WaitAMinute( GB , Mek , ReactionTime( Mek ) ); end; XMoveTowardsGoal := True; end else if OnTheMap( GB , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ) and ( HotMap[ HM , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ] < HotMap[ HM , P.X , P.Y ] ) and ( HotMap[ HM , P.X , P.Y ] < Random( 4 ) ) and not MoveBlocked( Mek , GB ) then begin if (( Mek^.G = GG_Mecha ) or ( CurrentStamina( Mek ) > 10 ) ) and MoveLegal( GB^.Scene , Mek , NAV_FullSpeed , GB^.ComTime ) then begin PrepAction( GB , Mek , NAV_FullSpeed ); end else if MoveLegal( GB^.Scene , Mek , NAV_NormSpeed , GB^.ComTime ) then begin PrepAction( GB , Mek , NAV_NormSpeed ); end else begin Exit( False ); end; XMoveTowardsGoal := True; end else begin { Check to see if there's a better direction we can be moving in. } D := -1; Best := HotMap[ HM , P.X , P.Y ]; for t := 0 to 7 do begin if OnTheMap( GB , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ) then begin if ( HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ] < Best ) and ( ColdMap[ HM , P.X + AngDir[T,1] , P.Y + AngDir[T,2] ] < 1 ) and not MovementBlocked( Mek,GB,P.X,P.Y,P.X + AngDir[T,1] , P.Y + AngDir[T,2]) then begin Best := HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ]; D := T; end; end; end; if D <> -1 then begin if CD <> D then begin Best := NAV_TurnRight; for t := 1 to 4 do begin if (( CD + T ) mod 8 ) = D then Best := NAV_TurnRight else if (( CD + 8 - T ) mod 8 ) = D then Best := NAV_TurnLeft; end; PrepAction( GB , Mek , Best ); end else begin if MoveBlocked( Mek , GB ) then begin Exit( False ); end else if ( HotMap[ HM , P.X ,P.Y ] > ( OptMax * 3 div 2 ) ) and (( Mek^.G = GG_Mecha ) or ( CurrentStamina( Mek ) > 10 ) ) and MoveLegal( GB^.Scene , Mek , NAV_FullSpeed, GB^.ComTime ) then begin PrepAction( GB , Mek , NAV_FullSpeed ); end else if MoveLegal( GB^.Scene , Mek , NAV_NormSpeed, GB^.ComTime ) then begin PrepAction( GB , Mek , NAV_NormSpeed ); end else begin Exit( False ); end; end; XMoveTowardsGoal := True; end else begin { If we don't have a good place to go, } { try changing move modes to the lowest. } if Random( 2 ) = 1 then begin ShiftGears( GB , Mek , False ); end; XMoveTowardsGoal := False; end; end; end; Procedure MoveTowardsGoal( GB: GameBoardPtr; Mek: GearPtr; HM: Integer ); { Front-end for the Extended Move Towards Goal. } begin if not XMoveTowardsGoal( GB , Mek , HM , 0 , 0 ) then Wander( Mek , GB ); end; Function DoorPresent( GB: GameBoardPtr; X,Y: Integer ): Boolean; { Return TRUE if there's a door here which a passive NPC shouldn't pass. } begin if not OnTheMap( GB , X , Y ) then begin DoorPresent := False; end else begin DoorPresent := TileTerrain( GB , X , Y ) = TERRAIN_Threshold; end; end; Procedure MillAround( GB: GameBoardPtr; Mek: GearPtr ); { Stay in roughly the same position, wander around a little bit. } var P,P0: Point; D: Integer; begin { No reason to panic. Just stand around; } { maybe move if it's okay. } if Random( 3 ) = 1 then begin PrepAction( GB , Mek , NAV_Stop ); end else if Random( 3 ) = 1 then begin { The passive character may walk sometimes. } P0 := GearCurrentLocation( Mek ); D := NAttValue( Mek^.NA , NAG_Location , NAS_D ); P.X := P0.X + AngDir[ D , 1 ]; P.Y := P0.Y + AngDir[ D , 2 ]; if OnTheMap( GB , P.X , P.Y ) then begin { Don't move foreword if the move is blocked } { or if the move would take the character over } { the threshold of a door. } if MovementBlocked( Mek , GB , P0.X , P0.Y , P.X , P.Y ) or DoorPresent( GB , P.X , P.Y ) then begin PrepAction( GB , Mek , NAV_Stop ); end else begin PrepAction( GB , Mek , NAV_NormSpeed ); end; end; end else begin if Random( 2 ) = 1 then begin PrepAction( GB , Mek , NAV_TurnRight ); end else begin PrepAction( GB , Mek , NAV_TurnLeft ); end; end; end; Procedure FleeFromGoal( GB: GameBoardPtr; Mek: GearPtr; HM: Integer ); { Using hotmap N as a guide, attempt to move away from the goal. } var P: Point; T,D,Best,CD: Integer; begin P := GearCurrentLocation( Mek ); { If our current direction of travel is bringing us closer to the } { target, keep moving in that direction, even if it isn't the } { optimal path. } CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); if OnTheMap( GB , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ) and ( HotMap[ HM , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ] > HotMap[ HM , P.X , P.Y ] ) and ( HotMap[ HM , P.X , P.Y ] > Random( 8 ) ) and not MoveBlocked( Mek , GB ) then begin PrepAction( GB , Mek , NAV_NormSpeed ); end else begin D := -1; Best := HotMap[ HM , P.X , P.Y ]; for t := 0 to 7 do begin if OnTheMap( GB , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ) then begin if ( HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ] > Best ) and ( ColdMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ] < 1 ) then begin Best := HotMap[ HM , P.X + AngDir[ T , 1 ] , P.Y + AngDir[ T , 2 ] ]; D := T; end; end; end; if D <> -1 then begin if CD <> D then begin Best := NAV_TurnRight; for t := 1 to 4 do begin if (( CD + T ) mod 8 ) = D then Best := NAV_TurnRight else if (( CD + 8 - T ) mod 8 ) = D then Best := NAV_TurnLeft; end; PrepAction( GB , Mek , Best ); end else begin PrepAction( GB , Mek , NAV_NormSpeed ); end; end else begin MillAround( GB , Mek ); end; end; end; Function HotMoveMode( Mek: GearPtr ): Integer; { Return the current move mode of this mecha, adjusted for } { characters who are walking. } var T: Integer; begin T := NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ); if ( T = MM_Walk ) and ( Mek^.G = GG_Character ) then T := 0; HotMoveMode := T; end; Procedure AIRepair( GB: GameBoardPtr; NPC,Target: GearPtr; Skill: Integer ); { This procedure acts as a frontend for the repair skill bits. } begin if not DoFieldRepair( GB , NPC , Target , Skill ) then begin SetNAtt( NPC^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end; end; Function SelectRepairTarget( GB: GameBoardPtr; Mek: GearPtr; Skill: Integer ): GearPtr; { Locate a target that needs repairs. } var T,BTar: GearPtr; Best,Dmg: Integer; begin T := GB^.Meks; BTar := Nil; Best := 0; while T <> Nil do begin if AreAllies( GB , Mek , T ) and ( ( T^.G = GG_Character ) or ( SAttValue( T^.SA , 'pilot' ) <> '' ) ) then begin Dmg := RepairNeededBySkill( T , Skill ); if ( Dmg > Best ) and CanRepairUsingSKill( Mek , T , Skill ) then begin BTar := T; Best := Dmg; end; end; T := T^.Next; end; SelectRepairTarget := BTar; end; Function SelectSocialTarget( GB: GameBoardPtr; NPC: GearPtr; MustBeSexy: Boolean ): GearPtr; { Select a character for NPC to interact with. } var M,Target: GearPtr; N,Team: Integer; Function IsGoodSocTarget: Boolean; { Return TRUE if M is a good target, or FALSE otherwise. } begin if ( NAttValue( M^.NA , NAG_Personal , NAS_CID ) = 0 ) or ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = Team ) or ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then begin IsGoodSocTarget := False; end else if not NotAnAnimal( M ) then begin IsGoodSocTarget := False; end else if MustBeSexy then begin IsGoodSocTarget := GearActive( M ) and ( not AreEnemies( GB , NPC , M ) ) and IsSexy( NPC , M ); end else begin IsGoodSocTarget := GearActive( M ) and ( not AreEnemies( GB , NPC , M ) ); end; end; begin { Make two passes. On the first pass, just count the number of candidates. } { On the second pass actually select one. } N := 0; Target := Nil; M := GB^.Meks; Team := NAttValue( NPC^.NA , NAG_Location , NAS_Team ); while M <> Nil do begin if IsGoodSocTarget then Inc( N ); M := M^.Next; end; { If any potential targets were found, pick one of them at random. } if N > 0 then begin M := GB^.Meks; N := Random( N ); while M <> Nil do begin if IsGoodSocTarget then begin Dec( N ); if N = -1 then Target := M; end; M := M^.Next; end; end; SelectSocialTarget := Target; end; {Procedure NPC_Flirtation( GB: GameBoardPtr; NPC , Target: GearPtr ); { NPC will flirt with TARGET. If successful, this will cause the PC to gain } { a reaction bonus from TARGET. } var skRoll: Integer; CID: LongInt; msg: String; M,PC: GearPtr; begin SkRoll := RollStep( SkillValue( NPC , 27 ) ); if SkRoll > 15 then begin { Report the success. } msg := MsgString( 'NPCFLIRT_Good' ); msg := ReplaceHash( msg , PilotName( NPC ) ); msg := ReplaceHash( msg , PilotName( TARGET ) ); DialogMsg( msg ); { Success! Improve the reaction score. } CID := NAttValue( Target^.NA , NAG_Personal , NAS_CID ); M := GB^.Meks; while M <> Nil do begin if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and GearActive( M ) then begin PC := LocatePilot( M ); if PC <> Nil then AddNAtt( PC^.NA , NAG_ReactionScore , CID , 1 + Random( 3 ) ); end; M := M^.Next; end; end else if SkRoll > 5 then begin { Okay... neither good nor bad. } msg := MsgString( 'NPCFLIRT_Okay' ); msg := ReplaceHash( msg , PilotName( NPC ) ); msg := ReplaceHash( msg , PilotName( TARGET ) ); DialogMsg( msg ); end else begin { Bad. This is just bad. } AddMoraleDmg( NPC , 15 ); msg := MsgString( 'NPCFLIRT_Bad' ); msg := ReplaceHash( msg , PilotName( TARGET ) ); msg := ReplaceHash( msg , PilotName( NPC ) ); DialogMsg( msg ); end; end;} {Procedure NPC_Chatting( GB: GameBoardPtr; NPC , Target: GearPtr ); { NPC will chat with TARGET. This may reveal a rumor. } var skRoll: Integer; msg: String; Rumors: SAttPtr; begin SkRoll := SkillRoll( GB , NPC , NAS_Conversation , STAT_Charm , 10 , 0 , False, True ); if SkRoll > 10 then begin { A rumor has been gained. } rumors := CreateRumorList( GB , Nil , Target ); if rumors <> Nil then begin msg := MsgString( 'NPCCHAT_Good' ); msg := ReplaceHash( msg , PilotName( NPC ) ); msg := msg + ' ' + SelectRandomSAtt( Rumors )^.info; DisposeSAtt( Rumors ); end else begin msg := MsgString( 'NPCCHAT_Okay' ); msg := ReplaceHash( msg , PilotName( NPC ) ); msg := ReplaceHash( msg , PilotName( TARGET ) ); end; DialogMsg( msg ); end else begin { Okay... neither good nor bad. } msg := MsgString( 'NPCCHAT_Okay' ); msg := ReplaceHash( msg , PilotName( NPC ) ); msg := ReplaceHash( msg , PilotName( TARGET ) ); DialogMsg( msg ); end; end;} Procedure LancemateUsefulAction( Mek: GearPtr; GB: GameBoardPtr ); { Do something useful! There are no enemies to be found... } const Num_AI_Repair = 2; AI_Repair_List: Array [1..Num_AI_Repair] of Byte = ( NAS_Medicine, NAS_Repair ); var HM,Target,T: Integer; NPC,TGear,Tool: GearPtr; CORD: Integer; { Continuous Orders } begin { See if there are any pending actions. } CORD := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders ); { If CORD < 0 , use a repair skill. } if CORD < 0 then begin Target := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ATarget ); TGear := LocateMekByUID( GB , Target ); { If this gear no longer needs repairs, quit. } if ( TGear = Nil ) or ( not CanRepairUsingSkill( Mek , TGear , Abs( CORD ) ) ) or ( CurrentMental( Mek ) < 1 ) then begin { Clear the continuous action. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end else if OnTheMap( GB , TGear ) then begin { If on the map, NPC must move towards TARGET. } if Range( GB , Mek , TGear ) > 3 then begin HM := GetHotMap( GB , Target , HotMoveMode( Mek ) , ORD_SeekSingleModel ); MoveTowardsGoal( GB , Mek , HM ); end else begin AIRepair( GB , Mek , TGear , Abs( CORD ) ); end; end else begin { If off the map, NPC can just autorepair it. } AIRepair( GB , Mek , TGear , Abs( CORD ) ); end; { end else if CORD = CORD_Flirt then begin Target := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ATarget ); TGear := LocateMekByUID( GB , Target ); { If this gear no longer qualifies, quit. } if ( TGear = Nil ) or AreEnemies( GB , Mek , TGear ) or ( Destroyed( TGear ) ) then begin { Clear the continuous action. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end else if OnTheMap( GB , TGear ) then begin { If on the map, NPC must move towards TARGET. } if Range( GB , Mek , TGear ) > 3 then begin { If the move cannot proceed, cancel the action. } HM := GetHotMap( GB , Target , HotMoveMode( Mek ) , ORD_SeekSingleModel ); if not XMoveTowardsGoal( GB , Mek , HM , 0 , 0 ) then SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end else begin { Close enough to flirt. Yay! } NPC_Flirtation( GB, Mek , TGEar ); WaitAMinute( GB , Mek , ReactionTime( Mek ) * 5 ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end; end else begin { If off the map, cancel the action. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end; end else if CORD = CORD_Chat then begin Target := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ATarget ); TGear := LocateMekByUID( GB , Target ); { If this gear no longer qualifies, quit. } if ( TGear = Nil ) or AreEnemies( GB , Mek , TGear ) or ( Destroyed( TGear ) ) then begin { Clear the continuous action. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end else if OnTheMap( GB , TGear ) then begin { If on the map, NPC must move towards TARGET. } if Range( GB , Mek , TGear ) > 3 then begin { If the move cannot proceed, cancel the action. } HM := GetHotMap( GB , Target , HotMoveMode( Mek ) , ORD_SeekSingleModel ); if not XMoveTowardsGoal( GB , Mek , HM , 0 , 0 ) then SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end else begin { Close enough to flirt. Yay! } NPC_Chatting( GB, Mek , TGEar ); WaitAMinute( GB , Mek , ReactionTime( Mek ) * 5 ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end; end else begin { If off the map, cancel the action. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , 0 ); end; } end else begin { Attempt to find a useful thing to do. } { Locate the NPC proper. } NPC := LocatePilot( Mek ); { Check to see whether or not this NPC has any repair skills } { to use. } t := 1; Target := 0; CORD := 0; if CurrentMental( Mek ) > 0 then begin while ( t <= Num_AI_Repair ) and ( Target = 0 ) do begin if ( NAttValue( NPC^.NA , NAG_Skill , AI_Repair_List[ t ] ) > 0 ) then begin TGear := SelectRepairTarget( GB , Mek , AI_Repair_List[ t ] ); if TGear <> Nil then begin Target := NAttValue( TGear^.NA , NAG_EpisodeData , NAS_UID ); CORD := -AI_Repair_List[ t ]; end; end; Inc( T ); end; end; { If no repair skills, try flirtation or conversation. } { if ( CORD = 0 ) and ( NAttValue( NPC^.NA , NAG_Skill , 27 ) > Random( 10 ) ) and ( Random( 3 ) = 1 ) and IsSafeArea( GB ) then begin TGEar := SelectSocialTarget( GB , NPC , True ); if TGear <> Nil then begin Target := NAttValue( TGear^.NA , NAG_EpisodeData , NAS_UID ); CORD := CORD_Flirt; end; end;} if ( CORD = 0 ) and ( NAttValue( NPC^.NA , NAG_Skill , NAS_Conversation ) > Random( 10 ) ) and ( Random( 3 ) = 1 ) and IsSafeArea( GB ) then begin TGEar := SelectSocialTarget( GB , NPC , False ); if TGear <> Nil then begin Target := NAttValue( TGear^.NA , NAG_EpisodeData , NAS_UID ); CORD := CORD_Chat; end; end; if CORD <> 0 then begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ContinuousOrders , CORD ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ATarget , Target ); end else begin HM := GetHotMap( GB , NAV_DefPlayerTeam , HotMoveMode( Mek ) , ORD_SeekTeam ); if not XMoveTowardsGoal( GB , Mek , HM , 5 , 10 ) then Wander( Mek , GB ); end; end; end; Procedure Seek_And_Destroy( Mek: GearPtr; GB: GameBoardPtr ); { Seek an enemy, destroy it if possible. } var HM: Integer; N1,D1,N2,D2: LongInt; { Numerator 1 , Numerator 2 , Denominator 1 , Denominator 2 } { Used for the OptMax, OptMin calculations. } Procedure CheckOptimumRange( Part: GearPtr ); { Check the weapons along this track to find out the optimum fighting } { ranges for MEK. } { This is done with a strange fraction calculation- find the average range } { of all weapons, weighted by the DC they do. } var Dmg,Rng: Integer; begin while Part <> Nil do begin { If PART is a weapon, it will affect our calculations. } if ReadyToFire( GB , Mek , Part ) then begin if Part^.G = GG_Module then begin Dmg := WeaponDC( Part ); D1 := D1 + Dmg div 2; end else if Part^.G = GG_Weapon then begin { Find the DMG and RNG of this weapon. } Dmg := WeaponDC( Part ); Rng := WeaponRange( GB , Part , RANGE_Long ); if Rng < 2 then begin D1 := D1 + Dmg div 2; D2 := D2 + Dmg * 6; end else if Rng < 18 then begin N1 := N1 + ( Rng * 2 * Dmg ); N2 := N2 + ( Rng * Dmg div 3 ); D1 := D1 + Dmg; D2 := D2 + Dmg; end else begin N1 := N1 + ( Rng * 2 * Dmg ); D1 := D1 + Dmg; N2 := N2 + ( Rng * Dmg ); D2 := D2 + Dmg; end; end; end; CheckOptimumRange( Part^.SubCom ); CheckOptimumRange( Part^.InvCom ); Part := Part^.Next; end; end; Procedure GetOptimumRange; { Determine the optimum range for this mecha. } var NPC: GearPtr; begin NPC := LocatePilot( Mek ); if ( NPC <> Nil ) and ( NAttValue( NPC^.NA , NAG_Personal , NAS_OptMax ) <> 0 ) then begin N1 := NAttValue( NPC^.NA , NAG_Personal , NAS_OptMax ); N2 := NAttValue( NPC^.NA , NAG_Personal , NAS_OptMin ); end else begin CheckOptimumRange( Mek^.SubCom ); end; end; begin { First, move towards the enemy, if necessary. } if Random( 3 ) = 1 then begin SelectMoveMode( Mek , GB ); end; HM := GetHotMap( GB , NAttValue( Mek^.NA , NAG_LOcation , NAS_Team ) , HotMoveMode( Mek ) , ORD_SeekEnemy ); { Determine optimum ranges. } N1 := 0; N2 := 0; D1 := 1; D2 := 1; GetOptimumRange; if not XMoveTowardsGoal( GB , Mek , HM , N1 div D1 , N2 div D2 ) then begin if NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam then begin LancemateUsefulAction( Mek , GB ); end else begin Wander( Mek , GB ); end; end else begin { Not doing anything special... Might as well launch a verbal attack. } if ( GB^.ComTime > NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ChatterRecharge ) ) then begin if Random( 50 ) = 1 then SayCombatTaunt( GB , Mek , 'CHAT_ATTACK' ); end; end; { Secondly, attack anyone within reach. } AttackTargetOfOppurtunity( GB , Mek ); end; Procedure SetNewOrders( GB: GameBoardPtr; Mek: GearPtr ); { MEK has apparently either completed what it set out to do, } { or the standing orders have become impossible. } var TN: Integer; TG: GearPtr; begin TN := NAttValue( Mek^.NA , NAG_Location , NAS_Team ); TG := LocateTeam( GB , TN ); if TG <> Nil then begin { Set the default orders listed in the TEAM gear. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Orders , TG^.Stat[ STAT_TeamOrders ] ); end else begin { If no team gear has been defined, use default orders } { SEARCH AND DESTROY. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Orders , NAV_SeekAndDestroy ); end; end; Procedure GOTO_SPOT( Mek: GearPtr; GB: GameBoardPtr ); { The outline for this behavior is as follows: } { - If not moving, set move towards target. } { - Fire at targets of oppurtunity. } { - Once target is reached, seek new orders. } var HM,X,Y,GX,GY: Integer; begin { Locate all the values we're gonna need. } X := NAttValue( Mek^.NA , NAG_Location , NAS_X ); Y := NAttValue( Mek^.NA , NAG_Location , NAS_Y ); GX := NAttValue( Mek^.NA , NAG_Location , NAS_GX ); GY := NAttValue( Mek^.NA , NAG_Location , NAS_GY ); if Random( 3 ) = 1 then begin SelectMoveMode( Mek , GB ); end; { If we have reached the spot where we want to go, } { its time to seek new orders. } if ( GX = X ) and ( GY = Y ) then begin SetNewOrders( GB , Mek ); end else begin HM := GetHotMap( GB , GX + ( GY * ( GB^.MAP_Width + 1 ) ) , HotMoveMode( Mek ) , ORD_SeekSpot ); MoveTowardsGoal( GB , Mek , HM ); { Secondly, attack an enemy within reach. } AttackTargetOfOppurtunity( GB , Mek ); end; end; Procedure GOTO_EDGE( Mek: GearPtr; GB: GameBoardPtr ); { The outline for this behavior is as follows: } { - If not moving, set move towards target. } { - Fire at targets of oppurtunity. } var HM,Edge: Integer; begin { Locate all the values we're gonna need. } Edge := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ATarget ); if Random( 2 ) = 1 then begin SelectMoveMode( Mek , GB ); end; HM := GetHotMap( GB , Edge , HotMoveMode( Mek ) , ORD_SeekEdge ); MoveTowardsGoal( GB , Mek , HM ); { Secondly, attack an enemy within reach. } AttackTargetOfOppurtunity( GB , Mek ); end; Procedure PASSIVE( Mek: GearPtr; GB: GameBoardPtr ); { This particular gear is going to act passively. It will not } { move around much, just stand there mostly. If it spots a } { hostile model it will run about randomly. } { This AI type is to be used for townsfolk and other unfortunates. } var HM: Integer; begin { First, move away from the enemy, if necessary. } HM := GetHotMap( GB , NAttValue( Mek^.NA , NAG_LOcation , NAS_Team ) , HotMoveMode( Mek ) , ORD_SeekEnemy ); FleeFromGoal( GB , Mek , HM ); end; Procedure RUNAWAY( Mek: GearPtr; GB: GameBoardPtr ); { This AI type is for models wishing to exit the board. } begin { This mode will likely run the NPC off the map. } if MoveBlocked( Mek , GB ) then begin SelectMoveMode( Mek , GB ); if Random( 2 ) = 1 then begin PrepAction( GB , Mek , NAV_TurnRight ); end else begin PrepAction( GB , Mek , NAV_TurnLeft ); end; end else begin PrepAction( GB , Mek , NAV_FullSpeed ); end; end; Procedure FOLLOW( Mek: GearPtr; GB: GameBoardPtr ); { Attempt to follow the target. } { Note that this is a "MAGIC" AI type, since the NPC will } { follow its target even if the target cannot be seen. } var UID,HM: Integer; begin { First, attempt to move closer to target. } UID := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ATarget ); HM := GetHotMap( GB , UID , HotMoveMode( Mek ) , ORD_SeekSingleModel ); MoveTowardsGoal( GB , Mek , HM ); { Secondly, attack an enemy within reach. } AttackTargetOfOppurtunity( GB , Mek ); end; Procedure GetPropInput( GB: GameBoardPtr; Mek: GearPtr ); { Props just kind of sit there. Usually. } begin if Mek^.S = GS_CombatProp then begin AttackTargetOfOppurtunity( GB , Mek ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ReactionTime( Mek ) ); end else begin PrepAction( GB , Mek , NAV_Stop ); SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ClicksPerRound ); end; end; Function AIUseInventory( NPC: GearPtr; GB: GameBoardPtr ): Boolean; { It's possible that this NPC has a status condition. Check to see } { whether or not they have any medicine to deal with it. } var Item,I2: GearPtr; UsedSomething: Boolean; begin { Assume FALSE until something gets used. } UsedSomething := FALSE; { Animals never do this. } if NotAnAnimal( NPC ) then begin NPC := LocatePilot( NPC ); Item := NPC^.InvCom; while ( Item <> Nil ) and not UsedSomething do begin I2 := Item^.Next; if ( Item^.G = GG_Consumable ) and ( Item^.Stat[ STAT_FoodEffectType ] <> 0 ) then begin if ( Item^.Stat[ STAT_FoodEffectType ] = FET_CureStatus ) and HasStatus( NPC , Item^.Stat[ STAT_FoodEffectMod ] ) then begin EatItem( GB , NPC , Item ); UsedSomething := True; end else if ( ( Item^.Stat[ STAT_FoodEffectType ] = FET_Healing ) or ( Item^.Stat[ STAT_FoodEffectType ] = FET_Regeneration ) ) and ( PercentDamaged( NPC ) < 90 ) then begin EatItem( GB , NPC , Item ); UsedSomething := True; end; end; Item := I2; end; end; AIUseInventory := UsedSomething; end; Procedure GetAIInput( Mek: GearPtr; GB: GameBoardPtr ); { MEK belongs to the computer team. Decide upon } { a course of action for it here. } var O: LongInt; begin { Before processing orders, check jump time, since the AI is } { stupid and will crash as often as possible. } O := NAttValue( Mek^.NA , NAG_Action , NAS_TimeLimit ); if ( O > 0 ) and (( O + CalcMoveTime( Mek , GB ) ) >= GB^.ComTime ) then begin { This is as far as this jump can go. Better land now. } PrepAction( GB , Mek , NAV_Stop ); { Switch to another movemode, since jumping will } { require some time to recharge. } ShiftGears( GB , Mek , False ); end; if Mek^.G = GG_Prop then begin GetPropInput( GB , Mek ); end else if not IsMasterGear( Mek ) then begin { If this is not a master gear, just wait. } SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ClicksPerRound ); end else if not AIUseInventory( mek , GB ) then begin { Otherwise, check the orders of the NPC unit, and branch accordingly. } O := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Orders ); case O of NAV_GotoSpot: GOTO_SPOT( Mek , GB ); NAV_SeekEdge: GOTO_EDGE( Mek , GB ); NAV_Passive: PASSIVE( Mek , GB ); NAV_RunAway: RUNAWAY( Mek , GB ); NAV_Follow: FOLLOW( Mek , GB ); else Seek_And_Destroy( Mek , GB ); end; if ( GB^.ComTime > NAttValue( Mek^.NA , NAG_EpisodeData , NAS_ChatterRecharge ) ) and ( Random( 80 ) = 1 ) and IsSafeArea( GB ) then begin SayCombatTaunt( GB , Mek , 'CHAT_SAFE' ); end; end; end; Procedure ConfusedInput( Mek: GearPtr; GB: GameBoardPtr ); { The mek is not in full control of its faculties. Move randomly. } var P: Point; CD: Integer; begin { Confused props just sit there and do nothing... much like regular props. } if Mek^.G = GG_Prop then Exit; { Determine current facing and position. } P := GearCurrentLocation( Mek ); CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); { If the current direction of travel doesn't lead off the map, } { and isn't blocked, just move foreword. } if OnTheMap( GB , P.X + AngDir[ CD , 1 ] , P.Y + AngDir[ CD , 2 ] ) and ( Random( 3 ) <> 1 ) and not MoveBlocked( Mek , GB ) then begin if Random( 2 ) = 1 then begin PrepAction( GB , Mek , NAV_FullSpeed ); end else begin PrepAction( GB , Mek , NAV_NormSpeed ); end; end else begin if Random( 2 ) = 1 then begin PrepAction( GB , Mek , NAV_TurnRight ); end else begin PrepAction( GB , Mek , NAV_TurnLeft ); end; end; end; Procedure BrownianMotion( GB: GameBoardPtr ); { Go through all the clouds and flames on the map, and update } { them. A few rules... } { 1. No more than one cloud and one flame per tile. } { 2. Clouds will move if there's a free square. } { 3. Fires will burn terrain and ay reproduce to free squares. } { 4. Both fires and clouds will do EFFECT if that SAtt is defined. } Procedure MetaEffect( MT: GearPtr; X,Y: Integer ); { This metaterrain has some effect. Better handle that } { here. } var fx,desc: String; N,T: Integer; Target: GearPtr; begin fx := SAttValue( MT^.SA , 'EFFECT' ); desc := SAttValue( MT^.SA , 'FX_DESC' ); N := NumGearsXY( GB , X , Y ); for t := 1 to N do begin target := FindGearXY( GB , X , Y , T ); if ( Target <> MT ) and NotDestroyed( Target ) then EffectFrontEnd( GB , Target , fx , desc ); end; end; var CloudMap: Array [1..MaxMapWidth,1..MaxMapWidth] of GearPtr; FireMap: Array [1..MaxMapWidth,1..MaxMapWidth] of GearPtr; ElseMap: Array [1..MaxMapWidth,1..MaxMapWidth] of Boolean; M,M2: GearPtr; P: Point; X,Y,T: Integer; begin { Start by filling out the cloudmap and the firemap. } for x := 1 to MaxMapWidth do begin for y := 1 to MaxMapWidth do begin CloudMap[ X , Y ] := Nil; FireMap[ X , Y ] := Nil; ElseMap[ X , Y ] := False; end; end; { Look for clouds and flames and store them in the map. } M := GB^.Meks; while M <> Nil do begin M2 := M^.Next; P := GearCurrentLocation( M ); if OnTheMap( GB , P.X , P.Y ) then begin if ( M^.G = GG_Metaterrain ) and ( M^.S = GS_MetaCloud ) then begin { If two clouds exist on the same tile, } { the one with the greatest duration wins. } { Remember that a negative duration really } { means infinite duration. } if CloudMap[ P.X , P.Y ] = Nil then begin CloudMap[ P.X , P.Y ] := M; end else if M^.Stat[ STAT_CloudDuration ] < 0 then begin RemoveGear( GB^.Meks , CloudMap[ P.X , P.Y ] ); CloudMap[ P.X , P.Y ] := M; end else if ( M^.Stat[ STAT_CloudDuration ] > CloudMap[ P.X , P.Y ]^.Stat[ STAT_CloudDuration ] ) and ( CloudMap[ P.X , P.Y ]^.Stat[ STAT_CloudDuration ] > 0 ) then begin RemoveGear( GB^.Meks , CloudMap[ P.X , P.Y ] ); CloudMap[ P.X , P.Y ] := M; end else begin RemoveGear( GB^.Meks , M ); end; end else if ( M^.G = GG_Metaterrain ) and ( M^.S = GS_MetaFire ) then begin { Fires are simpler than clouds. Just one } { fire per map square, no exceptions. } if FireMap[ P.X , P.Y ] = Nil then begin FireMap[ P.X , P.Y ] := M; end else begin RemoveGear( GB^.Meks , M ); end; end else begin ElseMap[ P.X , P.Y ] := True; end; end; M := M2; end; { Finally, go through each cloud/flame one more time and update } { them. } for X := 1 to MaxMapWidth do begin for Y := 1 to MaxMapWidth do begin { If there's a cloud here, deal with it. } if CloudMap[ X , Y ] <> Nil then begin { If the cloud has an effect, apply that } { effect to all models in the same tile. } if ( SAttValue( CloudMap[ X , Y ]^.SA , 'EFFECT' ) <> '' ) and ElseMap[ X , Y ] then begin MetaEffect( CloudMap[ X , Y ] , X , Y ); end; { If the cloud has a duration, deal with it. } if CloudMap[ X , Y ]^.Stat[ STAT_CloudDuration ] > 0 then Dec( CloudMap[ X , Y ]^.Stat[ STAT_CloudDuration ] ); if CloudMap[ X , Y ]^.Stat[ STAT_CloudDuration ] = 0 then begin RemoveGear( GB^.Meks , CloudMap[ X , Y ] ); Screen_Needs_Redraw := True; { If the cloud's time isn't finished, } { try moving it around. } end else begin { Pick a random spot next to the cloud. } { If it's empty and non-blocking, move } { the cloud there. } P.X := X + Random( 2 ) - Random( 2 ); P.Y := Y + Random( 2 ) - Random( 2 ); if OnTheMap( GB , P.X , P.Y ) and ( CloudMap[ P.X , P.Y ] = Nil ) and not TileBlocksLOS( GB , P.X , P.Y , 5 ) then begin CloudMap[ P.X , P.Y ] := CloudMap[ X , Y ]; CloudMap[ X , Y ] := Nil; SetNAtt( CloudMap[ P.X , P.Y ]^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( CloudMap[ P.X , P.Y ]^.NA , NAG_Location , NAS_Y , P.Y ); Screen_Needs_Redraw := True; end; end; end; { if CloudMap... } if FireMap[ X , Y ] <> Nil then begin { Handle the effects of fire here. } if ( SAttValue( FireMap[ X , Y ]^.SA , 'EFFECT' ) <> '' ) and ElseMap[ X , Y ] then begin MetaEffect( FireMap[ X , Y ] , X , Y ); end; { Fire needs smoke. If there's no smoke, } { probably add some. } if ( Random( 3 ) <> 1 ) and ( CloudMap[ X , Y ] = Nil ) then begin M := LoadNewSTC( 'SMOKE-1' ); if M <> Nil then begin M^.Scale := GB^.Scale; AppendGear( GB^.Meks , M ); M^.Stat[ STAT_CloudDuration ] := RollStep( 2 ); SetNAtt( M^.NA , NAG_Location , NAS_X , X ); SetNAtt( M^.NA , NAG_Location , NAS_Y , Y ); end; end; { Check to see if the fire will spread. } for t := 0 to 7 do begin P.X := X + AngDir[ t , 1 ]; P.Y := Y + AngDir[ t , 2 ]; if OnTheMap( GB , P.X , P.Y ) and TerrMan[ TileTerrain( GB, P.X , P.Y ) ].Flammable and ( FireMap[ P.X , P.Y ] = Nil ) and ( Random( 15 ) = 1 ) then begin M := LoadNewSTC( 'FIRE-1' ); if M <> Nil then begin AppendGear( GB^.Meks , M ); M^.Scale := GB^.Scale; SetNAtt( M^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( M^.NA , NAG_Location , NAS_Y , P.Y ); Screen_Needs_Redraw := True; end; end; end; { Check to see if the fire will go out. } if ( Random( 20 ) = 1 ) or not TerrMan[ TileTerrain( GB , X , Y ) ].Flammable then begin RemoveGear( GB^.Meks , FireMap[ X , Y ] ); Screen_Needs_Redraw := True; end else if Random( TerrMan[ TileTerrain( GB , X , Y ) ].DMG + 2 ) = 1 then begin DestroyTerrain( GB , X , Y ); if ( Random( 10 ) = 1 ) then RemoveGear( GB^.Meks , FireMap[ X , Y ] ); Screen_Needs_Redraw := True; end; end; { if FireMap... } end; {for Y} end; {for X} end; Procedure HandleEncounters( GB: GameBoardPtr ); { It's time to move encounters. } var M,PC: GearPtr; PC_Pos,Enc_Pos: Point; trigger: String; Ord,GX,GY,HM,MM,Best,FinalD,D: Integer; begin M := GB^.Meks; PC := GG_LocatePC( GB ); PC_Pos := GearCurrentLocation( PC ); HM := 0; while M <> Nil do begin if ( M^.G = GG_MetaTerrain ) and ( M^.S = GS_MetaEncounter ) and ( M^.STAT[ STAT_MetaVisibility ] >= 0 ) then begin { Move the encounter as appropriate. } Enc_Pos := GearCurrentLocation( M ); Ord := NAttValue( M^.NA , NAG_EpisodeData , NAS_Orders ); if M^.Stat[ STAT_Altitude ] <= 0 then begin MM := HMMM_EncounterGround; end else begin MM := MM_Fly; end; SetNAtt( M^.NA , NAG_Action , NAS_MoveMode , MM ); if Random( 100 ) < M^.Stat[ STAT_EncounterMove ] then begin if ( Ord <> NAV_Passive ) then begin if Ord = NAV_GotoSpot then begin GX := NAttValue( M^.NA , NAG_Location , NAS_GX ); GY := NAttValue( M^.NA , NAG_Location , NAS_GY ); HM := GetHotMap( GB , GX + ( GY * ( GB^.MAP_Width + 1 ) ) , MM , ORD_SeekSpot ); end else if Ord = NAV_SeekEdge then begin GX := NAttValue( M^.NA , NAG_EpisodeData , NAS_ATarget ); HM := GetHotMap( GB , GX , MM , ORD_SeekEdge ); end else if Ord = NAV_RunAway then begin HM := GetHotMap( GB , 0 , MM , ORD_SeekAnyEdge ); end else if Ord = NAV_Follow then begin GX := NAttValue( M^.NA , NAG_EpisodeData , NAS_ATarget ); HM := GetHotMap( GB , GX , MM , ORD_SeekSingleModel ); end else begin HM := GetHotMap( GB , PC_Pos.X + ( PC_Pos.Y * ( GB^.MAP_Width + 1 ) ) , MM , ORD_SeekSpot ); end; { We have a hot map. Move in the most advantageous direction. } Best := HotMap[ HM , Enc_pos.X , Enc_Pos.Y ]; FinalD := -1; for D := 0 to 7 do begin GX := Enc_Pos.X + AngDir[ D , 1 ]; GY := Enc_Pos.Y + AngDir[ D , 2 ]; if OnTheMap( GB , GX , GY ) and ( HotMap[ HM , GX , GY ] < Best ) then begin Best := HotMap[ HM , GX , GY ]; FinalD := D; end; end; if FinalD <> -1 then begin Enc_Pos.X := Enc_Pos.X + AngDir[ FinalD , 1 ]; Enc_Pos.Y := Enc_Pos.Y + AngDir[ FinalD , 2 ]; SetNAtt( M^.NA , NAG_Location , NAS_X , Enc_Pos.X ); SetNAtt( M^.NA , NAG_Location , NAS_Y , Enc_Pos.Y ); end; end else begin { if not Passive... } GX := Enc_Pos.X + Random( 2 ) - Random( 2 ); GY := Enc_Pos.Y + Random( 2 ) - Random( 2 ); if OnTheMap( GB , GX , GY ) and not MovementBlocked( M , GB , Enc_Pos.X , Enc_Pos.Y , GX , GY ) then begin Enc_Pos.X := GX; Enc_Pos.Y := GY; SetNAtt( M^.NA , NAG_Location , NAS_X , Enc_Pos.X ); SetNAtt( M^.NA , NAG_Location , NAS_Y , Enc_Pos.Y ); end; end; end; { if random(100) < EncounterMove } { Perform a vision check to see if this encounter will be spotted } { by the player. } VisionCheck( GB , M ); { If the PC and an encounter share the same space, the ATTACK script } { is triggered. } if ( Abs( Enc_Pos.X - PC_Pos.X ) <= 1 ) and ( Abs( Enc_Pos.Y - PC_Pos.Y ) <= 1 ) and ( ( NAttValue( M^.NA , NAG_Narrative , NAS_ScriptActivatedTimer ) + 5 ) < GB^.ComTime ) and (( GB^.Scene = Nil ) or ( NAttValue( GB^.Scene^.NA , NAG_SceneData , NAS_EncounterRecharge ) < GB^.ComTime )) then begin trigger := 'ATTACK'; TriggerGearScript( GB , M , trigger ); if GB^.Scene <> Nil then SetNAtt( GB^.Scene^.NA , NAG_SceneData , NAS_EncounterRecharge , GB^.ComTime + Standard_Encounter_Recharge ); { Only one encounter script can be triggered per iteration. } break; end else if ( HM > 0 ) and ( HotMap[ HM , Enc_Pos.X , Enc_Pos.Y ] = 0 ) then begin SetTrigger( GB , 'ENCGOAL' + BStr( M^.Stat[ STAT_Destination ] ) ); end; end; M := M^.Next; end; end; Function MOVE_MODEL_TOWARDS_SPOT( Mek: GearPtr; GB: GameBoardPtr; GX,GY: Integer ): Boolean; { A new life for an old procedure. } var HM,X,Y: Integer; begin { Locate all the values we're gonna need. } X := NAttValue( Mek^.NA , NAG_Location , NAS_X ); Y := NAttValue( Mek^.NA , NAG_Location , NAS_Y ); if Random( 3 ) = 1 then begin SelectMoveMode( Mek , GB ); end; { If we have reached the spot where we want to go, } { its time to seek new orders. } if ( GX = X ) and ( GY = Y ) then begin MOVE_MODEL_TOWARDS_SPOT := False; { If we're right next to the target but it's blocked, exit false. } end else if ( Abs( GX - X ) <= 1 ) and ( Abs( GY - Y ) <= 1 ) and MovementBlocked( Mek , GB , X , Y , GX , GY ) then begin MOVE_MODEL_TOWARDS_SPOT := False; end else begin HM := GetHotMap( GB , GX + ( GY * ( GB^.MAP_Width + 1 ) ) , HotMoveMode( Mek ) , ORD_SeekSpot ); { If there's no way from here to the desired location, exit FALSE. } if HotMap[ HM , X , Y ] > 1000 then Exit( False ); { Otherwise attempt to move. } MOVE_MODEL_TOWARDS_SPOT := XMoveTowardsGoal( GB , Mek , HM , 0 , 0 ); end; end; end. gearhead-2-0.701/arenacfe.pp000066400000000000000000001307401321074026100155260ustar00rootroot00000000000000unit ArenaCFE; { The Arena Combat Front End. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale; Procedure Monologue( GB: GameBoardPtr; NPC: GearPtr; Msg: String ); Procedure CombatDisplay( GB: GameBoardPtr ); Procedure BeginTurn( GB: GameBoardPtr; M: GearPtr ); Procedure AttackerFrontEnd( GB: GameBoardPtr; Attacker,Weapon: GearPtr; X,Y,Z,AtOp: Integer ); Procedure AttackerFrontEnd( GB: GameBoardPtr; Attacker,Weapon,Target: GearPtr; AtOp: Integer ); Procedure EffectFrontEnd( GB: GameBoardPtr; Target: GearPtr; FX_String,FX_Desc: String ); Procedure MassEffectFrontEnd( GB: GameBoardPtr; FX_String,FX_Desc: String ); Procedure StatusEffectCheck( GB: GameBoardPtr ); Procedure RandomExplosion( GB: GameBoardPtr ); Procedure AdvanceGameClock( GB: GameBoardPtr; BeQuick,GetHungry: Boolean ); Procedure QuickTime( GB: GameBoardPtr; Time: LongInt ); Procedure TransitTime( GB: GameBoardPtr; Time: LongInt ); Procedure DisplayConsoleHistory( GB: GameBoardPtr ); Procedure SayCombatTaunt( GB: GameBoardPtr; NPC: GearPtr; Msg_Label: String ); Procedure AI_Eject( Mek: GearPtr; GB: GameBoardPtr ); Procedure AI_Surrender( GB: GameBoardPtr; Mek: GearPtr ); Function MightSurrender( GB: GameBoardPtr; NPC: GearPtr ): Boolean; Procedure DoTaunt( GB: GameBoardPtr; Attacker,Target: GearPtr ); Procedure DoVerbalAttack( GB: GameBoardPtr; Attacker,Target: GearPtr ); Procedure ResolveAfterEffects( GB: GameBoardPtr ); implementation uses ability,effects,gearutil,ghchars,ghweapon,rpgdice,texutil,movement, ui4gh,sysutils,description,action,ghmodule,backpack, {$IFDEF ASCII} vidmap,vidgfx,vidinfo; {$ELSE} sdlgfx,sdlmap,sdlinfo,sdl; {$ENDIF} var NPC_Chatter_Standard: SAttPtr; Redraw_GB: GameBoardPtr; Procedure BattleMapDisplay( GB: GameBoardPtr ); { Redraw the display. } begin ClrScreen; {$IFNDEF ASCII} ScrollMap( GB ); {$ENDIF} RenderMap( GB ); RedrawConsole; {$IFNDEF ASCII} if Display_Mini_Map then DisplayMiniMap( GB ); if Focused_On_Mek <> Nil then DisplayModelStatus( GB , Focused_On_Mek , ZONE_PCStatus ); if OnTheMap( GB , tile_X , tile_y ) and ( tile_z >= LoAlt ) and ( tile_z <= ( HiAlt + 1 ) ) and ( model_map[ Tile_x , tile_y , tile_z ] <> Nil ) then begin QuickModelStatus( GB , model_map[ Tile_x , tile_y , tile_z ] ); end; InfoBox( ZONE_Clock ); {$ELSE} if Focused_On_Mek <> Nil then QuickModelStatus( GB , Focused_On_Mek ); ClockBorder; {$ENDIF} if Tactics_Turn_In_Progess then begin TacticsTimeInfo( GB ); end else begin CMessage( TimeString( GB^.ComTime ) , ZONE_Clock , StdWhite ); end; {$IFNDEF ASCII} Render_Off_Map_Models; {$ENDIF} end; Procedure WorldMapDisplay( GB: GameBoardPtr ); { Redraw the world map display. } var PC: GearPtr; begin ClrScreen; InfoBox( ZONE_Caption ); InfoBox( ZONE_SubCaption ); CMessage( GearName( GB^.Scene ) , ZONE_Caption , StdWhite ); CMessage( TimeString( GB^.ComTime ) , ZONE_SubCaption , StdWhite ); PC := GB^.Meks; while ( PC <> Nil ) and ( NAttValue( PC^.NA , NAG_Location , NAS_Team ) <> NAV_DefPlayerTeam ) do PC := PC^.Next; if PC <> Nil then RenderWorldMap( GB , PC , NAttValue(PC^.NA , NAG_Location , NAS_X ) , NAttValue(PC^.NA , NAG_Location , NAS_Y ) ); RedrawConsole; end; Procedure CombatDisplay( GB: GameBoardPtr ); { Redraw the display. } begin if ( GB = Nil ) then begin ClrScreen; end else if ( GB^.Scene <> Nil ) and ( GB^.Scene^.G = GG_World ) then begin WorldMapDisplay( GB ); end else begin BattleMapDisplay( GB ); end; end; Procedure ComDispRedraw(); { A very simple redraw procedure. } begin CombatDisplay( Redraw_GB ); end; Procedure BeginTurn( GB: GameBoardPtr; M: GearPtr ); { A player-controlled model is starting their tactics turn. Let the player } { know whose turn it is, and away to go. } const Scroll_Step = 0.3; var {$IFNDEF ASCII} P: Point; {$ENDIF} A: Char; msg: String; begin msg := ReplaceHash( MsgString( 'BEGIN_TACTICS_TURN' ) , PilotName( M ) ); FocusOn( M ); repeat CombatDisplay( GB ); InfoBox( ZONE_Caption ); GameMsg( msg , ZONE_Caption , InfoHilight ); DoFlip; A := RPGKey; until IsMoreKey( A ); end; Function DisplayAnnouncements( N: Integer ): Boolean; { Display all the announcements stored for sequence slice N. } { Return TRUE if an announcement was found, or FALSE otherwise. } var L: String; A: SAttPtr; MessageFound: Boolean; begin A := ATTACK_History; MessageFound := False; L := 'ANNOUNCE_' + BStr( N ) + '_'; while A <> Nil do begin if HeadMatchesString( L , A^.Info ) then begin MessageFound := True; DialogMsg( RetrieveAString( A^.Info ) ); end; A := A^.Next; end; DisplayAnnouncements := MessageFound; end; Procedure ProcessAnimations( GB: GameBoardPtr; var AnimList: GearPtr ); { Display all the queued animations, deleting them as we go along. } var AnimOb,A2: GearPtr; DelayThisFrame,PointDelay: Boolean; begin { Keep processing until we run out of animation objects. } while AnimList <> Nil do begin { Erase all current image overlays. } ClearOverlays; AnimOb := AnimList; { Assume there'll be no animation delay, unless } { otherwise requested. } DelayThisFrame := False; while AnimOb <> Nil do begin A2 := AnimOb^.Next; { Call a routine based upon the type of } { animation requested. } case AnimOb^.S of GS_Shot: PointDelay := ProcessShotAnimation( GB , AnimList , AnimOb ) or DelayThisFrame; GS_DamagingHit: PointDelay := ProcessPointAnimation( GB , AnimList , AnimOb ); GS_ArmorDefHit: PointDelay := ProcessPointAnimation( GB , AnimList , AnimOb ); GS_Parry: PointDelay := ProcessPointAnimation( GB , AnimList , AnimOb ); GS_Dodge: PointDelay := ProcessPointAnimation( GB , AnimList , AnimOb ); GS_Backlash: PointDelay := ProcessPointAnimation( GB , AnimList , AnimOb ); GS_AreaAttack: PointDelay := ProcessPointAnimation( GB , AnimList , AnimOb ); { If no routine was found to deal with the animation } { requested, just delete the gear. } else RemoveGear( AnimList , AnimOb ); end; DelayThisFrame := DelayThisFrame or PointDelay; { Move to the next animation. } AnimOb := A2; end; { Delay the animations, if appropriate. } if DelayThisFrame then begin CombatDisplay( GB ); DoFlip; {$IFDEF ASCII} if ( FrameDelay > 0 ) then Sleep(FrameDelay); {$ELSE} if ( FrameDelay > 0 ) then SDL_Delay(FrameDelay); {$ENDIF} end; end; ClearOverlays; { CombatDisplay( GB ); DoFlip;} end; Function DisplayEffectAnimations( GB: GameBoardPtr; N: Integer ): Boolean; { Display all the animations stored for sequence slice N. } { Return TRUE if an animation was found, or FALSE otherwise. } var A: SAttPtr; T: Integer; AnimFound: Boolean; AnimList,AnimItem: GearPtr; AnimLabel,AnimCode: String; begin A := ATTACK_History; AnimFound := False; AnimList := Nil; AnimLabel := SATT_Anim_Direction + BStr( N ) + '_'; { Start by creating the animation list. } while A <> Nil do begin if HeadMatchesString( AnimLabel , A^.Info ) then begin AnimFound := True; { Insert animation handling code here. } AnimItem := AddGear( AnimList , Nil ); AnimCode := RetrieveAString( A^.Info ); AnimItem^.S := ExtractValue( AnimCode ); T := 1; while ( AnimCode <> '' ) and ( T <= NumGearStats ) do begin AnimItem^.Stat[ T ] := ExtractValue( AnimCode ); Inc( T ); end; end; A := A^.Next; end; { Process each animation. } ProcessAnimations( GB , AnimList ); DisplayEffectAnimations := AnimFound; end; Procedure Display_Effect_History( GB: GameBoardPtr ); { Display all the messages stored by the attack routines and show all the } { animations requested. } var N: Integer; NoAnnounce,NoAnim: Boolean; begin N := 0; { Just keep going until we reach an iteration at which there are no more animations and } { no more announcements to display. That's how we know that we're finished. } repeat NoAnnounce := Not DisplayAnnouncements( N ); NoAnim := not DisplayEffectAnimations( GB , N ); Inc( N ); { Pump the events after the loop to keep Windows from doing the "Not Responding" } { thing during long enemy turns- thanks Buffered. } {$IFNDEF ASCII} if ( N mod 3 ) = 0 then SDL_PumpEvents(); {$ENDIF} until NoAnnounce and NoAnim; end; Function CloneMap( GB: GameBoardPtr ): GameBoardPtr; { Copy the map and all its contents. } var FakeGB: GameBoardPtr; mek,FakeMek: GearPtr; begin FakeGB := NewMap( GB^.Map_Width , GB^.Map_Height ); FakeGB^.ComTime := GB^.ComTime; FakeGB^.Scale := GB^.Scale; FakeGB^.map := GB^.Map; FakeGB^.Scene := GB^.Scene; FakeGB^.Camp := GB^.Camp; mek := GB^.Meks; while mek <> Nil do begin FakeMek := CloneGear( Mek ); AppendGear( FakeGB^.Meks , FakeMek ); Mek := Mek^.Next; end; CloneMap := FakeGB; end; Procedure DisposeMapClone( FakeGB: GameBoardPtr ); { Get rid of the fake map. } begin FakeGB^.Scene := Nil; DisposeMap( FakeGB ); end; Procedure AttackerFrontEnd( GB: GameBoardPtr; Attacker,Weapon: GearPtr; X,Y,Z,AtOp: Integer ); { This is a front end for the ATTACKER procedures. It calls those } { procedures, and also informs the player of what's going on } { both textually (description) and visually (graphics). } var FakeGB: GameBoardPtr; begin { Generate a fake gameboard to be used for screen output. } FakeGB := CloneMap( GB ); { Actually do the attack. } DoAttack(GB,Weapon,Nil,X,Y,Z,AtOp); { Report the effect of the attack. } Display_Effect_History( FakeGB ); DisposeMapClone( FakeGB ); { Resolve any crashes resulting from the attack. } ResolveAfterEffects( GB ); { AT the end, redisplay the map. } CombatDisplay( GB ); end; Procedure AttackerFrontEnd( GB: GameBoardPtr; Attacker,Weapon,Target: GearPtr; AtOp: Integer ); { This is a front end for the ATTACKER procedures. It calls those } { procedures, and also informs the player of what's going on } { both textually (description) and visually (graphics). } var FakeGB: GameBoardPtr; begin { Generate a fake gameboard to be used for screen output. } FakeGB := CloneMap( GB ); { Actually do the attack. } DoAttack(GB,Weapon,Target,0,0,0,AtOp); { Report the effect of the attack. } Display_Effect_History( FakeGB ); DisposeMapClone( FakeGB ); { Resolve any crashes resulting from the attack. } ResolveAfterEffects( GB ); { AT the end, redisplay the map. } CombatDisplay( GB ); end; Procedure EffectFrontEnd( GB: GameBoardPtr; Target: GearPtr; FX_String,FX_Desc: String ); { An effect string has just been triggered. Call the effect handler, } { then display the outcome for the user. } begin HandleEffectString( GB , Target , FX_String , FX_Desc ); Display_Effect_History( GB ); { Resolve any crashes resulting from the effect. } ResolveAfterEffects( GB ); end; Procedure MassEffectFrontEnd( GB: GameBoardPtr; FX_String,FX_Desc: String ); { An effect string has just been triggered. Call the effect handler, } { then display the outcome for the user. } begin MassEffectString( GB , FX_String , FX_Desc ); Display_Effect_History( GB ); { Resolve any crashes resulting from the effect. } ResolveAfterEffects( GB ); end; Procedure RandomExplosion( GB: GameBoardPtr ); { Stick a random explosion somewhere on the map. This procedure is used by the } { BOMB ASL command. } var X,Y: Integer; begin X := Random( GB^.MAP_Width ) + 1; Y := Random( GB^.MAP_HEIGHT ) + 1; Explosion( GB , X , Y , 5 , 8 ); { Report the effect of the attack. } Display_Effect_History( GB ); { Resolve any crashes resulting from the effect. } ResolveAfterEffects( GB ); end; Procedure StatusEffectCheck( GB: GameBoardPtr ); { Check all status effects, removing those which have expired } { and performing effects for those which haven't. } { This will also deal with a mecha's OVERLOAD condition, since } { I want the count decremented every 3 minutes or so and it } { would be inefficient to loop through the list twice. } var M: GearPtr; FX,FX2: NAttPtr; begin M := GB^.Meks; while M <> Nil do begin if GearActive( M ) and OnTheMap( GB , M ) then begin FX := M^.NA; while FX <> Nil do begin FX2 := FX^.Next; if ( FX^.G = NAG_StatusEffect ) and ( FX^.S >= 1 ) and ( FX^.S <= Num_Status_FX )then begin if SX_Effect_String[ FX^.S ] <> '' then begin EffectFrontEnd( GB , M , SX_Effect_String[ FX^.S ] , MSgString( 'Status_FXDesc' + BStr( FX^.S ) ) ); end; if ( FX^.V > 0 ) and ( SX_ResistTarget[ FX^.S ] = -1 ) then begin { Set rate of diminishment } if Random( 2 ) = 1 then Dec( FX^.V ); if FX^.V = 0 then SetNAtt( M^.NA , NAG_StatusEffect , FX^.S , 0 ); end else if ( FX^.V > 0 ) and ( SX_ResistTarget[ FX^.S ] > 0 ) and ( RollStep( SkillValue( M , NAS_Toughness , STAT_Ego ) ) > SX_ResistTarget[ FX^.S ] ) then begin { Diminishment determined by RESISTANCE } Dec( FX^.V ); if FX^.V = 0 then SetNAtt( M^.NA , NAG_StatusEffect , FX^.S , 0 ); end; end; FX := FX2; end; end; M := M^.Next; end; end; Procedure CyberneticsCheck( PC: GearPtr ); { Update the cybernetic trauma score for the PC. } const Num_Disfunction = 12; Dis_Index: Array [1..Num_Disfunction] of Byte = ( 6,7,8,9,10, 11,12,13,14,15, 16,17 ); Dis_Cost: Array [1..Num_Disfunction] of Byte = ( 30,35,45,50,55, 60,65,70,80,85, 90,95 ); var TT: Integer; { Total Trauma } SC: GearPtr; { Sub-Components of PC; looking for cyberware. } N: Integer; { Number of implants. } D: Integer; { Disfunction # } begin { To start with, add up all the trauma points the PC has. } TT := 0; N := 0; SC := PC^.SubCom; while SC <> Nil do begin if ( SC^.G = GG_Modifier ) and ( SC^.V = GV_CharaModifier ) then begin TT := TT + TraumaValue( SC ); end; SC := SC^.Next; end; { Reduce the total trauma by Ego/4, and reduce further if Cybertech talent known. } TT := TT - ( CStat( PC , STAT_Ego ) div 4 ) - Random( 2 ); if HasTalent( PC , NAS_Extropian ) then TT := TT - 5; { If there is any trauma, the PC must make a skill roll against it. } if TT > 0 then begin AddNAtt( PC^.NA , NAG_Condition , NAS_CyberTrauma , 1 ); if RollStep( SkillValue( PC , NAS_Toughness , STAT_Ego ) ) < ( TT + 5 ) then AddNAtt( PC^.NA , NAG_Condition , NAS_CyberTrauma , 1 ); { If the PC has enough trauma points to consider } { getting a disfunction, deal with that now. } if ( NAttValue( PC^.NA , NAG_Condition , NAS_CyberTrauma ) > 72 ) and ( Random( 3 ) = 1 ) then begin { Select a disfunction at random. The PC might } { get this if he doesn't already have it and } { if it's cheap enough. } D := Random( Num_Disfunction ) + 1; if ( NAttValue( PC^.NA , NAG_StatusEffect , Dis_Index[ D ] ) = 0 ) and ( Random( NAttValue( PC^.NA , NAG_Condition , NAS_CyberTrauma ) ) > Dis_Cost[ D ] ) then begin SetNAtt( PC^.NA , NAG_StatusEffect , Dis_Index[ D ] , -1 ); SetNAtt( PC^.NA , NAG_Condition , NAS_CyberTrauma , NAttValue( PC^.NA , NAG_Condition , NAS_CyberTrauma ) div 2 ); DialogMsg( ReplaceHash( MsgString( 'Disfunction_' + BStr( D ) ) , GearName( PC ) ) ); end; end; end; end; Procedure RegenerationCheck( MList: GearPtr; BeQuick,GetHungry: Boolean ); { Go through MList and all siblings and all children. Any gears } { found which are of type MEAT will recover one point of damage, } { if damaged. } { If BEQUICK, don't do a cybernetics check because the PC doesn't have any chance to } { go see a doctor. } const STAMINA_CHANCE = 30; { Determines speed of Stamina/Mental recovery. } var MAT,Drain,Recovery,N,Morale: Integer; PCTeam,CanRegen: Boolean; begin while MList <> Nil do begin PCTeam := ( NAttValue( MList^.NA , NAG_Location , NAS_Team ) = 1 ) and ( MList^.G = GG_Character ); if PCTeam then Morale := NAttValue( MList^.NA , NAG_Condition , NAS_MoraleDamage ); CanRegen := NAttValue( MList^.NA , NAG_StatusEffect , NAS_Anemia ) = 0; { Whether or not a gear can regenerate is determined } { by its material. } MAT := NAttValue( MList^.NA , NAG_GearOps , NAS_Material ); if ( MAT < 0 ) or ( MAT > NumMaterial ) then MAT := 0; if MAT_Regenerate[ MAT ] and NotDestroyed( MList ) and CanRegen then begin { If there's any HP damage, regenerate a point. } if ( NAttValue( MList^.NA , NAG_Damage , NAS_StrucDamage ) > 0 ) and ( Random( 200 ) < GearMaxDamage( MList ) ) then begin AddNAtt( MList^.NA , NAG_Damage , NAS_StrucDamage , -1 ); if PCTeam then AddMoraleDmg( MList , MORALE_HPRegen ); end; { Natural armor heals *MUCH* more slowly than normal HP damage. } if ( NAttValue( MList^.NA , NAG_Damage , NAS_ArmorDamage ) > 0 ) and ( Random( 500 ) < GearMaxArmor( MList ) ) then begin AddNAtt( MList^.NA , NAG_Damage , NAS_ArmorDamage , -1 ); if PCTeam then AddMoraleDmg( MList , MORALE_HPRegen ); end; end; { Also attempt to regenerate SP and MP here. } if MList^.G = GG_Character then begin Drain := NAttValue( MList^.NA , NAG_Condition , NAS_StaminaDown ); if ( Drain > 0 ) and CanRegen then begin Recovery := 0; N := CharStamina( MList ); if N > STAMINA_CHANCE then begin Recovery := N div STAMINA_CHANCE; N := N mod STAMINA_CHANCE; end; if Random( STAMINA_CHANCE ) <= N then Inc( Recovery ); if Recovery > Drain then Recovery := Drain; AddNAtt( MList^.NA , NAG_Condition , NAS_StaminaDown , -Recovery ); if PCTeam and ( Random( 8 ) = 1 ) then AddMoraleDmg( MList , 1 ); end; Drain := NAttValue( MList^.NA , NAG_Condition , NAS_MentalDown ); if ( Drain > 0 ) and CanRegen then begin Recovery := 0; N := CharMental( MList ); if N > STAMINA_CHANCE then begin Recovery := N div STAMINA_CHANCE; N := N mod STAMINA_CHANCE; end; if Random( STAMINA_CHANCE ) <= N then Inc( Recovery ); if Recovery > Drain then Recovery := Drain; AddNAtt( MList^.NA , NAG_Condition , NAS_MentalDown , -Recovery ); if PCTeam and ( Random( 8 ) = 1 ) then AddMoraleDmg( MList , 1 ); end; { Characters also get hungry... } if PCTeam and GetHungry then begin AddNAtt( MList^.NA , NAG_Condition , NAS_Hunger , 1 ); if NAttValue( MList^.NA , NAG_Condition , NAS_Hunger ) > Hunger_Penalty_Starts then begin DialogMsg( ReplaceHash( MsgString( 'REGEN_Hunger' ) , GearName( MList ) ) ); end; { Check for the cyber-disfunctions Depression, } { Rejection, and Irrational ANger here. } if NAttValue( MList^.NA , NAG_StatusEffect , NAS_Rejection ) <> 0 then begin { A character suffering REJECTION earns one extra trauma point per regen check. } AddNAtt( MList^.NA , NAG_Condition , NAS_CyberTrauma , 1 ); end; if NAttValue( MList^.NA , NAG_StatusEffect , NAS_Depression ) <> 0 then begin { A depressed character always loses morale. } AddMoraleDmg( MList , 1 ); end; if NAttValue( MList^.NA , NAG_StatusEffect , NAS_Anger ) <> 0 then begin { An angry character might pick up Villainous reputation. } if Random( 50 ) = 1 then AddReputation( MList , 1 , -1 ); end; { If nothing happened this regen check to make } { the PC feel worse, morale moves one point } { closer to zero. } if ( Random( 2 ) = 1 ) and ( Morale = NAttValue( MList^.NA , NAG_Condition , NAS_MoraleDamage ) ) then begin if Morale > 0 then begin AddNAtt( MList^.NA , NAG_Condition , NAS_MoraleDamage , -1 ); end else if Morale < 0 then begin AddNAtt( MList^.NA , NAG_Condition , NAS_MoraleDamage , 1 ); end; end; { Check the PC's cyberware... } if not BeQuick then CyberneticsCheck( MList ); end; end; { Check the children - InvCom and SubCom. } RegenerationCheck( MList^.InvCom , BeQuick , GetHungry ); RegenerationCheck( MList^.SubCom , BeQuick , GetHungry ); { Move to the next sibling. } MList := MList^.Next; end; end; Procedure ReduceOverload( GB: GameBoardPtr ); { Mecha lose one point of power overload every 10 seconds. } Procedure CheckOverloadAlongPath( M: GearPtr ); begin while M <> Nil do begin { Decrease OVERLOAD by 1 every 10 seconds } if NAttValue( M^.NA , NAG_Condition , NAS_PowerSpent ) > 0 then begin AddNAtt( M^.NA , NAG_Condition , NAS_PowerSpent , -1 ); end; CheckOverloadAlongPath( M^.SubCom ); CheckOverloadAlongPath( M^.InvCom ); M := M^.Next; end; end; begin CheckOverloadAlongPath( GB^.Meks ); end; Procedure AdvanceGameClock( GB: GameBoardPtr; BeQuick,GetHungry: Boolean ); { Increment the game clock and do any checks that need to be } { done. } { Set BEQUICK to TRUE in order to skip the 5min and halfhour triggers. } begin Inc( GB^.ComTime ); if (( GB^.Comtime mod AP_5Minutes ) = 0) and not BeQuick then SetTrigger( GB , TRIGGER_FiveMinutes ); if (( GB^.Comtime mod AP_HalfHour ) = 0) and not BeQuick then SetTrigger( GB , TRIGGER_HalfHour ); if ( GB^.Comtime mod AP_Hour ) = 0 then SetTrigger( GB , TRIGGER_Hour ); if ( GB^.Comtime mod AP_Quarter ) = 0 then SetTrigger( GB , TRIGGER_Quarter ); { Restore lost power every 10 seconds. } if ( GB^.ComTime mod 10 ) = 0 then begin ReduceOverload( GB ); end; { Once every 10 minutes, living gears regenerate. } if ( GB^.ComTime mod AP_10minutes ) = 0 then begin RegenerationCheck( GB^.Meks , BeQuick , GetHungry ); end; { Once every 3 minutes, update the status effects. } if ( GB^.ComTime mod AP_3minutes ) = 97 then StatusEffectCheck( GB ); end; Procedure QuickTime( GB: GameBoardPtr; Time: LongInt ); { Advance time quickly by the specified amount. } begin while Time > 0 do begin Dec( Time ); AdvanceGameClock( GB , True , True ); end; end; Procedure TransitTime( GB: GameBoardPtr; Time: LongInt ); { Advance time quickly by the specified amount. } { During this time the PC will not get hungry. } begin while Time > 0 do begin Dec( Time ); AdvanceGameClock( GB , True , False ); end; end; Procedure DisplayConsoleHistory( GB: GameBoardPtr ); { Display the console history, then restore the display. } begin {$IFDEF ASCII} MoreText( Console_History , MoreHighFirstLine( Console_History ) ); CombatDisplay( GB ); {$ELSE} Redraw_GB := GB; MoreText( Console_History , MoreHighFirstLine( Console_History ), @ComDispRedraw ); {$ENDIF} end; Function GetTauntString( NPC: GearPtr; Msg_Label: String ): String; { Determine an appropriate string for the type of taunt requested. } var MList: SAttPtr; Procedure HarvestMessages( LList: SAttPtr; head: String ); { Look through LList and collect all strings that match HEAD. } var M: SAttPtr; begin M := LList; while M <> Nil do begin if HeadMatchesString( head , M^.Info ) then StoreSAtt( MList , RetrieveAString( M^.Info ) ); M := M^.Next; end; end; var T,V: Integer; msg: String; begin { Start with an error check. } NPC := LocatePilot( NPC ); if NPC = Nil then Exit( '' ); { Initialize our message list to NIL. } MList := Nil; { First, search through NPC itself looking for appropriate messages. } HarvestMessages( NPC^.SA , msg_label ); { Next, pick some contenders from the standard chatter list. } { Only characters with CIDs get this. } HarvestMessages( NPC_Chatter_Standard , msg_label + '_ALL' ); for t := 1 to Num_Personality_Traits do begin V := NAttValue( NPC^.NA , NAG_CharDescription , -T ); if V > 0 then begin HarvestMessages( NPC_Chatter_Standard , msg_label + '_T' + BStr( T ) + '+' ); end else if V < 0 then begin HarvestMessages( NPC_Chatter_Standard , msg_label + '_T' + BStr( T ) + '-' ); end; end; if MList <> Nil then begin msg := SelectRandomSAtt( MList )^.Info; DisposeSAtt( MList ); end else begin msg := ''; end; GetTauntString := msg; end; Procedure SayCombatTaunt( GB: GameBoardPtr; NPC: GearPtr; Msg_Label: String ); { NPC is going to say something... maybe. Search the NPC gear for things to say, } { then search the standard chatter list for things to say, then pick one of them } { and say it. } { Note that this will not nessecarily be an actual taunt. It's more likely to be } { something completely different... but I didn't feel like calling this procedure } { "NPC_Mutters"... } var msg: String; begin { Make sure we have a proper NPC, and not a mecha. } NPC := LocatePilot( NPC ); if NPC = Nil then Exit; { Make sure combat taunts are enabled. } if No_Combat_Taunts then begin SetNAtt( FindRoot( NPC )^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + ( 2500 div CStat( NPC , STAT_Charm ) ) ); Exit; end; { If at least one phrase was found, and the NPC is visible, it can say something. } if NotAnAnimal( NPC ) and ( ( Msg_Label = 'CHAT_EJECT' ) or ( NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) <> 0 ) ) then begin if ( ( Msg_Label = 'CHAT_EJECT' ) or MekVisible( GB , FindRoot( NPC ) ) ) then begin msg := GetTauntString( NPC , Msg_Label ); if msg <> '' then DialogMsg( '[' + GearName( NPC ) + ']: ' + msg ); end; end; { Add the chatter recharge time. } SetNAtt( FindRoot( NPC )^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + ( 2500 div CStat( NPC , STAT_Charm ) ) ); end; Procedure Monologue( GB: GameBoardPtr; NPC: GearPtr; Msg: String ); { NPC is about to deliver a line. } var A: Char; begin NPC := LocatePilot( NPC ); repeat CombatDisplay( GB ); DoMonologueDisplay( GB , NPC , Msg ); DoFlip; A := RPGKey; until IsMoreKey( A ); DialogMsg( '[' + PilotName( NPC ) + ']: ' + Msg ); end; Procedure AI_Eject( Mek: GearPtr; GB: GameBoardPtr ); { This NPC is ejecting from his mecha! } var Pilot: GearPtr; Msg: String; begin { Better set the following triggers. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) ) ); SetTrigger( GB , TRIGGER_UnitEliminated + BStr( NAttValue( Mek^.NA , NAG_EpisodeData , NAS_UID ) ) ); repeat Pilot := ExtractPilot( Mek ); if Pilot <> Nil then begin Msg := GetTauntString( Pilot , 'CHAT_EJECT' ); Monologue( GB , Pilot , Msg ); DialogMsg( ReplaceHash( MsgString( 'EJECT_AI' ) , GearName( Pilot ) ) ); DeployGear( GB , Pilot , False ); end; until Pilot = Nil; end; Procedure AI_Surrender( GB: GameBoardPtr; Mek: GearPtr ); { This NPC is surrendering! } var Msg: String; begin { Better set the following triggers. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) ) ); SetTrigger( GB , TRIGGER_UnitEliminated + BStr( NAttValue( Mek^.NA , NAG_EpisodeData , NAS_UID ) ) ); if NAttValue( Mek^.NA , NAG_Personal , NAS_CID ) <> 0 then begin SetTrigger( GB , TRIGGER_NPCSurrendered + BStr( NAttValue( Mek^.NA , NAG_Personal , NAS_CID ) ) ); end; Msg := GetTauntString( Mek , 'CHAT_SURRENDER' ); Monologue( GB , Mek , Msg ); DialogMsg( ReplaceHash( MsgString( 'SURRENDER_AI' ) , GearName( Mek ) ) ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_SurrenderStatus , NAV_NowSurrendered ); end; Function MightEject( Mek: GearPtr ): Boolean; { Return TRUE if it's possible that MEK might eject given its } { current situation, or FALSE otherwise. } begin MightEject := GearActive( Mek ) and ( ( PercentDamaged( Mek ) < 75 ) or not HasAtLeastOneValidMovemode( Mek ) ); end; Function CapableOfSurrender( GB: GameBoardPtr; NPC: GearPtr ): Boolean; { This model might surrender, sometime, under some circumstances, not necessarily these. } begin CapableOfSurrender := GearActive( NPC ) and AreEnemies(GB,NAttValue(NPC^.NA,NAG_Location,NAS_Team),NAV_DefPlayerTeam) and (NAttValue(NPC^.NA,NAG_EpisodeData,NAS_SurrenderStatus) = 0) and NotAnAnimal( NPC ); end; Function MightSurrender( GB: GameBoardPtr; NPC: GearPtr ): Boolean; { Return TRUE if it's possible that NPC might surrender given its } { current situation, or FALSE otherwise. } begin MightSurrender := CapableOfSurrender( GB , NPC ) and (( CurrentStamina(NPC) = 0 ) or ( GearCurrentDamage( NPC ) < Random( 6 ) )); end; Function HasAtLeastOneValidWeapon( Mek: GearPtr ): Boolean; { Return TRUE if this mecha has at least one weapon capable of being used, } { or FALSE if all of its weapons have been destroyed/run out of ammo/otherwise } { neutralized. } var HALOVW: Boolean; Procedure CheckVWAlongPath( LList: GearPtr ); { Check along this path, recursing through children, looking } { for at least one valid weapon. } begin while ( llist <> Nil ) and not HALOVW do begin if NotDestroyed( llist ) then begin if LList^.G = GG_Weapon then begin { This could be it! It's a weapon, and it's not destroyed... } { The only thing that could hold us back now is ammo. } if ( LList^.S = GS_Ballistic ) or ( LList^.S = GS_Missile ) then begin HALOVW := LocateGoodAmmo( LList ) <> Nil; end else begin { No ammo, but don't need ammo. } HALOVW := True; end; end; if not HALOVW then begin CheckVWAlongPath( llist^.subcom ); CheckVWAlongPath( llist^.invcom ); end; end; llist := llist^.Next; end; end; begin { Assume FALSE until shown TRUE. } HALOVW := False; { Check the subcoms. } CheckVWAlongPath( Mek^.SubCom ); { Return the result. } HasAtLeastOneValidWeapon := HALOVW; end; Function ShouldEject( Mek: GearPtr; GB: GameBoardPtr ): Boolean; { Return TRUE if this mecha should eject, or FALSE otherwise. } var Dmg,PrevDmg,Intimidation,LeaderShip,TeamID: Integer; Team: GearPtr; begin { Error check- members of the PC team never eject. } TeamID := NAttValue( Mek^.NA , NAG_Location , NAS_Team ); if TeamID = NAV_DefPlayerTeam then Exit( False ); { Calculate the Intimidation and Leadership values. } Intimidation := 5; Leadership := TeamSkill( GB , TeamID , NAS_Intimidation , STAT_Ego ); if GB^.Scene <> Nil then begin Team := GB^.Scene^.SubCom; while Team <> Nil do begin if ( Team^.S <> TeamID ) and AreAllies( GB , Team^.S , TeamID ) then begin Dmg := TeamSkill( GB , Team^.S , NAS_Concentration , STAT_Ego ); if Dmg > Leadership then Leadership := Dmg; end else if ( Team^.S <> TeamID ) and AreEnemies( GB , Team^.S , TeamID ) then begin Dmg := TeamSkill( GB , Team^.S , NAS_Intimidation , STAT_Ego ); if Dmg > Intimidation then Intimidation := Dmg; end; Team := Team^.Next; end; end; Dmg := PercentDamaged( Mek ); if not HasAtLeastOneValidWeapon( Mek ) then Dmg := Dmg - 15; PrevDmg := 100 - NAttValue( Mek^.NA , NAG_EpisodeData , NAS_PrevDamage ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_PrevDamage , 100 - DMG ); if MightEject( Mek ) and ( DMG < PrevDmg ) then begin if CurrentMoveRate( GB^.Scene , Mek ) = 0 then Dmg := Dmg - 25; ShouldEject := Dmg < ( Random( 65 ) + RollStep( Intimidation ) - RollStep( Leadership ) ); end else ShouldEject := False; end; Function ShouldSurrender( GB: GameBoardPtr; NPC: GearPtr ): Boolean; { Check to see whether or not NPC should surrender. Surrender should take place } { if the following conditions are met: The NPC has no stamina left, the NPC is } { an enemy of Team1, the NPC has not previously surrendered, the NPC is not an } { animal, } { ...and Team1 can manage an intimidation roll. } { Surrender will be automatic if 50% of a target's limbs are destroyed. } Function BlackKnightSituation: Boolean; { The Black Knight situation happens when 50% or more of a target's limbs are } { disabled. Any rational person would surrender then. It's not just a flesh wound. } var limb: GearPtr; total,d_total: Integer; begin total := 0; d_total := 0; limb := NPC^.SubCom; while limb <> Nil do begin if Limb^.G = GG_Module then begin inc( Total ); if Destroyed( Limb ) then Inc( D_total ); end; limb := limb^.Next; end; BlackKnightSituation := ( Total > 0 ) and ( D_total >= ( Total div 2 ) ); end; var SkRank,NPC_Stamina,Dmg,PrevDmg: Integer; begin if Destroyed( NPC ) then Exit( False ); Dmg := PercentDamaged( NPC ); NPC_Stamina := CurrentStamina(NPC); if NPC_Stamina = 0 then Dmg := Dmg - 5; PrevDmg := 100 - NAttValue( NPC^.NA , NAG_EpisodeData , NAS_PrevDamage ); SetNAtt( NPC^.NA , NAG_EpisodeData , NAS_PrevDamage , 100 - DMG ); if BlackKnightSituation and CapableOfSurrender( GB , NPC ) then begin ShouldSurrender := True; end else if ( DMG < PrevDmg ) and MightSurrender( GB , NPC ) then begin SkRank := TeamSkill( GB , NAV_DefPlayerTeam , NAS_Intimidation , STAT_Ego ); PrevDmg := GearCurrentDamage( NPC ); if PrevDmg < 10 then SkRank := SkRank + 15 - PrevDmg; ShouldSurrender := RollStep( SkRank ) > ( CStat( NPC , STAT_Ego ) + Dmg div 10 ); end else begin ShouldSurrender := False; end; end; Procedure ResolveAfterEffects( GB: GameBoardPtr ); { Check the gameboard for mecha which have either crashed or charged. } { 1. Charge } { 2. Explosions } { 3. Crashes } { 4. Ejection/Surrender } var FakeGB: GameBoardPtr; Mek,Target: GearPtr; FX_Desc: String; V: LongInt; DidExplode: Boolean; begin { Check for charges first. } Mek := GB^.Meks; while Mek <> Nil do begin V := NAttValue( Mek^.NA , NAG_Action , NAS_WillCharge ); if GearActive( Mek ) and ( V <> 0 ) and OnTheMap( GB , Mek ) then begin Target := LocateMekByUID( GB , V ); if ( Target <> Nil ) and NotDestroyed( Target ) and OnTheMap( GB , Target ) then begin { Generate a fake gameboard to be used for screen output. } FakeGB := CloneMap( GB ); DoCharge( GB , Mek , Target ); { Report the effect of the attack. } Display_Effect_History( FakeGB ); DisposeMapClone( FakeGB ); end; SetNAtt( Mek^.NA , NAG_Action , NAS_WillCharge , 0 ); SetNAtt( Mek^.NA , NAG_Action , NAS_ChargeSpeed , 0 ); end; Mek := Mek^.Next; end; { Check for explosions and disappearance now. } Mek := GB^.Meks; while Mek <> Nil do begin V := NAttValue( Mek^.NA , NAG_Action , NAS_WillExplode ); if V > 0 then begin DidExplode := True; { Generate a fake gameboard to be used for screen output. } FakeGB := CloneMap( GB ); DoReactorExplosion( GB , Mek ); { Report the effect of the attack. } Display_Effect_History( FakeGB ); DisposeMapClone( FakeGB ); SetNAtt( Mek^.NA , NAG_Action , NAS_WillExplode , 0 ); end else begin DidExplode := False; end; V := NAttValue( Mek^.NA , NAG_Action , NAS_WillDisappear ); if ( V > 0 ) or DidExplode then begin { Shake down this model at its current location. } ShakeDown( GB , Mek , NAttValue( Mek^.NA , NAG_Location , NAS_X ) , NAttValue( Mek^.NA , NAG_Location , NAS_Y ) ); { Move this model off the map. } SetNAtt( Mek^.NA , NAG_Location , NAS_X , 0 ); { If this is a spontaneous disappearance, print a message. } if not DidExplode then DialogMsg( ReplaceHash( MsgString( 'Model_Disappeared' ) , GearName( Mek ) ) ); SetNAtt( Mek^.NA , NAG_Action , NAS_WillDisappear , 0 ); end; Mek := Mek^.Next; end; { Check for crashes now. } Mek := GB^.Meks; while Mek <> Nil do begin V := NAttValue( Mek^.NA , NAG_Action , NAS_WillCrash ); if ( V > 0 ) and OnTheMap( GB , Mek ) then begin { Generate a fake gameboard to be used for screen output. } FakeGB := CloneMap( GB ); if Mek^.G = GG_Character then begin FX_Desc := MsgString( 'FXDESC_FALL' ); end else begin FX_Desc := MsgString( 'FXDESC_CRASH' ); end; HandleEffectString( GB , Mek , BStr( V ) + ' ' + FX_CauseDamage + ' 10 0 SCATTER' , FX_Desc ); { Report the effect of the attack. } Display_Effect_History( FakeGB ); DisposeMapClone( FakeGB ); SetNAtt( Mek^.NA , NAG_Action , NAS_WillCrash , 0 ); end; Mek := Mek^.Next; end; { Finally, check for ejection and surrender. } Mek := GB^.Meks; while Mek <> Nil do begin V := NAttValue( Mek^.NA , NAG_Action , NAS_MightGiveUp ); if V <> 0 then begin if ( Mek^.G = GG_Mecha ) and ShouldEject( Mek , GB ) then begin AI_EJECT( Mek , GB ); end else if ( Mek^.G = GG_Character ) and ShouldSurrender( GB , Mek ) then begin AI_SURRENDER( GB , Mek ); end; SetNAtt( Mek^.NA , NAG_Action , NAS_MightGiveUp , 0 ); end; Mek := Mek^.Next; end; end; Procedure DoTaunt( GB: GameBoardPtr; Attacker,Target: GearPtr ); { ATTACKER is going to taunt TARGET, whose mother probably smells of elderberries. } const MinTauntTarget = 5; var AtRoll,DefRoll,CounterRoll: Integer; msg: String; begin { Make the defense roll. Since most characters don't have the Taunt skill } { there's a minimum target number of 5. } DefRoll := SkillRoll( GB , Target , NAS_Toughness , STAT_Ego , 0 , 0 , False , True ); if DefRoll < MinTauntTarget then DefRoll := MinTauntTarget; AddMentalDown( Attacker , 1 ); AtRoll := SkillRoll( GB , Attacker , NAS_Taunt , STAT_Charm , DefRoll , 0 , False , True ); msg := GetTauntString( Attacker , 'CHAT_VA.ATTACK' ); Monologue( GB , Attacker , msg ); CounterRoll := SkillRoll( GB , Target , NAS_Taunt , STAT_Charm , AtRoll , 0 , False , True ); if CounterRoll > AtRoll then begin msg := GetTauntString( Target , 'CHAT_VA.RIPOSTE' ); Monologue( GB , Target , msg ); Attacker := LocatePilot( Attacker ); if Attacker <> Nil then begin AddNAtt( Attacker^.NA , NAG_StatusEffect , NAS_Flummoxed , 1 + Random( 10 ) ); AddMoraleDmg( Attacker , 1 + AtRoll - DefRoll ); AddMentalDown( Attacker , 1 + Random( 4 ) ); end; { Countering a verbal attack sets the Target's recharge. } { It also makes sure that the attacker won't try a taunt again for 5 minutes. } SetNAtt( Target^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + 91 + Random( 120 ) ); SetNAtt( Attacker^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + 300 + Random( 120 ) ) end else if AtRoll > DefRoll then begin msg := GetTauntString( Target , 'CHAT_VA.SUCCESS' ); Monologue( GB , Target , msg ); Target := LocatePilot( Target ); if Target <> Nil then begin AddNAtt( Target^.NA , NAG_StatusEffect , NAS_Flummoxed , 1 + Random( 10 ) ); AddMoraleDmg( Target , 1 + AtRoll - DefRoll ); end; { Insulting your enemies makes you happy. } AddMoraleDmg( Attacker , -( 5 + Random( 10 ) ) ); end else begin msg := GetTauntString( Target , 'CHAT_VA.FAILURE' ); Monologue( GB , Target , msg ); { A failure delays the next taunt for at least five minutes. } SetNAtt( Attacker^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + 300 + Random( 120 ) ) end; end; Function SituationalEjectionModifier( GB: GameBoardPtr; Attacker,Target: GearPtr ): Integer; { Return the situational modifier to the verbal attack roll. This modifier is based } { on the following: } { - Relative damage level of the attacker } { - Being outnumbered } { - Mobility status } { - Weapon status } { A higher value is worse for the attacker. } var SEM,A,T: Integer; mek: GearPtr; begin { Start with a basic modifier of +5. } SEM := 5; { Relative damage levels. } A := PercentDamaged( Attacker ); T := PercentDamaged( Target ); if A < T then SEM := SEM + ( ( T - A ) div 5 ); { Being outnumbered. } A := 0; T := 0; mek := GB^.Meks; while mek <> Nil do begin if IsMasterGear( mek ) and OnTheMap( GB , mek ) and GearActive( mek ) then begin if AreAllies( GB , Target , mek ) then Inc( T ) else if AreEnemies( GB , Target , mek ) then Inc( A ); end; mek := mek^.next; end; if A > T then begin SEM := SEM - ( A - T ); end else if T > A then begin SEM := SEM + ( ( T - A ) div 2 ); end; { Mobility status. } if not HasAtLeastOneValidMovemode( Target ) then SEM := SEM - 3; { Weapon status. } if not HasAtLeastOneValidWeapon( Target ) then SEM := SEM - 5; SituationalEjectionModifier := SEM; end; Procedure DoVerbalAttack( GB: GameBoardPtr; Attacker,Target: GearPtr ); { ATTACKER is going to spew abuse upon TARGET. If ATTACKER is a member of the } { PC team and TARGET is in hard shape, this attack has a chance of } { causing surrender or ejection. Note that the ATTACKER has only one chance to } { get an ejection, and every time he fails to gain a surrender it gets harder. } const MinSurrenderDefRoll = 5; MinEjectDefRoll = 10; ConversationEjectPenalty = 3; Function SelectTactic: Integer; { ATTACKER has to select a tactic to use against TARGET. } { This tactic is going to be one of the interaction skills. } { The nominees are: CONVERSATION and INTIMIDATION. } var Con_Rank,Int_Rank: Integer; begin Con_Rank := SkillValue( Attacker , NAS_Conversation , STAT_Charm ) - ConversationEjectPenalty; if Con_Rank < 0 then Con_Rank := 0; Int_Rank := SkillValue( Attacker , NAS_Intimidation , STAT_Ego ); if Random( Int_Rank + Con_Rank ) < Int_Rank then begin SelectTactic := NAS_Intimidation; end else begin SelectTactic := NAS_Conversation; end; end; var AtSkill,AtRoll,DefRoll: Integer; msg: String; begin if ( Target^.G = GG_Character ) then begin DefRoll := SkillRoll( GB , Target , NAS_Concentration , STAT_Ego , 0 , 0 , False , True ); if DefRoll < MinSurrenderDefRoll then DefRoll := MinSurrenderDefRoll; AddMentalDown( Attacker , 3 ); AtSkill := SelectTactic; msg := GetTauntString( Attacker , 'CHAT_VA.FORCESURRENDER.' + BStr( AtSkill ) ); if AtSkill = NAS_Conversation then begin AtRoll := SkillRoll( GB , Attacker , AtSkill , STAT_Charm , DefRoll , -5 - NAttValue( Target^.NA , NAG_EpisodeData , NAS_TauntResistance ) , False , True ); end else begin AtRoll := SkillRoll( GB , Attacker , AtSkill , STAT_Ego , DefRoll , -NAttValue( Target^.NA , NAG_EpisodeData , NAS_TauntResistance ) , False , True ); end; AddNAtt( Target^.NA , NAG_EpisodeData , NAS_TauntResistance , 1 + Random(3) ); Monologue( GB , Attacker , msg ); if AtRoll > DefRoll then begin if MightSurrender( GB , Target ) then begin { The surrender procedure will print a message, so no need to do that here. } AI_Surrender( GB , target ); end else begin { No surrender- just abuse MP and SP. } AddMentalDown( Target , 1 + ( AtRoll - DefRoll ) div 3 ); AddStaminaDown( Target , 1 + ( AtRoll - DefRoll ) div 3 ); end; end else begin msg := GetTauntString( Target , 'CHAT_VA.FORCEFAILURE' ); Monologue( GB , Target , msg ); end; end else if ( target^.G = GG_Mecha ) and ( NAttValue( Attacker^.NA , NAG_Location, NAS_Team ) = NAV_DefPlayerTeam ) and MightEject( Target ) then begin DefRoll := SkillRoll( GB , Target , NAS_Concentration , STAT_Ego , 0 , NAttValue( Target^.NA , NAG_EpisodeData , NAS_TauntResistance ) , False , True ); if DefRoll < MinEjectDefRoll then DefRoll := MinEjectDefRoll; AddMentalDown( Attacker , 3 ); AtSkill := SelectTactic; msg := GetTauntString( Attacker , 'CHAT_VA.FORCEEJECT.' + BStr( AtSkill ) ); if AtSkill = NAS_Conversation then begin AtRoll := SkillRoll( GB , Attacker , AtSkill , STAT_Charm , DefRoll , -SituationalEjectionModifier( GB , Attacker , Target ) - ConversationEjectPenalty , False , True ); end else begin AtRoll := SkillRoll( GB , Attacker , AtSkill , STAT_Ego , DefRoll , -SituationalEjectionModifier( GB , Attacker , Target ) , False , True ); end; AddNAtt( Target^.NA , NAG_EpisodeData , NAS_TauntResistance , 1 + Random( 6 ) ); Monologue( GB , Attacker , msg ); if AtRoll > DefRoll then begin { The eject procedure will print a message, so no need to do that here. } AI_Eject( target , GB ); end else begin msg := GetTauntString( Target , 'CHAT_VA.FORCEFAILURE' ); Monologue( GB , Target , msg ); end; end else begin DoTaunt( GB , Attacker , Target ); end; end; initialization NPC_Chatter_Standard := LoadStringList( NPC_Chatter_File ); finalization DisposeSAtt( NPC_Chatter_Standard ); end. gearhead-2-0.701/arenaplay.pp000066400000000000000000001312611321074026100157350ustar00rootroot00000000000000unit arenaplay; { This unit holds the combat loop for Arena. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale; Const SATT_Artifact = 'ARTIFACT'; Procedure CombatMain( Camp: CampaignPtr ); Function ScenePlayer( Camp: CampaignPtr ; Scene: GearPtr; var PCForces: GearPtr ): Integer; implementation uses ability,aibrain,arenacfe,arenascript,backpack,gearutil,ghmodule,ghholder, ghchars,ghprop,ghweapon,grabgear,menugear,movement,pcaction, playwright,randmaps,rpgdice,skilluse,texutil,ui4gh,wmonster, action,narration,gearparser,customization, {$IFDEF ASCII} vidmap,vidgfx; {$ELSE} sdlmap,sdlgfx; {$ENDIF} const DEBUG_ON: Boolean = False; Procedure ProcessMovement( GB: GameBoardPtr; Mek: GearPtr ); { Call the LOCALE movement routine, then update the display } { here if need be. } var result,Team: Integer; begin { Call the movement procedure, and store the result. } result := EnactMovement( GB , Mek ); { Depending upon what happened, update the display. } if result > 0 then begin { Check for previously unseen enemies. } if OnTheMap( GB , NAttValue( Mek^.NA , NAG_Location , NAS_X ) , NAttValue( Mek^.NA , NAG_Location , NAS_Y ) ) then VisionCheck( GB , Mek ) { Print message if mek has fled the battle. } else begin DialogMSG( PilotName( Mek ) + ' has left this area.'); { Set trigger here. } Team := NAttValue( Mek^.NA , NAG_Location , NAS_Team ); SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( Team ) ); SetTrigger( GB , TRIGGER_UnitEliminated + BStr( NAttValue( Mek^.NA , NAG_EpisodeData , NAS_UID ) ) ); end; { Check for charges and crashes. } ResolveAfterEffects( GB ); end; end; Procedure GetMekInput( Mek: GearPtr; Camp: CampaignPtr; ControlByPlayer: Boolean ); { Decide what the mek in question is gonna do next. } begin { This procedure has to branch depending upon whether we have a } { player controlled mek or a computer controlled mek. } { Branch the first - If this mecha has a HAYWIRE status effect } { it may move randomly 50% of the time. } if Confused( Mek ) and ( Random( 2 ) = 1 ) then begin ConfusedInput( Mek , Camp^.GB ); { end else if ( NAttValue( Mek^.NA , NAG_Location , NAS_SmartAction ) <> 0 ) and ( CurrentMoveRate( Camp^.GB^.Scene , Mek ) > 0 ) then begin { This model is performing a continuous action. Go handle that. } RLSmartAction( Camp^.GB , Mek ); } end else if ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = 1 ) or ControlByPlayer then begin { It's a player mek. } {$IFDEF ASCII} FocusOn( Mek ); {$ENDIF} GetPlayerInput( Mek , Camp ); end else begin { it's a computer mek. } GetAIInput( Mek , Camp^.GB ); end; end; Procedure CheckMapScroll( GB: GameBoardPtr ); { Space maps don't have well defined borders. If everyone moves over to one side } { of the map, the entire map contents will shift to try and center things. } Function IsActiveParticipant( Part: GearPtr ): Boolean; { Return TRUE if PART is an active participant in the battle for } { purposes of map scrolling. } begin IsActiveParticipant := GearActive( Part ); end; Function GetDelta( axis,a,b: Integer ): Integer; { Determine whether or not the map should be scrolled along this } { axis, and if so in what direction. } { A is the boundary of the "low zone", B is the boundary of the "high zone". } { If one zone is occupied and the other isn't, scroll the map in that direction. } var M: GearPtr; Low_Zone_Occupied,High_Zone_Occupied: Boolean; P: Integer; begin M := GB^.Meks; Low_Zone_Occupied := False; High_Zone_Occupied := False; while ( M <> Nil ) and not ( Low_Zone_Occupied and High_Zone_Occupied ) do begin if OnTheMap( GB , M ) and IsActiveParticipant( M ) then begin P := NAttValue( M^.NA , NAG_Location , Axis ); if P < A then Low_Zone_Occupied := True else if P > B then High_Zone_Occupied := True; end; M := M^.Next; end; if Low_Zone_Occupied and not High_Zone_Occupied then GetDelta := -1 else if High_Zone_Occupied and not Low_Zone_Occupied then GetDelta := 1 else GetDelta := 0; end; var DX,DY: Integer; M: GearPtr; begin { Only do scrolling while there's enemies about. } if IsSafeArea( GB ) then Exit; DX := GetDelta( NAS_X , GB^.map_width div 3 + 1 , GB^.map_width * 2 div 3 ); DY := GetDelta( NAS_Y , GB^.map_height div 3 + 1 , GB^.map_height * 2 div 3 ); if ( DX = 0 ) and ( DY = 0 ) then Exit; M := GB^.Meks; while M <> Nil do begin if OnTheMap( GB , M ) then begin AddNAtt( M^.NA , NAG_Location , NAS_X , -DX ); AddNAtt( M^.NA , NAG_Location , NAS_Y , -DY ); end; M := M^.Next; end; end; Procedure MaybeDoTaunt( GB: GameBoardPtr; Mek: GearPtr ); { Mek might do a taunt against one of its enemies. This is how we resolve } { things: First, check for a target. If a target is found, taunt it. } { Whether a target was found or not, set the taunt recharge. } Function FindTauntTarget: GearPtr; { Locate a target for taunting. } var Target,TL: GearPtr; begin Target := Nil; TL := GB^.Meks; while TL <> Nil do begin if AreEnemies( GB , Mek , TL ) and OnTheMap( GB , TL ) and CanSpeakWithTarget( GB , Mek , TL ) and GearActive( TL ) and NotAnAnimal( TL ) and ( TL^.G <> GG_Prop ) and MekCanSeeTarget( GB , Mek , TL ) then begin if Target = Nil then Target := TL else if Range( gb , Target , Mek ) > Range( gb , TL , Mek ) then Target := TL; end; TL := TL^.Next; end; FindTauntTarget := Target; end; var Target: GearPtr; begin if CurrentMental( Mek ) > 1 then begin Target := FindTauntTarget; if Target <> Nil then begin { Set the recharge before doing the taunt, since the DoTaunt procedure } { may set a different recharge time depending on what happens there. } SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + 121 + Random( 180 ) ); DoTaunt( GB , Mek , Target ); end else begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + 1 + Random( 30 ) ); end; end else begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ChatterRecharge , GB^.ComTime + 61 + Random( 120 ) ); end; end; Procedure CheckMeks( Camp: CampaignPtr ); { Check through all the meks in this scenario. If it's time } { for one to move according to its ETA, call the movement } { procedure. } var M: GearPtr; ETA: LongInt; PCMoved,PCActed: Boolean; PC: GearPtr; begin M := Camp^.GB^.meks; PCMoved := False; PCActed := False; PC := Nil; while M <> Nil do begin { If this gameboard should be exited, better stop processing meks. } { We perform the check here in case some script action happening before } { the first mecha moved caused this condition. } if not KeepPlayingSC( Camp^.GB ) then break; if IsMasterGear( M ) then begin { Check for actions in progress. } if NotDestroyed( M ) and OnTheMap( Camp^.GB , M ) then begin ETA := NAttValue( M^.NA , NAG_Action , NAS_MoveETA ); if ETA <= Camp^.GB^.ComTime then begin ProcessMovement( Camp^.GB , M ); if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and GearActive( M ) then begin PC := M; PCMoved := True; end; end; end; { Check for input. } if GearActive( M ) and OnTheMap( Camp^.GB , M ) then begin ETA := NAttValue( M^.NA , NAG_Action , NAS_CallTime ); if ETA <= Camp^.GB^.ComTime then begin GetMekInput( M , Camp , False ); if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then PCActed := True; end else if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and ( ETA = ( Camp^.GB^.ComTime + 1 ) ) then begin { We're going to update the display next second; don't bother doing it now. } PCActed := True; end; end; { Check for drift. } if ( NAttValue( M^.NA , NAG_Action , NAS_DriftSpeed ) > 0 ) and OnTheMap( Camp^.GB , M ) then begin ETA := NAttValue( M^.NA , NAG_Action , NAS_DriftETA ); if ETA <= Camp^.GB^.ComTime then begin DoDrift( Camp^.GB , M ); if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and GearActive( M ) then begin PCMoved := True; PC := M; end; end; end; { Check for taunting and other acts of opportunity. } if ( Camp^.GB^.ComTime > NAttValue( M^.NA , NAG_EpisodeData , NAS_ChatterRecharge ) ) and GearActive( M ) and KeepPlayingSC( Camp^.GB ) then begin if HasSkill( M , NAS_Taunt ) then begin MaybeDoTaunt( Camp^.GB , M ); end else begin SetNAtt( M^.NA , NAG_EpisodeData , NAS_ChatterRecharge , Camp^.GB^.ComTime + 300 ); end; end; end; { if IsMasterGear then... } M := M^.Next; end; if PCMoved and ( PC <> Nil ) then begin if ( Camp^.GB^.Scene <> Nil ) and ( Camp^.GB^.Scene^.Stat[ STAT_SpaceMap ] <> 0 ) then CheckMapScroll( Camp^.GB ); if not PCActed then begin FocusOn( PC ); CombatDisplay( Camp^.GB ); DoFLip; end; end; end; Procedure UniversalVisionCheck( GB: GameBoardPtr ); { Do a vision check for every model on the board. } var M: GearPtr; begin { First, we need to make sure the shadow map is up to date. } UpdateShadowMap( GB ); { Next, go through each gear on the gameboard, doing vision checks as needed. } M := GB^.Meks; while M <> Nil do begin if IsMasterGear( M ) and OnTheMap( GB , M ) then VisionCheck( GB , M ); M := M^.Next; end; { Finally, focus on the PC. } M := GG_LocatePC( GB ); if M <> Nil then FocusOn( M ); end; Function CurrentControlMode( GB: GameBoardPtr ): Integer; { Return the control mode currently being used. } begin { If no GameBoard or scene found, return NAV_Clock } if ( GB = Nil ) or ( GB^.Scene = Nil ) then begin CurrentControlMode := NAV_ClockMode; end else begin { We have a scene. Return the stored value. } CurrentControlMode := NAttValue( GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod ); end; end; Procedure CombatMain( Camp: CampaignPtr ); { This is the main meat-and-potatoes combat procedure. } { Actually, it's pretty simple. All the difficult work is } { done by the procedures it calls. } { Man, I can't believe how many outdated comments I have lying around here. } { If you read something which seems prima facae absurd, it's probably a leftover } { from ages long past. } var FX_String,FX_Desc: String; begin { Get rid of the old AI pathfinding maps. } ClearHotMaps; { Initialize the FX_Strings } if Camp^.GB^.Scene <> Nil then begin case NATtValue( Camp^.GB^.Scene^.NA , NAG_EnvironmentData , NAS_Atmosphere ) of NAV_Vacuum: begin FX_String := '1 DAMAGE 10 0 0 0 ArmorIgnore GasAttack NoMetal CanResist'; FX_Desc := MsgString( 'ENVIRO_VACUUM' ); end; else begin FX_String := ''; FX_Desc := ''; end; end; end else begin FX_String := ''; FX_Desc := ''; end; {Start main combat loop here.} {Keep going until we're told to quit.} while KeepPlayingSC( Camp^.GB ) and ( CurrentControlMode( Camp^.GB ) = NAV_ClockMode ) do begin AdvanceGameClock( Camp^.GB , False , True ); { Once every 10 minutes, roll for random monsters. } if ( Camp^.GB^.ComTime mod AP_10minutes ) = 233 then RestockRandomMonsters( Camp^.GB ); { Once every hour, make sure the PC is still alive. } if ( Camp^.GB^.ComTime mod AP_Hour ) = 0 then SetTrigger( Camp^.GB , 'NU1' ); { Update clouds every 30 seconds. } if ( Camp^.GB^.ComTime mod 30 ) = 0 then BrownianMotion( Camp^.GB ); { Update encounters every 20 seconds. } if ( Camp^.GB^.ComTime mod 20 ) = 2 then HandleEncounters( Camp^.GB ); { Handle environmental effects every 2 minutes. } if ( FX_String <> '' ) and ( ( Camp^.GB^.ComTime mod 120 ) = 17 ) then MassEffectFrontEnd( Camp^.GB , FX_String , FX_Desc ); HandleTriggers( Camp^.GB ); CheckMeks( Camp ); if Screen_Needs_Redraw and Thorough_Redraw then begin CombatDisplay( Camp^.GB ); DoFlip; Screen_Needs_Redraw := False; end; {end main combat loop.} end; end; Function CanTakeTurn( GB: GameBoardPtr; M: GearPtr ): Boolean; { Return TRUE if M can act in this turn. } begin CanTakeTurn := GearOperational( M ) and OnTheMap( GB , M ); end; Procedure TacticsTurn( Camp: CampaignPtr; M: GearPtr; IsPlayerMek: Boolean ); { It's time for this mecha to act. } { Give it 60 seconds in which to do everything. } var CallTime,ETA: LongInt; BeginTime,EndTime: LongInt; DidBeginTurn: Boolean; PCMoved: Boolean; begin { Get rid of the old AI pathfinding maps. } ClearHotMaps; DidBeginTurn := False; BeginTime := NAttValue( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_TacticsTurnStart ); EndTime := BeginTime + TacticsRoundLength - 1; Repeat PCMoved := False; { Check for Mecha's action first. } ETA := NAttValue( M^.NA , NAG_Action , NAS_MoveETA ); if ETA <= Camp^.GB^.ComTime then begin ProcessMovement( Camp^.GB , M ); if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and GearActive( M ) then begin PCMoved := True; end; end; { Check for drift. } if NAttValue( M^.NA , NAG_Action , NAS_DriftSpeed ) > 0 then begin ETA := NAttValue( M^.NA , NAG_Action , NAS_DriftETA ); if ETA <= Camp^.GB^.ComTime then begin DoDrift( Camp^.GB , M ); if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and GearActive( M ) then begin PCMoved := True; end; end; end; if PCMoved and ( Camp^.GB^.Scene <> Nil ) and ( Camp^.GB^.Scene^.Stat[ STAT_SpaceMap ] <> 0 ) then CheckMapScroll( Camp^.GB ); { Check for input. } CallTime := NAttValue( M^.NA , NAG_Action , NAS_CallTime ); if ( CallTime <= Camp^.GB^.ComTime ) and CanTakeTurn( Camp^.GB , M ) then begin if GearOperational( M ) then begin if IsPlayerMek and not DidBeginTurn then begin BeginTurn( Camp^.GB , M ); DidBeginTurn := True; Tactics_Turn_In_Progess := True; end; GetMekInput( M , Camp , IsPlayerMek ); if ( Calltime >= NAttValue( M^.NA , NAG_Action , NAS_CallTime ) ) and not IsPlayerMek then begin { This model is apparently wasting time, somehow. } SetNAtt( M^.NA , NAG_Action , NAS_CallTime , Camp^.GB^.ComTime + 1); end; end else begin SetNAtt( M^.NA , NAG_Action , NAS_CallTime , Camp^.GB^.ComTime + 60); end; end else begin inc( Camp^.GB^.ComTime ); end; { Handle triggers now. } HandleTriggers( Camp^.GB ); until ( Camp^.GB^.ComTime >= EndTime ) or ( not OnTheMap( Camp^.GB , M ) ) or Destroyed( M ) or ( not KeepPlayingSC( Camp^.GB ) ) or ( CurrentControlMode( Camp^.GB ) <> NAV_TacticsMode ); { At the end, reset the comtime. } Camp^.GB^.ComTime := BeginTime; { Turn off the tactics turn indicators. } Tactics_Turn_In_Progess := False; end; Procedure TacticsMain( Camp: CampaignPtr ); { This is the main meat-and-potatoes combat procedure. } { It functions as the above procedure, but a bit more strangely. } { You see, in order to have a tactics mode without changing any other part } { of the program, this procedure must fool all the PC-input and AI routines } { into believing that the clock is ticking, whereas in fact it's just ticking } { for that one particular model for a stretch of 60 seconds. } { PRECONDITION: Camp^.GB^.Scene <> Nil } var M: GearPtr; Team,T: Integer; FoundPCToAct: Boolean; FX_String,FX_Desc: String; begin { Get rid of the old AI pathfinding maps. } ClearHotMaps; { Initialize the FX_Strings } if Camp^.GB^.Scene <> Nil then begin case NATtValue( Camp^.GB^.Scene^.NA , NAG_EnvironmentData , NAS_Atmosphere ) of NAV_Vacuum: begin FX_String := '1 DAMAGE 10 0 0 0 ArmorIgnore GasAttack NoMetal CanResist'; FX_Desc := MsgString( 'ENVIRO_VACUUM' ); end; else begin FX_String := ''; FX_Desc := ''; end; end; end else begin FX_String := ''; FX_Desc := ''; end; {Start main combat loop here.} {Keep going until we're told to quit.} while KeepPlayingSC( Camp^.GB ) and ( CurrentControlMode( Camp^.GB ) = NAV_TacticsMode ) do begin HandleTriggers( Camp^.GB ); { Each round lasts one minute. } { Handle the player mecha first. } repeat FoundPCToAct := False; M := Camp^.GB^.Meks; while ( M <> Nil ) and KeepPlayingSC( Camp^.GB ) do begin team := NAttValue( M^.NA , NAG_Location , NAS_Team ); if ( Team = NAV_DefPlayerTeam ) or ( Team = NAV_LancemateTeam ) then begin if NotDestroyed( M ) and OnTheMap( Camp^.GB , M ) then begin if CanTakeTurn( Camp^.GB , M ) and ( NAttValue( M^.NA , NAG_Action , NAS_CallTime ) < ( Camp^.GB^.ComTime + TacticsRoundLength - 1 ) ) then begin FoundPCToAct := True; end; TacticsTurn( Camp , M , True ); end; end; M := M^.Next; end; until ( not FoundPCToAct ) or ( CurrentControlMode( Camp^.GB ) <> NAV_TacticsMode ); { Handle the enemy mecha next, as long as the game hasn't been quit. } if KeepPlayingSC( Camp^.GB ) and ( CurrentControlMode( Camp^.GB ) = NAV_TacticsMode ) then begin { Handle NPC mecha } M := Camp^.GB^.Meks; while M <> Nil do begin team := NAttValue( M^.NA , NAG_Location , NAS_Team ); if ( Team <> NAV_DefPlayerTeam ) and ( Team <> NAV_LancemateTeam ) and ( Team <> 0 ) then begin if NotDestroyed( M ) and OnTheMap( Camp^.GB , M ) then begin TacticsTurn( Camp , M , False ); end; end; M := M^.Next; end; { Advance the clock by 60 seconds. } for T := 1 to TacticsRoundLength do AdvanceGameClock( Camp^.GB , False , True ); AddNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_TacticsTurnStart , TacticsRoundLength ); HandleTriggers( Camp^.GB ); { Update clouds every round. } for team := 1 to ( TacticsRoundLength div 30 ) do BrownianMotion( Camp^.GB ); { Handle environmental effects every other round. } if ( FX_String <> '' ) and ( ( ( Camp^.GB^.ComTime div TacticsRoundLength ) mod 2 ) = 1 ) then MassEffectFrontEnd( Camp^.GB , FX_String , FX_Desc ); { Once every 10 rounds, roll for random monsters. } if ( ( Camp^.GB^.ComTime div TacticsRoundLength ) mod 10 ) = 0 then RestockRandomMonsters( Camp^.GB ); end; end; end; Procedure PreparePCForces( GB: GameBoardPtr; var PCForces: GearPtr ); { ******************************* } { *** PC Forces PreProcessing *** } { ******************************* } { Before sticking the PCs on the map, must first check whether or not } { to stick them in mecha. } Function IsValidForScene( Mek: GearPtr ): Boolean; { Return TRUE if this mecha is valid for this scene, or FALSE otherwise. } begin IsValidForScene := MekCanEnterScene( Mek , GB^.Scene ); end; var PCT,PC2,PCMek: GearPtr; msg: String; begin { Pass One - Set PC Team for all units. } PCT := PCForces; while PCT <> Nil do begin { The exact team is going to depend on whether this is the primary PC or } { just a lancemate. } if NAttValue( PCT^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_CTPrimary then begin SetNAtt( PCT^.NA , NAG_Location , NAS_Team , NAV_LancemateTeam ); end else begin SetNAtt( PCT^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); end; PCT := PCT^.Next; end; { Pass Two - Insert pilots into mecha as appropriate. } PCT := PCForces; while PCT <> Nil do begin PC2 := PCT^.Next; { If this gear is a character, and is at a smaller scale than } { the map, check to see if he/she has a mecha to get into. } if ( PCT^.G = GG_Character ) and ( PCT^.Scale < GB^.Scale ) then begin PCMek := FindPilotsMecha( PCForces , PCT ); if ( PCMek <> Nil ) and ( PCMek^.Scale <= GB^.Scale ) and HasAtLeastOneValidMovemode( PCMek ) then begin if IsValidForScene( PCMek ) then begin { A mek has been found. Insert the pilot into it. } DelinkGear( PCForces , PCT ); { If the pilot is a lancemate, so is the mecha. } if NAttValue( PCT^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_CTPrimary then begin SetNAtt( PCMek^.NA , NAG_Location , NAS_Team , NAV_LancemateTeam ); end; if not BoardMecha( PCMek , PCT ) then begin { The pilot couldn't board the mecha for whatever reason. } { Stick the pilot back in the list, at the beginning. } PCT^.Next := PCForces; PCForces := PCT; end; end else begin { This mecha isn't valid for the scene. Post a note. } msg := ReplaceHash( MsgString( 'PrepPCF_InvalidMecha' ) , GearName( PCT ) ); msg := ReplaceHash( msg , GearName( PCMek ) ); DialogMsg( msg ); end; end; end; PCT := PC2; end; end; Function NonRecoveryScene( GB: GameBoardPtr ): Boolean; { Return TRUE if this scene isn't a good location for recovery. } begin NonRecoveryScene := ( GB^.Scene = Nil ) or ( not AStringHasBString( SAttValue( GB^.Scene^.SA , 'TYPE' ) , 'PUBLIC' ) ); end; Function ShouldDeployLancemate( GB: GameBoardPtr; LM , Scene: GearPtr ): Boolean; { Return TRUE if LM should be placed on this map, or FALSE if LM should be } { kept on the sidelines. } begin if AStringHasBString( SAttValue( Scene^.SA , 'SPECIAL' ) , 'SOLO' ) then begin ShouldDeployLancemate := False; end else if LM^.Scale < ( Scene^.V - 1 ) then begin ShouldDeployLancemate := False; end else if ( LM^.G = GG_Character ) and ( NAttValue( LM^.NA , NAG_Damage , NAS_OutOfAction ) <> 0 ) and NonRecoveryScene( GB ) then begin ShouldDeployLancemate := False; end else begin ShouldDeployLancemate := True; end; end; Procedure PrepareTeams( GB: GameBoardPtr ); { Go through all the teams in play. If any of them have a DEPLOY script, } { call that now. } { These scripts will typically be used to request dynamic opponents. } var T: GearPtr; d: String; begin if ( GB^.Scene = Nil ) then exit; T := GB^.Scene^.SubCom; while T <> Nil do begin if ( T^.G = GG_Team ) and ( SAttValue( T^.SA , 'DEPLOY' ) <> '' ) then begin d := 'DEPLOY'; TriggerGearScript( GB , T , D ); end; T := T^.Next; end; end; Procedure MoveToPublicScene( GB: GameBoardPtr; Scene0 , it: GearPtr ); { We have a NPC in this oversized scene. We don't want to deploy them here, } { so move them to a sensible place. } var RootScene, PublicScene: GearPtr; begin { Make sure the root scene is a root scene. } RootScene := FindRootScene( Scene0 ); if RootScene <> Nil then begin PublicScene := SearchForScene( RootScene , Nil , GB , 'PUBLIC (BUILDING|MEETING)' ); if PublicScene <> Nil then begin InsertInvCom( PublicScene , it ); ChooseTeam( it , PublicScene ); StripNAtt( it , NAG_Location ); StripNAtt( it , NAG_Damage ); StripNAtt( it , NAG_WeaponModifier ); StripNAtt( it , NAG_Condition ); StripNAtt( it , NAG_StatusEffect ); if XXRan_Debug then begin DialogMsg( 'Moving ' + GearName( it ) + ' to ' + GearName( PublicScene ) + '.' ); end; end else begin { Stick the character back where it was originally. } InsertInvCom( Scene0 , it ); end; end else begin InsertInvCom( Scene0 , it ); end; end; Procedure DeployJJang( Camp: CampaignPtr; Scene,PCForces: GearPtr ); { Deploy the game forces as described in the Scene. } var it,it2: GearPtr; begin if DEBUG_ON then DialogMsg( 'DeployJJang' ); { ERROR CHECK - If this campaign already has a GameBoard, no need to } { deploy anything. It was presumably just restored from disk and should } { be fully stocked. } if Camp^.GB <> Nil then Exit; { Record the tactics turn start time. } { This gets reset along with the scene, but should not be reset for saved games. } SetNAtt( Scene^.NA , NAG_SceneData , NAS_TacticsTurnStart , Camp^.ComTime ); { Generate the map for this scene. It will either be created } { randomly or drawn from the frozen maps. } Camp^.gb := UnfreezeLocation( GearName( Scene ) , Camp^.Maps ); if Camp^.GB = Nil then Camp^.gb := RandomMap( SCene ); Camp^.GB^.ComTime := Camp^.ComTime; Camp^.gb^.Scene := Scene; Camp^.gb^.Scale := Scene^.V; { Get the PC Forces ready for deployment. } PreparePCForces( Camp^.GB , PCForces ); { Stick the metaterrain on the map, since the PC position may well be } { determined by this. } it := Scene^.InvCom; while it <> Nil do begin it2 := it^.Next; { Check to see if this is metaterrain. } if ( it^.G = GG_MetaTerrain ) then begin DelinkGear( Scene^.InvCom , it ); DeployGear( Camp^.gb , it , True ); end; it := it2; end; { Stick the PC forces on the map. } { Clear the PC_TEAM saved position. } PC_Team_X := 0; while PCForces <> Nil do begin it2 := PCForces^.Next; it := PCForces; DelinkGear( PCForces , it ); if NAttValue( it^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam then begin DeployGear( Camp^.gb , it , GearActive( it ) AND ( ( it^.Scale <= Camp^.GB^.Scale ) or ( ( Camp^.GB^.Scene <> Nil ) and ( Camp^.GB^.Scene^.G = GG_World ) ) ) ); end else begin if GearActive( it ) AND ( it^.Scale <= Camp^.GB^.Scale ) AND ShouldDeployLancemate( Camp^.GB , it , Scene ) then begin DeployGear( Camp^.gb , it , True ); SetNAtt( it^.NA , NAG_Damage , NAS_OutOfAction , 0 ); end else begin DeployGear( Camp^.gb , it , False ); end; end; PCForces := it2; end; { Check the orders of the lancemates. } SetLancemateOrders( Camp^.GB ); { Stick the local NPCs on the map. } it := Scene^.InvCom; while it <> Nil do begin it2 := it^.Next; { Check to see if this is a character. } if ( it^.G >= 0 ) then begin DelinkGear( Scene^.InvCom , it ); if NAttValue( it^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam then begin DeployGear( Camp^.gb , it , ( it^.G = GG_Character ) ); end else if ( it^.G = GG_Character ) and ( Camp^.GB^.Scale > 2 ) and ( NAttValue( it^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then begin { Don't deposit NPCs on outdoors maps. Move them to a public scene instead. } MoveToPublicScene( Camp^.GB , Scene , it ); end else begin EquipThenDeploy( Camp^.gb , it , ( ( it^.Scale <= Scene^.V ) or ( it^.G = GG_Character ) ) ); end; end; it := it2; end; { Set the encounter recharge, so the PC doesn't get ambushed right away. } SetNAtt( Scene^.NA , NAG_SceneData , NAS_EncounterRecharge , Camp^.GB^.ComTime + Standard_Encounter_Recharge ); { Finally, deploy any temp forces and perform initialization requested by teams. } PrepareTeams( Camp^.GB ); end; Function IsGlobalGear( NPC: GearPtr ): Boolean; { This function will decide whether or not the NPC is global. } { Global NPCs are stored as subcomponents of the ADVENTURE } { gear. } begin IsGlobalGear := NAttValue( NPC^.NA , NAG_ParaLocation , NAS_OriginalHome ) <> 0; end; Function ShouldDeleteDestroyed( GB: GameBoardPtr; Mek: GearPtr ): Boolean; { Return TRUE if MEK should be deleted, or FALSE otherwise. } { MEK shouldn't be deleted if it's an artefact. } begin ShouldDeleteDestroyed := not AStringHasBString( SAttValue( Mek^.SA , 'TYPE' ) , SAtt_Artifact ); end; Procedure PutAwayGear( Camp: CampaignPtr; var Mek,PCForces: GearPtr ); { The game is over. Put MEK wherever it belongs. } function ShouldBeMoved: Boolean; { MEK is a member of the player team. } { Return TRUE if Mek should be moved, or FALSE otherwise. } { It should be moved if it's a character, if it's the } { PC's chosen mecha, or if the current scene is dynamic } { or a metascene. Got all that? } begin if ( Camp^.GB^.Scene = Nil ) or IsInvCom( Camp^.GB^.Scene ) or ( Camp^.GB^.Scene^.S < 0 ) then begin ShouldBeMoved := True; end else if ( Camp^.GB^.Scene^.G = GG_MetaScene ) then begin ShouldBeMoved := True; end else if Mek^.G = GG_Character then begin ShouldBeMoved := True; end else if SAttValue( Mek^.SA , 'PILOT' ) <> '' then begin ShouldBeMoved := True; end else begin ShouldBeMoved := False; end; end; begin if Mek = Nil then begin Exit; end else if ( Mek^.G = GG_MetaTerrain ) and ( Mek^.S = GS_MetaFire ) then begin DisposeGear( Mek ); end else if ( Mek^.G = GG_MetaTerrain ) and ( Mek^.S = GS_MetaEncounter ) and ( Mek^.Stat[ STAT_Destination ] < 0 ) and MetaSceneNotInUse( Camp^.Source , Mek^.Stat[ STAT_Destination ] ) then begin DisposeGear( Mek ); end else if Destroyed( Mek ) and ShouldDeleteDestroyed( Camp^.GB , Mek ) then begin { If Mek is a character and not an animal, update the Death counter. } if ( Mek^.G = GG_Character ) and NotAnAnimal( Mek ) and ( Camp^.Source <> Nil ) then begin RecordFatality( Camp , Mek ); end; DisposeGear( Mek ); end else if NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Temporary ) <> 0 then begin DisposeGear( Mek ); end else if ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and ShouldBeMoved then begin { Strip the location & visibility info. } StripNAtt( Mek , NAG_Location ); StripNAtt( Mek , NAG_Visibility ); StripNAtt( Mek , NAG_Action ); StripNAtt( Mek , NAG_EpisodeData ); { Get rid of FLUMMOX, BURN, and BLIND conditions. } SetNAtt( Mek^.NA , NAG_StatusEffect , NAS_Burn , 0 ); SetNAtt( Mek^.NA , NAG_StatusEffect , NAS_Blinded , 0 ); SetNAtt( Mek^.NA , NAG_StatusEffect , NAS_Flummoxed , 0 ); { Store the mecha in the PCForces list. } Mek^.Next := PCForces; PCForces := Mek; end else if ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and ShouldBeMoved then begin { Strip the location & visibility info. } StripNAtt( Mek , NAG_Location ); StripNAtt( Mek , NAG_Visibility ); StripNAtt( Mek , NAG_Action ); StripNAtt( Mek , NAG_EpisodeData ); { Get rid of FLUMMOX, BURN, and BLIND conditions. } SetNAtt( Mek^.NA , NAG_StatusEffect , NAS_Burn , 0 ); SetNAtt( Mek^.NA , NAG_StatusEffect , NAS_Blinded , 0 ); SetNAtt( Mek^.NA , NAG_StatusEffect , NAS_Flummoxed , 0 ); { Make sure to record that this is a lancemate, if appropriate. } if ( Mek^.G = GG_Character ) and ( NAttValue( Mek^.NA , NAG_CharDescription , NAS_CharType ) = 0 ) then SetNAtt( Mek^.NA , NAG_CharDescription , NAS_CharType , NAV_CTLancemate ); { Store the mecha in the PCForces list. } Mek^.Next := PCForces; PCForces := Mek; end else begin { Strip the stuff we don't want to save. } StripNAtt( Mek , NAG_Visibility ); StripNAtt( Mek , NAG_Action ); StripNAtt( Mek , NAG_EpisodeData ); StripNAtt( Mek , NAG_Condition ); if Camp^.GB^.Scene <> Nil then begin if IsGlobalGear( Mek ) and IsInvCom( Camp^.GB^.Scene ) then begin StripNAtt( Mek , NAG_Location ); StripNAtt( Mek , NAG_Damage ); PutAwayGlobal( Camp^.GB , Mek ); end else begin InsertInvCom( Camp^.GB^.Scene , Mek ); end; end else begin DisposeGear( Mek ); end; end; end; Procedure ApplyEmergencyHealing( Adv: GearPtr; GB: GameboardPtr ); { Apply healing to any character or mecha on the PC's team that has been destroyed. } { Anything not restored to health by this procedure is likely to be deleted. If that } { includes the PC, then the game is over. } var PC: GearPtr; team,T,SkRk: LongInt; begin PC := GB^.Meks; while PC <> Nil do begin team := NAttValue( PC^.NA , NAG_Location , NAS_Team ); if ( team = NAV_DefPlayerTeam ) or ( team = NAV_LancemateTeam ) then begin if Destroyed( PC ) then begin { Check every repair skill for applicability. } for t := 0 to NumMaterial do begin if ( TotalRepairableDamage( PC , T ) > 0 ) and TeamHasSkill( GB , NAV_DefPlayerTeam , Repair_Skill_Needed[ T ] ) then begin { Determine how many repair points it's possible } { to apply. } if ( PC^.G = GG_Mecha ) then begin SkRk := RollStep( TeamSkill( GB , NAV_DefPlayerTeam , Repair_Skill_Needed[ T ] , STAT_Knowledge ) ) - 5; end else begin SkRk := RollStep( TeamSkill( GB , NAV_DefPlayerTeam , Repair_Skill_Needed[ T ] , STAT_Knowledge ) ) - 7; end; if SkRk < 0 then SkRk := 0; ApplyEmergencyRepairPoints( PC , T , SkRk ); if PC^.G = GG_Character then SetNAtt( PC^.NA , NAG_Damage , NAS_OutOfAction , 1 ); end; end; { Checking the repair skills. } { What happense next depends on whether this is arena mode or RPG mode. } if ( Adv <> Nil ) and ( Adv^.S = GS_ArenaCampaign ) then begin { Killed PCs who don't get the medicine roll in arena mode are out of luck. } { Record a message in the scene to tell whether this gear is recovered } { or destroyed. } if PC^.G = GG_Character then begin { It's a character. The message will be handled by the medic. } if NotDestroyed( PC ) then begin AddSAtt( GB^.Scene^.SA , ARENAREPORT_CharRecovered , GearName( PC ) ); end else begin AddSAtt( GB^.Scene^.SA , ARENAREPORT_CharDied , GearName( PC ) ); end; end else begin { It's a thing. The message will be handled by the mechanic. } if NotDestroyed( PC ) then begin AddSAtt( GB^.Scene^.SA , ARENAREPORT_MechaRecovered , GearName( PC ) ); end else begin AddSAtt( GB^.Scene^.SA , ARENAREPORT_MechaDestroyed , GearName( PC ) ); end; end; end else begin if ( PC^.G = GG_Character ) and ( Team = NAV_DefPlayerTeam ) and Destroyed( PC ) then begin { At this point in time, the PC is dead. Attempt to load a } { rescue scenario. If the rescue fails, then the PC will be } { perminantly dead. } if ( NAttValue( PC^.NA , NAG_Personal , NAS_Resurrections ) < ((NAttValue( PC^.NA , NAG_CharDescription , NAS_Heroic ) div 10 ) + 1 + RollStep( 1 ) ) ) and StartRescueScenario( GB , PC , '*DEATH' ) then begin AddNAtt( PC^.NA , NAG_Personal , NAS_Resurrections , 1 ); if Random( 3 ) = 1 then ApplyPerminantInjury( PC ); AddReputation( PC , 6 , -10 ); AddMoraleDmg( PC , 100 ); end; end else if GearActive( PC ) then begin StripNAtt( PC , NAG_StatusEffect ); if PC^.G = GG_Mecha then begin DialogMsg( ReplaceHash( MsgString( 'DJ_MECHARECOVERED' ) , GearName( PC ) ) ); end else if ( PC^.G = GG_Character ) and ( Team = NAV_DefPlayerTeam ) then begin StartRescueScenario( GB , PC , '*RECOVERY' ); AddReputation( PC , 6 , -10 ); AddMoraleDmg( PC , 100 ); end else begin DialogMsg( ReplaceHash( MsgString( 'DJ_OUTOFACTION' ) , PilotName( PC ) ) ); end; end; end; { If ArenaCampaign ... Else } end; { if Destroyed... } end; PC := PC^.Next; end; end; Procedure PreparePCForDelink( GB: GameBoardPtr ); { Check the PC forces; restore any dead characters based on the repair skills } { posessed by the party; maybe call a rescue procedure. } var PC,TruePC: GearPtr; team: LongInt; begin { Step One: Delink the pilots from their mecha. } PC := GB^.Meks; while PC <> Nil do begin team := NAttValue( PC^.NA , NAG_Location , NAS_Team ); if ( PC^.G = GG_Mecha ) and ( ( team = NAV_DefPlayerTeam ) or ( team = NAV_LancemateTeam ) ) then begin repeat TruePC := ExtractPilot( PC ); if TruePC <> Nil then begin AppendGear( GB^.Meks , TruePC ); end; until TruePC = Nil; end; PC := PC^.Next; end; { Step Two: Apply emergency healing to all. } { If this scene is of a NORESCUE type, don't bother. } if ( GB^.Scene = Nil ) or ( not AStringHasBString( SAttValue( GB^.Scene^.SA , 'SPECIAL' ) , 'NORESCUE' ) ) then begin ApplyEmergencyHealing( FindRoot( GB^.Scene ) , GB ); end; { Step Three: Remove PILOT tags from mecha whose pilots are } { no longer with us. } PC := GB^.Meks; while PC <> Nil do begin team := NAttValue( PC^.NA , NAG_Location , NAS_Team ); if ( team = NAV_DefPlayerTeam ) or ( team = NAV_LancemateTeam ) then begin if ( PC^.G = GG_Mecha ) and ( SAttValue( PC^.SA , 'PILOT' ) <> '' ) then begin TruePC := SeekGearByName( GB^.Meks , SAttValue( PC^.SA , 'PILOT' ) ); if ( TruePC = Nil ) or Destroyed( TruePC ) then begin SetSAtt( PC^.SA , 'PILOT <>' ); { Also set the mecha's team to the PC team. } SetNAtt( PC^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); end; end; end; PC := PC^.Next; end; end; Procedure DoPillaging( GB: GameBoardPtr ); { Pillage everything that isn't nailed down. } { PreparePCForDelink should have already separated the PC from the mecha. } var PC,Mek,M,M2: GearPtr; Cash,NID: LongInt; begin { ERROR CHECK: If this is a NOPILLAGE scene, exit. } if ( GB^.Scene <> Nil ) and AStringHasBString( SAttValue( GB^.Scene^.SA, 'SPECIAL' ) , 'NOPILLAGE' ) then Exit; Cash := 0; { Locate the PC and the mecha, if appropriate. } PC := GG_LocatePC( GB ); Mek := FindPilotsMecha( GB^.Meks , PC ); { If the PC is alive and on the map, begin pillaging. } if ( PC <> Nil ) and ( OnTheMap( GB , PC ) or OnTheMap( GB , Mek ) ) then begin { First pass: Shakedown anything that's destroyed. } M := GB^.Meks; while M <> Nil do begin if OnTheMap( GB , M ) and IsMasterGear( M ) and Destroyed( M ) then begin cash := cash + SHakeDown( GB , M , 1 , 1 ); end; M := M^.Next; end; { Second pass: Pick up anything we can! } M := GB^.Meks; while M <> Nil do begin M2 := M^.Next; if OnTheMap( GB , M ) and NotDestroyed( M ) and ( M^.G > 0 ) and not IsMasterGear( M ) then begin if IsLegalInvcom( PC , M ) then begin DelinkGear( GB^.Meks , M ); { Clear the item's location values. } StripNAtt( M , NAG_Location ); InsertInvCom( PC , M ); NID := NAttValue( M^.NA , NAG_Narrative , NAS_NID ); if NID <> 0 then SetTrigger( GB , TRIGGER_GetItem + BStr( NID ) ); end else if IsLegalInvCom( Mek , M ) then begin DelinkGear( GB^.Meks , M ); { Clear the item's location values. } StripNAtt( M , NAG_Location ); InsertInvCom( Mek , M ); NID := NAttValue( M^.NA , NAG_Narrative , NAS_NID ); if NID <> 0 then SetTrigger( GB , TRIGGER_GetItem + BStr( NID ) ); end; end; M := M2; end; { Finally, hand the PC any money that was found. } PC := LocatePilot( PC ); if ( PC <> Nil ) and ( Cash > 0 ) then AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , Cash ); end; end; Function DelinkJJang( Camp: CampaignPtr ): GearPtr; { Delink all the components of the scenario, filing them away } { for fututure use. Return a pointer to the surviving PC forces. } var PCForces,Mek,Pilot: GearPtr; begin if DEBUG_ON then DialogMsg( 'DelinkJJang' ); { Step one - Delete obsoleted teams. } { A team will be deleted if it has no members, if it isn't the } { player team or the neutral team, and if it has no wandering } { monsters allocated. } DeleteObsoleteTeams( Camp^.GB ); if DEBUG_ON then DialogMsg( 'Team update complete.' ); { Step two - Remove all models from game board. } { Initialize the PC Forces to Nil. } PCForces := Nil; { Prepare the PCForces for delinkage. } PreparePCForDelink( Camp^.GB ); { Step one-and-a-half: If this is a dynamic scene, and is safe, and pillaging } { is enabled, then pillage away! } if IsInvCom( Camp^.GB^.Scene ) and IsSafeArea( Camp^.GB ) and Pillage_On then begin DoPillaging( Camp^.GB ); end; { Keep processing while there's gears to process. } while Camp^.GB^.Meks <> Nil do begin { Delink the first gear from the list. } Mek := Camp^.GB^.Meks; Pilot := Nil; DelinkGear( Camp^.GB^.Meks , Mek ); { Decide what to do with this gear. } { - If a mecha or disembodied module, remove its pilots. } { - if on player team, store in PCForces } { - if not on player team, store in GB^.Scene } { - if destroyed, delete it } if ( Mek^.G = GG_Mecha ) or ( Mek^.G = GG_Module ) then begin { Delink the pilot, and add to the list. } repeat Pilot := ExtractPilot( Mek ); if Pilot <> Nil then begin PutAwayGear( Camp , Pilot , PCForces ); end; until Pilot = Nil; end; { Send MEK to its destination. } PutAwayGear( Camp , Mek , PCForces ); end; DelinkJJang := PCForces; end; Function WorldPlayer( Camp: CampaignPtr ; Scene: GearPtr; var PCForces: GearPtr ): Integer; { The player is about to explore the world map. Hooray! } { This uses a separate procedure from regular exploration. } var it: Integer; begin DeployJjang( Camp , Scene , PCForces ); it := WorldMapMain( Camp ); PCForces := DelinkJJang( Camp ); { Save the final ComTime in the Campaign. } Camp^.ComTime := Camp^.GB^.ComTime; Camp^.GB^.Scene := Nil; DisposeMap( Camp^.gb ); WorldPlayer := it; end; Function RealScenePlayer( Camp: CampaignPtr ; Scene: GearPtr; var PCForces: GearPtr ): Integer; { Construct then play a scenario. } { Note that this procedure ABSOLUTELY DEFINITELY requires that } { the SCENE gear be defined. } var N: Integer; T: String; begin DeployJJang( Camp , Scene , PCForces ); { Once everything is deployed, save the campaign. } if DoAutoSave then PCSaveCampaign( Camp , GG_LocatePC( Camp^.GB ) , False ); if CurrentControlMode( Camp^.GB ) = 0 then begin if ( Camp^.Source <> Nil ) and ( Camp^.Source^.G = GG_Adventure ) then begin if Camp^.Source^.S = GS_ArenaCampaign then begin if Arena_Use_Tactics then SetNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_TacticsMode ) else SetNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_ClockMode ); end else begin if RPG_Use_Tactics then SetNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_TacticsMode ) else SetNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_ClockMode ); end; end else begin SetNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_ClockMode ); end; end; { Perform some initialization. } { To start with, do a vision check for everyone, } { then set up the display. } UniversalVisionCheck( Camp^.GB ); CombatDisplay( Camp^.GB ); { Set the gameboard's pointer to the campaign. } Camp^.GB^.Camp := Camp; { Set the STARTGAME trigger, and update all props. } SetTrigger( Camp^.GB , TRIGGER_StartGame ); T := 'UPDATE'; CheckTriggerAlongPath( T , Camp^.GB , Camp^.GB^.Meks , True ); { Add some random monsters, if appropriate. } RestockRandomMonsters( Camp^.GB ); { Update the moods. } UpdateMoods( Camp^.GB ); { Do some graphics initializing, if needed. } {$IFNDEF ASCII} InitGraphicsForScene( Camp^.GB ); {$ENDIF} { Now that everything is set, keep playing until we get the signal to quit. } Repeat if CurrentControlMode( Camp^.GB ) = NAV_ClockMode then begin CombatMain( Camp ); end else begin TacticsMain( Camp ); end; until not KeepPlayingSC( Camp^.GB ); { Handle the last pending triggers. } SetTrigger( Camp^.GB , TRIGGER_EndGame ); HandleTriggers( Camp^.GB ); { Clear the control mode. } if ( Camp^.GB <> Nil ) and ( Camp^.GB^.Scene <> Nil ) then SetNAtt( Camp^.GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , 0 ); PCForces := DelinkJJang( Camp ); { Save the final ComTime in the Campaign. } Camp^.ComTime := Camp^.GB^.ComTime; { Get rid of the Focused_On_Mek. } FocusOn( Nil ); { If SCENE is a part of Camp\Source, the map needs to be saved. } { Otherwise dispose of the map and the scene together. } if ( FindGearIndex( Camp^.Source , Camp^.GB^.Scene ) <> -1 ) then begin if ( SAttValue( Camp^.GB^.Scene^.SA , 'NAME' ) <> '' ) and not AStringHasBString( SAttValue( Camp^.GB^.Scene^.SA , 'SPECIAL' ) , SPECIAL_Unchartable ) then begin FreezeLocation( GearName( Scene ) , Camp^.GB , Camp^.Maps ); end; Camp^.gb^.Scene := Nil; end; { Record the returncode before freeing the gameboard. } N := Camp^.gb^.ReturnCode; DisposeMap( Camp^.gb ); RealScenePlayer := N; end; Function ScenePlayer( Camp: CampaignPtr ; Scene: GearPtr; var PCForces: GearPtr ): Integer; { Call the appropriate player routine based on scene type. } begin if ( Scene <> Nil ) and ( Scene^.G = GG_World ) then begin ScenePlayer := WorldPlayer( Camp , Scene , PCForces ); end else begin ScenePlayer := RealScenePlayer( Camp , Scene , PCForces ); end; end; end. gearhead-2-0.701/arenascript.pp000066400000000000000000006400371321074026100163020ustar00rootroot00000000000000unit arenascript; { This unit holds the scripting language for GearHead. } { It's pretty similar to the scripts developed for DeadCold. } { Basically, certain game events will add a trigger to the } { list. In the main combat procedure, if there are any pending } { triggers, they will be checked against the events list to } { see if anything happens. } { Both the triggers and the event scripts will be stored as } { normal string attributes. } { This unit also handles conversations with NPCs, since those } { are written using the scripting language and may use any } { commands available there + a few special commands. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale, {$IFDEF ASCII} vidmenus,vidgfx; {$ELSE} sdlmenus,sdl; {$ENDIF} const NAG_ScriptVar = 0; NAG_SkillCounter = -16; { Counter for skill tests. } Max_Plots_Per_Story = 5; NAG_ArenaData = -18; { Used to store information about mecha arena combat } NAS_ArenaState = 1; { Determines what exactly is happening at the arena now. } NAV_AS_Vacant = 0; { No fight now, no fight scheduled } NAV_AS_Ready = 1; { Start fight next time PC enters scene } NAV_AS_Battle = 2; { Battle in progress } NAV_AS_Win = 3; { PC has won the battle } NAV_AS_Loss = 4; { PC hass lost the battle } NAS_ArenaWins = 2; { # of times PC has won match } NAS_ArenaThreat = 4; { Threat value of enemy mecha } NAS_ArenaForces = 5; { % of generic enemies to fight } NAS_ChallengerID = 6; { NPC challenger present during battle } NAS_ChallengerHome = 7; { Where to return champion after fight } NAS_ArenaRecharge = 8; { Time when next fight can take place } { When playing in arena mode, the following string attributes will be added to the scene } { following battle. } ARENAREPORT_CharDied = 'AR_PCDIED'; ARENAREPORT_CharRecovered = 'AR_PCRECOVERED'; ARENAREPORT_MechaDestroyed = 'AR_MECHADIED'; ARENAREPORT_MechaRecovered = 'AR_MECHARECOVERED'; ARENAREPORT_MechaObtained = 'AR_MECHAOBTAINED'; ARENAREPORT_Personal = 'AR_PERSONAL'; var { This gear pointer will be created if a dynamic scene is requested. } SCRIPT_DynamicEncounter: GearPtr; { **************************************** } { *** INTERACTION GLOBAL VARIABLES *** } { **************************************** } { These variables hold information that may be needed anywhere } { while interaction is taking place, but are undefined if } { interaction is not taking place. } { IntMenu should let procedures know whether or not interaction } { is currently happening or not- if IntMenu <> Nil, we're in the } { middle of a conversation and all other interaction variables } { should have good values. } IntMenu: RPGMenuPtr; { Interaction Menu } I_PC,I_NPC: GearPtr; { Pointers to the PC & NPC Chara gears } I_Rumors: SAttPtr; { List of rumors. } I_Persona: GearPtr; { The conversation currently being used. } Grabbed_Gear: GearPtr; { This gear can be acted upon by } { generic commands. } lancemate_tactics_persona: GearPtr; { Persona for setting lancemate tactics. } BLANK_PERSONA: GearPtr; { Simple persona for default NPCs. } rumor_leads: GearPtr; { Mini-conversations for finding rumors. } NeedGC: Boolean; Procedure SetLancemateOrders( GB: GameBoardPtr ); Function NumLancemateSlots( Adv,PC: GearPtr ): Integer; Procedure BrowseMemoType( GB: GameBoardPtr; Tag: String ); Function BasicSkillTarget( Renown: Integer ): Integer; Function HardSkillTarget( Renown: Integer ): Integer; Function Calculate_Reward_Value( GB: GameBoardPtr; Renown,Percent: LongInt ): LongInt; Function ScriptValue( var Event: String; GB: GameBoardPtr; Scene: GearPtr ): LongInt; Function AS_GetString( Source: GearPtr; Key: String ): String; Function ScriptMessage( msg_label: String; GB: GameBoardPtr; Source: GearPtr ): String; Function NPCScriptMessage( const msg_label: String; GB: GameBoardPtr; NPC, Source: GearPtr ): String; Procedure InvokeEvent( Event: String; GB: GameBoardPtr; Source: GearPtr; var Trigger: String ); Procedure AddLancemate( GB: GameBoardPtr; NPC: GearPtr ); Function AddLancemateFrontEnd( GB: GameBoardPtr; PC,NPC: GearPtr; CanCancel: Boolean ): Boolean; Procedure RemoveLancemate( GB: GameBoardPtr; Mek: GearPtr; DoMessage: Boolean ); Procedure HandleInteract( GB: GameBoardPtr; PC,NPC,Persona: GearPtr ); Procedure DoTalkingWIthNPC( GB: GameBoardPtr; PC,Mek: GearPtr; ByTelephone: Boolean ); Function TriggerGearScript( GB: GameBoardPtr; Source: GearPtr; var Trigger: String ): Boolean; Function CheckTriggerAlongPath( var T: String; GB: GameBoardPtr; Plot: GearPtr; CheckAll: Boolean ): Boolean; Procedure HandleTriggers( GB: GameBoardPtr ); Function StartRescueScenario( GB: GameBoardPtr; PC: GearPtr; Context: String ): Boolean; Procedure DoScriptGC( GB: GameBoardPtr ); implementation uses action,arenacfe,ability,gearutil,ghchars,gearparser,ghmodule,backpack, ghprop,ghweapon,grabgear,interact,menugear,playwright,rpgdice, services,texutil,ui4gh,wmonster,narration,description,skilluse, ghintrinsic,movement,minigame,customization,aibrain,mpbuilder, {$IFDEF ASCII} vidmap,vidinfo; {$ELSE} sdlgfx,sdlmap,sdlinfo; {$ENDIF} const CMD_Chat = -2; CMD_Join = -3; CMD_Quit = -4; CMD_WhereAreYou = -5; CMD_AskAboutRumors = -6; Debug_On: Boolean = False; var script_macros,value_macros,Default_Scene_Scripts: SAttPtr; ASRD_GameBoard: GameBoardPtr; ASRD_MemoMessage: String; local_triggers: SAttPtr; { ****************************** } { *** REDRAW PROCEDURES *** } { ****************************** } Procedure InteractRedraw; { Redraw the screen for whatever interaction is going to go on. } begin CombatDisplay( ASRD_GameBoard ); SetupInteractDisplay( PlayerBlue ); if I_NPC <> Nil then begin DisplayInteractStatus( ASRD_GameBoard , I_NPC , CHAT_React , I_Endurance ); end; GameMsg( CHAT_Message , ZONE_InteractMsg , InfoHiLight ); end; Procedure ArenaScriptReDraw; { Redraw the combat screen for some menu usage. } begin if ASRD_GameBoard <> Nil then CombatDisplay( ASRD_GameBoard ); end; Procedure MemoPageReDraw; { Redraw the combat screen for some menu usage. } begin if ASRD_GameBoard <> Nil then CombatDisplay( ASRD_GameBoard ); SetupMemoDisplay; GameMsg( ASRD_MemoMessage , ZONE_MemoText , StdWhite ); end; Procedure ChoiceReDraw; { Redraw the combat screen for some menu usage. } begin if ASRD_GameBoard <> Nil then CombatDisplay( ASRD_GameBoard ); SetupMemoDisplay; GameMsg( ASRD_MemoMessage , ZONE_MemoMenu , StdWhite ); end; { **************************** } { *** EVERYTHING ELSE *** } { **************************** } Function BasicSkillTarget( Renown: Integer ): Integer; { Return an appropriate target for skill rolls for someone of the listed renown. } var it: Integer; begin it := Renown div 8 + 3; if it < 5 then it := 5; BasicSkillTarget := it; end; Function HardSkillTarget( Renown: Integer ): Integer; { Return a difficult target for skill rolls for someone of the listed renown. } var it: Integer; begin it := Renown div 7 + 10; if it < 9 then it := 9; HardSkillTarget := it; end; Function SocSkillTarget( GB: GameBoardPtr; Renown: Integer ): Integer; { Return a social difficult target for skill rolls based on Renown and modified } { by the relationship between I_PC and I_NPC. } var it,react: Integer; begin it := Renown div 7 + 7; if it < 10 then it := 10; { If the PC and NPC exist, apply the special modifier. } if ( I_PC <> Nil ) and ( I_NPC <> Nil ) then begin react := ReactionScore( GB^.Scene , I_PC , I_NPC ); it := it - ( react div 10 ); end; SocSkillTarget := it; end; Function CreateRumorList( GB: GameBoardPtr; Rumor_Source: GearPtr; SkRoll: Integer; DoHarvest: Boolean; var Rumor_Error: Boolean; const Rumemo_Lead,Rumor_Head: String ): SAttPtr; { RUMOR_HEAD is the rumor type to collect: RUMORs or RUMEMOs. } { RUMEMO_LEAD is the leading message appended to a rumor if we're } { harvesting them for RUMEMOs. } { If DoHarvest is TRUE, all found rumors will be changed to RUMEMOs. } { ...if FALSE, then SkRoll is ignored and all messages are returned. } { Search through the adventure, revealing rumors as you go. When a rumor } { is discovered, do the following: } { - Check to see if SkRoll is high enough to reveal it. } { - If so, add it to the list of rumors. } { - Also convert the original rumor to a rumor memo. } { If no rumors are found at all, set RUMOR_ERROR to TRUE; otherwise FALSE. } var InfoList: SAttPtr; Function RumorSKTarget( Part: gearPtr ): Integer; { Return the skill roll needed to learn this rumor. } var Plot: GearPtr; it: Integer; begin { If we're dealing with a plot-based rumor, get the difficulty number from the plot. } Plot := PlotMaster( GB , Part ); if Plot <> Nil then begin { Use the difficulty rating for this plot. } it := SocSkillTarget( GB , NAttValue( Plot^.NA , NAG_Narrative , NAS_DifficultyLevel ) ); end else begin it := 5; end; { In order to keep the process somewhat unpredictable, randomize the target } { just a bit. } it := it - 5 + RollStep( 3 ); if Random( 3 ) = 1 then it := it + Random( 4 ); RumorSKTarget := it; end; Procedure AddThisRumor( Part: GearPtr; Rumor: SAttPtr ); { Add this rumor to the list... maybe. } var msg,rh: String; begin msg := RetrieveAString( Rumor^.Info ); if DoHarvest then begin if SkRoll > RumorSkTarget( Part ) then begin { This rumor has been retrieved. } StoreSAtt( InfoList , msg ); rh := RetrieveAPreamble( rumor^.Info ); Rumor^.Info := 'RUMEMO' + Copy( rh , Length( Rumor_Head ) + 1 , Length( rh ) ) + ' <' + RuMemo_Lead + ' ' + msg + '>'; end; end else begin { We aren't converting to RUMEMO right now. Ignore the skill roll } { and simply return the message without changing anything. } StoreSAtt( InfoList , msg ); end; { Set RUMOR_ERROR to FALSE, since we just found one. } RUMOR_ERROR := FALSE; end; Procedure GetRumorFromGear( P: GearPtr ); { Retrieve the rumor info from this gear, without caring about } { what kind of gear it is. Well, for the most part, anyhow... } var Rumor: SAttPtr; Level: LongInt; begin { First add the basic rumor. } Rumor := FindSAtt( P^.SA , Rumor_Head ); if Rumor <> Nil then AddThisRumor( P , Rumor ); { Next add the quest rumor. } Level := NAttValue( P^.NA , NAG_Narrative , NAS_PlotID ); if Level < 0 then begin Rumor := FindSAtt( P^.SA , Rumor_Head + BStr( NAttValue( FindRoot( GB^.Scene )^.NA , NAG_PlotStatus , Level ) ) ); if Rumor <> Nil then AddThisRumor( P , Rumor ); end else if (( P^.G = GG_Persona ) or ( P^.G = GG_MetaScene )) and ( P^.Parent <> Nil ) and ( P^.Parent^.G = GG_Plot ) then begin { Finally add the plot rumor. } Rumor := FindSAtt( P^.SA , Rumor_Head + BStr( NAttValue( FindRoot( GB^.Scene )^.NA , NAG_PlotStatus , Level ) ) ); if Rumor <> Nil then AddThisRumor( P , Rumor ); end; end; Procedure RumorWorkup( P: GearPtr ); Forward; Procedure ExtractData( P: GearPtr ); { Store all relevant info from PART. } { If P is of certain types, we're gonna have to harvest the data from } { its associated bits. Characters also need the data from their Personas, } { gates to metascenes need to check there, and scenes get faction data. } var Persona: GearPtr; begin if ( P <> Rumor_Source ) and ( P^.G <> GG_Persona ) then begin if P <> GB^.Scene then GetRumorFromGear( P ); if P^.G = GG_Character then begin Persona := SeekPersona( GB , NAttValue( P^.NA , NAG_Personal , NAS_CID ) ); if Persona <> Nil then begin { Previously we'd just collect the rumor from the persona } { and be done with it, but since Quests have been introduced } { the rumor associated with a given NPC can change depending } { on quest state. } GetRumorFromGear( Persona ); end; end else if ( P^.G = GG_MetaTerrain ) and ( P^.Stat[ STAT_Destination ] < 0 ) then begin { Find the metascene, and do a complete rumor work-up of it. } Persona := FindActualScene( GB , P^.Stat[ STAT_Destination ] ); if Persona <> Nil then begin RumorWorkup( Persona ); end; end; end; end; Procedure RumorWorkup( P: GearPtr ); { Do a complete rumor workup on P, gathering info from it } { and all its child gears. } var P2: GearPtr; begin if P = Nil then Exit; ExtractData( P ); P2 := P^.SubCom; while P2 <> Nil do begin RumorWorkup( P2 ); P2 := P2^.Next; end; P2 := P^.InvCom; while P2 <> Nil do begin RumorWorkup( P2 ); P2 := P2^.Next; end; end; var Part: GearPtr; begin { Assume an error until we find a rumor to prove us wrong. } Rumor_Error := TRUE; InfoList := Nil; Part := FindRootScene( GB^.Scene ); RumorWorkup( Part ); Part := GB^.Meks; while Part <> Nil do begin ExtractData( Part ); Part := Part^.Next; end; CreateRumorList := InfoList; end; Function RevealRumors( GB: GameBoardPtr; NPC: GearPtr; SkRoll: Integer; var Rumor_Error: Boolean ): SAttPtr; { Reveal some rumors! Call the CreateRumorList procedure with a standard reveal. } begin RevealRumors := CreateRumorList( GB, NPC, SkRoll, TRUE, Rumor_Error, ReplaceHash( MsgString( '#SaidThat' ) , PilotName( NPC ) ), 'RUMOR' ); end; Function ReviewRumorMemos( GB: GameBoardPtr ): SAttPtr; { Create the list of rumor memos. } var Rumor_Error: Boolean; { A dummy variable. } begin ReviewRumorMemos := CreateRumorList( GB, Nil, 0, FALSE, Rumor_Error, '', 'RUMEMO' ); end; Procedure BrowseMemoType( GB: GameBoardPtr; Tag: String ); { Create a list, then browse the memos based upon this } { TAG type. Possible options are MEMO, NEWS, and EMAIL. } var MemoList,M: SAttPtr; Adv: GearPtr; Procedure HarvestPlotMemos( LList: SAttPtr ); { This list may contain plot memos. How to tell? The first four } { characters will be "MEMO". It's just like harvesting the history. } begin while LList <> Nil do begin if UpCase( Copy( LList^.Info , 1 , 4 ) ) = 'MEMO' then begin StoreSAtt( MemoList , RetrieveAString( LList^.Info ) ); end; LList := LList^.Next; end; end; Procedure CreateMemoList( Part: GearPtr; Tag: String ); { Look through all gears in the structure recursively, } { looking for MEMO string attributes to store in our list. } var msg: String; QID: LongInt; begin while Part <> Nil do begin if Part^.G <> GG_AbsolutelyNothing then begin if ( tag = 'MEMO' ) and ( Part^.G = GG_Plot ) then begin { This is a plot. It may have subplot memos. These are } { memos that have the PLOTID attached to their butts. Why? } { Because I realized, somewhat late, that a plot which can } { contain multiple narrative threads really needs multiple } { memos as well. } HarvestPlotMemos( Part^.SA ); end else begin { Not a plot. Just do the regular harvesting work, then. } msg := SAttValue( Part^.SA , Tag ); if msg <> '' then StoreSAtt( MemoList , msg ); { This part may also have a quest-related message attached } { to it. See if that's so. } QID := NAttValue( Part^.NA , NAG_Narrative , NAS_PlotID ); if ( QID <> 0 ) then begin msg := SAttValue( Part^.SA , Tag + '_' + BStr( NAttValue( Adv^.NA , NAG_PlotStatus , Qid ) ) ); if msg <> '' then StoreSAtt( MemoList , msg ); end; end; CreateMemoList( Part^.SubCom , Tag ); CreateMemoList( Part^.InvCom , Tag ); end; { if Part = AbsolutelyNothing } Part := Part^.Next; end; end; Procedure AddQuestMemos; { Quest memos work a bit differently than other memos. First, } { they only appear so long as the quest they're assigned to is } { active (i.e. it has a nonnegative QID). } var SA,SA2: SAttPtr; msg_head: String; qid: LongInt; begin SA := Adv^.SA; while SA <> Nil do begin SA2 := SA^.next; { If this string attribute is potentially a quest memo, } { try to extract its QuestID. If this memo is no longer } { valid then delete it. } if HeadMatchesString( 'MEMO_' , SA^.Info ) then begin msg_head := RetrieveAPreamble( SA^.Info ); msg_head := Copy( msg_head , 6 , Length( msg_head ) ); qid := ExtractValue( msg_head ); if ( qid <> 0 ) and ( NAttValue( Adv^.NA , NAG_PlotStatus , Qid ) > -1 ) then begin { Add it to the list. } StoreSAtt( MemoList , RetrieveAString( SA^.Info ) ); end else begin { Invalid quest memo. Get rid of it. } RemoveSAtt( Adv^.SA , SA ); end; end; SA := SA2; end; end; Function PlaceMemoPhoneCall( PC: GearPtr; MMsg: String ): Boolean; { We want to make a phone call to someone mentioned in this memo. } { Search through all the NPCs on the game board and within the current city. } { Add them to a menu. Then, query the menu, and do talking with the selected NPC. } { Return TRUE if a conversation was had, or FALSE otherwise. } Procedure CheckAlongList( RPM: RPGMenuPtr; LList: GearPtr ); { Check along this list for NPCs mentioned in the memo, recursively } { searching through children as well. } { Add any good NPCs found to the menu, using their CID as the key. } var Name: String; CID: LongInt; begin while LList <> Nil do begin if LList^.G = GG_Character then begin { This is a character. Is it somebody we're looking for? } Name := GearName( LList ); CID := NAttValue( LList^.NA , NAG_Personal , NAS_CID ); if ( CID <> 0 ) and ( Pos( Name , MMsg ) > 0 ) and CanContactByPhone( GB , LList ) then begin AddRPGMenuItem( RPM , GearName( LList ) , CID ); end; end else begin { Not a character. Recurse like mad! } CheckAlongList( RPM , LList^.SubCom ); CheckAlongList( RPM , LList^.InvCom ); end; LList := LList^.Next; end; end; var RPM: RPGMenuPtr; city,NPC: GearPtr; CID: LongInt; begin { Step One- Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoMenu ); CheckAlongList( RPM , GB^.Meks ); City := FindRootScene( GB^.Scene ); if City <> Nil then begin CheckAlongList( RPM , City^.SubCom ); CheckAlongList( RPM , City^.InvCom ); end; RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); if RPM^.NumItem > 0 then begin AddRPGMenuItem( RPM , MsgString( 'Cancel' ) , -1 ); end else begin AddRPGMenuItem( RPM , MsgString( 'MEMO_CALL_NoPeople' ) , -1 ); end; { Step Two- Query the menu and locate the NPC. } CID := SelectMenu( RPM , @MemoPageRedraw ); DisposeRPGMenu( RPM ); { Step Three- Pass the request along to HandleInteract. } if CID > -1 then begin NPC := GG_LocateNPC( CID , GB , GB^.Scene ); if NPC <> Nil then begin DoTalkingWithNPC( GB , PC , NPC , True ); end; end else NPC := Nil; PlaceMemoPhoneCall := NPC <> Nil; end; Procedure BrowseList; { Actually browse the created list. } var RPM: RPGMenuPtr; N,D: Integer; PC: GearPtr; begin { Locate the PC. We need it for the PComm capability. } PC := GG_LocatePC( GB ); if MemoList <> Nil then begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoMenu ); AddRPGMenuItem( RPM , MsgString( 'MEMO_Next' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'MEMO_Prev' ) , 2 ); if ( PC <> Nil ) and HasPCommCapability( PC , PCC_Phone ) then AddRPGMenuItem( RPM , MsgString( 'MEMO_Call' ) , 3 ); AddRPGMenuKey( RPM , KeyMap[ KMC_East ].KCode , 1 ); AddRPGMenuKey( RPM , KeyMap[ KMC_West ].KCode , 2 ); AlphaKeyMenu( RPM ); RPM^.Mode := RPMNoCleanup; N := 1; repeat M := RetrieveSAtt( MemoList , N ); ASRD_GameBoard := GB; ASRD_MemoMessage := M^.Info; D := SelectMenu( RPM , @MemoPageRedraw ); if D = 1 then begin N := N + 1; if N > NumSAtts( MemoList ) then N := 1; end else if D = 2 then begin N := N - 1; if N < 1 then N := NumSAtts( MemoList ); end else if D = 3 then begin { We want to place a phone call to someone mentioned } { in this memo. Make it so. } PlaceMemoPhoneCall( PC , M^.Info ); D := -1; end; until ( D = -1 ) or not KeepPlayingSC( GB ); DisposeSAtt( MemoList ); DisposeRPGMenu( RPM ); end; end; Function NoMemoError: String; { Return a string which will explain to the user that there are } { no memos of the selected type. } var msg: String; begin msg := MsgString( 'MEMO_No_' + Tag ); if msg = '' then msg := ReplaceHash( MsgString( 'MEMO_None' ) , LowerCase( Tag ) ); NoMemoError := msg; end; begin { Error check first - we need the GB and the scene for this. } if ( GB = Nil ) or ( GB^.Scene = Nil ) then Exit; tag := UpCase( Tag ); MemoList := Nil; Adv := FindRoot( GB^.Scene ); if ( Tag = 'RUMEMO' ) then begin MemoList := ReviewRumorMemos( GB ); end else begin CreateMemoList( Adv , Tag ); end; if Tag = 'MEMO' then AddQuestMemos; { Sort the memo list. } if MemoList <> Nil then SortStringList( MemoList ) else StoreSAtt( MemoList , NoMemoError ); BrowseList; end; Function YesNoMenu( GB: GameBoardPtr; Prompt,YesMsg,NoMsg: String ): Boolean; { This will open up a small window in the middle of the map } { display, then prompt the user for a choice. } { Return TRUE if the "yes" option was selected, or FALSE if } { the "no" option was selected. } { This function performs no screen cleanup. } var rpm: RPGMenuPtr; N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoMenu ); AddRPGMenuItem( RPM , YesMsg , 1 ); AddRPGMenuItem( RPM , NoMsg , -1 ); RPM^.Mode := RPMNoCancel; ASRD_GameBoard := GB; ASRD_MemoMessage := Prompt; N := SelectMenu( RPM , @MemoPageRedraw ); DisposeRPGMenu( RPM ); { Do cleanup before branching. } CombatDisplay( GB ); YesNoMenu := N <> -1; end; Procedure SetLancemateOrders( GB: GameBoardPtr ); { Go through the lancemates, and assign any orders they might need. } var PCUID,DefOrder: LongInt; mek: gearPtr; begin { Step one- find the PC's UID. } mek := GB^.meks; while mek <> Nil do begin if ( NAttValue( mek^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and IsMasterGear( mek ) then begin { This must be the PC. } PCUID := NAttValue( mek^.NA , NAG_EpisodeData , NAS_UID ); end; mek := mek^.Next; end; { Step two- look for the lancemates and set their orders. } mek := GB^.meks; while mek <> Nil do begin if ( NAttValue( mek^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and IsMasterGear( mek ) then begin DefOrder := NAttValue( mek^.NA , NAG_Personal , NAS_LancemateOrders ); if DefOrder = NAV_Passive then begin SetNAtt( mek^.NA , NAG_EpisodeData , NAS_Orders , NAV_Passive ); end else if DefOrder = NAV_Follow then begin SetNAtt( mek^.NA , NAG_EpisodeData , NAS_Orders , NAV_Follow ); SetNAtt( mek^.NA , NAG_EpisodeData , NAS_ATarget , PCUID ); end else begin SetNAtt( mek^.NA , NAG_EpisodeData , NAS_Orders , NAV_SeekAndDestroy ); end; end; mek := mek^.Next; end; end; Function NumLancemateSlots( Adv,PC: GearPtr ): Integer; { Return the number of freely-selected lancemates this PC can have. } var N: Integer; begin { You get one lancemate for free. } N := 1; { You can earn extra lancemates via merit badges. } if HasMeritBadge( Adv , NAS_MB_Lancemate2 ) then begin Inc( N ); if HasMeritBadge( Adv, NAS_MB_Lancemate3 ) then Inc( N ); end; { Or, you can take a talent for one extra. } if HasTalent( PC , NAS_Entourage ) then begin Inc( N ); end; NumLancemateSlots := N; end; Function CanJoinLance( GB: GameBoardPtr; PC,NPC: GearPtr ): Boolean; { Return TRUE if NPC can join the lance right now, or FALSE otherwise. } var ERen: Integer; { Lancemate Points needed, Effective Renown } CanJoin: Boolean; begin ERen := NAttValue( PC^.NA , NAG_CharDescription , NAS_Renowned ); if ERen < 15 then ERen := 15; ERen := ERen + CStat( PC , STAT_Charm ); CanJoin := True; if ( NPC = Nil ) or ( NPC^.G <> GG_Character ) then begin CanJoin := False; end else if NAttValue( NPC^.NA , NAG_CharDescription , NAS_Renowned ) > ERen then begin CanJoin := False; end else if ( GB <> Nil ) and ( ReactionScore( GB^.Scene , PC , NPC ) < 10 ) then begin CanJoin := False; end else if ( GB <> Nil ) and not ( OnTheMap( GB , FindRoot( NPC ) ) and IsFoundAlongTrack( GB^.Meks , FindRoot( NPC ) ) ) then begin { Can only join if in the same scene as the PC. } CanJoin := False; end else if PersonaUsedByQuest( FindRoot( GB^.Scene ) , NPC ) then begin CanJoin := False; end; CanJoinLance := CanJoin; end; Function SceneName( GB: GameBoardPtr; ID: Integer; Exact: Boolean ): String; { Find the name of the scene with the given ID. If no such } { scene can be found, return a value that should let the player } { know an error has been commited. } { If EXACT=TRUE, use the EXACT_NAME attribute instead of the } { regular name. The reason for this is that sometimes there's } { some ambiguity with the common name of a scene: if I say "Cayley } { Rock", do I mean the city in general or the main station } { specifically? } var msg: String; Part: GearPtr; begin if ( GB = Nil ) or ( GB^.Scene = Nil ) then begin SceneName := 'XXX'; end else begin { Look for the scene along the subcomponents of the } { adventure. This is to make sure we don't accidentally } { pick a dynamic scene with the right ID. } { Also, if we have a metascene instead of a regular scene, then } { we want the name of the entrance instead of the name of the } { scene itself. } if ID > 0 then begin Part := FindActualScene( GB , ID ); end else begin Part := FindSceneEntrance( FindRoot( GB^.Scene ) , GB , ID ); end; if Exact then begin msg := SAttValue( Part^.SA , 'EXACT_NAME' ); if msg = '' then msg := GearName( Part ); SceneName := msg; end else begin SceneName := GearName( Part ); end; end; end; Function FindRandomMekID( GB: GameBoardPtr; Team: Integer ): LongInt; { Locate a random mek belonging to TEAM. } var NumMeks,N,T,MID: Integer; Mek: GearPtr; begin { Start out by finding out how many meks belong to this team } { anyways. } NumMeks := NumOperationalMasters( GB , Team ); MID := 0; { If there are actually members on this team, select one randomly. } if NumMeks > 0 then begin { Decide what mek to take, and initialize the } { search variables. } N := Random( NumMeks ) + 1; T := 0; Mek := GB^.Meks; while Mek <> Nil do begin if ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = Team ) and GearOperational( Mek ) then begin Inc( T ); if T = N then MID := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_UID ); end; Mek := Mek^.Next; end; end; FindRandomMekID := MID; end; Function FindRandomPilotID( GB: GameBoardPtr; Team: Integer ): LongInt; { Locate a random pilot belonging to TEAM. } var NumMeks,N,T,MID: Integer; Mek,P: GearPtr; begin { Start out by finding out how many meks belong to this team } { anyways. } NumMeks := NumOperationalMasters( GB , Team ); MID := 0; { If there are actually members on this team, select one randomly. } if NumMeks > 0 then begin { Decide what mek to take, and initialize the } { search variables. } N := Random( NumMeks ) + 1; T := 0; Mek := GB^.Meks; while Mek <> Nil do begin if ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = Team ) and GearOperational( Mek ) then begin Inc( T ); if T = N then begin P := LocatePilot( Mek ); if P <> Nil then MID := NAttValue( P^.NA , NAG_EpisodeData , NAS_UID ); end; end; Mek := Mek^.Next; end; end; FindRandomPilotID := MID; end; Function FindRootUID( GB: GameBoardPtr; ID: LongInt ): LongInt; { Find the ID of the root of the specified gear. } var Part: GearPtr; begin { First, find the part being pointed to. } Part := LocateMekByUID( GB , ID ); { Locate its root. } if Part <> Nil then begin Part := FindRoot( Part ); { Return the root's UID. } FindRootUID := NAttValue( Part^.NA , NAG_EpisodeData , NAS_UID ); { If there was an error locating the part, return 0. } end else FindRootUID := 0; end; Function NumPCMeks( GB: GameBoardPtr ): Integer; { Return the number of mecha belonging to team 1. } { It doesn't matter if they're on the board or not, nor whether or } { not they are destroyed. } var M: GearPtr; N: Integer; begin N := 0; if GB <> Nil then begin M := GB^.Meks; while M <> Nil do begin { If this is a mecha, and it belongs to team 1, } { increment the counter. } if ( M^.G = GG_Mecha ) and ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then Inc( N ); M := M^.Next; end; end; NumPCMeks := N; end; Function FindPCScale( GB: GameBoardPtr ): Integer; { Return the scale of the PC. Generally this will be 0 if the } { PC is on foot, 1 or 2 if the PC is in a mecha, unless the PC } { is a storm giant or a zentradi in which case anything goes. } var PC: GearPtr; begin PC := GG_LocatePC( GB ); if PC <> Nil then begin FindPCScale := FindRoot( PC )^.Scale; end else begin FindPCScale := 0; end; end; Function Calculate_Reward_Value( GB: GameBoardPtr; Renown,Percent: LongInt ): LongInt; { Return an appropriate reward value, based on the listed } { threat level and percent scale. } const Min_Reward_Value = 3000; var RV: LongInt; begin { Calculate the base reward value. } RV := Calculate_Threat_Points( Renown + 10 , 100 ) div 64 * Percent div 100; if RV < Min_Reward_Value then RV := Min_Reward_Value; { Modify this for the PC's talents. } if GB <> Nil then begin if TeamHasTalent( GB , NAV_DefPlayerTeam , NAS_BusinessSense ) then RV := ( RV * 5 ) div 4; end; Calculate_Reward_Value := RV; end; Function Calculate_Asking_Price( GB: GameBoardPtr; Renown,Percent: LongInt ): LongInt; { Return an appropriate asking price, based on the listed } { threat level, percent scale, and reaction score with I_NPC. } const Min_Asking_Price = 3000; var RV,React: LongInt; begin { Calculate the base reward value. } RV := Calculate_Threat_Points( Renown , 25 ) * Percent div 100; if RV < Min_Asking_Price then RV := Min_Asking_Price; { Modify this for the reaction score. } if ( I_NPC <> Nil ) and ( I_PC <> Nil ) and ( GB <> Nil ) then begin React := ReactionScore( GB^.Scene , I_PC , I_NPC ); if React < 0 then RV := RV * ( 100 - 2 * React ) div 100 else if React > 20 then RV := RV * ( 220 - React ) div 200; end; Calculate_Asking_Price := RV; end; Function Count_Faction_Buddies( GB: GameBoardPtr; FacID: Integer ): Integer; { Return the number of positive relationships the PC has with this } { particular faction. } function CountBuddies( LList: GearPtr ): LongInt; { Count the number of buddies along this path. } var N: LongInt; begin N := 0; while LList <> Nil do begin if ( LList^.G = GG_Character ) and ( NAttValue( LList^.NA , NAG_Relationship , 0 ) > 0 ) and ( GetFactionID( LList ) = FacID ) and NotDestroyed( LList ) then Inc( N ); N := N + CountBuddies( LList^.SubCom ); N := N + CountBuddies( LList^.InvCom ); LList := LList^.Next; end; CountBuddies := N; end; var Adv: GearPtr; begin if ( GB = Nil ) or ( GB^.Scene = Nil ) then Exit( 0 ); Adv := FindRoot( GB^.Scene ); Count_Faction_Buddies := CountBuddies( GB^.Meks ) + CountBuddies( Adv^.SubCom ) + CountBuddies( Adv^.InvCom ); end; Function FindLocalMacro( const cmd: String; GB: GameBoardPtr; Source: GearPtr ): String; { Locate the local macro described by "cmd". } var it: String; Plot: GearPtr; begin it := SAttValue( Source^.SA , cmd ); if it = '' then begin Plot := PlotMaster( GB , Source ); if Plot <> Nil then it := SAttValue( Plot^.SA , cmd ); if it = '' then begin Plot := StoryMaster( GB , Source ); if Plot <> Nil then it := SAttValue( Plot^.SA , cmd ); end; end; if it = '' then DialogMsg( 'ERROR: Local macro ' + cmd + ' not found.' ); FindLocalMacro := it; end; Procedure InitiateMacro( GB: GameBoardPtr; Source: GearPtr; var Event: String; ProtoMacro: String ); { Initialize the provided macro, and stick it to the head of } { event. To initialize just go through it word by word, replacing } { all question marks with words taken from EVENT. } function NeededParameters( cmd: String ): Integer; { Return the number of parameters needed by this function. } const NumStandardFunctions = 20; StdFunName: Array [1..NumStandardFunctions] of string = ( '-', 'GNATT', 'GSTAT', '?PILOT', '?MECHA', 'SKROLL', 'THREAT', 'REWARD', 'ESCENE', 'RANGE', 'FXPNEEDED','*','MAPTILE','PCSKILLVAL','CONCERT', 'SKILLTAR', 'HARDSKILLTAR', 'SOCSKILLTAR', 'PRICE', 'FACBUDDIES' ); StdFunParam: Array [1..NumStandardFunctions] of byte = ( 1,2,1,1,1, 1,2,2,1,2, 1,2,2,2,2, 1,1,1,2,1 ); var it,T: Integer; Mac: String; begin it := 0; CMD := UpCase( CMD ); { If a hardwired function, look up the number of parameters } { from the above list. } for t := 1 to NumStandardFunctions do begin if CMD = StdFunName[ t ] then it := StdFunParam[ t ]; end; { If a macro function, derive the number of parameters } { from the number of ?s in the macro. } if it = 0 then begin Mac := SAttValue( Value_Macros , Cmd ); if ( Mac = '' ) and ( Cmd <> '' ) and ( Cmd[1] = '&' ) then Mac := FindLocalMacro( cmd , GB , Source ); if Mac <> '' then begin for t := 1 to Length( Mac ) do begin if Mac[ T ] = '?' then Inc( it ); end; end; end; NeededParameters := it; end; function GetSwapText: String; { Grab the swap text from EVENT. If it's a function } { that we just grabbed, better grab the parameters as } { well. Oh, bother... } var it,cmd: String; N: Integer; begin { Grab the beginning of the swap text. } it := ExtractWord( Event ); { Check to see if it's a function. Get rid of the - first } { since it'll screw up our check. } cmd := it; if ( Length( cmd ) > 1 ) and ( cmd[1] = '-' ) then DeleteFirstChar( cmd ); N := NeededParameters( cmd ); While N > 0 do begin it := it + ' ' + GetSwapText(); Dec( N ); end; GetSwapText := it; end; var Cmd,NewMacro,LastWord: String; begin NewMacro := ''; LastWord := ''; while ProtoMacro <> '' do begin cmd := ExtractWord( ProtoMacro ); if cmd = '?' then begin LastWord := GetSwapText; cmd := LastWord; end else if cmd = '!' then begin cmd := LastWord; end; NewMacro := NewMacro + ' ' + cmd; end; Event := NewMacro + ' ' + Event; end; Procedure InitiateLocalMacro( GB: GameBoardPtr; var Event , cmd: String; Source: GearPtr ); { Attempt to initiate a local macro. The local macro will be located } { in Source, or its plot, or its story. } var it: String; begin it := FindLocalMacro( cmd , GB , Source ); if it <> '' then InitiateMacro( GB , Source , Event , it ); end; Function SV_Range( GB: GameBoardPtr; UID1,UID2: LongInt ): Integer; { Locate the two gears pointed to by the UIDs, then calculate } { the range between them. If one or both are NIL, or if one or } { both are off the map, return 0. } var M1,M2: GearPtr; begin { Error check. } if GB = Nil then Exit( 0 ); M1 := LocateMekByUID( GB , UID1 ); M2 := LocateMekByUID( GB , UID2 ); if ( M1 <> Nil ) and OnTheMap( GB , M1 ) and ( M2 <> Nil ) and OnTheMap( GB , M2 ) then begin SV_Range := Range( GB , M1 , M2 ); end else begin SV_Range := 0; end; end; Function SV_PCSkillVal( GB: GameBoardPtr; Skill,Stat: Integer ): Integer; { Return the PC's base skill value. This used to be easy until those } { stupid lancemates came along... Check all PCs and lancemates, and } { return the highest value. } var M: GearPtr; HiSkill,T: Integer; begin { Error check. } if GB = Nil then Exit( 0 ); M := GB^.Meks; HiSkill := 0; while M <> Nil do begin T := NAttValue( M^.NA , NAG_Location , NAS_Team ); if GearActive( M ) and ( ( T = NAV_DefPlayerTeam ) or ( T = NAV_LancemateTeam ) ) then begin T := SkillValue( M , Skill , Stat ); if T > HiSkill then HiSkill := T; end; M := M^.Next; end; SV_PCSkillVal := HiSkill; end; Function SV_WorldID( GB: GameBoardPtr ): Integer; { Return the ID number of the current world. } var Scene: GearPtr; begin Scene := FindWorld( GB , GB^.Scene ); if Scene <> Nil then SV_WorldID := Scene^.S else SV_WorldID := 0; end; Function AV_ProcessConcert( GB: GameBoardPtr; AudienceSize,SkillTarget: Integer ): Integer; { A concert is beginning! Yay! } var PC: GearPtr; begin PC := GG_LocatePC( GB ); AV_ProcessConcert := DoConcert( GB , PC , AudienceSize , SkillTarget ); end; Function ScriptValue( var Event: String; GB: GameBoardPtr; Scene: GearPtr ): LongInt; { Normally, numerical values will be stored as constants. } { Sometimes we may want to do algebra, or use the result of } { scenario variables as the parameters for commands. That's } { what this function is for. } { *** IMPORTANT *** } { When adding new functions, remember to add them to the InitiateMacro } { procedure as well. } { *** IMPORTANT *** } var Old_Grabbed_Gear,PC: GearPtr; VCode,VC2: LongInt; SV: LongInt; SMsg,S2: String; begin { Save the grabbed gear, to restore it later. } Old_Grabbed_Gear := Grabbed_Gear; SMsg := UpCase(ExtractWord( Event )); SV := 0; { Start by checking for value macros. } if SMsg = '' then begin { An empty string is a bad thing. } SV := 0; DialogMsg( 'WARNING: Blank scriptvalue from ' + GearName( Scene ) ); end else if SAttValue( Value_Macros , SMsg ) <> '' then begin { Install the macro, then re-call this procedure to get } { the results. } InitiateMacro( GB , scene , Event , SAttValue( Value_Macros , SMsg ) ); SV := ScriptValue( Event , gb , scene ); { If the command starts with a &, this means it's a local macro. } end else if Smsg[1] = '&' then begin InitiateLocalMacro( GB , Event , SMsg , Scene ); SV := ScriptValue( Event , gb , scene ); end else if ( SMsg = 'GNATT' ) then begin { Get a Numeric Attribute from the currently grabbed gear. } VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); if Grabbed_Gear <> Nil then begin SV := NAttValue( Grabbed_Gear^.NA , VCode , VC2 ); end; end else if ( SMsg = 'GSTAT' ) then begin { Get a Numeric Attribute from the currently grabbed gear. } VCode := ScriptValue( Event , GB , Scene ); if ( Grabbed_Gear <> Nil ) then begin if ( VCode >= 1 ) and ( VCode <= NumGearStats ) then begin SV := Grabbed_Gear^.Stat[ VCode ]; end; end; end else if ( SMsg = 'GV' ) then begin { Get the V descriptor from the currently grabbed gear. } if ( Grabbed_Gear <> Nil ) then begin SV := Grabbed_Gear^.V; end; end else if ( SMsg = 'GS' ) then begin { Get the S descriptor from the currently grabbed gear. } if ( Grabbed_Gear <> Nil ) then begin SV := Grabbed_Gear^.S; end; end else if ( SMsg = 'GSCENE' ) then begin { Get the sceneID of the grabbed gear. } if ( Grabbed_Gear <> Nil ) then begin SV := FindSceneID( Grabbed_Gear , GB ); end; end else if ( SMsg = 'WORLDID' ) then begin { Return the ID of the current world. } SV := SV_WorldID( GB ); end else if ( SMsg = 'FXPNEEDED' ) then begin { Return how many faction XP points needed for next level. } VCode := ScriptValue( Event , GB , Scene ); SV := ( VCode + 1 ) * 5; end else if ( SMsg = 'CONCERT' ) then begin VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := AV_ProcessConcert( GB , VCode , VC2 ); end else if ( SMsg = 'MAPTILE' ) then begin { Return the terrain of the requested map tile. } VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); if ( GB <> Nil ) and OnTheMap( GB , VCode , VC2 ) then begin SV := TileTerrain( GB , VCode , VC2 ); end else begin SV := 0; end; end else if Attempt_Gear_Grab( SMsg , Event , GB , Scene ) then begin { The correct Grabbed_Gear was set by the above call, } { so just recurse to find the value we want. Am I looming } { closer to functional programming or something? } SV := ScriptValue( Event , gb , scene ); end else if ( SMsg = 'COMTIME' ) then begin { Return the current combat time. } if ( GB <> Nil ) then begin SV := GB^.ComTime; end; end else if ( SMsg = 'NEXTDAY' ) then begin { Return the start of the next day. } if ( GB <> Nil ) then begin SV := GB^.ComTime + 86400 - GB^.ComTime mod 86400; end; end else if ( SMsg = 'PCSKILLVAL' ) then begin { Return the PC team's highest skill value. } VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := SV_PCSkillVal( GB , VCode , VC2 ); end else if ( SMsg = 'SCENEID' ) then begin { Return the current scene's unique ID. } { Only do this if we're in the real scene! } SV := RealSceneID( GB^.Scene ); end else if ( SMsg = 'REACT' ) then begin { Return the current reaction score between PC & NPC. } if ( IntMenu <> Nil ) then begin if GB = Nil then begin SV := ReactionScore( Nil , I_PC , I_NPC ); end else begin SV := ReactionScore( GB^.Scene , I_PC , I_NPC ); end; end; end else if ( SMsg = 'SKROLL' ) then begin { Return a skill roll from the PC. } VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); if ( VCode >= 1 ) and ( VCode <= NumSkill ) then begin SV := SkillRoll( GB , GG_LocatePC( GB ) , VCode , VC2 , 0 , 0 , True , True ); end else begin DialogMsg( 'ERROR: Illegal Skill Roll ' + BStr( VCode ) + ': ' + Event ); SV := 0; end; PC := GG_LocatePC( GB ); if PC <> Nil then DoleSkillExperience( PC , VCode , 5 ); end else if SMsg = 'PCMEKS' then begin SV := NumPCMeks( GB ); end else if ( SMsg = 'THREAT' ) then begin VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := Calculate_Threat_Points( VCode , VC2 ); end else if ( SMsg = 'FACBUDDIES' ) then begin { Count the number of positive relationships the PC has } { with members of the provided faction. } VCode := ScriptValue( Event , GB , Scene ); SV := Count_Faction_Buddies( GB , VCode ); end else if ( SMsg = '*' ) then begin VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := VCode * VC2; end else if ( SMsg = 'REWARD' ) then begin VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := Calculate_Reward_Value( GB , VCode , VC2 ); end else if ( SMsg = 'PRICE' ) then begin VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := Calculate_Asking_Price( GB , VCode , VC2 ); end else if ( SMsg = 'RANGE' ) then begin VCode := ScriptValue( Event , GB , Scene ); VC2 := ScriptValue( Event , GB , Scene ); SV := SV_Range( GB , VCode , VC2 ); end else if ( SMsg = 'SKILLTAR' ) then begin VCode := ScriptValue( Event , GB , Scene ); SV := BasicSkillTarget( VCode ); end else if ( SMsg = 'HARDSKILLTAR' ) then begin VCode := ScriptValue( Event , GB , Scene ); SV := HardSkillTarget( VCode ); end else if ( SMsg = 'SOCSKILLTAR' ) then begin VCode := ScriptValue( Event , GB , Scene ); SV := SocSkillTarget( GB , VCode ); end else if SMsg = 'PCSCALE' then begin SV := FindPCScale( GB ); end else if ( SMsg = 'ESCENE' ) then begin { Find out what element to find the scene for. } if ( Scene <> Nil ) then begin VCode := ExtractValue( Event ); { Find out what plot we're dealing with. } Scene := PlotMaster( GB , Scene ); if ( Scene <> Nil ) and ( VCode >= 1 ) and ( VCode <= Num_Plot_Elements ) then begin SV := ElementLocation( FindRoot( Scene ) , Scene , VCode , GB ); end; end; end else if ( SMsg[1] = 'D' ) then begin { Roll a die, return the result. } DeleteFirstChar( SMsg ); Event := SMsg + ' ' + Event; VCode := ScriptValue( Event , GB , Scene );; if VCode < 1 then VCode := 1; SV := Random( VCode ) + 1; { As a last resort, see if the first character shows up in the } { scripts file. If so, use that. } end else if SAttValue( Value_Macros , SMsg[1] ) <> '' then begin { Install the macro, then re-call this procedure to get } { the results. } S2 := SMsg[ 1 ]; DeleteFirstChar( SMsg ); Event := SMsg + ' ' + Event; InitiateMacro( GB , scene , Event , SAttValue( Value_Macros , S2 ) ); SV := ScriptValue( Event , gb , scene ); end else if ( SMsg = '?PILOT' ) then begin VCode := ScriptValue( Event , GB , Scene ); SV := FindRandomPilotID( GB , VCode ); end else if ( SMsg = '?MECHA' ) then begin VCode := ScriptValue( Event , GB , Scene ); SV := FindRandomMekID( GB , VCode ); end else if ( SMsg[1] = 'T' ) and ( gb <> Nil ) then begin { Return the number of gears on the provided team. } DeleteFirstChar( SMsg ); VCode := ExtractValue( SMsg ); SV := NumActiveMasters( GB , VCode ); end else if ( SMsg[1] = '@' ) and ( gb <> Nil ) then begin { Return the root gear of the gear indicated by the } { rest of this expression. Return 0 if there's an error. } DeleteFirstChar( SMsg ); VCode := ScriptValue( SMsg , gb , scene ); if VCode <> 0 then SV := FindRootUID( GB , VCode ) else SV := 0; end else if SMsg[1] = '-' then begin { We want the negative of the value to follow. } DeleteFirstChar( SMsg ); event := SMsg + ' ' + event; SV := -ScriptValue( event , gb , scene ); end else begin { No command was given, so this must be a constant value. } S2 := SMsg; SV := ExtractValue( SMsg ); if ( SV = 0 ) and ( S2 <> '' ) and ( S2 <> '0' ) then begin DialogMsg( 'WARNING: Script value ' + S2 + ' in ' + GearName( scene ) ); DialogMsg( 'CONTEXT: ' + event ); end; end; { Restore the grabbed gear before exiting. } Grabbed_Gear := Old_Grabbed_Gear; ScriptValue := SV; end; Function AS_GetString( Source: GearPtr; Key: String ): String; { Check the SOURCE for a SAtt with the provided KEY. } { If none can be found, search the default list for SOURCE's type. } { Normally, getting a string attribute could be handled simply by the } { SAttValue function. But, I had some trouble with my doors getting so } { #$#@%! huge, so I decided to write the function as a space-saver. } var msg: String; begin if Source <> Nil then begin msg := SAttValue( Source^.SA , Key ); if ( msg = '' ) and ( Source^.G = GG_MetaTerrain ) and ( Source^.S >= 1 ) and ( Source^.S <= NumBasicMetaTerrain ) then begin msg := SAttValue( Meta_Terrain_Scripts[ Source^.S ] , Key ); end else if ( msg = '' ) and ( ( Source^.G = GG_Scene ) or ( Source^.G = GG_MetaScene ) or ( Source^.G = GG_World ) ) then begin msg := SAttValue( Default_Scene_Scripts , Key ); end; if ( msg = '' ) and HeadMatchesString( 'CLUE_' , Key ) then begin msg := 'VMsg AS_YouHaveNoClue 0'; end; end else begin msg := ''; end; AS_GetString := msg; end; Function EncounterMapType( GB: GameBoardPtr; X,Y: Integer ): Integer; { The PC has met an encounter at point X,Y on the current map. Return the } { map generator which this encounter should use. } begin if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and AStringHasBString( SAttValue( GB^.Scene^.SA , 'SPECIAL' ) , 'UNIFORM' ) then begin EncounterMapType := GB^.Scene^.Stat[ STAT_MapGenerator ]; end else if GB <> Nil then begin EncounterMapType := TileTerrain( GB , X , Y ) end else begin EncounterMapType := 1; end; end; Procedure AS_SetExit( GB: GameBoardPtr; RC: Integer ); { Several things need to be done when exiting the map. } { This procedure should centralize most of them. } var Dest,Src,SrcHome: GearPtr; T: Integer; begin { Only process this request if we haven't already set an exit. } if ( GB <> Nil ) and ( not GB^.QuitTheGame ) then begin GB^.QuitTheGame := True; GB^.ReturnCode := RC; if GB^.Scene <> Nil then begin if IsInvCom( GB^.SCene ) then begin SCRIPT_Gate_To_Seek := GB^.Scene^.S; end else begin SCRIPT_Gate_To_Seek := RealSceneID( GB^.Scene ); end; Dest := FindActualScene( GB , RC ); { Set the return value for metascenes. } if ( Dest = Nil ) and ( SCRIPT_DynamicEncounter = Nil ) then begin { If we've been asked to exit to a nonexistant scene, } { try to re-enter the current scene again. } GB^.ReturnCode := FindGearScene( GB^.Scene , GB ); end else if ( RC < 0 ) and ( Dest <> Nil ) then begin { This is a metascene. Set the entrance value and the map generator } { if appropriate. } if ( NAttValue( Dest^.NA , NAG_Narrative , NAS_EntranceScene ) = 0 ) and ( GB^.Scene^.G = GG_Scene ) then begin SetNAtt( Dest^.NA , NAG_Narrative , NAS_EntranceScene , GB^.Scene^.S ); end; { If the metascene has no map generator set, better set the map } { generator from the tile the entrance is sitting upon. } if Dest^.Stat[ STAT_MapGenerator ] = 0 then begin Src := FindSceneEntrance( FindRoot( GB^.Scene ) , GB , RC ); if ( Src <> Nil ) and OnTheMap( GB , Src ) and IsFoundAlongTrack( GB^.Meks , Src ) then begin Dest^.Stat[ STAT_MapGenerator ] := EncounterMapType( GB , NAttValue( Src^.NA , NAG_Location , NAS_X ) , NAttValue( Src^.NA , NAG_Location , NAS_Y ) ); { If this will make the encounter a space map, set the map-scroll tag. } if Dest^.Stat[ STAT_MapGenerator ] = TERRAIN_Space then Dest^.Stat[ STAT_SpaceMap ] := 1; SrcHome := GB^.Scene; end else begin { Copy the entrance's map generator and hope for the best. } SrcHome := FindActualScene( GB , FindGearScene( Src , GB ) ); if SrcHome = Nil then SrcHome := GB^.Scene; Dest^.Stat[ STAT_MapGenerator ] := SrcHome^.Stat[ STAT_MapGenerator ]; { If this will make the encounter a space map, set the map-scroll tag. } if Dest^.Stat[ STAT_MapGenerator ] = TERRAIN_Space then Dest^.Stat[ STAT_SpaceMap ] := 1; end; { Also copy over the tileset + backdrop. } SetNAtt( Dest^.NA , NAG_SceneData , NAS_TileSet , NAttValue( SrcHome^.NA , NAG_SceneData , NAS_TileSet ) ); SetNAtt( Dest^.NA , NAG_SceneData , NAS_Backdrop , NAttValue( SrcHome^.NA , NAG_SceneData , NAS_Backdrop ) ); { Copy the environmental effects from the parent scene. } for t := 1 to Num_Environment_Variables do begin SetNAtt( Dest^.NA , NAG_EnvironmentData , T , NAttValue( SrcHome^.NA , NAG_EnvironmentData , T ) ); end; end; end else if ( Dest <> Nil ) and ( Dest^.G = GG_World ) then begin { If we're exiting to the world, the gate to seek } { should be the root scene. } Src := FindRootScene( GB^.Scene ); if Src <> Nil then SCRIPT_Gate_To_Seek := Src^.S; end; end; end; end; Procedure ProcessExit( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { An exit command has been received. } var SID: Integer; begin SID := ScriptValue( Event , GB , Source ); if ( SID < 0 ) and ( FindActualScene( GB , SID ) = Nil ) then begin DialogMsg( MsgString( 'AS_EXIT_NOMETASCENE' ) ); end else begin AS_SetExit( GB , SID ); end; end; Procedure ProcessForceExit( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { An exit command has been received and will not be denied. } var SID: Integer; begin SID := ScriptValue( Event , GB , Source ); GB^.QuitTheGame := False; AS_SetExit( GB , SID ); end; Procedure ProcessReturn( GB: GameBoardPtr ); { An exit command has been received. } Procedure ReturnToScene( DefaultSID: Integer ); { Return from the current scene to some place appropriate. If a } { "ReturnTo" value has been stored, go there. Otherwise, return to } { the default scene given as a parameter. } var RtS: Integer; { Return To Scene } begin if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin RtS := NAttValue( GB^.Scene^.NA , NAG_Narrative , NAS_ReturnToScene ); end else RtS := 0; if RtS <> 0 then begin SetNAtt( GB^.Scene^.NA , NAG_Narrative , NAS_ReturnToScene , 0 ); AS_SetExit( GB , RtS ); end else begin AS_SetExit( GB , DefaultSID ); end; end; begin if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and ( FindROot( GB^.Scene )^.G = GG_Adventure ) and ( FindROot( GB^.Scene )^.S = GS_ArenaCampaign ) then begin { In an arena campaign, return always returns with an exit value of 1. } GB^.QuitTheGame := True; GB^.ReturnCode := 1; end else if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and ( GB^.Scene^.G = GG_MetaScene ) then begin ReturnToScene( NAttValue( GB^.Scene^.NA , NAG_Narrative , NAS_EntranceScene ) ); end else if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and IsInvCom( GB^.Scene ) then begin if GB^.Scene^.S <> 0 then begin AS_SetExit( GB , GB^.Scene^.S ); GB^.Scene^.S := 0; { Eliminated old error check that doesn't appear to do anything anymore. In case of error, roll back to v0.420 or so. } end; end else if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and ( GB^.Scene^.Parent <> Nil ) and ( GB^.Scene^.Parent^.G = GG_Scene ) then begin ReturnToScene( GB^.Scene^.Parent^.S ); end else begin ReturnToScene( 0 ); end; end; Function FactionRankName( GB: GameBoardPtr; Source: GearPtr; FID,FRank: Integer ): String; { Return the generic name of rank FRank from faction FID. } var F: GearPtr; it: String; begin { First find the faction. } F := GG_LocateFaction( FID , GB , Source ); { Do range checking on the FRank score obtained. } if FRank < 0 then FRank := 0 else if FRank > 8 then FRank := 8; { If no faction was found, return the default rank title. } if F = Nil then begin it := MSgString( 'FacRank_' + BStr( FRank ) ); end else begin { First check the faction for a name there. } { If the faction has no name set, use the default. } it := SAttValue( F^.SA , 'FacRank_' + BStr( FRank ) ); if it = '' then it := MSgString( 'FacRank_' + BStr( FRank ) ); end; FactionRankName := it; end; Function PCRankName( GB: GameBoardPtr; Source: GearPtr ): String; { Return the name of the PC's rank with the PC's faction. } { if the PC has no rank, return the basic string. } var FID: Integer; A,F: GearPtr; begin { The PC's faction ID is located in the adventure gear, so } { locate that first. } A := GG_LocateAdventure( GB , Source ); if A <> Nil then begin { The faction rank score is located in the faction itself. } { So now, let's locate that. } FID := NAttValue( A^.NA , NAG_Personal , NAS_FactionID ); F := GG_LocateFaction( FID , GB , Source ); if F <> Nil then begin { Call the general Faction Rank function. } PCRankName := FactionRankName( GB , Source , FID , NAttValue( A^.NA , NAG_Experience , NAS_FacLevel ) ); end else begin { No faction found. Return the default value. } PCRankName := MsgSTring( 'FACRANK_0' ); end; end else begin { Adventure not found. Return default "peon". } PCRankName := MsgSTring( 'FACRANK_0' ); end; end; Function FormatMemoString( GB: GameBoardPtr; const Msg: String ): String; { Add the name of the city to the memo. } var RootScene: GearPtr; begin RootScene := FindRootScene( GB^.Scene ); if ( RootScene <> Nil ) and ( msg <> '' ) then begin FormatMemoString := GearName( RootScene ) + ': ' + msg; end else begin FormatMemoString := msg; end; end; Function PlotHintString( GB: GameBoardPtr; Source: GearPtr; N: LongInt; AddMemo: Boolean ): String; { Determine hint string N for this plot. } { Also store a rumor memo. } Function GenericHintString: String; { We can't find a valid hint string for this plot. Just return } { some generic advice. } begin GenericHintString := MsgString( 'GENERIC_HINT_STRING_' + BStr( Random( 6 ) + 1 ) ); end; var Plot: GearPtr; IsFound: Boolean; msg,memo_msg: String; begin { Find the plot. We need it if we are to proceed. } Plot := PlotMaster( GB , Source ); if Plot = Nil then Exit( GenericHintString ); { Start looking for the correct message. } repeat msg := ScriptMessage( 'HINT_' + BStr( N ) , GB , Plot ); DeleteWhiteSpace( msg ); IsFound := True; if ( Length( msg ) > 1 ) and ( msg[1] = ':' ) then begin { This is a redirect. } DeleteFirstChar( msg ); N := ExtractValue( msg ); IsFound := False; end; until IsFound; if msg = '' then PlotHintString := GenericHintString else begin { We found a hint. Save the plot memo. } if AddMemo and ( Plot <> Nil ) and ( I_NPC <> Nil ) then begin memo_msg := ReplaceHash( MsgString( 'PLOT_HINT_MEMO' ) , GearName( I_NPC ) ); memo_msg := FormatMemoString( GB , ReplaceHash( memo_msg , msg ) ); SetSAtt( Plot^.SA , 'MEMO' + BStr( N ) + ' <' + memo_msg + '>' ); end; PlotHintString := msg; end; end; Procedure FormatMessageString( var msg: String; gb: GameBoardPtr; Scene: GearPtr ); { There are a number of formatting commands which may be used } { in an arenascript message string. } var S0,S1,w: String; ID,ID2: LongInt; Part: GearPtr; begin S0 := msg; S1 := ''; while S0 <> '' do begin w := ExtractWord( S0 ); if ( W <> '' ) and ( W[1] = '\' ) then begin W := UpCase( W ); if W = '\MEK' then begin { Insert the name of a specified gear. } ID := ScriptValue( S0 , GB , Scene ); Part := LocateMekByUID( GB , ID ); if Part <> Nil then begin W := GearName( Part ); end else begin W := 'ERROR!!!'; end; end else if W = '\PILOT' then begin { Insert the name of a specified gear. } ID := ScriptValue( S0 , GB , Scene ); Part := LocateMekByUID( GB , ID ); if Part <> Nil then begin W := PilotName( Part ); end else begin W := 'ERROR!!!'; end; end else if W = '\ELEMENT' then begin { Insert the name of a specified plot element. } ID := ScriptValue( S0 , GB , Scene ); Part := PlotMaster( GB , Scene ); if Part <> Nil then begin W := ElementName( FindRoot( GB^.Scene ) , Part , ID , GB ); end; end else if W = '\NARRATIVE' then begin { Insert the name of a specified plot element. } ID := ScriptValue( S0 , GB , Scene ); Part := StoryMaster( GB , Scene ); if Part <> Nil then begin W := ElementName( FindRoot( GB^.Scene ) , Part , ID , GB ); end; end else if W = '\PERSONA' then begin { Insert the name of a specified persona. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateNPC( ID , GB , Scene ); W := GEarName( Part ); end else if W = '\HINT' then begin { Insert the hint of a specified plot element. } ID := ScriptValue( S0 , GB , Scene ); W := PlotHintString( GB , Scene , ID , False ); end else if W = '\HINT_MEMO' then begin { Insert the hint of a specified plot element and store a memo about it. } ID := ScriptValue( S0 , GB , Scene ); W := PlotHintString( GB , Scene , ID , True ); end else if W = '\ITEM' then begin { Insert the name of a specified item. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateItem( ID , GB , Scene ); W := GEarName( Part ); end else if W = '\ITEM_DESC' then begin { Insert the description of a specified item. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateItem( ID , GB , Scene ); if Part <> Nil then W := SAttValue( Part^.SA , 'ITEM_DESC' ) else W := 'ERROR: can''t find ITEM_DESC for item ' + BStr( ID ); end else if W = '\ITEM_HISTORY' then begin { Insert the description of a specified item. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateItem( ID , GB , Scene ); if Part <> Nil then W := SAttValue( Part^.SA , 'ITEM_HISTORY' ) else W := 'ERROR: can''t find ITEM_DESC for item ' + BStr( ID ); end else if W = '\ITEM_USAGE' then begin { Insert the description of a specified item. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateItem( ID , GB , Scene ); if Part <> Nil then W := SAttValue( Part^.SA , 'ITEM_USAGE' ) else W := 'ERROR: can''t find ITEM_DESC for item ' + BStr( ID ); end else if W = '\SECRET' then begin { Insert a plot secret here. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateItem( ID , GB , Scene ); if Part <> Nil then W := ScriptMessage( 'msg' , GB , Part ) else W := 'ERROR: can''t find MSG for secret ' + BStr( ID ); end else if W = '\FACTION' then begin { Insert the name of a specified faction. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateFaction( ID , GB , Scene ); W := GEarName( Part ); end else if W = '\FACTION_DESIG' then begin { Insert the name of a specified faction. } ID := ScriptValue( S0 , GB , Scene ); Part := GG_LocateFaction( ID , GB , Scene ); if Part <> Nil then W := SAttValue( Part^.SA , 'DESIG' ) else W := 'ERROR:Faction_Not_Found'; end else if W = '\SCENE' then begin { Insert the name of a specified scene. } ID := ScriptValue( S0 , GB , Scene ); W := SceneName( GB , ID , False ); end else if W = '\EXACT_SCENE' then begin { Insert the name of a specified scene. } ID := ScriptValue( S0 , GB , Scene ); W := SceneName( GB , ID , True ); end else if W = '\VAL' then begin { Insert the value of a specified variable. } ID := ScriptValue( S0 , GB , Scene ); W := BStr( ID ); end else if W = '\PC' then begin { The name of the PC. } W := GearName( LocatePilot( GG_LocatePC( GB ) ) ); end else if W = '\PCJOB' then begin { The name of the PC. } Part := LocatePilot( GG_LocatePC( GB ) ); if Part <> Nil then W := LowerCase( SAttValue( Part^.SA , 'JOB' ) ); end else if W = '\CHATNPC' then begin { The name of the Chat NPC. } W := GearName( I_NPC ); end else if W = '\CHATNPCMECHA' then begin { The name of the Chat NPC's mecha. } Part := FindRoot( I_NPC ); if ( Part <> Nil ) and ( Part^.G = GG_Mecha ) then begin W := FullGearName( Part ); end else begin W := MsgString( 'MECHA' ); end; end else if W = '\SOURCE' then begin { The name of the PC. } W := GearName( Scene ); end else if W = '\OPR' then begin { Object Pronoun } ID := ScriptValue( S0 , GB , Scene ); if ID = 0 then begin Part := LocatePilot( GG_LocatePC( GB ) ); end else begin Part := GG_LocateNPC( ID , GB , Scene ); end; if Part <> Nil then begin W := MsgString( 'OPR_' + BStr( NAttValue( Part^.NA , NAG_CharDescription , NAS_Gender ) ) ); end else begin W := 'it'; end; end else if W = '\SPR' then begin { Object Pronoun } ID := ScriptValue( S0 , GB , Scene ); if ID = 0 then begin Part := LocatePilot( GG_LocatePC( GB ) ); end else begin Part := GG_LocateNPC( ID , GB , Scene ); end; if Part <> Nil then begin W := MsgString( 'SPR_' + BStr( NAttValue( Part^.NA , NAG_CharDescription , NAS_Gender ) ) ); end else begin W := 'it'; end; end else if W = '\PPR' then begin { Object Pronoun } ID := ScriptValue( S0 , GB , Scene ); if ID = 0 then begin Part := LocatePilot( GG_LocatePC( GB ) ); end else begin Part := GG_LocateNPC( ID , GB , Scene ); end; if Part <> Nil then begin W := MsgString( 'PPR_' + BStr( NAttValue( Part^.NA , NAG_CharDescription , NAS_Gender ) ) ); end else begin W := 'its'; end; end else if W = '\OFFSPRING' then begin { Son or Daughter, depending on gender } ID := ScriptValue( S0 , GB , Scene ); if ID = 0 then begin Part := LocatePilot( GG_LocatePC( GB ) ); end else begin Part := GG_LocateNPC( ID , GB , Scene ); end; if Part <> Nil then begin if NAttValue( Part^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then W := MsgString( 'SON' ) else W := MsgString( 'DAUGHTER' ); end else begin W := 'sprog'; end; end else if W = '\SIBLING' then begin { Sister or Brother, depending on gender } ID := ScriptValue( S0 , GB , Scene ); if ID = 0 then begin Part := LocatePilot( GG_LocatePC( GB ) ); end else begin Part := GG_LocateNPC( ID , GB , Scene ); end; if Part <> Nil then begin if NAttValue( Part^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then W := MsgString( 'BROTHER' ) else W := MsgString( 'SISTER' ); end else begin W := 'sib'; end; end else if W = '\RANK' then begin { The faction rank of the PC. } W := PCRankName( GB , Scene ); end else if W = '\FACRANK' then begin { A generic faction rank, not nessecarilt belonging } { to the PC. } ID := ScriptValue( S0 , GB , Scene ); ID2 := ScriptValue( S0 , GB , Scene ); W := FactionRankName( GB , Scene , ID , ID2 ); end else if W = '\DATE' then begin ID := ScriptValue( S0 , GB , Scene ); W := TimeString( ID ); end; end; if ( W <> '' ) and ( S1 <> '' ) and ( IsPunctuation( W[1] ) or ( S1[Length(S1)] = '$' ) or ( S1[Length(S1)] = '@' ) ) then begin S1 := S1 + W; end else begin S1 := S1 + ' ' + W; end; end; msg := S1; end; Function ConditionAccepted( Event: String; gb: GameBoardPtr; Source: GearPtr ): Boolean; { Run a conditional script. } { If it returns 'ACCEPT', this function returns true. } var T: String; { The trigger to be used. } begin { Error check - an empty condition is always true. } if Event = '' then Exit( True ); { Generate the trigger. } T := 'null'; { Execute the provided event. } InvokeEvent( Event , GB , Source , T ); { If the trigger was changed, that counts as a success. } ConditionAccepted := T = 'ACCEPT'; end; Function ScriptMessage( msg_label: String; GB: GameBoardPtr; Source: GearPtr ): String; { Retrieve and format a message from the source. } var N,T: Integer; C,msg: String; MList,M: SAttPtr; begin { Create the list of possible strings. } MList := Nil; C := AS_GetString( Source , 'C' + msg_label ); { The master condition must be accepted in order to continue. } if ConditionAccepted( C , GB , Source ) and ( Source <> Nil ) then begin msg := AS_GetString( Source , msg_label ); if msg <> '' then StoreSAtt( MList , msg ); msg := msg_label + '_'; N := NumHeadMatches( msg , Source^.SA ); for t := 1 to N do begin M := FindHeadMatch( msg , Source^.SA , T); C := SAttValue( Source^.SA , 'C' + RetrieveAPreamble( M^.info ) ); if ConditionAccepted( C , GB , Source ) then begin StoreSAtt( MList , RetrieveAString( M^.Info ) ); end; end; end; { If any messages were found, pick one. } if MList <> Nil then begin msg := SelectRandomSAtt( MList )^.Info; DisposeSAtt( MList ); FormatMessageString( msg , gb , source ); end else begin msg := ''; end; ScriptMessage := Msg; end; Function NPCScriptMessage( const msg_label: String; GB: GameBoardPtr; NPC, Source: GearPtr ): String; { Get a script message, temporarily setting the I_NPC to the provided NPC. } var Temp_NPC: GearPtr; msg: String; begin Temp_NPC := I_NPC; I_NPC := NPC; msg := ScriptMessage( msg_label , GB , Source ); I_NPC := Temp_NPC; NPCScriptMessage := Msg; end; Function GetTheMessage( head: String; idnum: Integer; GB: GameBoardPtr; Scene: GearPtr ): String; { Just call the SCRIPTMESSAGE with the correct label. } begin GetTheMessage := ScriptMessage( head + BStr( idnum ) , GB , Scene ); end; Procedure ProcessPrint( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then print the specified message. } var msg: String; id: Integer; begin id := ScriptValue( Event , GB , Scene ); msg := getTheMessage( 'msg', id , GB , Scene ); if msg <> '' then DialogMsg( msg ); end; Procedure ProcessAlert( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then print the specified message. } var id: Integer; msg: String; begin id := ScriptValue( Event , GB , Scene ); msg := getTheMessage( 'msg', id , GB , Scene ); if msg <> '' then begin YesNoMenu( GB , msg , '' , '' ); DialogMsg( msg ); end; end; Procedure ProcessGMonologue( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { An NPC is about to say something. } var id: Integer; { Message ID } msg: String; NPC: GearPtr; begin { Record the NPC, and find the message number. } NPC := Grabbed_Gear; id := ScriptValue( Event , GB , Source ); { Locate the NPC and the message. } if NPC <> Nil then begin msg := NPCScriptMessage( 'msg' + BStr( id ) , GB , NPC , Source ); if ( msg <> '' ) then begin Monologue( GB , NPC , msg ); { The monologue will do its own output. } end; end; end; Procedure ProcessAddDebriefing( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Add a message for a certain NPC to speak during the debriefing. } { Note that the first word in the debriefing message will be the ID of the } { character meant to deliver the line. } var cid,id: Integer; { Character ID, Message ID } NPC: GearPtr; msg: String; begin { Find the two needed numeric valies. } cid := ScriptValue( Event , GB , Source ); id := ScriptValue( Event , GB , Source ); { Locate the message. } NPC := GG_LocateNPC( CID , GB , Source ); msg := NPCScriptMessage( 'msg' + BStr( id ) , GB , NPC , Source ); if ( msg <> '' ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin SetSAtt( GB^.Scene^.SA , ARENAREPORT_Personal + ' <' + BStr( cid ) + ' ' + msg + '>' ); end; end; Procedure ProcessMemo( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then store the specified message. } var id: Integer; msg: String; begin id := ScriptValue( Event , GB , Scene ); msg := FormatMemoString( GB , getTheMessage( 'msg', id , GB , Scene ) ); if ( Scene <> Nil ) then SetSAtt( Scene^.SA , 'MEMO <' + msg + '>' ); end; Procedure ProcessPMemo( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Locate and then store the specified message in the plot gear, } { along with an ID number. } var id,plotid: LongInt; msg: String; begin plotid := ScriptValue( Event , GB , Source ); id := ScriptValue( Event , GB , Source ); msg := FormatMemoString( GB , getTheMessage( 'msg', id , GB , Source ) ); Source := PlotMaster( GB , Source ); if ( Source <> Nil ) then SetSAtt( Source^.SA , 'MEMO' + BStr( plotid ) + ' <' + msg + '>' ); end; Procedure ProcessSMemo( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then store the specified message in the story gear. } var id: Integer; msg: String; begin id := ScriptValue( Event , GB , Scene ); msg := FormatMemoString( GB , getTheMessage( 'msg', id , GB , Scene ) ); Scene := StoryMaster( GB , Scene ); if ( Scene <> Nil ) then SetSAtt( Scene^.SA , 'MEMO <' + msg + '>' ); end; Procedure ProcessQMemo( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Locate and then store the specified message. } var qid,mid: LongInt; msg: String; begin { Determine the Quest ID and the Message ID. } qid := ScriptValue( Event , GB , Source ); mid := ScriptValue( Event , GB , Source ); msg := FormatMemoString( GB , getTheMessage( 'msg', mid , GB , Source ) ); { Store this message in the adventure. } Source := GG_LocateAdventure( GB , Source ); { Quest memos look like regular memos but their tag is followed by an underscore } { and the Quest ID. } if ( Source <> Nil ) then SetSAtt( Source^.SA , 'MEMO_' + BStr( qid ) + ' <' + msg + '>' ); end; Procedure ProcessEMail( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then store the specified message. } var msg: String; PC: GearPtr; id: Integer; begin id := ScriptValue( Event , GB , Scene ); if id <> 0 then begin msg := getTheMessage( 'msg' , id , GB , Scene ); if ( Scene <> Nil ) and ( msg <> '' ) then SetSAtt( Scene^.SA , 'EMAIL <' + msg + '>' ); PC := GG_LocatePC( GB ); if ( PC <> Nil ) and HasPCommCapability( PC , PCC_EMail ) and ( msg <> '' ) then DialogMsg( MsgString( 'AS_EMail' ) ); end else if Scene <> Nil then begin SetSAtt( Scene^.SA , 'EMAIL <>' ); end; end; Procedure ProcessHistory( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then store the specified message. } var id: Integer; msg: String; Adv: GearPtr; begin id := ScriptValue( Event , GB , Scene ); msg := getTheMessage( 'msg' , id , GB , Scene ); Adv := GG_LocateAdventure( GB , Scene ); if ( msg <> '' ) and ( Adv <> Nil ) then AddSAtt( Adv^.SA , 'HISTORY' , msg ); end; Procedure ProcessVictory( GB: GameBoardPtr ); { Sum up the entire campaign in a list of SAtts, then print to } { a file. } const InvStr = '+'; SubStr = '>'; Num_Significant_Relationships = 5; Significant_Relationship: Array [1..Num_Significant_Relationships] of Integer = ( NAV_ArchEnemy, NAV_ArchAlly, NAV_Friend, NAV_Family, NAV_Lover ); var VList,SA: SAttPtr; PC,Fac,Adv: GearPtr; T,V: LongInt; msg,fname: String; Procedure CheckAlongPath( Part: GearPtr; TabPos,Prefix: String ); { CHeck along the path specified, adding info to } { the victory list. } var msg: String; begin while Part <> Nil do begin if ( Part^.G <> GG_AbsolutelyNothing ) then begin StoreSAtt( VList , tabpos + prefix + GearName( Part ) ); msg := ExtendedDescription( GB , Part ); if msg <> '' then StoreSAtt( VList , tabpos + ' ' + msg ); end; if Part^.G <> GG_Cockpit then begin CheckAlongPath( Part^.InvCom , TabPos + ' ' , InvStr ); CheckAlongPath( Part^.SubCom , TabPos + ' ' , SubStr ); end; Part := Part^.Next; end; end;{CheckAlongPath} begin { Initialize our list to NIL. } VList := Nil; DialogMsg( MsgString( 'HISTORY_AnnounceVictory' ) ); repeat CombatDisplay( GB ); DoFlip; Until IsMoreKey( RPGKey ); { Locate the PC, add PC-specific information. } PC := LocatePilot( GG_LocatePC( GB ) ); Adv := FindRoot( GB^.Scene ); if PC <> Nil then begin { Store the name. } fname := GearName( PC ); StoreSAtt( VList , fname ); StoreSAtt( VList , JobAgeGenderDesc( PC ) ); StoreSAtt( VList , TimeString( GB^.ComTime ) ); StoreSAtt( VList , ' ' ); { Store the stats. } for t := 1 to 8 do begin msg := MsgString( 'StatName_' + BStr( t ) ); while Length( msg ) < 20 do msg := msg + ' '; msg := msg + BStr( PC^.Stat[ T ] ); V := ( PC^.Stat[ T ] + 2 ) div 3; if V > 7 then V := 7; msg := msg + ' (' + MsgString( 'STATRANK' + BStr( V ) ) + ')'; StoreSAtt( VList , msg ); end; StoreSAtt( VList , ' ' ); { Add info on the PC's XP and credits. } msg := MsgString( 'INFO_XP' ); V := NAttVAlue( PC^.NA , NAG_Experience , NAS_TotalXP ); msg := msg + ' ' + BStr( V ); StoreSAtt( VList , msg ); msg := MsgString( 'INFO_XPLeft' ); V := V - NAttVAlue( PC^.NA , NAG_Experience , NAS_SpentXP ); msg := msg + ' ' + BStr( V ); StoreSAtt( VList , msg ); msg := MsgString( 'INFO_Credits' ); V := NAttVAlue( PC^.NA , NAG_Experience , NAS_Credits ); msg := msg + ' ' + BStr( V ); StoreSAtt( VList , msg ); { Store the faction and rank. } Fac := GG_LocateFaction( NAttValue( PC^.NA , NAG_Personal , NAS_FactionID ) , GB , Nil ); if Fac <> Nil then begin msg := ReplaceHash( MsgString( 'HISTORY_FACTION' ) , PCRankName( GB , Nil ) ); msg := ReplaceHash( msg , GearName( Fac ) ); StoreSAtt( VList , msg ); StoreSAtt( VList , ' ' ); end; { Store the relationships and fatalities. } if Adv <> Nil then begin for t := 1 to Num_Significant_Relationships do begin V := CountGearsByIDTag( Adv , NAG_Relationship , 0 , Significant_Relationship[ t ] ) + CountGearsByIDTag( GB^.Meks , NAG_Relationship , 0 , Significant_Relationship[ t ] ); if V > 1 then begin StoreSAtt( VList , ReplaceHash( MsgSTring( 'HISTORY_RELATIONS_' + BStr( T ) ) , BStr( V ) ) ); end else if V = 1 then begin StoreSAtt( VList , MsgSTring( 'HISTORY_RELATION_' + BStr( T ) ) ); end; end; for t := 1 to Num_Fatality_Types do begin V := NAttValue( Adv^.NA , NAG_Narrative , Fatality_Base + T ); if V > 1 then begin StoreSAtt( VList , ReplaceHash( MsgSTring( 'HISTORY_FATALITIES_' + BStr( T ) ) , BStr( V ) ) ); end else if V = 1 then begin StoreSAtt( VList , MsgSTring( 'HISTORY_FATALITY_' + BStr( T ) ) ); end; end; end; StoreSAtt( VList , ' ' ); { Store the personality traits. } for t := 1 to Num_Personality_Traits do begin V := NATtValue( PC^.NA , NAG_CharDescription , -T ); if V <> 0 then begin Msg := ReplaceHash( MsgString( 'HISTORY_Traits' ) , PersonalityTraitDesc( T , V ) ); Msg := ReplaceHash( msg , BStr( Abs( V ) ) ); StoreSAtt( VList , msg ); end; end; StoreSAtt( VList , ' ' ); { Store the talents. } V := 0; for t := 1 to NumTalent do begin if HasTalent( PC , T ) then begin msg := MsgString( 'TALENT' + BStr( T ) ); StoreSAtt( VList , msg ); msg := ' ' + MsgString( 'TALENTDESC' + BStr( T ) ); StoreSAtt( VList , msg ); inc( V ); end; end; if V > 0 then StoreSAtt( VList , ' ' ); { Store the skill ranks. } for t := 1 to NumSkill do begin V := NATtValue( PC^.NA , NAG_Skill , T ); if V > 0 then begin Msg := ReplaceHash( MsgString( 'HISTORY_Skills' ) , MsgString( 'SKILLNAME_' + BStr( T ) ) ); Msg := ReplaceHash( msg , BStr( V ) ); StoreSAtt( VList , msg ); end; end; StoreSAtt( VList , ' ' ); { Store info on the PC's body and equipment. } CheckAlongPath( PC^.InvCom , ' ' , '+' ); CheckAlongPath( PC^.SubCom , ' ' , '>' ); StoreSAtt( VList , ' ' ); end else begin { No PC found, so filename will be "out.txt". } fname := 'out'; end; if Adv <> Nil then begin { Once the PC wins, unlock the adventure. } Adv^.V := 1; SA := Adv^.SA; while SA <> Nil do begin if UpCase( Copy( SA^.Info , 1 , 7 ) ) = 'HISTORY' then begin StoreSAtt( VList , RetrieveAString( SA^.Info ) ); end; SA := SA^.Next; end; end; { Add info on the PC's mechas. } PC := GB^.Meks; while PC <> Nil do begin if ( NAttValue( PC^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and ( PC^.G = GG_Mecha ) then begin StoreSAtt( VList , FullGearName( PC ) ); CheckAlongPath( PC^.InvCom , ' ' , '+' ); CheckAlongPath( PC^.SubCom , ' ' , '>' ); StoreSAtt( VList , ' ' ); end; PC := PC^.Next; end; SaveStringList( Config_Directory + FName + '.txt' , VList ); {$IFDEF ASCII} MoreText( VList , 1 ); {$ELSE} ASRD_GameBoard := GB; MoreText( VList , 1 , @ArenaScriptReDraw ); {$ENDIF} DisposeSAtt( VList ); end; Procedure ProcessNews( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then store the specified message. } var msg: String; id: Integer; begin id := ScriptValue( Event , GB , Scene ); msg := getTheMessage( 'msg' , id , GB , Scene ); if ( msg <> '' ) and ( Scene <> Nil ) then SetSAtt( Scene^.SA , 'NEWS <' + msg + '>' ); end; Procedure ProcessValueMessage( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { Locate and then print the specified message. } var msg: String; V: LongInt; begin { FInd the message we're supposed to print. } msg := ExtractWord( Event ); msg := MsgString( msg ); { Find the value to insert. } V := ScriptValue( Event , GB , Scene ); { Insert the value. } msg := ReplaceHash( msg , BStr( V ) ); if msg <> '' then DialogMsg( msg ); end; Procedure ProcessSay( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Locate and then print the specified message. } var id: Integer; msg: String; begin { Error check- if not in a conversation, call the PRINT } { routine instead. } if IntMenu = Nil then begin ProcessPrint( Event , GB , Source ); Exit; end; id := ScriptValue( Event , GB , Source ); msg := getTheMessage( 'msg' , id , GB , Source ); if msg <> '' then begin CHAT_Message := msg; end; end; Procedure ProcessSayPlotMsg( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Locate and then print the specified message. } Procedure PauseConversation(); { Before moving on to the rest of the conversation, take a } { minute to read this message. } var RPM: RPGMenuPtr; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InteractMenu ); AddRPGMenuItem( RPM , MsgString( 'Continue' ), -1 ); ASRD_GameBoard := GB; CHAT_React := ReactionScore( GB^.Scene , I_PC , I_NPC ); SelectMenu( RPM , @InteractRedraw ); DisposeRPGMenu( RPM ); end; var id: Integer; plot: GearPtr; msg: String; begin { Error check- if not in a conversation, call the PRINT } { routine instead. } if IntMenu = Nil then begin ProcessPrint( Event , GB , Source ); Exit; end; id := ScriptValue( Event , GB , Source ); plot := PlotMaster( GB , Source ); msg := getTheMessage( 'msg' , id , GB , plot ); if msg <> '' then begin YesNoMenu( GB , REplaceHash( MsgString( 'Debrief_Intro' ) , PilotName( I_NPC ) ) , '' , '' ); CHAT_Message := msg; PauseConversation(); end; end; Procedure ProcessAddChat( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Add a new item to the IntMenu. } var N: Integer; Msg: String; begin { Error check - this command can only work if the IntMenu is } { already allocated. } if ( IntMenu <> Nil ) and ( Source <> Nil ) then begin { First, determine the prompt number. } N := ScriptValue( Event , GB , Source ); msg := getthemessage( 'PROMPT' , N , GB , Source ); DeleteWhiteSpace( msg ); if Msg <> '' then begin AddRPGMenuItem( IntMenu , Msg , N ); RPMSortAlpha( IntMenu ); end; end; end; Procedure ProcessSayAnything( GB: GameBoardPtr ); { Print a random message in the interact message area. } { New- if there are any memes floating about, select one of those for the message. } Procedure CleanMemes( City: GearPtr ); { Most memes come with a "best before" date. If they are not revealed to the player } { by this time, they get deleted. This can be useful for things like news events. } var M,M2: GearPtr; begin M := City^.SubCom; while M <> Nil do begin M2 := M^.Next; if ( M^.G = GG_Meme ) and ( NAttValue( M^.NA , NAG_MemeData , NAS_MemeTimeLimit ) > 0 ) and ( NAttValue( M^.NA , NAG_MemeData , NAS_MemeTimeLimit ) < GB^.ComTime ) then begin RemoveGear( City^.SubCom , M ); end; M := M2; end; end; Function NumMeme( City: GearPtr ): Integer; { Return the number of active memes in this city. } var M: GearPtr; N: Integer; begin if City = Nil then Exit( 0 ); M := City^.SubCom; N := 0; while M <> Nil do begin if ( M^.G = GG_Meme ) or ( M^.G = GG_CityMood ) then Inc( N ); M := M^.Next; end; NumMeme := N; end; Function GetMeme( City: GearPtr; N: Integer ): GearPtr; { Return the requested meme. } var S,M: GearPtr; begin S := City^.SubCom; M := Nil; while ( S <> Nil ) and ( N > 0 ) do begin if ( S^.G = GG_Meme ) or ( S^.G = GG_CityMood ) then begin Dec( N ); if N = 0 then M := S; end; S := S^.Next; end; GetMeme := M; end; var N: Integer; City,Meme: GearPtr; msg: String; begin City := FindRootScene( GB^.Scene ); { Before checking for memes, delete any expired memes that might still be kicking around. } if City <> Nil then CleanMemes( City ); N := NumMeme( City ); if ( I_NPC <> Nil ) and ( I_PC <> Nil ) and ( GB <> Nil ) and ( ReactionScore( GB^.Scene , I_PC , I_NPC ) <= -Random( 10 ) ) then begin { If talking to someone who dislikes you, you're most likely to get } { a brushoff. } msg := MsgString( 'BUZZOFF_' + BStr( Random( 5 ) + 1 ) ); end else if N > 0 then begin Meme := GetMeme( City , Random( N ) + 1 ); msg := ScriptMessage( 'Msg' , GB , Meme ); if msg = '' then begin msg := IdleChatter end else begin { A message was successfully extracted from this meme. Increment the } { message counter and maybe delete it. } AddNAtt( Meme^.NA , NAG_MemeData , NAS_NumViews , 1 ); if ( Meme^.G = GG_Meme ) and ( NAttValue( Meme^.NA , NAG_MemeData , NAS_NumViews ) >= NAttValue( Meme^.NA , NAG_MemeData , NAS_MaxMemeViews ) ) then begin RemoveGear( City^.SubCom , Meme ); end; end; end else begin msg := IdleChatter; end; CHAT_Message := msg; end; Procedure ProcessActivateMeme( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Activate a meme. The meme should be stored in SOURCE, the plot master of SOURCE, or the } { story master of SOURCE. If the named meme cannot be found this will generate an error. } var MemeID,SceneID: LongInt; Meme,Scene: GearPtr; begin { Get the meme ID and the scene ID. } MemeID := ScriptValue( event , GB , source ); SceneID := ScriptValue( event , GB , source ); Meme := GG_LocateItem( MemeID , GB , Source ); Scene := FindRootScene( FindActualScene( GB , SceneID ) ); if ( Meme = Nil ) or ( Meme^.G <> GG_Meme ) then begin DialogMsg( 'ERROR: Meme ' + BStr( MemeID ) + ' not found. Context: ' + Event ); end else if Scene = Nil then begin DialogMsg( 'ERROR: ActivateMeme failed, scene ' + BStr( SceneID ) + ' not found. Context: ' + Event ); end else begin DelinkGearForMovement( GB , Meme ); InsertSubCom( Scene , Meme ); { If this meme has a time limit, set that now. } if NAttValue( Meme^.NA , NAG_MemeData , NAS_MemeTimeLimit ) > 0 then begin AddNAtt( Meme^.NA , NAG_MemeData , NAS_MemeTimeLimit , GB^.ComTime ); end; end; end; Procedure ProcessGSetNAtt( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { The script is going to assign a value to one of the scene } { variables. } var G,S: Integer; V: LongInt; begin { Find the variable ID number and the value to assign. } G := ScriptValue( event , GB , scene ); S := ScriptValue( event , GB , scene ); V := ScriptValue( event , GB , scene ); if Debug_On then dialogmsg( 'GAddNAtt: ' + GearName( Grabbed_Gear ) + ' ' + BStr( G ) + '/' + BStr( S ) + '/' + BStr( V ) ); if Grabbed_Gear <> Nil then SetNAtt( Grabbed_Gear^.NA , G , S , V ); end; Procedure ProcessGAddNAtt( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { The script is going to add a value to one of the scene } { variables. } var G,S: Integer; V: LongInt; begin { Find the variable ID number and the value to assign. } G := ScriptValue( event , GB , scene ); S := ScriptValue( event , GB , scene ); V := ScriptValue( event , GB , scene ); if Debug_On then dialogmsg( 'GAddNAtt: ' + GearName( Grabbed_Gear ) + ' ' + BStr( G ) + '/' + BStr( S ) + '/' + BStr( V ) ); if Grabbed_Gear <> Nil then AddNAtt( Grabbed_Gear^.NA , G , S , V ); end; Procedure ProcessGSetStat( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { The script is going to add a value to one of the scene } { variables. } var Slot,Value: Integer; begin { Find the variable ID number and the value to assign. } Slot := ScriptValue( event , GB , scene ); Value := ScriptValue( event , GB , scene ); if Grabbed_Gear <> Nil then begin Grabbed_Gear^.Stat[ Slot ] := Value; ResizeCharacter(Grabbed_Gear); end; end; Procedure ProcessGAddStat( var Event: String; GB: GameBoardPtr; Scene: GearPtr ); { The script is going to add a value to one of the scene } { variables. } var Slot,Value: Integer; begin { Find the variable ID number and the value to assign. } Slot := ScriptValue( event , GB , scene ); Value := ScriptValue( event , GB , scene ); if Grabbed_Gear <> Nil then begin Grabbed_Gear^.Stat[ Slot ] := Grabbed_Gear^.Stat[ Slot ] + Value; ResizeCharacter(Grabbed_Gear); end; end; Procedure ProcessGSetSAtt( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Store a string attribute in the grabbed gear. } var Key,Info: String; begin Key := ExtractWord( Event ); Info := ExtractWord( Event ); if Source <> Nil then Info := AS_GetString( Source , Info ); FormatMessageString( info , GB , source ); DeleteWhiteSpace( info ); if Grabbed_Gear <> Nil then SetSAtt( Grabbed_Gear^.SA , Key + ' <' + Info + '>' ); end; Procedure IfSuccess( var Event: String ); { An IF call has generated a "TRUE" result. Just get rid of } { any ELSE clause that the event string might still be holding. } var cmd: String; begin { Extract the next word from the script. } cmd := ExtractWord( Event ); { If the next word is ELSE, we have to also extract the label. } { If the next word isn't ELSE, better re-assemble the line... } if UpCase( cmd ) = 'ELSE' then ExtractWord( Event ) else Event := cmd + ' ' + Event; end; Procedure IfFailure( var Event: String; Scene: GearPtr ); { An IF call has generated a "FALSE" result. See if there's } { a defined ELSE clause, and try to load the next line. } var cmd: String; begin { Extract the next word from the script. } cmd := ExtractWord( Event ); if UpCase( cmd ) = 'ELSE' then begin { There's an else clause. Attempt to jump to the } { specified script line. } cmd := ExtractWord( Event ); Event := AS_GetString( Scene , CMD ); end else begin { There's no ELSE clause. Just cease execution of this } { line by setting it to an empty string. } Event := ''; end; end; Procedure ProcessIfGInPlay( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return true if the Grabbed_Gear is on the map and operational. } { Return false otherwise. } begin if ( Grabbed_Gear <> Nil ) and OnTheMap( GB , Grabbed_Gear ) and GearOperational( Grabbed_Gear ) and IsFoundAlongTrack( GB^.Meks , FindRoot( Grabbed_Gear ) ) then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfGOK( var Event: String; Source: GearPtr ); { If the grabbed gear is OK, count as true. If it is destroyed, } { or if it can't be found, count as false. } begin if ( Grabbed_Gear <> Nil ) and NotDestroyed( Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGDead( var Event: String; Source: GearPtr ); { If the grabbed gear is dead or nil, count as true. } begin if ( Grabbed_Gear = Nil ) or Destroyed( Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGSexy( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { If the grabbed gear is sexy to the PC, count as true. If it is not, } { or if it can't be found, count as false. } var PC: GearPtr; begin PC := GG_LOcatePC( GB ); if ( Grabbed_Gear <> Nil ) and ( PC <> Nil ) and IsSexy( PC , Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGSealed( var Event: String; Source: GearPtr ); { If the grabbed gear is EnviroSealed, count as true. If not, } { count as false. } begin if ( Grabbed_Gear <> Nil ) and IsEnviroSealed( Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGArchEnemy( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { If the grabbed gear is an enemy of the PC, or belongs to a faction that's } { an enemy of the PC, count as true. } var Adv: GearPtr; begin Adv := GG_LOcateAdventure( GB , Source ); if ( Grabbed_Gear <> Nil ) and ( Adv <> Nil ) and IsArchEnemy( Adv , Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGArchAlly( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { If the grabbed gear is an ally of the PC, or belongs to a faction that's } { an ally of the PC, count as true. } var Adv: GearPtr; begin Adv := GG_LOcateAdventure( GB , Source ); if ( Grabbed_Gear <> Nil ) and ( Adv <> Nil ) and IsArchAlly( Adv , Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGHasSkill( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { If the grabbed gear has the skill, count as true. If it doesn't, } { or if it can't be found, count as false. } var Skill: Integer; begin Skill := ScriptValue( Event , GB , Source ); if ( Grabbed_Gear <> Nil ) and HasSkill( Grabbed_Gear , Skill ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfGCanJoinLance( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return true if the Grabbed_Gear can join the PC's lance, or FALSE otherwise. } { Please note that the NPC must be in play in order to join the lance. } var PC: GearPtr; begin PC := GG_LocatePC( GB ); if ( Grabbed_Gear <> Nil ) and OnTheMap( GB , Grabbed_Gear ) and GearOperational( Grabbed_Gear ) and IsFoundAlongTrack( GB^.Meks , FindRoot( Grabbed_Gear ) ) and CanJoinLance( GB , PC , Grabbed_Gear ) then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfMechaCanEnterScene( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return true if the PC's mecha can enter the requested scene. } { Return false otherwise. } var PC,Mek,Scene: GearPtr; SceneID: Integer; begin { Locate the PC, the PC's mecha, and the target scene. } PC := FindRoot( GG_LocatePC( GB ) ); if ( PC <> Nil ) then begin if PC^.G = GG_Mecha then Mek := PC else Mek := FindPilotsMecha( GB^.Meks , PC ); end; SceneID := ScriptValue( Event , GB , Source ); Scene := FindActualScene( GB , SceneID ); if ( PC <> Nil ) and ( Mek <> Nil ) and ( Scene <> Nil ) and NotDestroyed( Mek ) and MekCanEnterScene( Mek , Scene ) then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfMeritBadge( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return true if the PC has the requested merit badge. } { Return false otherwise. } var Adv: GearPtr; Badge: Integer; begin Badge := ScriptValue( Event , GB , Source ); Adv := GG_LocateAdventure( GB , Source ); if ( Adv <> Nil ) and HasMeritBadge( Adv , Badge ) then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfTeamCanSeeGG( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { If the grabbed gear can be seen by the requested team, return TRUE. } var Team: Integer; begin Team := ScriptValue( Event , GB , Source ); if ( Grabbed_Gear <> Nil ) and TeamCanSeeTarget( GB , Team , Grabbed_Gear ) then begin IfSuccess( Event ); end else IfFailure( Event , Source ); end; Procedure ProcessIfFaction( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Check to see if the requested faction is active or not. } var FID: Integer; Fac: GearPtr; begin { Locate the requested Faction ID, and from there locate } { the faction gear itself. } FID := ScriptValue( Event , GB , Source ); Fac := GG_LocateFaction( FID , GB , Source ); { If the faction was found, see whether or not it's active. } if Fac <> Nil then begin if FactionIsInactive( Fac ) then IfFailure( Event , Source ) else IfSuccess( Event ); { If said faction cannot be found, it counts as a failure. } end else IfFailure( Event , Source ); end; Procedure ProcessIfStoryless( var Event: String; Source: GearPtr ); { Return true if the SOURCE has no story linked. } { Return false otherwise. } var story: GearPtr; begin if Source <> Nil then begin story := Source^.InvCom; while ( story <> Nil ) and ( story^.G <> GG_Story ) do story := story^.Next; if Story = Nil then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfEqual( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Two values are supplied as the arguments for this procedure. } { If they are equal, that's a success. } var a,b: LongInt; begin { Determine the two values. } A := ScriptValue( Event , gb , Source ); B := ScriptValue( Event , gb , Source ); if A = B then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessIfNotEqual( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Two values are supplied as the arguments for this procedure. } { If they are not equal, that's a success. } var a,b: LongInt; begin { Determine the two values. } A := ScriptValue( Event , gb , Source ); B := ScriptValue( Event , gb , Source ); if A <> B then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessIfGreater( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Two values are supplied as the arguments for this procedure. } { If A > B, that's a success. } var a,b: LongInt; begin { Determine the two values. } A := ScriptValue( Event , gb , Source ); B := ScriptValue( Event , gb , Source ); if A > B then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessIfKeyItem( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Process TRUE if the specified key item is in the posession of the PC. } { We'll define this as being in the posession of any member of team } { one... Process FALSE if it isn't. } var NID: Integer; FoundTheItem: Boolean; PC: GearPtr; begin { Start by assuming FALSE, then go looking for it. } FoundTheItem := False; { Find out what Key Item we're looking for. } NID := ScriptValue( Event , GB , Source ); if ( GB <> Nil ) and ( NID <> 0 ) then begin { Search through every gear on the map. } PC := GB^.Meks; while PC <> Nil do begin { If this gear belongs to the player team, check it } { for the wanted item. } if NAttValue( PC^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam then begin { Set FOUNDTHEITEM to TRUE if the specified key item } { is the PC gear itself, if it's in the subcoms of the PC, } { or if it's somewhere in the inventory of the PC. } if NAttValue( PC^.NA , NAG_Narrative , NAS_NID ) = NID then FoundTheItem := True else if SeekGearByIDTag( PC^.SubCom , NAG_Narrative , NAS_NID , NID ) <> Nil then FoundTheItem := True else if SeekGearByIDTag( PC^.InvCom , NAG_Narrative , NAS_NID , NID ) <> Nil then FoundTheItem := True; end; { Move to the next gear to check. } PC := PC^.Next; end; end; { Finally, do something appropriate depending upon whether or not } { the item was found. } if FoundTheItem then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessIfGHasItem( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Process TRUE if the specified key item is in the posession of the grabbed gear. } var NID: Integer; FoundTheItem: Boolean; begin { Start by assuming FALSE, then go looking for it. } FoundTheItem := False; { Find out what Key Item we're looking for. } NID := ScriptValue( Event , GB , Source ); if ( Grabbed_Gear <> Nil ) and ( NID <> 0 ) then begin if NAttValue( Grabbed_Gear^.NA , NAG_Narrative , NAS_NID ) = NID then FoundTheItem := True else if SeekGearByIDTag( Grabbed_Gear^.SubCom , NAG_Narrative , NAS_NID , NID ) <> Nil then FoundTheItem := True else if SeekGearByIDTag( Grabbed_Gear^.InvCom , NAG_Narrative , NAS_NID , NID ) <> Nil then FoundTheItem := True; end; { Finally, do something appropriate depending upon whether or not } { the item was found. } if FoundTheItem then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessIfYesNo( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Two values are supplied as the arguments for this procedure. } { If they are equal, that's a success. } var Desc,YesPrompt,NoPrompt: String; it: Boolean; ID: Integer; begin { Find all the needed messages. } id := ScriptValue( Event , GB , Source ); Desc := GetTheMessage( 'msg' , id , GB , Source ); id := ScriptValue( Event , GB , Source ); YesPrompt := GetTheMessage( 'msg' , id , GB , Source ); id := ScriptValue( Event , GB , Source ); NoPrompt := GetTheMessage( 'msg' , id , GB , Source ); it := YesNoMenu( GB , Desc , YesPrompt , NoPrompt ); if it then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessIfScene( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return TRUE if the current scene matches the provided } { description, or FALSE otherwise. } var Desc: String; begin Desc := ExtractWord( Event ); if Source <> Nil then Desc := AS_GetString( Source , Desc ); if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and PartMatchesCriteria( SceneDesc( GB^.Scene ) , Desc ) then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfSafeArea( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return TRUE if the current scene is a Safe Area (as determined } { by the function of the same name), or FALSE otherwise. } begin if ( GB <> Nil ) and IsSafeArea( GB ) then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfSkillTest( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return TRUE if the PC makes the requested skill roll, or FALSE otherwise. } { If the PC has already attempted this skill roll, he can't succeed unless } { he's improved his skill level. } var PC: GearPtr; Skill,SkStat,SkTar,SkRank,SkRoll: Integer; begin PC := GG_LocatePC( GB ); Skill := ScriptValue( Event , GB , Source ); SkStat := ScriptValue( Event , GB , Source ); SkRank := SkillRank( PC , Skill ) + 1; SkTar := ScriptValue( Event , GB , Source ); if ( Source <> Nil ) and ( SkRank <= NAttValue( Source^.NA , NAG_SkillCounter , Skill ) ) then begin IfFailure( Event , Source ); end else begin SkRoll := SkillRoll( GB , PC , Skill , SkStat , SkTar , 0 , True , True ); if ( SkRoll >= SkTar ) then begin IfSuccess( Event ); end else begin if Source <> Nil then SetNAtt( Source^.NA , NAG_SkillCounter , Skill , SkRank ); IfFailure( Event , Source ); end; end; end; Procedure ProcessIfUSkillTest( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Return TRUE if the PC makes the requested skill roll, or FALSE otherwise. } var Skill,SkStat,SkTar,SkRoll: Integer; begin Skill := ScriptValue( Event , GB , Source ); SkStat := ScriptValue( Event , GB , Source ); SkTar := ScriptValue( Event , GB , Source ); SkRoll := SkillRoll( GB , GG_LocatePC( GB ) , Skill , SkStat , SkTar , 0 , IsSafeArea( GB ) , True ); if SkRoll >= SkTar then begin IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessIfNoObjections( var Event: String; gb: GameBoardPtr; Source: GearPtr ); { Run a trigger through the narrative gears. } { If none of them BLOCK it, then count the result as TRUE. } var T: String; { The trigger to be used. } Adv: GearPtr; begin { Generate the trigger, which is in the same format as for COMPOSE. } { It's a trigger label plus a numeric value. } T := ExtractWord( Event ); T := T + BStr( ScriptValue( Event, GB, Source ) ); { Check the trigger along the adventure's invcoms, } { where all the narrative components should be located. } Adv := GG_LocateAdventure( GB , Source ); if Adv <> Nil then begin CheckTriggerAlongPath( T, GB, Adv^.InvCom , False ); end; { If the trigger wasn't blocked, that counts as a success. } if T <> '' then IfSuccess( Event ) else IfFailure( Event , Source ); end; Procedure ProcessTeamOrders( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { This procedure is used to assign a behavior type to } { every master unit on the designated team. } const OrderParams: Array [0..NumAITypes] of Byte = ( 0,2,1,0,0,1 ); var Team: Integer; OrderName: String; T,OrderCode: Integer; Mek: GearPtr; P: Array [1..2] of Integer; begin { Record the team number. } Team := ScriptValue( Event , gb , Source ); { Figure out what order we're supposed to be assigning. } OrderName := UpCase( ExtractWord( Event ) ); OrderCode := -1; for t := 0 to NumAITypes do begin if OrderName = AI_Type_Label[ t ] then OrderCode := T; end; { If a valid order was received, process it. } if OrderCode > -1 then begin for t := 1 to OrderParams[ OrderCode ] do P[T] := ScriptValue( Event , gb , Source ); { Go through each of the meks and, if they are part } { of the specified team, assign the specified order. } Mek := gb^.Meks; while Mek <> Nil do begin if NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = Team then begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Orders , OrderCode ); { DEFAULT BEHAVIOR- If number of params = 1, assume it to be a mek ID. } { If number of params = 2, assume it to be a map location. } if OrderParams[ OrderCode ] = 1 then begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_ATarget , P[1] ); end else if OrderParams[ OrderCode ] = 2 then begin SetNAtt( Mek^.NA , NAG_Location , NAS_GX , P[1] ); SetNAtt( Mek^.NA , NAG_Location , NAS_GY , P[2] ); end; end; Mek := Mek^.Next; end; end; end; Procedure ProcessCompose( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A new item is going to be added to the scene list. } var Trigger,Ev2: String; P: Integer; begin if Source = Nil then exit; { Extract the values we need. } Trigger := ExtractWord( Event ); P := ScriptValue( Event , GB , Source ); Ev2 := AS_GetString( Source , ExtractWord( Event ) ); StoreSAtt( Source^.SA , Trigger + BStr( P ) + ' <' + Ev2 + '>' ); end; Procedure ProcessNewChat( GB: GameBoardPtr ); { Reset the dialog menu with the standard options. } begin { Error check - make sure the interaction menu is active. } if IntMenu = Nil then begin Exit; { If there are any menu items currently in the list, get rid } { of them. } end else if IntMenu^.FirstItem <> Nil then begin ClearMenu( IntMenu ); end; AddRPGMenuItem( IntMenu , MsgString( 'NEWCHAT_Goodbye' ) , -1 ); if ( GB <> Nil ) and OnTheMap( GB , FindRoot( I_NPC ) ) and IsFoundAlongTrack( GB^.Meks , FindRoot( I_NPC ) ) then begin { Only add the JOIN command if this NPC is in the same scene as the PC. } if ( I_PC <> Nil ) and HasTalent( I_PC , NAS_Camaraderie ) then begin if ( I_NPC <> Nil ) and ( NAttValue( I_NPC^.NA , NAG_Relationship , 0 ) >= NAV_Friend ) and ( NAttValue( I_NPC^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then AddRPGMenuItem( IntMenu , MsgString( 'NEWCHAT_Join' ) , CMD_Join ); end else begin if ( I_NPC <> Nil ) and ( NAttValue( I_NPC^.NA , NAG_Relationship , 0 ) >= NAV_ArchAlly ) and ( NAttValue( I_NPC^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then AddRPGMenuItem( IntMenu , MsgString( 'NEWCHAT_Join' ) , CMD_Join ); end; end; if ( I_NPC <> Nil ) and ( NAttValue( I_NPC^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and ( NAttValue( I_NPC^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_TempLancemate ) and IsSafeArea( GB ) and IsSubCom( GB^.Scene ) then AddRPGMenuItem( IntMenu , MsgSTring( 'NEWCHAT_QuitLance' ) , CMD_Quit ); if not ( OnTheMap( GB , FindRoot( I_NPC ) ) and IsFoundAlongTrack( GB^.Meks , FindRoot( I_NPC ) ) ) then AddRPGMenuItem( IntMenu , MsgString( 'NewChat_WhereAreYou' ) , CMD_WhereAreYou ); if ( NAttValue( I_NPC^.NA , NAG_Personal , NAS_RumorRecharge ) < GB^.ComTime ) and ( NAttValue( I_NPC^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then AddRPGMenuItem( IntMenu , MsgString( 'NEWCHAT_AskAboutRumors' ) , CMD_AskAboutRumors ); RPMSortAlpha( IntMenu ); end; Procedure ProcessEndChat; { End this conversation by clearing the menu. } begin { Error check - make sure the interaction menu is active. } if IntMenu = Nil then begin Exit; end else begin ClearMenu( IntMenu ); end; end; Procedure RevertPersona( var Event: String; GB: GameBoardPtr; var Source: GearPtr ); { We don't wanna use this persona anymore. Attempt to locate } { I_NPC's original persona, and start the GREETING event. } var Adv,Persona2: GearPtr; begin { Error check- if there's no defined NPC, } { we can't very well be expected to locate a persona. } if ( I_NPC = Nil ) or ( Source = Nil ) or ( Source^.G <> GG_Persona ) then begin Event := ''; Exit; end; { Locate the adventure and hopefully the persona. } Adv := GG_LocateAdventure( GB , Source ); if Adv <> Nil then begin Persona2 := SeekGear( Adv , GG_Persona , NAttValue( I_NPC^.NA , NAG_Personal , NAS_CID ) , False ); if ( Persona2 <> Nil ) and ( Persona2 <> Source ) then begin { Change the event script to the requested line. } Event := AS_GetString( Persona2 , 'GREETING' ); I_Persona := Persona2; Source := Persona2; end else begin { A different persona wasn't found. Do nothing. } Event := 'NewChat SayAnything'; end; end else begin { The adventure wasn't found. Do nothing. } Event := 'NewChat SayAnything'; end; end; Procedure ProcessGoto( var Event: String; Source: GearPtr ); { Attempt to jump to a different line of the script. } { If no line label is provided, or if the label can't be } { found, this procedure sets EVENT to an empty string. } var destination: String; begin { Error check- if there's no defined source, we can't very } { well jump to another line, can we? } if Source = Nil then begin Event := ''; Exit; end; destination := ExtractWord( Event ); if destination <> '' then begin { Change the event script to the requested line. } Event := AS_GetString( Source , destination ); end else begin { No label was provided. Just return a blank line. } Event := ''; end; end; Procedure ProcessSeekTerr( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Assign a value to SCRIPT_Terrain_To_Seek. } var Terrain: Integer; begin Terrain := ScriptValue( event , GB , Source ); SCRIPT_Terrain_To_Seek := Terrain; end; Procedure CantOpenBusiness( var Event: String; GB: GameBoardPtr ); { The business can't be opened. Print an error message and } { cancel the rest of the event. } var Scene: Integer; msg: String; begin Event := ''; Scene := FindSceneID( I_NPC , GB ); if Scene <> 0 then begin msg := ReplaceHash( msgString( 'CantOpenShop_WithScene' ) , SceneName( GB , Scene , True ) ); end else begin msg := msgString( 'CantOpenShop' ); end; CHAT_Message := msg; end; Procedure ProcessShop( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Retrieve the WARES line, then pass it all on to the OpenShop } { procedure. } var Wares: String; begin { Retrieve the WARES string. } Wares := ExtractWord( Event ); if Wares <> '' then begin { Change the event script to the requested line. } Wares := AS_GetString( Source , Wares ); end; { Only open the shop if the NPC is on the current map. } if IsFoundAlongTrack( GB^.Meks , FindRoot( I_NPC ) ) then begin { Pass all info on to the OPENSHOP procedure. } OpenShop( GB , I_PC , I_NPC , Wares ); end else begin { Call the error handler. } CantOpenBusiness( Event , GB ); end; end; Procedure ProcessSchool( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Retrieve the WARES line, then pass it all on to the OpenSchool } { procedure. } var Wares: String; begin { Retrieve the WARES string. } Wares := ExtractWord( Event ); if Wares <> '' then begin { Change the event script to the requested line. } Wares := AS_GetString( Source , Wares ); end; { Only open the shop if the NPC is on the current map. } if IsFoundAlongTrack( GB^.Meks , FindRoot( I_NPC ) ) then begin { Pass all info on to the OPENSHOP procedure. } OpenSchool( GB , I_PC , I_NPC , Wares ); end else begin { Call the error handler. } CantOpenBusiness( Event , GB ); end; end; Procedure ProcessExpressDelivery( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Call the ExpressDelivery procedure. } begin { Only open the shop if the NPC is on the current map. } if IsFoundAlongTrack( GB^.Meks , FindRoot( I_NPC ) ) then begin { Pass all info on to the ExpressDelivery procedure. } ExpressDelivery( GB , I_PC , I_NPC ); end else begin { Call the error handler. } CantOpenBusiness( Event , GB ); end; end; Procedure ProcessShuttle( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Call the ExpressDelivery procedure. } begin { Only open the shop if the NPC is on the current map. } if IsFoundAlongTrack( GB^.Meks , FindRoot( I_NPC ) ) then begin { Pass all info on to the ExpressDelivery procedure. } ShuttleService( GB , I_PC , I_NPC ); end else begin { Call the error handler. } CantOpenBusiness( Event , GB ); end; end; Procedure ProcessEndPlot( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { This particular plot is over- mark it for deletion. } { First, though, check to see if there are any subcomponents that } { need to be moved around. } begin { If we have a valid SOURCE, attempt to end the plot. } if ( Source <> Nil ) then begin { It's possible that our SOURCE is a PERSONA rather than } { a PLOT, so if SOURCE isn't a PLOT move to its parent. } Source := PlotMaster( GB , Source ); if ( Source <> Nil ) and ( Source^.G = GG_Plot ) and ( NAttValue( Source^.NA , NAG_Narrative , NAS_PlotID ) > 0 ) then begin EndPlot( GB , Source^.Parent , Source ); NeedGC := True; end; SetTrigger( GB , 'UPDATE' ); end; end; Procedure ProcessEndPlotsByConID( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { End all plots associated with the requested mood. } var CID: LongInt; Adv,Plot: GearPtr; begin { Determine the ControllerID. } CID := ScriptValue( Event , GB , Source ); { Locate the adventure. } Adv := GG_LocateAdventure( GB , Source ); { If we have a valid ADV, attempt to end the attached plots. } if ( Adv <> Nil ) then begin Plot := Adv^.InvCom; while Plot <> Nil do begin if ( Plot^.G = GG_Plot ) and ( NAttValue( Plot^.NA , NAG_Narrative , NAS_PlotID ) > 0 ) and ( NAttValue( Plot^.NA , NAG_Narrative , NAS_ControllerID ) = CID ) then begin EndPlot( GB , Adv , Plot ); end; Plot := Plot^.Next; end; { Finally, set an UPDATE trigger. } SetTrigger( GB , 'UPDATE' ); end; end; Procedure CleanUpStoryPlots( GB: GameBoardPtr; Story: GearPtr ); { Give a CLEANUP trigger to all the story plots, then move the } { plots which survive to the adventure invcoms. } var T: String; Part,P2,Adv: GearPtr; begin { Send a CLEANUP trigger to the invcoms. } { This should erase all the plots that want to be erased, } { and leave all the plots which want to be moved. } T := 'CLEANUP'; CheckTriggerAlongPath( T , GB , Story^.InvCom , False ); { Check whatever is left over. } Part := Story^.InvCom; Adv := GG_LocateAdventure( GB , STory ); while Part <> Nil do begin P2 := Part^.Next; if Part^.G = GG_Plot then begin DelinkGear( Story^.InvCom , Part ); if Adv <> Nil then begin InsertInvCom( Adv , Part ); end else begin DisposeGear( Part ); end; end; Part := P2; end; end; Procedure ProcessEndStory( GB: GameBoardPtr; Source: GearPtr ); { This particular story is over- mark it for deletion. } { First, though, pass a CLEANUP trigger to any subcomponents that } { may need to be cleaned up. } begin Source := StoryMaster( GB , Source ); if ( Source <> Nil ) and ( Source^.G = GG_Story ) then begin CleanupStoryPlots( GB , Source ); { Mark the story for deletion. } Source^.G := GG_AbsolutelyNothing; NeedGC := True; SetTrigger( GB , 'UPDATE' ); end; end; Procedure ProcessPurgeStory( GB: GameBoardPtr; Source: GearPtr ); { Eliminate all plots from this story. } begin { If we have a valid SOURCE, check the invcoms. } if ( Source <> Nil ) and ( Source^.G = GG_Story ) then begin { Send a CLEANUP trigger to the invcoms, } { then move the survivors to the Adventure. } CleanupStoryPlots( GB , Source ); SetTrigger( GB , 'UPDATE' ); end; end; Procedure ProcessPumpNews( GB: GameBoardPtr ); { Once every six hours or so, check through all the QUESTs and MOODs and } { see if they've got anything new to do. PumpNews will trigger the PUMPNEWS } { script of the subcoms of every city in this world. } var T: String; world,city: GearPtr; begin { Error check } if ( GB = Nil ) or ( GB^.Scene = Nil ) then Exit; { Start by locating the world. } world := FindWorld( GB , GB^.Scene ); if world = Nil then Exit; T := 'PUMPNEWS'; { Go through the world's cities, springing the trigger at each. } city := world^.SubCom; while city <> Nil do begin if City^.G = GG_Scene then begin CheckTriggerAlongPath( T , GB , City^.SubCom , False ); end; city := city^.Next; end; end; Procedure ProcessTReputation( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Something has happened to affect the PC's reputation. } { Record the change. } var T,R,V: Integer; begin { Error check - this procedure only works if GB is defined. } if ( GB = Nil ) then Exit; T := ScriptValue( Event , GB , Source ); R := ScriptValue( Event , GB , Source ); V := ScriptValue( Event , GB , Source ); SetTeamReputation( GB , T , R , V ); end; Procedure ProcessLoseRenown( GB: GameBoardPtr ); { The PC has just done something to lose face. Reduce the RENOWN attribute } { by 25% of its total or 5 points, whichever is more severe. } var PC: GearPtr; Renown: Integer; begin PC := LocatePilot( GG_LocatePC( GB ) ); if PC <> Nil then begin Renown := NAttValue( PC^.NA , NAG_CHarDescription , NAS_Renowned ); if Renown > 23 then begin AddReputation( PC , NAS_Renowned , -( Renown div 4 ) ); end else begin AddReputation( PC , NAS_Renowned , -5 ); end; end; end; Procedure ProcessMechaPrize( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { The player has just won a mecha. Cool! } var Factions,FName,msg: String; Renown,Theme,ModPoints: Integer; MList,Mek,PC,Adv: GearPtr; begin { ERROR CHECK - We need the gameboard to exist!!! } if GB = Nil then Exit; { Find the adventure; it'll be needed later. } Adv := GG_LocateAdventure( GB , Source ); { First, find the file name of the mecha file to look for. } { Because this mecha is gonna be randomly determined we'll need some information } { for that. First, determine the factions whose mecha will be considered. Second, } { we need the renown level the mecha will be appropriate for. } Factions := ExtractWord( Event ); if Source <> Nil then begin Factions := ScriptMessage( Factions , GB , Source ); end; Renown := ScriptValue( Event , GB , Source ); Theme := ScriptValue( Event , GB , Source ); ModPoints := ScriptValue( Event , GB , Source ); { Call the random mecha picker. } FName := SelectMechaByFactionAndRenown( Factions , Renown ); { Attempt to load the suggested mecha. } MList := LoadGearPattern( FName , Design_Directory ); { Next confirm that something was loaded. } if MList <> Nil then begin { Something was loaded. Yay! Pick one of the gears } { at random, clone it, stick it on the game board, } { and get rid of the list we loaded. } Mek := CloneGear( SelectRandomGear( MList ) ); DisposeGear( MList ); { If modifications were requested, do those now. } if ModPoints > 0 then begin MechaMakeover( Mek , 0 , Theme , ModPoints ); end; SetSATt( Mek^.SA , 'SDL_COLORS <' + Random_Mecha_Colors + '>' ); SetNAtt( Mek^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); DeployGear( GB , Mek , False ); if ( Adv <> Nil ) and ( Adv^.S = GS_ArenaCampaign ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin { This is Arena mode. Store the mecha announcement for the } { mission debriefing. } AddSAtt( GB^.Scene^.SA , ARENAREPORT_MechaObtained , FullGearName( Mek ) ); end else begin { This is RPG mode. Report the mecha directly. } msg := ReplaceHash( MsgString( 'MechaPrize_Announce' ) , FullGearName( Mek ) ); AToAn( msg ); DialogMsg( msg ); PC := GG_LocatePC( GB ); if FindPilotsMecha( GB^.Meks , PC ) = Nil then AssociatePilotMek( GB^.Meks , PC , Mek ); end; end; end; Procedure ProcessDeleteGG( GB: GameBoardPtr; var Source: GearPtr ); { Delete the grabbed gear. } { Only physical gears can be deleted in this way. } begin if ( Grabbed_Gear <> Nil ) and (( Grabbed_Gear^.G >= 0 ) or ( Grabbed_Gear^.G = GG_CityMood )) then begin { Make sure we aren't currently using the grabbed gear. } if ( IntMenu <> Nil ) and ( I_NPC = Grabbed_Gear ) then begin ProcessEndChat; I_NPC := Nil; end; if Source = Grabbed_Gear then begin Source := Nil; end; { Delete the gear, if it can be found. } if IsSubCom( Grabbed_Gear ) then begin RemoveGear( Grabbed_Gear^.Parent^.SubCom , Grabbed_Gear ); end else if IsInvCom( Grabbed_Gear ) then begin RemoveGear( Grabbed_Gear^.Parent^.InvCom , Grabbed_Gear ); end else if ( GB <> Nil ) and IsFoundAlongTrack( GB^.Meks , Grabbed_Gear) then begin RemoveGear( GB^.Meks , Grabbed_Gear ); end; end; end; Procedure ProcessMoveGG( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Move the grabbed gear to the specified scene. } { Only physical gears can be moved in this way. } { If the specified scene is 0, the gear will be "frozen" isntead. } var SID: Integer; { Scene ID. } Scene: GearPtr; begin { Check to make sure we have a valid gear to move. } if ( Grabbed_Gear <> Nil ) and ( Grabbed_Gear^.G >= 0 ) then begin DelinkGearForMovement( GB , Grabbed_Gear ); { Find the new scene to stick our gear into. } SID := ScriptValue( Event , GB , Source ); if SID <> 0 then begin Scene := FindActualScene( GB , SID ); if Scene = Nil then Scene := GG_LocateAdventure( GB , Source ); end else begin Scene := GG_LocateAdventure( GB , Source ); end; InsertInvCom( Scene , Grabbed_Gear ); { If inserting a character, better choose a team. } if IsAScene( Scene ) and IsMasterGear( Grabbed_Gear ) then begin ChooseTeam( Grabbed_Gear , Scene ); end; end; end; Procedure ProcessMoveAndPacifyGG( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Move the grabbed gear to the specified scene, } { setting its team to a nonagressive one. } { Only physical gears can be moved in this way. } { If the specified scene is 0, the gear will be "frozen" isntead. } var SID: Integer; { Scene ID. } Scene: GearPtr; begin { Check to make sure we have a valid gear to move. } if ( Grabbed_Gear <> Nil ) and ( Grabbed_Gear^.G >= 0 ) then begin DelinkGearForMovement( GB , Grabbed_Gear ); { Find the new scene to stick our gear into. } SID := ScriptValue( Event , GB , Source ); if SID <> 0 then begin Scene := FindActualScene( GB , SID ); if Scene = Nil then Scene := GG_LocateAdventure( GB , Source ); end else begin Scene := GG_LocateAdventure( GB , Source ); end; InsertInvCom( Scene , Grabbed_Gear ); { Set the TEAMDATA here. } if IsACombatant( Grabbed_Gear ) then begin SetSAtt( Grabbed_Gear^.SA , 'TEAMDATA ' ); end else begin SetSAtt( Grabbed_Gear^.SA , 'TEAMDATA ' ); end; { If inserting a character, better choose a team. } if IsAScene( Scene ) and IsMasterGear( Grabbed_Gear ) then begin ChooseTeam( Grabbed_Gear , Scene ); end; end; end; Procedure ProcessDeployGG( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Move the grabbed gear to the current scene. } { Only physical gears can be moved in this way. } var TID: Integer; begin { Check to make sure we have a valid gear to move. } if ( Grabbed_Gear <> Nil ) and ( GB <> Nil ) and ( Grabbed_Gear^.G >= 0 ) then begin DelinkGearForMovement( GB , Grabbed_Gear ); { Find the new team for our gear. } TID := ScriptValue( Event , GB , Source ); SetNAtt( Grabbed_Gear^.NA , NAG_Location , NAS_Team , TID ); { Stick it on the map, and maybe do a redraw. } EquipThenDeploy( GB , Grabbed_Gear , True ); end; end; Procedure ProcessDynaGG( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Move the grabbed gear to the dynamic scene. } { Only physical gears can be moved in this way. } var TID: Integer; { Team ID. } begin { Check to make sure we have a valid gear to move. } if ( Grabbed_Gear <> Nil ) and ( Grabbed_Gear^.G >= 0 ) and ( SCRIPT_DynamicEncounter <> Nil ) then begin DelinkGearForMovement( GB , Grabbed_Gear ); { Find out which team to stick the NPC in. } TID := ScriptValue( Event , GB , Source ); SetNAtt( Grabbed_Gear^.NA , NAG_Location , NAS_Team , TID ); { Perform the insertion. } InsertInvCom( SCRIPT_DynamicEncounter , Grabbed_Gear ); end; end; Procedure ProcessGiveGG( GB: GameBoardPtr ); { Give the grabbed gear to the PC. } { Only physical gears can be moved in this way. } var DelinkOK: Boolean; PC: GearPtr; begin PC := GG_LocatePC( GB ); if ( Grabbed_Gear <> Nil ) and ( Grabbed_Gear^.G >= 0 ) and (( PC = Nil ) or ( FindGearIndex( Grabbed_Gear , PC ) < 0 )) then begin { Delink the gear, if it can be found. } if IsSubCom( Grabbed_Gear ) then begin DelinkGear( Grabbed_Gear^.Parent^.SubCom , Grabbed_Gear ); DelinkOK := True; end else if IsInvCom( Grabbed_Gear ) then begin DelinkGear( Grabbed_Gear^.Parent^.InvCom , Grabbed_Gear ); DelinkOK := True; end else if ( GB <> Nil ) and IsFoundAlongTrack( GB^.Meks , Grabbed_Gear) then begin DelinkGear( GB^.Meks , Grabbed_Gear ); DelinkOK := True; end else begin DelinkOK := False; end; if DelinkOK then begin GivePartToPC( GB , Grabbed_Gear , PC ); end; end; end; Procedure ProcessGNewPart( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Stick an item from the standard items list on the gameboard, } { then make GRABBED_GEAR point to it. } { This function will first look in the STC file, then the Monster } { file, then the NPC file. } var IName: String; begin { First determine the item's designation. } IName := ExtractWord( Event ); if Source <> Nil then begin IName := AS_GetString( Source , IName ); end; { As long as we have a GB, try to stick the item there. } if GB <> Nil then begin Grabbed_Gear := LoadNewSTC( IName ); if Grabbed_Gear = Nil then Grabbed_Gear := LoadNewMonster( IName ); if Grabbed_Gear = Nil then Grabbed_Gear := LoadNewNPC( IName , True ); if Grabbed_Gear = Nil then Grabbed_Gear := LoadNewItem( IName ); { If we found something, stick it on the map. } if Grabbed_Gear <> Nil then begin { Clear the designation. } SetSAtt( Grabbed_Gear^.SA , 'DESIG <>' ); { Deploy the item. } EquipThenDeploy( GB , Grabbed_Gear , False ); end; { Any further processing must be done by other commands. } end; end; Procedure BuildGenericEncounter( GB: GameBoardPtr; Source: GearPtr; Scale: Integer ); { Create a SCENE gear, then do everything except stock it with } { enemies. } const DefaultMapSize = 50; var Team,Src: GearPtr; T: Integer; begin { First, if for some reason there's already a dynamic encounter in } { place, get rid of it. } if SCRIPT_DynamicEncounter <> Nil then DisposeGear( SCRIPT_DynamicEncounter ); { Allocate a new dynamic encounter, then fill in the blanks. } SCRIPT_DynamicEncounter := NewGear( Nil ); SCRIPT_DynamicEncounter^.G := GG_Scene; SCRIPT_DynamicEncounter^.Stat[ STAT_MapWidth ] := DefaultMapSize; SCRIPT_DynamicEncounter^.Stat[ STAT_MapHeight ] := DefaultMapSize; if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then SCRIPT_DynamicEncounter^.S := GB^.Scene^.S; SCRIPT_DynamicEncounter^.V := Scale; { Copy over the PlotID of the source. } Src := PlotMaster( GB , Source ); if Src <> Nil then begin SetNAtt( SCRIPT_DynamicEncounter^.NA , NAG_Narrative , NAS_PlotID , NAttValue( Src^.NA , NAG_Narrative , NAS_PlotID ) ); end; { Add a TEAM gear for each of the player and the enemy teams. } { We need to do this so that we'll have some control over the placement } { of the player and the enemies. } Team := AddGear( SCRIPT_DynamicEncounter^.SubCom , SCRIPT_DynamicEncounter ); Team^.G := GG_Team; Team^.S := NAV_DefPlayerTeam; SetNAtt( Team^.NA , NAG_SideReaction , NAV_DefEnemyTeam , NAV_AreEnemies ); SetNAtt( Team^.NA , NAG_ParaLocation , NAS_X , DefaultMapSize div 5 ); SetNAtt( Team^.NA , NAG_ParaLocation , NAS_Y , DefaultMapSize div 5 ); Team := AddGear( SCRIPT_DynamicEncounter^.SubCom , SCRIPT_DynamicEncounter ); Team^.G := GG_Team; Team^.S := NAV_DefEnemyTeam; SetNAtt( Team^.NA , NAG_SideReaction , NAV_DefPlayerTeam , NAV_AreEnemies ); SetNAtt( Team^.NA , NAG_ParaLocation , NAS_X , ( DefaultMapSize * 4 ) div 5 ); SetNAtt( Team^.NA , NAG_ParaLocation , NAS_Y , ( DefaultMapSize * 4 ) div 5 ); SetSAtt( Team^.SA , 'Deploy ' ); { Set the default map generator of the Dynamic Encounter based on the position of } { the PC. } Src := FindRoot( GG_LocatePC( GB ) ); if ( Src <> Nil ) then begin SCRIPT_DynamicEncounter^.Stat[ STAT_MapGenerator ] := EncounterMapType( GB , NAttValue( Src^.NA , NAG_Location , NAS_X ) , NAttValue( Src^.NA , NAG_Location , NAS_Y ) ); { If this will make the encounter a space map, set the map-scroll tag. } if SCRIPT_DynamicEncounter^.Stat[ STAT_MapGenerator ] = TERRAIN_Space then SCRIPT_DynamicEncounter^.Stat[ STAT_SpaceMap ] := 1; end; { If this metascene doesn't have environmental effects by default, } { copy the environmental effects from the parent scene. } Src := FindActualScene( GB , FindGearScene( Src , GB ) ); if Src <> Nil then begin { Copy the environmental effects from the parent scene. } for t := 1 to Num_Environment_Variables do begin SetNAtt( SCRIPT_DynamicEncounter^.NA , NAG_EnvironmentData , T , NAttValue( Src^.NA , NAG_EnvironmentData , T ) ); end; { Also copy over the tileset + backdrop. } SetNAtt( SCRIPT_DynamicEncounter^.NA , NAG_SceneData , NAS_TileSet , NAttValue( Src^.NA , NAG_SceneData , NAS_TileSet ) ); SetNAtt( SCRIPT_DynamicEncounter^.NA , NAG_SceneData , NAS_Backdrop , NAttValue( Src^.NA , NAG_SceneData , NAS_Backdrop ) ); end; { Set the exit values in the game board. } if GB <> Nil then begin AS_SetExit( GB , 0 ); end; end; Procedure ProcessNewD( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Create a new scene for a dynamic encounter to take place on. } var Scale: Integer; begin Scale := ScriptValue( Event , GB , Source ); BuildGenericEncounter( GB , Source , Scale ); end; Procedure ProcessWMecha( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Fill current scene with enemies. } var TID,Renown,Strength,LMs: LongInt; begin { Find out the team, and how many enemies to add. } TID := ScriptValue( Event , GB , Source ); Renown := ScriptValue( Event , GB , Source ); Strength := ScriptValue( Event , GB , Source ); { If this team is an enemy of the player team, add extra STRENGTH based on the number of } { lancemates. This extra STRENGTH will not entirely cancel out the usefulness of lancemates, } { but will reduce it slightly, thereby allowing solo PCs to exist. } if AreEnemies( GB , TID , NAV_DefPlayerTeam ) then begin LMs := LancematesPresent( GB ); if LMs > 0 then Strength := Strength + ( 25 * LMs ); end; AddTeamForces( GB , TID , Renown , Strength ); end; { ProcessWMecha } Procedure ProcessWMonster( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Fill current scene with monsters. } var TID,Renown,Strength: LongInt; Team: GearPtr; MonType: String; begin { Find out the team, and how many enemies to add. } TID := ScriptValue( Event , GB , Source ); Renown := ScriptValue( Event , GB , Source ); Strength := ScriptValue( Event , GB , Source ); { Determine the type of monster to add. This should be indicated by } { the team to which the monsters belong. } Team := LocateTeam( GB , TID ); if Team <> Nil then MonType := SAttValue( Team^.SA , 'TYPE' ) else MonType := 'ROBOT NINJA'; if MonType = '' then MonType := 'ROBOT NINJA'; StockBoardWithMonsters( GB , Renown , Strength , TID , MonType ); end; { ProcessWMonster } Function NumberOfPlots( Part: GearPtr ): Integer; { Check the number of plots this PART has loaded. } var P: GearPtr; N: Integer; begin P := Part^.InvCom; N := 0; while P <> Nil do begin if P^.G = GG_Plot then Inc( N ); P := P^.Next; end; NumberOfPlots := N; end; Function CurrentPCRenown( GB: GameBoardPtr ): Integer; { Return the current renown score of the PC. } var PC: GearPtr; begin PC := LocatePilot( GG_LocatePC( GB ) ); if PC <> Nil then begin CurrentPCRenown := NAttValue( PC^.NA , NAG_CharDescription , NAS_Renowned ); end else begin CurrentPCRenown := 0; end; end; Function AS_ContentInsertion( GB: GameBoardPtr; Source: GearPtr; ConReq: String; Threat: Integer ): Boolean; { Attempt to load and initialize the requested plot or story. } var Adv,Slot,Plot: GearPtr; EList: ElementTable; Context,msg: String; T,ENum: Integer; begin ClearElementTable( EList ); Adv := GG_LocateAdventure( GB , Source ); Context := ExtractWord( ConReq ); { Fill out the element table. } if Source^.G = GG_Faction then begin EList[1].EValue := Source^.S; EList[1].EType := 'F'; Slot := Adv; end else if Source^.G = GG_Scene then begin EList[1].EValue := Source^.S; EList[1].EType := 'S'; Slot := Adv; end else if Source^.G = GG_Story then begin Slot := Source; end else begin Slot := Adv; end; { If this plot is being spawned by a plot or by a quest, we may need to } { pass along some elements. } Plot := PlotMaster( GB , Source ); if Plot <> Nil then begin T := 1; while ConReq <> '' do begin ENum := ExtractValue( ConReq ); if ( ENum >= 0 ) and ( ENum <= Num_Plot_Elements ) then begin msg := SAttValue( Plot^.SA , 'ELEMENT' + BSTr( ENum ) ); if msg <> '' then begin EList[ T ].EType := msg[1]; EList[ T ].EValue := ElementID( Plot , ENum ); if EList[ T ].EType = 'C' then DialogMsg( 'ERROR: Content Insertion ' + Context + ' passed character element.' ) else if EList[ T ].EType = 'M' then DialogMsg( 'ERROR: Content Insertion ' + Context + ' passed metascene element.' ); end else begin DialogMsg( 'ERROR: Content Insertion ' + Context + ' requested invalid element.' ); end; end; Inc( T ); end; end; if AddRandomPlot( GB , Slot , Context , EList , Threat ) then begin AS_ContentInsertion := True; end else begin AS_ContentInsertion := False; end; end; Procedure ProcessStartStory( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A new story gear is about to be loaded. } var FName: String; Renown: Integer; begin { First, find the content request and the threat value to use. } FName := ExtractWord( Event ); if Source <> Nil then begin FName := AS_GetString( Source , FName ); end else begin FName := ''; end; Renown := ScriptValue( Event , GB , Source ); { Call the above procedure to see if it works or not. } if AS_ContentInsertion( GB , Source , FName , Renown ) then begin SetTrigger( GB , 'UPDATE' ); IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end; Procedure ProcessStartPlot( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A new plot is being loaded, probably by a story. } { For now I'm going to treat such plots as global. } var FName: String; Renown: Integer; begin { First, find the content request and the threat value to use. } FName := ExtractWord( Event ); if Source <> Nil then begin FName := AS_GetString( Source , FName ); end else begin FName := ''; end; Renown := ScriptValue( Event , GB , Source ); if ( Source <> Nil ) and ( Source^.G = GG_Story ) and ( NumberOfPlots( Source ) >= Max_Plots_Per_Story ) then begin { Can't load a new plot at this time. } IfFailure( Event , Source ); end else begin if AS_ContentInsertion( GB , Source , FName , Renown ) then begin SetTrigger( GB , 'UPDATE' ); IfSuccess( Event ); end else begin { File was not loaded successfully. } IfFailure( Event , Source ); end; end; end; Procedure ProcessSetMood( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A CityMood is about to be unleashed on the game world. } var ID: LongInt; Mood,City: GearPtr; begin { First, find the mood and the city. } ID := ScriptValue( Event , GB , Source ); Mood := GG_LocateItem( ID , GB , Source ); ID := ScriptValue( Event , GB , Source ); City := FindRootScene( FindActualScene( GB , ID ) ); { Call the above procedure to see if it works or not. } if ( Mood <> Nil ) and ( City <> Nil ) then begin DelinkGearForMovement( GB , Mood ); if InsertMood( City , Mood , GB ) then begin SetTrigger( GB , 'UPDATE' ); IfSuccess( Event ); end else begin IfFailure( Event , Source ); end; end else begin IfFailure( Event , Source ); end; end; Procedure ProcessUpdatePlots( var Trigger,Event: String; GB: GameBoardPtr; Source: GearPtr ); { Check the current city and all of its moods. Attempt to load new plots as appropriate. } var Threat: Integer; begin { Final error check- based on the config options, maybe exit. } if Load_Plots_At_Start and ( UpCase( Trigger ) <> 'START' ) then exit else if ( not Load_Plots_At_Start ) and ( UpCase( Trigger ) = 'START' ) then exit; Threat := CurrentPCRenown( GB ); UpdatePlots( GB , Threat ); SetTrigger( GB , 'UPDATE' ); end; Procedure ProcessCheckComponents( GB: GameBoardPtr; Source: GearPtr ); { Check to see if this source needs to load a new component. If so, } { then do that. } var Adv: GearPtr; begin { If we have a source, and that source has its "LoadNextComponent" value set, } { then we have work to do here... } if ( Source <> Nil ) and ( NAttValue( Source^.NA , NAG_XXRAN , NAS_LoadNextComponent ) = 0 ) then begin PrepareNewComponent( Source , GB ); SetNAtt( Source^.NA , NAG_XXRAN , NAS_LoadNextComponent , 1 ); { Check to see whether or not the episode number has increased. } if NAttValue( Source^.NA , NAG_XXRAN , NAS_EpisodeNumber ) > NAttValue( Source^.NA , NAG_XXRAN , NAS_LastEpisodeNoted ) then begin SetNAtt( Source^.NA , NAG_XXRAN , NAS_LastEpisodeNoted , NAttValue( Source^.NA , NAG_XXRAN , NAS_EpisodeNumber ) ); Adv := GG_LocateAdventure( GB , Source ); AddSAtt( Adv^.SA , 'HISTORY' , '...' ); AddSAtt( Adv^.SA , 'HISTORY' , ReplaceHash( MsgString( 'HISTORY_NEWEPISODE' ) , BStr( NAttValue( Source^.NA , NAG_XXRAN , NAS_EpisodeNumber ) ) ) ); end; end; end; Procedure AlterStoryDescriptors( Story: GearPtr; var Changes: String ); { Alter the story descriptors based on the provided changes. If the +P Propp state is changed, } { reset the story's ProppAdvancement attribute. } { Note that CHANGES will be utterly destroyed by the AlterDescriptors call below. } var Base: String; begin Base := SAttValue( Story^.SA , 'CONTEXT' ); AlterDescriptors( Base , Changes ); SetSAtt( Story^.SA , 'CONTEXT <' + Base + '>' ); end; Procedure ProcessGAlterContext( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Alter the context by the changes string provided. } var Story: GearPtr; Changes: String; begin Story := Grabbed_Gear; Changes := ExtractWord( Event ); Changes := SAttValue( Source^.SA , Changes ); if Story <> Nil then begin AlterStoryDescriptors( Story , Changes ); end; end; Procedure ProcessNextComp( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Prepare things for the next component to be loaded. } Procedure ContinueConversation(); { We want to make a choice, but there's already a conversation } { going on. Best to end that conversation with a [Continue] tag, } { then we can return to it later. } var RPM: RPGMenuPtr; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InteractMenu ); AddRPGMenuItem( RPM , MsgString( 'Continue' ), -1 ); ASRD_GameBoard := GB; CHAT_React := ReactionScore( GB^.Scene , I_PC , I_NPC ); SelectMenu( RPM , @InteractRedraw ); DisposeRPGMenu( RPM ); end; Function SeekDCGear( Story: GearPtr; DCID: Integer ): GearPtr; { Locate the plot corresponding to the dramatic choice selected } { by the PC. } var LList: GearPtr; begin LList := Story^.InvCom; while ( LList <> Nil ) and ( ( NAttValue( LList^.NA , NAG_XXRan , NAS_IsDramaticChoicePlot ) = 0 ) or ( LList^.V <> DCID ) ) do LList := LList^.Next; SeekDCGear := LList; end; Procedure MakeDramaticChoice( Story: GearPtr ); { In order to move on, the PC must decide what they're going } { to do next. } var RPM: RPGMenuPtr; DC: GearPtr; N: Integer; Trigger: String; begin { Step One- create the list of dramatic choices. } CreateChoiceList( GB , Story ); { Step Two- assemble these choices into a menu. This is tougher } { than it looks, since if we are currently in a conversation } { we want the choices merged with the conversation. } if I_NPC <> Nil then begin ContinueConversation; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InteractMenu ); end else begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoText ); end; RPM^.Mode := RPMNoCancel; DC := Story^.InvCom; while DC <> Nil do begin { How do we identify the dramatic choice options? They've been marked } { with a special NA tag. Do we do any error checking to make sure } { they're legit? Hell no! } if NAttValue( DC^.NA , NAG_XXRan , NAS_IsDramaticChoicePlot ) <> 0 then begin { This is a dramatic choice. Add it to the menu. } AddRPGMenuItem( RPM , ScriptMessage( 'PROMPT' , GB , DC ) , DC^.V ); end; DC := DC^.Next; end; if RPM^.NumItem < 1 then begin DialogMsg( 'ERROR: No dramatic choices found!' ); N := -1; end else if I_NPC <> Nil then begin CHAT_Message := MsgString( 'Make_Dramatic_Choice' ); N := SelectMenu( RPM , @InteractRedraw ); end else begin ASRD_MemoMessage := MsgString( 'Make_Dramatic_Choice' ); N := SelectMenu( RPM , @ChoiceRedraw ); end; { Step Three- record the choice and execute its initialization } { script. } SetNAtt( Story^.NA , NAG_XXRan , NAS_DramaticChoice , N ); { Locate the choice's gear. } DC := SeekDCGear( Story , N ); if DC <> Nil then begin Trigger := 'CHOICE'; TriggerGearScript( GB , DC , Trigger ); if I_NPC <> Nil then begin CHAT_Message := ScriptMessage( 'REPLY' , GB , DC ); end else begin YesNoMenu( GB , ScriptMessage( 'ALERT' , GB , DC ) , '' , '' ); end; end else begin DialogMsg( 'ERROR: Dramatic Choice ' + BStr( N ) + ' not found.' ); end; { Step Four- dispose of the remaining choices, and the menu. } ClearChoiceList( Story ); DisposeRPGMenu( RPM ); end; var Story: GearPtr; begin Story := StoryMaster( GB , Source ); if Story = Nil then begin DialogMsg( 'ERROR: ProcessNextComp called but story not found' ); Exit; end; if ( Story <> Nil ) and ( GB <> Nil ) then begin MakeDramaticChoice( Story ); SetNAtt( Story^.NA , NAG_XXRAN , NAS_LoadNextComponent , 0 ); { Set this component for possible deletion. } { First make sure we have the plot itself. } Source := PlotMaster( GB , Source ); EndPlot( GB , Source^.Parent , Source ); SetTrigger( GB , 'UPDATE' ); end; end; Procedure ProcessAttack( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Team 1 is going to attack Team 2. } var t1,T2: Integer; Team1,Mek: GearPtr; begin { Error check - We need that gameboard! } if GB = Nil then Exit; { Read the script values. } T1 := ScriptValue( Event , GB , Source ); T2 := ScriptValue( Event , GB , Source ); { Find the attacking team, and set the enemy value. } Team1 := LocateTeam( GB , T1 ); if Team1 <> Nil then begin SetNAtt( Team1^.NA , NAG_SideReaction , T2 , NAV_AreEnemies ); end; { Locate each member of the team and set AIType to SEEK AND DESTROY. } Mek := GB^.Meks; while Mek <> Nil do begin if NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = T1 then begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Orders , NAV_SeekAndDestroy ); end; Mek := Mek^.Next; end; end; Procedure ProcessSalvage( GB: GameBoardPtr ); { It's looting time!!! Check every mecha on the game board; if it's } { not operational but not destroyed, switch its TEAM to NAV_DefPlayerTeam. } Function CanSalvage( M: GearPtr ): Boolean; { Return TRUE if M can maybe be salvaged, or FALSE otherwise. } var Team: Integer; begin Team := NAttValue( M^.NA , NAG_Location, NAS_Team ); CanSalvage := ( M^.G = GG_Mecha ) and ( Team <> NAV_DefPlayerTeam ) and ( Team <> NAV_LancemateTeam ) and ( not GearOperational( M ) ); end; var Mek,M2,PC: GearPtr; CanScavenge: Boolean; T: Integer; RPts: LongInt; begin { ERROR CHECK - GB must be defined!!! } if GB = Nil then Exit; { Check to see if the PC has the Scavenger talent. } PC := GG_LocatePC( GB ); CanScavenge := TeamHasTalent( GB , NAV_DefPlayerTeam , NAS_TechVulture ); { Loop through every mek on the board. } Mek := GB^.Meks; while Mek <> Nil do begin if CanSalvage( Mek ) then begin { This is a salvagable part. To start with, remove its pilot(s). } repeat M2 := ExtractPilot( Mek ); if M2 <> Nil then DeployGear( GB , M2 , False ); until M2 = Nil; { Apply emergency repair to it. } if CanScavenge or ( Random( 6 ) = 1 ) then begin for t := 0 to NumMaterial do begin if ( TotalRepairableDamage( Mek , T ) > 0 ) then begin { Determine how many repair points it's possible } { to apply. } RPts := RollStep( TeamSkill( GB , NAV_DefPlayerTeam , Repair_Skill_Needed[ T ] , STAT_Knowledge ) ) - 15; if RPts > 0 then begin ApplyEmergencyRepairPoints( Mek , T , RPts ); end; end; end; { Checking the repair skills. } end; { If at the end of this the mecha is NotDestroyed, it may be } { added to the PC team. If it is destroyed, the PC has a chance } { to use Tech Vulture. } if NotDestroyed( Mek ) then begin SetNAtt( Mek^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Temporary , 0 ); { Also move off the map, to prevent the runaway salvage bug. } SetNAtt( Mek^.NA , NAG_Location , NAS_X , 0 ); { Record that this is a salvaged mek. } SetNAtt( Mek^.NA , NAG_MissionReport , NAS_WasSalvaged , 1 ); end else if CanScavenge then begin M2 := SelectRandomGear( Mek^.SubCom ); if NotDestroyed( M2 ) and CanBeExtracted( M2 ) and ( SkillRoll( GB , PC , NAS_Repair , STAT_Knowledge , 7 , 0 , True , True ) > 7 ) then begin ExtractMechaPart( Mek^.SubCom , M2 ); SetNAtt( M2^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); SetSAtt( M2^.SA , 'DESIG <' + GearName( Mek ) + '>' ); AppendGear( GB^.Meks , M2 ); { Record that this is a salvaged part. } SetNAtt( M2^.NA , NAG_MissionReport , NAS_WasSalvaged , 1 ); end; end; end; Mek := Mek^.Next; end; end; Procedure ProcessRetreat( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { When this command is invoked, all the functioning masters } { belonging to the listed team are removed from the map. A } { Number Of Units trigger is then set. } var Team: Integer; Mek: GearPtr; begin { ERROR CHECK - GB must be defined!!! } if GB = Nil then Exit; { Find out which team is running away. } Team := ScriptValue( event , GB , Source ); { Loop through every mek on the board. } Mek := GB^.Meks; while Mek <> Nil do begin if GearOperational( Mek ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = Team ) then begin SetNAtt( Mek^.NA , NAG_Location , NAS_X , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_Y , 0 ); end; Mek := Mek^.Next; end; { Set the trigger. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( Team ) ); end; Procedure ProcessAirRaidSiren( GB: GameBoardPtr ); { When this command is invoked, all the functioning masters } { belonging to all NPC teams are ordered to run for their lives. } var Mek: GearPtr; begin { ERROR CHECK - GB must be defined!!! } if GB = Nil then Exit; { Loop through every mek on the board. } Mek := GB^.Meks; while Mek <> Nil do begin if GearOperational( Mek ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) <> NAV_DefPlayerTeam ) then begin SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Orders , NAV_RunAway ); end; Mek := Mek^.Next; end; end; Procedure ProcessGRunAway( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { When this command is invoked, the grabbed gear is removed } { from the map. A Number Of Units trigger is then set. } var Mek,NPC: GearPtr; begin { ERROR CHECK - GB must be defined!!! } if ( GB = Nil ) or ( Grabbed_Gear = Nil ) then Exit; { Loop through every mek on the board. } Mek := GB^.Meks; while Mek <> Nil do begin if Mek = Grabbed_Gear then begin SetNAtt( Mek^.NA , NAG_Location , NAS_X , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_Y , 0 ); { Set the trigger. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( NAttValue( MEK^.NA , NAG_Location , NAS_Team ) ) ); end else if IsMasterGear( Mek ) then begin NPC := LocatePilot( Mek ); if ( NPC <> Nil ) and ( NPC = Grabbed_Gear ) then begin SetNAtt( Mek^.NA , NAG_Location , NAS_X , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_Y , 0 ); { Set the trigger. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( NAttValue( MEK^.NA , NAG_Location , NAS_Team ) ) ); end; end; Mek := Mek^.Next; end; end; Procedure ProcessTime( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Advance the game clock by a specified amount. } { FOr long periods of time, we don't want the PC to get hungry, as this will } { result in an obscene number of "You are hungry" messages. So, break the time } { into hour periods and give the PC some food between each. } var N,OriginalHunger: LongInt; PC: GearPtr; begin { Find out how much to adjust the value by. } N := ScriptValue( Event , GB , Source ); PC := LocatePilot( GG_LocatePC( GB ) ); if PC <> Nil then begin OriginalHunger := NAttValue( PC^.NA , NAG_Condition , NAS_Hunger ); if OriginalHunger > ( Hunger_Penalty_Starts - 15 ) then OriginalHunger := Hunger_Penalty_Starts - 16; end; while N > 0 do begin if N > 3600 then begin if PC <> Nil then SetNAtt( PC^.NA , NAG_Condition , NAS_Hunger , OriginalHunger ); QuickTime( GB , 3600 ); N := N - 3600; end else begin QuickTime( GB , N ); N := 0; end; end; end; Procedure ProcessForceChat( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Force the player to talk with the specified NPC. } var N: LongInt; begin { Find out which NPC to speak with. } N := ScriptValue( Event , GB , Source ); if GB <> Nil then begin StoreSAtt( GB^.Trig , '!TALK ' + BStr( N ) ); end; end; Procedure ProcessAnnounce( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { The PC has done something special; show an announcement. } var tag: String; N: LongInt; begin { Find out which message to display. } tag := ExtractWord( Event ); N := ScriptValue( Event , GB , Source ); if GB <> Nil then begin StoreSAtt( GB^.Trig , '!ANNOUNCE ' + tag + BStr( N ) ); end; end; Procedure ProcessTrigger( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A new trigger will be placed in the trigger queue. } var BaseTrigger: String; N: LongInt; begin { Find out the trigger's details. } BaseTrigger := ExtractWord( Event ); N := ScriptValue( Event , GB , Source ); if GB <> Nil then begin StoreSAtt( GB^.Trig , BaseTrigger + BStr( N ) ); end; end; Procedure ProcessTrigger0( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A new trigger will be placed in the trigger queue. } var BaseTrigger: String; begin { Find out the trigger's details. } BaseTrigger := ExtractWord( Event ); if GB <> Nil then begin StoreSAtt( GB^.Trig , BaseTrigger ); end; end; Procedure ProcessLTrigger( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { A new trigger will be placed in the local trigger queue. } var BaseTrigger: String; begin { Find out the trigger's details. } BaseTrigger := ExtractWord( Event ); if GB <> Nil then begin StoreSAtt( local_triggers , BaseTrigger ); end; end; Procedure ProcessTransform( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Alter the appearance of the SOURCE gear. } var N: LongInt; S: String; Procedure SwapSAtts( tag: String ); begin S := AS_GetString ( Source , tag + BStr( N ) ); if S <> '' then SetSAtt( Source^.SA , tag + ' <' + S + '>' ); end; begin { Find out which aspect to change to. } N := ScriptValue( Event , GB , Source ); {Switch all known dispay descriptors. } SwapSAtts( 'ROGUECHAR' ); SwapSAtts( 'NAME' ); SwapSAtts( 'SDL_SPRITE' ); SwapSAtts( 'SDL_COLORS' ); SetNAtt( Source^.NA , NAG_Display , NAS_PrimaryFrame , NAttValue( Source^.NA , NAG_Display , N ) ); if GB <> Nil then begin { While we're here, redo the shadow map. } UpdateShadowMap( GB ); end; end; Procedure ProcessMoreText( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Load and display a text file. } var FName: String; txt,L: SAttPtr; begin { First, find the file name of the text file to look for. } FName := ExtractWord( Event ); if Source <> Nil then begin FName := AS_GetString( Source , FName ); end else begin FName := ''; end; { Secondly, load and display the file. } if FName <> '' then begin txt := LoadStringList( Series_Directory + FName ); if txt <> Nil then begin { Process the text. } L := txt; while L <> Nil do begin FormatMessageString( L^.Info , GB , Source ); L := L^.Next; end; {$IFDEF ASCII} MoreText( txt , 1 ); {$ELSE} ASRD_GameBoard := GB; MoreText( txt , 1 , @ArenaScriptReDraw ); {$ENDIF} DisposeSAtt( txt ); end; end; end; { ProcessMoreText } Procedure ProcessMoreMemo( var Event: String; GB: GameBoardPtr ); { View messages of a certain type - EMAIL, NEWS, or MEMO. } var Key: String; begin { First, find the memo key to use. } Key := ExtractWord( Event ); { Secondly, send this to the memo browser. } BrowseMemoType( GB , Key ); { Finally, update the display. } CombatDisplay( GB ); end; { ProcessMoreMemo } Procedure ProcessSeekGate( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Aim for a specific gate when entering the next level. } var N: LongInt; begin { Find out which gate we're talking about. } N := ScriptValue( Event , GB , Source ); SCRIPT_Gate_To_Seek := N; end; Procedure ProcessUpdateProps( GB: GameBoardPtr ); { Just send an UPDATE trigger to all items on the gameboard. } var T: String; begin T := 'UPDATE'; if GB <> Nil then begin CheckTriggerAlongPath( T , GB , GB^.Meks , True ); end; end; Procedure ProcessBlock( var T: String ); { Erase the trigger, so as to prevent other narrative gears } { from acting upon it. } begin { Do I really need to comment this line? } T := ''; end; Procedure ProcessAccept( var T: String ); { Set the trigger to ACCEPT so the CONDITIONACCEPTED function } { knows that it's been accepted. } begin { Do I really need to comment this line? } T := 'ACCEPT'; end; Procedure ProcessBomb( GB: GameBoardPtr ); { Drop a bomb on the town. Yay! } begin if not GB^.QuitTheGame then RandomExplosion( GB ); end; Procedure ProcessXPV( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Give some experience points to all PCs and lancemates. } const LM_Renown_Lag = 20; { Lancemates will lag one renown band behind PC. } Procedure DoRapidLeveling( NPC,PC: GearPtr; Renown: Integer ); { Search through this NPC's skills. If you find one that is lower } { than acceptable for the provided Renown, increase it. } var OptRank,SpecSkill,Skill,N,SkRank: Integer; CanGetBonus: Array [1..NumSkill] of Boolean; begin if NPC = Nil then Exit; { Clamp the renown score. } if Renown < 1 then Renown := 1; { Advance the NPC's renown. } if NAttValue( NPC^.NA , NAG_CharDescription , NAS_Renowned ) < Renown then AddNAtt( NPC^.NA , NAG_CharDescription , NAS_Renowned , 1 ); { Maybe advance the NPC's reaction score. } { This will depend on the current score bonus and the renown level. } N := Random( Renown + 5 ) div 2; if N > NAttValue( PC^.NA , NAG_ReactionScore , NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) ) then AddReact( GB , PC , NPC , 1 ); { Determine the skill level to use. } OptRank := SkillRankForRenown( Renown ); { Determine the specialist skill of this model. } SpecSkill := NAttValue( NPC^.NA , NAG_Personal , NAS_SpecialistSkill ); { Count how many skills could use a level-up. } N := 0; for Skill := 1 to NumSkill do begin { If this is the specialist skill, it can go one higher. } SkRank := SkillRank( NPC , Skill ); if ( Skill = SpecSkill ) and ( SkRank > 1 ) then Dec( SkRank ); if ( SkRank > 0 ) and ( SkRank < OptRank ) then begin Inc( N ); CanGetBonus[ Skill ] := True; end else CanGetBonus[ Skill ] := False; end; { If any skills need boosting, select one at random and do that now. } if N > 0 then begin { Select one skill at random } N := Random( N ); { Find it, and give a +1 bonus. } for Skill := 1 to NumSkill do begin if CanGetBonus[ Skill ] then begin Dec( N ); if N = -1 then begin AddNAtt( NPC^.NA , NAG_Skill , Skill , 1 ); Break; end; end; end; end; end; var XP,T,Renown,LMs: LongInt; M,PC,NPC: GearPtr; begin { Find out how much to give. } XP := ScriptValue( Event , GB , Source ); { Count the lancemates, reduce XP if too many. } LMs := LancematesPresent( GB ); if LMs > 4 then LMs := 4; if LMs > 1 then XP := ( XP * ( 5 - LMs ) ) div 4; { Locate the PC, and find its Renown score. } PC := LocatePilot( GG_LocatePC( GB ) ); if PC <> Nil then begin Renown := NAttValue( PC^.NA , NAG_CharDescription , NAS_Renowned ); end else begin Renown := 1; end; { We'll rapidly level one of the lancemates at random. } { Select a model. } LMs := Random( LancematesPresent( GB ) ) + 1; { Search for models to give XP to. } if GB <> Nil then begin M := GB^.Meks; while M <> Nil do begin T := NAttValue( M^.NA , NAG_Location , NAS_Team ); if ( T = NAV_DefPlayerTeam ) then begin DoleExperience( M , XP ); end else if ( T = NAV_LancemateTeam ) and OnTheMap( GB , M ) then begin DoleExperience( M , XP ); { Locate the pilot. } NPC := LocatePilot( M ); { Only regular lancemates get rapid leveling- Pets and temps don't. } if IsRegularLancemate( M ) then begin Dec( LMs ); if ( LMs = 0 ) and ( NPC <> Nil ) then DoRapidLeveling( NPC , PC , Renown - LM_Renown_Lag ); { Lancemates are also eligible for training events. } if NAttValue( NPC^.NA , NAG_Narrative , NAS_LancemateTraining_Total ) < Renown then begin AddNAtt( NPC^.NA , NAG_Narrative , NAS_LancemateTraining_Total , 1 ); end; end; end; M := M^.Next; end; end; DialogMsg( ReplaceHash( MSgString( 'AS_XPV' ) , Bstr( XP ) ) ); end; Procedure ProcessGSkillXP( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Give some skill experience points to the grabbed gear. } var Sk,XP: LongInt; begin { Find out what skill to give XP for, and how much XP to give. } Sk := ScriptValue( Event , GB , Source ); XP := ScriptValue( Event , GB , Source ); { As long as we have a grabbed gear, go for it! } if Grabbed_Gear <> Nil then begin DoleSkillExperience( Grabbed_Gear , Sk , XP ); end; end; Procedure ProcessGMental( GB: GameBoardPtr ); { The grabbed gear is doing something. Make it wait, and spend } { five mental points. } begin { As long as we have a grabbed gear, go for it! } if Grabbed_Gear <> Nil then begin WaitAMinute( GB , Grabbed_Gear , ReactionTime( Grabbed_Gear ) * 3 ); AddMentalDown( Grabbed_Gear , 5 ); end; end; Procedure ProcessGStamina( GB: GameBoardPtr ); { The grabbed gear is doing something. Make it wait, and spend } { five stamina points. } begin { As long as we have a grabbed gear, go for it! } if Grabbed_Gear <> Nil then begin WaitAMinute( GB , Grabbed_Gear , ReactionTime( Grabbed_Gear ) * 3 ); AddStaminaDown( Grabbed_Gear , 5 ); end; end; Procedure ProcessGQuitLance( GB: GameBoardPtr ); { The grabbed gear will quit the lance. } begin if Grabbed_Gear <> Nil then begin RemoveLancemate( GB , Grabbed_Gear , False ); end; end; Procedure ProcessGJoinLance( GB: GameBoardPtr ); { The grabbed gear will join the lance. } begin if Grabbed_Gear <> Nil then begin { If the Grabbed_Gear is not in play, move it to the current scene. } if not IsFoundAlongTrack( GB^.Meks , FindRoot( Grabbed_Gear ) ) then begin DelinkGearForMovement( GB , Grabbed_Gear ); SetNAtt( Grabbed_Gear^.NA , NAG_Location , NAS_Team , NAV_LancemateTeam ); { Stick it on the map. } EquipThenDeploy( GB , Grabbed_Gear , True ); end; AddLancemateFrontEnd( GB , GG_LocatePC( GB ) , Grabbed_Gear , False ); end; end; Procedure ProcessGSkillLevel( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Set the skill points for the grabbed gear. } var Renown: Integer; begin { Find out what level the NPC should be at. } Renown := ScriptValue( Event , GB , Source ); { As long as we have a grabbed gear, go for it! } if ( Grabbed_Gear <> Nil ) then begin { Record the character's new renown score and mark as a combatant. } SetNAtt( Grabbed_Gear^.NA , NAG_CharDescription , NAS_Renowned , Renown ); SetNAtt( Grabbed_Gear^.NA , NAG_CharDescription , NAS_IsCombatant , 1 ); SetSkillsAtLevel( Grabbed_Gear , Renown ); end; end; Procedure ProcessGMoraleDmg( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Give some morale points to the grabbed gear. } var M: LongInt; begin { Find out how much morale change. } M := ScriptValue( Event , GB , Source ); { As long as we have a grabbed gear, go for it! } if Grabbed_Gear <> Nil then begin AddMoraleDMG( Grabbed_Gear , M ); end; end; Procedure ProcessDrawTerr( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Alter a single gameboard tile. } var X,Y,T: LongInt; begin { Find out where and what to adjust. } X := ScriptValue( Event , GB , Source ); Y := ScriptValue( Event , GB , Source ); T := ScriptValue( Event , GB , Source ); if ( GB <> NIl ) and OnTheMap( GB , X , Y ) and ( T >= 1 ) and ( T <= NumTerr ) then begin SetTerrain( GB , X , Y , T ); end; end; Procedure ProcessArenaRep( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { The arena unit has just won or lost a mission. Adjust its reputation } { accordingly. } var D: LongInt; Adv: GearPtr; begin { Find out how much to adjust. } D := ScriptValue( Event , GB , Source ); { Find the adventure. } Adv := GG_LocateAdventure( GB , Source ); if ( Adv <> NIl ) then begin AddNAtt( Adv^.NA , NAG_CharDescription , -6 , D ); if NAttValue( Adv^.NA , NAG_CharDescription , -6 ) > 100 then begin SetNAtt( Adv^.NA , NAG_CharDescription , -6 , 100 ); end else if NAttValue( Adv^.NA , NAG_CharDescription , -6 ) < 0 then begin SetNAtt( Adv^.NA , NAG_CharDescription , -6 , 0 ); end; end; end; Procedure ProcessAddReact( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Adjust the reaction score of I_NPC. } var DReact: integer; { How much do we want to change? } PC: GearPtr; begin DReact := ScriptValue( Event , GB , Source ); PC := LocatePilot( GG_LocatePC( GB ) ); AddReact( GB , PC , I_NPC , DReact ); end; Procedure ProcessMagicMap( GB: GameBoardPtr ); { Make every tile on the map visible, then redraw. } var X,Y: Integer; begin for X := 1 to GB^.MAP_Width do begin for Y := 1 to GB^.MAP_Height do begin SetVisibility( GB , X , Y , True ); end; end; CombatDisplay( GB ); end; Procedure ProcessGOpenInv( GB: GameBoardPtr ); { Attempt to access the inventory of the grabbed gear. } begin if ( Grabbed_Gear <> Nil ) and ( GB <> Nil ) then begin PCTradeItems( GB , GG_LocatePC( GB ) , Grabbed_Gear ); end; end; Procedure CheckMechaEquipped( GB: GameBoardPtr ); { A dynamic encounter is about to be entered. The PC is going to } { want a mecha for it, most likely. } var PC,Mek: GearPtr; begin { Error check - make sure we have a gameboard to start with. } if GB = Nil then Exit; { Find the PC. If the PC doesn't have a mek equipped, then } { prompt for one to be equipped. } PC := GG_LocatePC( GB ); if ( PC <> Nil ) and ( PC^.G <> GG_Mecha ) then begin Mek := FindPilotsMecha( GB^.Meks , PC ); if ( Mek = Nil ) and ( NumPCMeks( GB ) > 0 ) then begin {$IFDEF ASCII} GameMSG( MsgString( 'ARENASCRIPT_CheckMechaEquipped' ) , ZONE_UsagePrompt , InfoGreen ); {$ENDIF} FHQ_SelectMechaForPilot( GB , PC ); end; end; end; Procedure ProcessSetEncounter( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { Search for encounters leading to a specified scene. Set their activation value. } var SID,DEID,AVal: LongInt; { SceneID, Dungeon Entrance ID, Activation Value } Adv: GearPtr; DestScene: GearPtr; Procedure CheckAlongPath( LList: GearPtr ); { Check for encounters along this list and throughout all children. } begin while LList <> Nil do begin if ( LList^.G = GG_MetaTerrain ) and ( LList^.S = GS_MetaEncounter ) and ( LList^.Stat[ STAT_Destination ] = SID ) then begin SetNAtt( LList^.NA , NAG_Narrative , NAS_EncounterActive , AVal ); end; CheckALongPath( LList^.SubCom ); CheckALongPath( LList^.InvCom ); LList := LList^.Next; end; end; begin { Find out which scene and what value. } SID := ScriptValue( Event , GB , Source ); AVal := ScriptValue( Event , GB , Source ); { Locate the scene. If this is a dungeon, we want to activate the encounter for } { the entrance rather than the level provided. } DestScene := FindActualScene( GB , SID ); if DestScene <> Nil then begin DEID := NAttValue( DestScene^.NA , NAG_Narrative , NAS_DungeonEntrance ); if DEID <> 0 then SID := DEID; end; { Locate the adventure. } Adv := GG_LocateAdventure( GB , Source ); if Adv <> Nil then CheckAlongPath( Adv^.SubCom ); { Also check the currently deployed gears. } if GB <> Nil then CheckAlongPath( GB^.Meks ); end; Procedure ProcessTrainNPC( var Event: String; GB: GameBoardPtr; Source: GearPtr ); { An NPC is going to learn a new skill or talent. Cool! } const MaxTNPCChoices = 10; var CID: LongInt; NPC: GearPtr; CList,msg: String; CanGainSkill,CanGainTalent: Boolean; N,T: Integer; Choices: Array [1..MaxTNPCChoices] of Integer; begin { Find out which NPC, and the list of choices. } CID := ScriptValue( Event , GB , Source ); NPC := GG_LocateNPC( CID , GB , Source ); CList := ExtractWord( Event ); if Source <> Nil then begin CList := AS_GetString( Source , CList ); end else begin CList := ''; end; { If this lancemate cannot develop at this time, exit. } if not LancemateCanDevelop( NPC ) then Exit; { Find out if the NPC can learn a new skill or a new talent. } CanGainSkill := NumberOfSpecialties( NPC ) < NumberOfSkillSlots( NPC ); CanGainTalent := NumFreeTalents( NPC ) > 0; { Copy the choices list over to the array. } N := 0; while ( CList <> '' ) and ( N < MaxTNPCChoices ) do begin T := ScriptValue( CList , GB , Source ); if (( T > 0 ) and CanGainSkill and ( NAttValue( NPC^.NA , NAG_Skill , T ) = 0 ) ) or (( T < 0 ) and CanGainTalent and ( NAttValue( NPC^.NA , NAG_Talent , Abs(T) ) = 0 ) and CanLearnTalent( NPC , Abs(T) ) ) then begin Inc( N ); Choices[ N ] := T; end; DeleteWhiteSpace( CList ); end; { If we have some valid choices, pick one at random. Yay! } if N > 0 then begin msg := ReplaceHash( MsgString( 'TRAINNPC_BASIC' ) , GearName( NPC ) ); N := Random( N ) + 1; if Choices[ N ] > 0 then begin SetNAtt( NPC^.NA , NAG_Skill , Choices[ N ] , 1 ); msg := ReplaceHash( msg , MsgString( 'SKILLNAME_' + BStr( Choices[ N ] ) ) ); end else begin ApplyTalent( NPC , Abs( Choices[ N ] ) ); msg := ReplaceHash( msg , MsgString( 'TALENT' + BStr( Abs( Choices[ N ] ) ) ) ); end; end else begin { No valid choices. Um... just increase a random stat. } msg := ReplaceHash( MsgString( 'TRAINNPC_STATS' ) , GearName( NPC ) ); N := Random( 8 ) + 1; NPC^.Stat[ N ] := NPC^.Stat[ N ] + 1; msg := ReplaceHash( msg , MsgString( 'STATNAME_' + BStr( N ) ) ); end; { Increase the training spent counter. } AddNAtt( NPC^.NA , NAG_Narrative , NAS_LancemateTraining_Spent , 1 ); YesNoMenu( GB , msg , '' , '' ); end; Procedure InvokeEvent( Event: String; GB: GameBoardPtr; Source: GearPtr; var Trigger: String ); { Do whatever is requested by game script EVENT. } { SOURCE refers to the virtual gear which is currently being } { used- it may be a SCENE gear, or a CONVERSATION gear, or } { whatever else I might add in the future. } var cmd: String; begin { Store the time this gear was last invoked. } if ( Source <> Nil ) and ( GB <> Nil ) then SetNAtt( Source^.NA , NAG_Narrative , NAS_ScriptActivatedTimer , GB^.ComTime ); { Process the event string. } while ( Event <> '' ) do begin cmd := UpCase( ExtractWord( Event ) ); if SAttValue( Script_Macros , cmd ) <> '' then begin { Install the macro. } InitiateMacro( GB , Source , Event , SAttValue( Script_Macros , cmd ) ); end else if ( cmd <> '' ) and ( cmd[1] = '&' ) then begin { Install a local macro. } InitiateLocalMacro( GB , Event , cmd , Source ); end else if not Attempt_Gear_Grab( Cmd , Event , GB , Source ) then begin { If this is a gear-grabbing command, our work here is done. } if cmd = 'EXIT' then ProcessExit( Event , GB , Source ) else if cmd = 'FORCEEXIT' then ProcessForceExit( Event , GB , Source ) else if cmd = 'GADDNATT' then ProcessGAddNAtt( Event , GB , Source ) else if cmd = 'GSETNATT' then ProcessGSetNAtt( Event , GB , Source ) else if cmd = 'GADDSTAT' then ProcessGAddStat( Event , GB , Source ) else if cmd = 'GSETSTAT' then ProcessGSetStat( Event , GB , Source ) else if cmd = 'GSETSATT' then ProcessGSetSAtt( Event , GB , Source ) else if cmd = 'DELETEGG' then ProcessDeleteGG( GB , Source ) else if cmd = 'MOVEGG' then ProcessMoveGG( Event , GB , Source ) else if cmd = 'MOVEANDPACIFYGG' then ProcessMoveAndPacifyGG( Event , GB , Source ) else if cmd = 'DEPLOYGG' then ProcessDeployGG( Event , GB , Source ) else if cmd = 'DYNAGG' then ProcessDynaGG( Event , GB , Source ) else if cmd = 'GIVEGG' then ProcessGiveGG( GB ) else if cmd = 'GNEWPART' then ProcessGNewPart( Event , GB , Source ) else if cmd = 'RETURN' then ProcessReturn( GB ) else if cmd = 'PRINT' then ProcessPrint( Event , GB , Source ) else if cmd = 'ALERT' then ProcessAlert( Event , GB , Source ) else if cmd = 'GMONOLOGUE' then ProcessGMonologue( Event , GB , Source ) else if cmd = 'ADDDEBRIEFING' then ProcessAddDebriefing( Event , GB , Source ) else if cmd = 'MEMO' then ProcessMemo( Event , GB , Source ) else if cmd = 'PMEMO' then ProcessPMemo( Event , GB , Source ) else if cmd = 'SMEMO' then ProcessSMemo( Event , GB , Source ) else if cmd = 'QMEMO' then ProcessQMemo( Event , GB , Source ) else if cmd = 'NEWS' then ProcessNews( Event , GB , Source ) else if cmd = 'EMAIL' then ProcessEMail( Event , GB , Source ) else if cmd = 'HISTORY' then ProcessHistory( Event , GB , Source ) else if cmd = 'VICTORY' then ProcessVictory( GB ) else if cmd = 'VMSG' then ProcessValueMessage( Event , GB , Source ) else if cmd = 'SAY' then ProcessSay( Event , GB , Source ) else if cmd = 'SAYPLOTMSG' then ProcessSayPlotMsg( Event , GB , Source ) else if cmd = 'SAYANYTHING' then ProcessSayAnything( GB ) else if cmd = 'ACTIVATEMEME' then ProcessActivateMeme( Event , GB , Source ) else if cmd = 'IFGINPLAY' then ProcessIfGInPlay( Event , GB , Source ) else if cmd = 'IFGOK' then ProcessIfGOK( Event , Source ) else if cmd = 'IFGDEAD' then ProcessIfGDead( Event , Source ) else if cmd = 'IFGSEXY' then ProcessIfGSexy( Event , GB , Source ) else if cmd = 'IFGSEALED' then ProcessIfGSealed( Event , Source ) else if cmd = 'IFGARCHENEMY' then ProcessIfGArchEnemy( Event , GB , Source ) else if cmd = 'IFGARCHALLY' then ProcessIfGArchAlly( Event , GB , Source ) else if cmd = 'IFGHASSKILL' then ProcessIfGHasSkill( Event , GB , Source ) else if cmd = 'IFGCANJOINLANCE' then ProcessIfGCanJoinLance( Event , GB , Source ) else if cmd = 'IFMECHACANENTERSCENE' then ProcessIfMechaCanEnterScene( Event , GB , Source ) else if cmd = 'IFTEAMCANSEEGG' then ProcessIfTeamCanSeeGG( Event , GB , Source ) else if cmd = 'IFFACTION' then ProcessIfFaction( Event , GB , Source ) else if cmd = 'IFSCENE' then ProcessIfScene( Event , GB , Source ) else if cmd = 'IFSAFEAREA' then ProcessIfSafeArea( Event , GB , Source ) else if cmd = 'IFKEYITEM' then ProcessIfKeyItem( Event , GB , Source ) else if cmd = 'IFGHASITEM' then ProcessIfGHasItem( Event , GB , Source ) else if cmd = 'IF=' then ProcessIfEqual( Event , GB , Source ) else if cmd = 'IF#' then ProcessIfNotEqual( Event , GB , Source ) else if cmd = 'IFG' then ProcessIfGreater( Event , GB , Source ) else if cmd = 'IFSTORYLESS' then ProcessIfStoryless( Event , Source ) else if cmd = 'IFYESNO' then ProcessIfYesNo( Event , GB , Source ) else if cmd = 'IFSKILLTEST' then ProcessIfSkillTest( Event , GB , Source ) else if cmd = 'IFUSKILLTEST' then ProcessIfUSkillTest( Event , GB , Source ) else if cmd = 'IFNOOBJECTIONS' then ProcessIfNoObjections( Event , GB , Source ) else if cmd = 'IFMERITBADGE' then ProcessIfMeritBadge( Event , GB , Source ) else if cmd = 'TORD' then ProcessTeamOrders( Event , GB , Source ) else if cmd = 'COMPOSE' then ProcessCompose( Event , GB , Source ) else if cmd = 'BLOCK' then ProcessBlock( Trigger ) else if cmd = 'ACCEPT' then ProcessAccept( Trigger ) else if cmd = 'NEWCHAT' then ProcessNewChat( GB ) else if cmd = 'ENDCHAT' then ProcessEndChat else if cmd = 'REVERTPERSONA' then RevertPersona( Event , GB , Source ) else if cmd = 'GOTO' then ProcessGoto( Event , Source ) else if cmd = 'ADDCHAT' then ProcessAddChat( Event , GB , Source ) else if cmd = 'SEEKTERR' then ProcessSeekTerr( Event , GB , Source ) else if cmd = 'SHOP' then ProcessShop( Event , GB , Source ) else if cmd = 'SCHOOL' then ProcessSchool( Event , GB , Source ) else if cmd = 'EXPRESSDELIVERY' then ProcessExpressDelivery( Event , GB , Source ) else if cmd = 'SHUTTLE' then ProcessShuttle( Event , GB , Source ) else if cmd = 'ENDPLOT' then ProcessEndPlot( Event , GB , Source ) else if cmd = 'ENDPLOTSBYCONID' then ProcessEndPlotsByConID( Event , GB , Source ) else if cmd = 'ENDSTORY' then ProcessEndStory( GB , Source ) else if cmd = 'PURGESTORY' then ProcessPurgeStory( GB , Source ) else if cmd = 'TREPUTATION' then ProcessTReputation( Event , GB , Source ) else if cmd = 'LOSERENOWN' then ProcessLoseRenown( GB ) else if cmd = 'XPV' then ProcessXPV( Event , GB , Source ) else if cmd = 'MECHAPRIZE' then ProcessMechaPrize( Event , GB , Source ) else if cmd = 'NEWD' then ProcessNewD( Event , GB , Source ) else if cmd = 'WMECHA' then ProcessWMecha( Event , GB , Source ) else if cmd = 'WMONSTER' then ProcessWMonster( Event , GB , Source ) else if cmd = 'STARTPLOT' then ProcessStartPlot( Event , GB , Source ) else if cmd = 'UPDATEPLOTS' then ProcessUpdatePlots( Trigger , Event , GB , Source ) else if cmd = 'STARTSTORY' then ProcessStartStory( Event , GB , Source ) else if cmd = 'SETMOOD' then ProcessSetMood( Event , GB , Source ) else if cmd = 'CHECKCOMPONENTS' then ProcessCheckComponents( GB , Source ) else if cmd = 'NEXTCOMP' then ProcessNextComp( Event , GB , Source ) else if cmd = 'GALTERCONTEXT' then ProcessGAlterContext( Event , GB , Source ) else if cmd = 'ATTACK' then ProcessAttack( Event , GB , Source ) else if cmd = 'SALVAGE' then ProcessSalvage( GB ) else if cmd = 'RETREAT' then ProcessRetreat( Event , GB , Source ) else if cmd = 'GRUNAWAY' then ProcessGRunAway( Event , GB , Source ) else if cmd = 'AIRRAIDSIREN' then ProcessAirRaidSiren( GB ) else if cmd = 'FORCECHAT' then ProcessForceChat( Event , GB , Source ) else if cmd = 'ANNOUNCE' then ProcessAnnounce( Event , GB , Source ) else if cmd = 'TIME' then ProcessTime( Event , GB , Source ) else if cmd = 'TRANSFORM' then ProcessTransform( Event , GB , Source ) else if cmd = 'SEEKGATE' then ProcessSeekGate( Event , GB , Source ) else if cmd = 'TRIGGER' then ProcessTrigger( Event , GB , Source ) else if cmd = 'TRIGGER0' then ProcessTrigger0( Event , GB , Source ) else if cmd = 'LTRIGGER' then ProcessLTrigger( Event , GB , Source ) else if cmd = 'UPDATEPROPS' then ProcessUpdateProps( GB ) else if cmd = 'MORETEXT' then ProcessMoreText( Event , GB , Source ) else if cmd = 'MOREMEMO' then ProcessMoreMemo( Event , GB ) else if cmd = 'BOMB' then ProcessBomb( GB ) else if cmd = 'GSKILLXP' then ProcessGSkillXP( Event , GB , Source ) else if cmd = 'GSKILLLEVEL' then ProcessGSkillLevel( Event , GB , Source ) else if cmd = 'GMORALEDMG' then ProcessGMoraleDmg( Event , GB , Source ) else if cmd = 'DRAWTERR' then ProcessDrawTerr( Event , GB , Source ) else if cmd = 'MAGICMAP' then ProcessMagicMap( GB ) else if cmd = 'GMENTAL' then ProcessGMental( GB ) else if cmd = 'GSTAMINA' then ProcessGStamina( GB ) else if cmd = 'GQUITLANCE' then ProcessGQuitLance( GB ) else if cmd = 'GJOINLANCE' then ProcessGJoinLance( GB ) else if cmd = 'GOPENINV' then ProcessGOpenInv( GB ) else if cmd = 'ARENAREP' then ProcessArenaRep( Event , GB , Source ) else if cmd = 'ADDREACT' then ProcessAddReact( Event , GB , Source ) else if cmd = 'PUMPNEWS' then ProcessPumpNews( GB ) else if cmd = 'SETENCOUNTER' then ProcessSetEncounter( Event , GB , Source ) else if cmd = 'TRAINNPC' then ProcessTrainNPC( Event , GB , Source ) else if cmd <> '' then begin DialogMsg( 'ERROR: Unknown ASL command ' + cmd ); DialogMsg( 'CONTEXT: ' + event ); end end; { If not GrabGear } end; { Process rounding-up events here. } if ( SCRIPT_DynamicEncounter <> Nil ) and ( SCRIPT_DynamicEncounter^.V > 0 ) then CheckMechaEquipped( GB ); end; Procedure HandleWhereAreYou( GB: GameBoardPtr ); { The PC has asked the NPC where he is. The NPC will tell the PC } { his or her current location. } var SID: Integer; begin SID := FindSceneID( I_NPC , GB ); if SID <> 0 then begin CHAT_Message := ReplaceHash( MsgString( 'WHEREAREYOU_IAMHERE' ) , SceneName( GB , SID , True ) ); end else begin CHAT_Message := MsgString( 'WHEREAREYOU_Dunno' ); end; end; Procedure HandleAskAboutRumors( GB: GameBoardPtr; Source: GearPtr ); { The PC wants to find some rumors. Make it so. } var RL,RL_Skill: GearPtr; SkVal,BestScore,SkRoll: Integer; NPCDesc,RL_Script: String; Rumor_List,R: SAttPtr; Rumor_Error: Boolean; RPM: RPGMenuPtr; begin { Error check- no rumor scumming. } if NAttValue( I_NPC^.NA , NAG_Personal , NAS_RumorRecharge ) > GB^.ComTime then begin Chat_Message := MsgString( 'RUMOR_IAlreadyToldYou' + BStr( Random( 6 ) + 1 ) ); Exit; end; { Step one- Locate an appropriate skill. } RL_Skill := Nil; RL := rumor_leads; BestScore := 0; NPCDesc := XNPCDesc( GB , GG_LocateAdventure( GB , Source ) , I_NPC ); while ( RL <> Nil ) do begin if ( RL^.S >= 1 ) and ( RL^.S <= NumSkill ) and HasSkill( I_PC , RL^.S ) and PartMatchesCriteria( NPCDesc , SAttValue( RL^.SA , 'REQUIRES' ) ) then begin { This skill might be of use. Check it to see. } if RL_Skill = Nil then begin RL_Skill := RL; BestScore := SkillValue( I_PC , RL^.S , STAT_Charm ); end else begin SkVal := SkillValue( I_PC , RL^.S , STAT_Charm ); if SkVal > BestScore then begin RL_Skill := RL; BestScore := SkVal; end; end; end; RL := RL^.Next; end; { Make the skill roll. } if ( RL_Skill <> Nil ) and ( BestScore > 3 ) then begin { Make the skill roll here. } SkRoll := SkillRoll( GB , I_PC , RL_Skill^.S , STAT_Charm , BasicSkillTarget( NAttValue( I_PC^.NA , NAG_CharDescription , NAS_Renowned ) ) , 0 , False , True ); end else begin { Make a default crappy skill roll, and find the default lead. } SkRoll := RollStep( 3 ); RL_Skill := SeekCurrentLevelGear( rumor_leads , GG_Persona , 0 ); end; { If this NPC has a relationship with the PC, the skill roll cannot fall } { beneath an amount appropriate to the reaction score. } if NAttValue( I_NPC^.NA , NAG_Relationship , 0 ) > 0 then begin SkVal := BasicSkillTarget( ReactionScore( GB^.Scene , I_PC , I_NPC ) ); if SkVal > SkRoll then SkRoll := SkVal; end; { Harvest a list of rumors based on the skill roll. } { As the rumors are added to the list, convert them to memos. } Rumor_List := RevealRumors( GB , I_NPC , SkRoll , Rumor_Error ); { If our rumor list is not empty, do the lead-in and then present those } { rumors to the player. If it is empty, print a know nothing message. } if Rumor_List <> Nil then begin { Play the RL_Skill dialog. } RL_Script := AS_GetString( RL_Skill , 'START' ); { Use NPCDesc as a dummy trigger. } InvokeEvent( RL_Script , GB , RL_Skill , NPCDesc ); { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InteractMenu ); RPM^.Mode := RPMNoCancel; AddRPGMenuItem( RPM , MsgString( 'Continue' ) , 1 ); { Show the rumors next. } R := Rumor_List; while R <> Nil do begin SelectMenu( RPM , @InteractRedraw ); Chat_Message := MadLibString( RLI_List ) + ' ' + R^.Info; R := R^.Next; end; { Dispose of the rumor list. } DisposeSAtt( Rumor_List ); DisposeRPGMenu( RPM ); end else begin { Set the chat message. } Chat_Message := MsgString( 'RUMOR_NoRumors' + BStr( Random( 6 ) + 1 ) ); end; { Set the recharge counter for this NPC. } if not Rumor_Error then SetNAtt( I_NPC^.NA , NAG_Personal , NAS_RumorRecharge , GB^.ComTime + 21600 + Random( 7200 ) - Random( 7200 ) ) else SetNAtt( I_NPC^.NA , NAG_Personal , NAS_RumorRecharge , GB^.ComTime + 10 ); end; Procedure AddLancemate( GB: GameBoardPtr; NPC: GearPtr ); { Add the listed NPC to the PC's lance. } var Mecha,Pilot: GearPtr; Timer: LongInt; begin { This NPC will have to quit their current team to do this... } { so, better set a trigger. } SetTrigger( GB , TRIGGER_NumberOfUnits + BStr( NAttValue( NPC^.NA , NAG_Location , NAS_Team ) ) ); { If this is the first time the lancemate has joined, add the NPC's mecha. } if IsACombatant( NPC ) and ( NAttValue( NPC^.NA , NAG_Narrative , NAS_GaveLMMecha ) = 0 ) then begin Mecha := SelectNPCMecha( GB , GB^.Scene , NPC ); if Mecha <> Nil then begin SetNAtt( Mecha^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); DeployGear( GB , Mecha , False ); AssociatePilotMek( GB^.Meks , NPC , Mecha ); SetNAtt( NPC^.NA , NAG_Narrative , NAS_GaveLMMecha , 1 ); end; end; { Set the plot recharge timer to at least 12 hours from now. } Timer := NAttValue( NPC^.NA , NAG_Personal , NAS_PlotRecharge ); if Timer < ( GB^.ComTime + 43200 ) then SetNAtt( NPC^.NA , NAG_Personal , NAS_PlotRecharge , GB^.ComTime + 43200 ); Pilot := LocatePilot( NPC ); if Pilot <> NPC then SetNAtt( Pilot^.NA , NAG_Location , NAS_Team , NAV_LancemateTeam ); SetNAtt( NPC^.NA , NAG_Location , NAS_Team , NAV_LancemateTeam ); SetLancemateOrders( GB ); end; Function AddLancemateFrontEnd( GB: GameBoardPtr; PC,NPC: GearPtr; CanCancel: Boolean ): Boolean; { NPC wants to join the lance. If NPC isn't a temp lancemate, and the lance is already } { full, prompt to remove a member before NPC can join. } { Return TRUE if the lancemate was added, or FALSE otherwise. } var NoCancel: Boolean; RPM: RPGMenuPtr; LM: GearPtr; N: Integer; begin NoCancel := True; { Step One- Check NPC's temp status, then count up the number of lancemates } { to see if there's gonna be a problem. } if NAttValue( NPC^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_TempLancemate then begin { Initialize some values. } ASRD_GameBoard := GB; ASRD_MemoMessage := ReplaceHash( MsgString( 'JOINFE_Need_Space' ) , GearName( NPC ) ); { Count up the number of lancemates. } while ( LancematesPresent( GB ) >= NumLancemateSlots( GB^.Scene , PC ) ) and NoCancel do begin { Remove lancemates to make room for NPC. } { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoMenu ); { Create the list. } LM := GB^.Meks; N := 1; while LM <> Nil do begin if ( NAttValue( LM^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and GearActive( LM ) and IsRegularLancemate( LM ) then begin AddRPGMenuItem( RPM , PilotName( LM ) , N ); end; Inc( N ); LM := LM^.Next; end; RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); if CanCancel then AddRPGMenuItem( RPM , MsgString( 'Cancel' ) , -1 ) else RPM^.Mode := RPMNoCancel; N := SelectMenu( RPM , @MemoPageReDraw ); DisposeRPGMenu( RPM ); if N = -1 then NoCancel := False else begin LM := RetrieveGearSib( GB^.Meks , N ); RemoveLancemate( GB , LM , True ); end; end; end; { Step Two- we apparently have room now. Add the lancemate to the party. } if NoCancel then AddLancemate( GB , NPC ); AddLancemateFrontEnd := NoCancel; end; Procedure AttemptJoin( GB: GameBoardPtr ); { I_NPC will attempt to join the party. Yay! } var LMP: Integer; { Lancemates Present } begin { Make sure we've got an NPC to deal with. } if I_NPC = Nil then Exit; { Also make sure the NPC isn't currently a member of the team. } if NAttValue( I_NPC^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam then Exit; { Need two more available lancemate points than are currently in use. } if CanJoinLance( GB , I_PC , I_NPC ) then begin if AddLancemateFrontEnd( GB , I_PC , I_NPC , True ) then CHAT_Message := MsgString( 'JOIN_JOIN' ) else CHAT_Message := MsgString( 'JOIN_Cancel' ); end else begin LMP := LancematesPresent( GB ); if ReactionScore( GB^.Scene , I_PC , I_NPC ) < 25 then begin CHAT_Message := MsgString( 'JOIN_REFUSE' ); end else if LMP >= NumLancemateSlots( GB^.Scene , I_PC ) then begin CHAT_Message := MsgString( 'JOIN_NOPOINT' ); end else begin CHAT_Message := MsgString( 'JOIN_BUSY' ); end; end; end; Procedure RemoveLancemate( GB: GameBoardPtr; Mek: GearPtr; DoMessage: Boolean ); { Remove NPC from the party. } { If this location isn't a good one for the LM, move the LM to a better place. } var NPC,DestScene: GearPtr; msg: String; begin if Mek^.G = GG_Mecha then begin NPC := ExtractPilot( Mek ); if NPC = Nil then Exit; DeployGear( GB , NPC , False ); end else begin NPC := Mek; end; if IsInvCom( GB^.Scene ) or ( not IsSafeArea( GB ) ) then begin { This isn't a good place for the NPC to stay. Better move it } { to somewhere else. } { Step One- Locate the destination scene. } DestScene := SearchForScene( FindRootScene( GB^.Scene ) , Nil , GB , '(BUILDING|MEETING) PUBLIC -ENEMY' ); if DestScene <> Nil then begin { Step Two- Announce the lancemate's destination. } msg := ReplaceHash( MsgString( 'QUIT_LANCE_GO' ) , GearName( DestScene ) ); { Step Three- Delink the lancemate and send it there. } { Note that NPC might actually be the NPC's mecha. Deal with it. } DelinkGearForMovement( GB , NPC ); InsertInvCom( DestScene , NPC ); SetSAtt( NPC^.SA , 'TEAMDATA ' ); ChooseTeam( NPC , DestScene ); end else begin { Fail. Just stick the NPC here, for now. } msg := MsgString( 'QUIT_LANCE' ); SetSAtt( NPC^.SA , 'TEAMDATA ' ); ChooseTeam( NPC , GB^.Scene ); end; end else begin { The LM can just hang around here until the PC gets back. } msg := MsgString( 'QUIT_LANCE' ); SetSAtt( NPC^.SA , 'TEAMDATA ' ); ChooseTeam( NPC , GB^.Scene ); end; { Set the rumor recharge to six hours from now. After all, the LM } { was hanging around with the PC... they need some time to hear things. } SetNAtt( NPC^.NA , NAG_Personal , NAS_RumorRecharge , GB^.ComTime + 21600 ); if DoMessage then begin if NPC = I_NPC then begin CHAT_Message := msg; end else begin Monologue( GB , NPC , msg ); end; end; end; Procedure HandleQuit( GB: GameBoardPtr ); { I_NPC will quit the party. } begin if I_NPC = Nil then Exit; RemoveLancemate( GB , I_NPC , True ); end; Procedure PruneNothings( var LList: GearPtr ); { Traverse the list. Anything marked as ABSOLUTELYNOTHING gets deleted, along with } { all of its children gears. That's tough, but that's life... } var L,L2: GearPtr; begin L := LList; while L <> Nil do begin L2 := L^.Next; if L^.G = GG_AbsolutelyNothing then begin RemoveGear( LList , L ); end else begin PruneNothings( L^.SubCom ); PruneNothings( L^.InvCom ); end; L := L2; end; end; Procedure HandleInteract( GB: GameBoardPtr; PC,NPC,Persona: GearPtr ); { The player has just entered a conversation. } { HOW THIS WORKS: The interaction menu is built by an ASL script. } { the player selects one of the provided responses, which will } { either trigger another script ( V >= 0 ) or call one of the } { standard interaction routines ( V < 0 ) } var IntScr: String; { Interaction Script } N,FreeRumors: Integer; RTT: LongInt; { ReTalk Time } T: String; MI: RPGMenuItemPtr; { For removing options once they've been used. } begin { Start by allocating the menu. } IntMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InteractMenu ); {IntMenu^.Mode := RPMEscCancel;} { If this persona has been marked as "NoEscape", make sure the PC } { can't quit the conversation just by pressing ESC. } if ( Persona <> Nil ) and ASTringHasBString( SAttValue( Persona^.SA , 'SPECIAL' ) , 'NOESCAPE' ) then IntMenu^.Mode := RPMNoCancel; { Initialize interaction variables. } I_PC := PC; I_NPC := NPC; ASRD_GameBoard := GB; { Since the conversation can be switched by REVERTPERSONA and maybe some other } { effects, from this point onwards use I_PERSONA rather than PERSONA. } if Persona <> Nil then I_Persona := Persona else I_Persona := BLANK_PERSONA; { Add a divider to the skill roll history. } SkillCommentDivider; { If the NPC is fully recharged from talking with you last time, } { get full endurance of 10. Otherwise, only gets partial endurance. } if NAttValue( NPC^.NA , NAG_Personal , NAS_ReTalk ) > GB^.ComTime then begin I_Endurance := 1; end else begin I_Endurance := 10; end; { Determine the number of "Free" rumors the PC will get. } FreeRumors := 0; N := ReactionScore( GB^.Scene , PC , NPC ); if N > 20 then FreeRumors := ( N - 13 ) div 7; N := CStat( PC , STAT_Charm ); if N > 12 then FreeRumors := FreeRumors + Random( N - 11 ); { Invoke the greeting event. } if ( NAttValue( NPC^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and ( NAttValue( NPC^.NA , NAG_Chardescription , NAS_CharType ) <> NAV_TempLancemate ) and (( Persona = Nil ) or (( Persona^.Parent <> Nil ) and ( Persona^.Parent^.G = GG_Scene ))) then begin { Lancemates won't use their local personas while part of the lance. } { Hence the mother of all conditionals above... } I_PERSONA := lancemate_tactics_persona; end; if I_Persona <> Nil then begin IntScr := AS_GetString( I_Persona , 'GREETING' ); end else begin { If there is no standard greeting, set the event to } { build the default interaction menu. } IntScr := 'SAYANYTHING NEWCHAT'; end; T := 'Greeting'; InvokeEvent( IntScr , GB , I_Persona , T ); repeat { Print the NPC description. } { This could change depending upon what the PC does. } if IntMenu^.NumItem > 0 then begin ASRD_GameBoard := GB; CHAT_React := ReactionScore( GB^.Scene , PC , NPC ); N := SelectMenu( IntMenu , @InteractRedraw ); end else begin { If the menu is empty, we must leave this procedure. } { More importantly, we better not do anything in } { the conditional below... Set N to equal a "goodbye" result. } N := -1; end; if ( N >= 0 ) and ( I_Persona <> Nil ) then begin { One of the placed options have been triggered. } { Attempt to find the appropriate script to } { invoke. } IntScr := AS_GetString( I_Persona , 'RESULT' + BStr( N ) ); InvokeEvent( IntScr , GB , I_Persona , T ); end else if N = CMD_Join then begin AttemptJoin( GB ); { After attempting to join, remove the JOIN option. } MI := SetItemByValue( IntMenu , CMD_Join ); if MI <> Nil then RemoveRPGMenuItem( IntMenu , MI ); SetItemByPosition( IntMenu , 1 ); end else if N = CMD_Quit then begin HandleQuit( GB ); ClearMenu( IntMenu ); end else if N = CMD_WhereAreYou then begin HandleWhereAreYou( GB ); end else if N = CMD_AskAboutRumors then begin HandleAskAboutRumors( GB , Persona ); MI := SetItemByValue( IntMenu , CMD_AskAboutRumors ); if MI <> Nil then RemoveRPGMenuItem( IntMenu , MI ); SetItemByPosition( IntMenu , 1 ); end; until ( N = -1 ) or ( IntMenu^.NumItem < 1 ) or ( I_Endurance < 1 ) or ( I_NPC = Nil ) or ( I_Persona = Nil ); { If the menu is empty, pause for a minute. Or at least a keypress. } if IntMenu^.NumItem < 1 then begin InteractRedraw; DoFlip; MoreKey; end; { If the conversation ended because the NPC ran out of patience, } { store a negative reaction modifier. } if I_Endurance < 1 then begin AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) , -5 ); { Overchatting is a SOCIABLE action. } AddReputation( PC , 3 , 1 ); end; { Set the ReTalk value. } { Base retalk time is 1500 ticks; may be raised or lowered depending } { upon the NPC's ENDURANCE and also how well the NPC likes the PC. } if ( I_NPC <> Nil ) and ( SCRIPT_DynamicEncounter = Nil ) then begin RTT := GB^.ComTime + 1500 - ( 15 * ReactionScore( GB^.Scene , PC , NPC ) ) - ( 50 * I_Endurance ); if RTT < ( GB^.ComTime + AP_Minute ) then RTT := GB^.ComTime + AP_Minute; SetNAtt( NPC^.NA , NAG_Personal , NAS_ReTalk , RTT ); end; { Set the NumberOfConversations counter. } if I_NPC <> Nil then AddNAtt( NPC^.NA , NAG_Personal , NAS_NumConversation , 1 ); I_NPC := Nil; I_Persona := Nil; { Get rid of the menu. } DisposeRPGMenu( IntMenu ); DisposeSAtt( I_Rumors ); end; Procedure PCDoVerbalAttack( GB: GameBoardPtr; PC,NPC: GearPtr ); { The PC wants to verbally abuse this NPC. If it's possible do so. If it } { isn't possible, then explain why. } begin NPC := LocatePilot( NPC ); if CurrentMental( PC ) < 1 then begin DialogMsg( MsgSTring( 'VERBALATTACK_NoMP' ) ); end else if ( NPC <> Nil ) and CanSpeakWithTarget( GB , PC , NPC ) then begin if AreEnemies( GB , PC , NPC ) then begin DoVerbalAttack( GB , PC , FindRoot( NPC ) ); { When the PC taunts an enemy, it takes an action. } SetNAtt( PC^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ReactionTime( PC ) ); end else begin DialogMsg( ReplaceHash( MsgSTring( 'TAUNT_OnlyEnemies' ) , GearName( NPC ) ) ); end; end else begin DialogMsg( ReplaceHash( MsgSTring( 'TALKING_TooFar' ) , GearName( NPC ) ) ); end; end; Procedure DoTalkingWIthNPC( GB: GameBoardPtr; PC,Mek: GearPtr; ByTelephone: Boolean ); { Actually handle the talking with an NPC already selected. } var Persona,NPC: GearPtr; CID: Integer; React: Integer; ReTalk: LongInt; begin NPC := LocatePilot( Mek ); if ( NPC <> Nil ) and GearOperational( NPC ) and AreEnemies( GB , Mek , PC ) and NotAnAnimal( NPC ) and IsFoundAlongTrack( GB^.Meks , FindRoot( NPC ) ) and ( NATtValue( NPC^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) <> NAV_NowSurrendered ) then begin PCDoVerbalAttack( GB , PC , NPC ); end else if ( NPC <> Nil ) and GearOperational( NPC ) then begin if ByTelephone or CanSpeakWithTarget( GB , PC , NPC ) then begin CID := NAttValue( NPC^.NA , NAG_Personal , NAS_CID ); if CID <> 0 then begin { Everything should be okay to talk... Now see if the NPC wants to. } { Determine the NPC's RETALK and REACT values. } ReTalk := NAttValue( NPC^.NA , NAG_Personal , NAS_Retalk ); React := ReactionScore( GB^.Scene , PC , NPC ); Persona := SeekPersona( GB , CID ); { Surrendered NPCs never refuse to talk. } if NATtValue( NPC^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) = NAV_NowSurrendered then begin DialogMsg( ReplaceHash( MsgSTring( 'TALKING_Start' ) , GearName( NPC ) ) ); HandleInteract( GB , PC , NPC , Persona ); CombatDisplay( gb ); { If the NPC really doesn't like the PC, } { they'll refuse to talk on principle. } end else if ( ( React + RollStep( SkillValue ( PC , NAS_Conversation , STAT_Ego ) ) ) < -Random( 120 ) ) or ( AreEnemies( GB , NPC , PC ) and IsFoundAlongTrack( GB^.Meks , NPC ) ) then begin DialogMsg( ReplaceHash( MsgSTring( 'TALKING_RefuseHard' ) , GearName( NPC ) ) ); SetNAtt( NPC^.NA , NAG_Personal , NAS_Retalk , GB^.ComTime + 1500 ); { If the NPC is ready to talk, is friendly with the PC, or has a PERSONA gear defined, } { they'll be willing to talk. } end else if ( ReTalk < GB^.ComTime ) or ( Random( 50 ) < ( React + 20 ) ) or ( Persona <> Nil ) then begin DialogMsg( ReplaceHash( MsgSTring( 'TALKING_Start' ) , GearName( NPC ) ) ); HandleInteract( GB , PC , NPC , Persona ); CombatDisplay( gb ); end else begin DialogMsg( ReplaceHash( MsgSTring( 'TALKING_RefuseSoft' ) , GearName( NPC ) ) ); end; WaitAMinute( GB , PC , ReactionTime( PC ) ); end else begin DialogMsg( MsgSTring( 'TALKING_NoReply' ) ); end; end else begin DialogMsg( ReplaceHash( MsgSTring( 'TALKING_TooFar' ) , GearName( NPC ) ) ); end; end else begin DialogMsg( 'Not found!' ); end; end; Procedure ForceInteract( GB: GameBoardPtr; CID: LongInt ); { Attempt to force the PC to converse with the provided NPC. } var PC,NPC,Interact: GearPtr; begin { Locate all the required elements. } PC := LocatePilot( GG_LocatePC( GB ) ); NPC := SeekGearByCID( GB^.Meks , CID ); if NPC = Nil then NPC := SeekGearByCID( FindRoot( GB^.Scene ) , CID ); Interact := SeekPersona( GB , CID ); if ( PC <> Nil ) and ( NPC <> Nil ) and NotDestroyed( PC ) then begin { Before initiating the conversation, get rid of the } { recharge timer, since the NPC initiated this chat } { and won't get pissed off. } SetNAtt( NPC^.NA , NAG_Personal , NAS_ReTalk , 0 ); { Print an appropriate message. } if NPC^.Parent = Nil then begin { The NPC has no parent, so it must be on the gameboard } { and not in a mecha. Use the conversation message. } DialogMsg( ReplaceHash( MsgString( 'FORCECHAT_SPEAK' ) , GearName( NPC ) ) ); end else begin { Use the contact message. } DialogMsg( ReplaceHash( MsgString( 'FORCECHAT_CONTACT' ) , GearName( NPC ) ) ); end; { Hand everything to the interaction procedure. } HandleInteract( GB , PC , NPC , Interact ); end; end; Function TriggerGearScript( GB: GameBoardPtr; Source: GearPtr; var Trigger: String ): Boolean; { Attempt to trigger the requested script in this gear. If the } { script cannot be found, then do nothing. } var E: String; it: Boolean; begin it := False; if Source <> Nil then begin E := AS_GetString( Source , Trigger ); if E <> '' then begin InvokeEvent( E , GB , Source , Trigger ); it := True; end; end; TriggerGearScript := it; end; Function CheckTriggerAlongPath( var T: String; GB: GameBoardPtr; Plot: GearPtr; CheckAll: Boolean ): Boolean; { Check all the active narrative gears in this list (plots, stories, and factions) } { looking for events which match the provided trigger. } { Return TRUE if an event was invoked, or FALSE if no event was encountered. } var P2: GearPtr; it,I2: Boolean; LT,TempList: SAttPtr; { Local Trigger counter } LT_tmp: STring; { The content of the local trigger currently being processed. } begin it := False; while ( Plot <> Nil ) and ( T <> '' ) do begin P2 := Plot^.Next; if CheckAll or ( Plot^.G = GG_Plot ) or ( Plot^.G = GG_Faction ) or ( Plot^.G = GG_Story ) or ( Plot^.G = GG_Adventure ) or ( Plot^.G = GG_CityMood ) then begin { FACTIONs and STORYs can hold active plots in their InvCom. } if ( Plot^.G = GG_Faction ) or ( Plot^.G = GG_Story ) or ( Plot^.G = GG_Adventure ) then CheckTriggerAlongPath( T , GB , Plot^.InvCom , CheckAll); { Initialize the list of local triggers. } if local_triggers <> Nil then DisposeSATt( local_triggers ); { Check the trigger against this gear's scripts. } I2 := TriggerGearScript( GB , Plot , T ); it := it or I2; { If any local triggers were tripped, call them now. } while local_triggers <> Nil do begin TempList := local_triggers; local_triggers := Nil; while TempList <> Nil do begin LT := TempList; LT_Tmp := LT^.Info; RemoveSAtt( TempList , LT ); TriggerGearScript( GB , Plot , LT_Tmp ); end; end; { The trigger above might have changed the } { structure, so reset P2. } P2 := Plot^.Next; { Remove the plot, if it's been advanced. } {if Plot^.G = GG_AbsolutelyNothing then begin if IsInvCom( Plot ) then RemoveGear( Plot^.Parent^.InvCom , Plot ) else if IsSubCom( Plot ) then RemoveGear( Plot^.Parent^.SubCom , Plot ); end;} end; Plot := P2; end; CheckTriggerAlongPath := it; end; Procedure HandleTriggers( GB: GameBoardPtr ); { Go through the list of triggers, enacting events if any are } { found. Deallocate the triggers as they are processed. } var TList,TP: SAttPtr; { Trigger List , Trigger Pointer } E,msg: String; City: GearPtr; begin IntMenu := Nil; { Only try to implement triggers if this gameboard has a scenario } { defined. } if GB^.Scene <> Nil then begin { Some of the events we process might add their own } { triggers to the list. So, we check all the triggers } { currently set, then look at the GB^.Trig list again } { to see if any more got put there. } while GB^.Trig <> Nil do begin { Copy the list pointer to TList, clear the } { list pointer from GB, and set the pointer } { to the first trigger. } TList := GB^.Trig; GB^.Trig := Nil; TP := TList; while TP <> Nil do begin { Commands can be embedded in the triggers list. } { The actual point of this is to allow scripts } { to automatically activate interactions & props. } if ( Length( TP^.Info ) > 0 ) and ( TP^.Info[1] = '!' ) then begin { Copy the command. } E := UpCase( ExtractWord( TP^.Info ) ); DeleteFirstChar( E ); if E = 'TALK' then begin ForceInteract( GB , ExtractValue( TP^.Info ) ); end else if E = 'ANNOUNCE' then begin msg := msgString( TP^.Info ); if msg <> '' then YesNoMenu( GB , msg , '' , '' ); end; { Clear this trigger. } TP^.Info := ''; end else if TP^.Info <> '' then begin { If there is a SAtt in the scenario description } { named after this trigger description, it will } { happen now. First, see if such an event exists. } { Check the PLOTS, FACTIONS and STORIES in } { Adventure/InvCom first. } if GB^.Scene^.Parent <> Nil then begin CheckTriggerAlongPath( TP^.Info , GB , FindRoot( GB^.Scene ) , False ); end; { Check for quests and moods in the current city next. } City := FindRootScene( GB^.Scene ); if ( City <> Nil ) and ( TP^.Info <> '' ) then begin CheckTriggerAlongPath( TP^.Info , GB , City^.SubCom , False ); end; { Check the current scene last. } if TP^.Info <> '' then TriggerGearScript( GB , GB^.Scene , TP^.Info ); end; TP := TP^.Next; end; { Get rid of the trigger list. } DisposeSAtt( TList ); end; DoScriptGC( GB ); end; end; Function StartRescueScenario( GB: GameBoardPtr; PC: GearPtr; Context: String ): Boolean; { Attempt to load a rescue scenario for the PC. } Function RescueContext: String; { Generate a string telling everything that needs to be told about } { the PC's current location. } var RC: String; C: GearPtr; begin RC := Context; AddTraits( RC , SAttValue( GB^.Scene^.SA , 'CONTEXT' ) ); AddTraits( RC , SATtValue( GB^.Scene^.SA , 'DESIG' ) ); AddTraits( RC , SAttValue( GB^.Scene^.SA , 'TYPE' ) ); AddTraits( RC , SAttValue( GB^.Scene^.SA , 'TERRAIN' ) ); { Next add the data for the city we're located in, its faction, and the } { world that it's located in. } C := FindRootScene( GB^.Scene ); if C <> Nil then begin AddTraits( RC , SAttValue( C^.SA , 'DESIG' ) ); C := SeekFaction( FindRoot( GB^.Scene ) , NAttValue( C^.NA , NAG_Personal , NAS_FactionID ) ); if C <> Nil then AddTraits( RC , SAttValue( C^.SA , 'DESIG' ) ); end; RescueContext := RC; end; { RescueContext } var Rescue_list,R: GearPtr; ItWorked: Boolean; begin { If no scene, can't be rescued. Sorry. } if GB^.Scene = Nil then Exit( False ); { Assume the rescue failed unless shown otherwise. } ItWorked := False; { Load the rescue files. } Rescue_List := AggregatePattern( 'RESCUE_*.txt' , Series_Directory ); { Generate the complete context for the rescue. } Context := RescueContext; while Rescue_List <> Nil do begin R := FindNextComponent( Rescue_List , Context ); if R <> Nil then begin DelinkGear( Rescue_List , R ); SetNAtt( R^.NA , NAG_ElementID , 1 , GB^.Scene^.S ); if InsertPlot( FindRootScene( GB^.Scene ) , FindRoot( GB^.Scene ) , R , GB , CurrentPCRenown( GB ) ) then begin { Start by printing a message, since the time taken by the } { rescue scenario is likely to cause a noticeable delay. } DialogMsg( MsgString( 'JustAMinute' ) ); CombatDisplay( GB ); DoFLip; DoCompleteRepair( PC ); DisposeGear( Rescue_List ); SetTrigger( GB , 'UPDATE' ); HandleTriggers( GB ); ItWorked := True; end; end else begin DisposeGear( Rescue_List ); end; end; StartRescueScenario := ItWorked; end; Procedure DoScriptGC( GB: GameBoardPtr ); { Get rid of any ABSOLUTELYNOTHINGS that may be hanging about. } var Adv: GearPtr; begin if NeedGC then begin Adv := GG_LocateAdventure( GB, Nil ); if Adv <> Nil then begin PruneNothings( Adv^.SubCom ); PruneNothings( Adv^.InvCom ); NeedGC := False; end; end; end; initialization SCRIPT_DynamicEncounter := Nil; Grabbed_Gear := Nil; Script_Macros := LoadStringList( Script_Macro_File ); if Script_Macros = Nil then DialogMsg( 'ERROR: Script Macros failed.' ); Value_Macros := LoadStringList( Value_Macro_File ); if Value_Macros = Nil then DialogMsg( 'ERROR: Value Macros failed.' ); Default_Scene_Scripts := LoadStringList( Data_Directory + 'scene.txt' ); lancemate_tactics_persona := LoadFile( 'lmtactics.txt' , Data_Directory ); BLANK_PERSONA := LoadFile( 'blankpersona.txt' , Data_Directory ); rumor_leads := LoadFile( 'rumor_leads.txt' , Data_Directory ); local_triggers := Nil; I_NPC := Nil; I_Persona := Nil; IntMenu := Nil; NeedGC := False; finalization if SCRIPT_DynamicEncounter <> Nil then begin DisposeGear( SCRIPT_DynamicEncounter ); end; DisposeSAtt( Script_Macros ); DisposeSAtt( Value_Macros ); DisposeSAtt( Default_Scene_Scripts ); DisposeGear( lancemate_tactics_persona ); DisposeGear( BLANK_PERSONA ); DisposeGear( rumor_leads ); if local_triggers <> Nil then DisposeSATt( local_triggers ); end. gearhead-2-0.701/attacktest.pas000066400000000000000000000257761321074026100163110ustar00rootroot00000000000000program attacktest; uses gears,gearutil,locale,gearparser,effects,randmaps,ghchars,ghweapon,texutil,ghintrinsic,ability,movement,action; const Tar_X = 9; Tar_Y = 8; Num_Trials = 2000; Skill_Level = 50; Target_Move_Action = NAV_NormSpeed; var results: SAttPtr; Procedure DeleteAllClouds( GB: GameBoardPtr ); { Get rid of any clouds that may be cluttering up the map. } var M,M2: GearPtr; begin M := GB^.Meks; while M <> Nil do begin M2 := M^.Next; if M^.G = GG_MetaTerrain then begin RemoveGear( GB^.Meks , M ); end; M := M2; end; end; Procedure TestThisWeapon( GB: GameBoardPtr; AMaster , Attacker , Target: GearPtr ); { Test the provided weapon. It must already be installed in AMaster. } { Run this attack a lot of times, and see how long it takes to destroy } { the target. } var TTK: LongInt; { Times To Kill } T,TT,N: LongInt; Dmg,Total,AtOp: LongInt; P1,P2: GearPtr; Histiogram: Array [1..50] of Integer; begin Total := 0; P1 := LocatePilot( AMaster ); P2 := LocatePilot( Target ); StripNAtt( FindRoot( Attacker ) , NAG_Damage ); StripNAtt( FindRoot( Target ) , NAG_Damage ); AtOp := Attacker^.Stat[ STAT_BurstValue ]; { Clear the histiogram. } for t := 1 to 50 do Histiogram[ t ] := 0; for T := 1 to Num_Trials do begin { See how many attacks it takes before the target is destroyed. } N := 0; { Prep the movement modes. } SetNAtt( AMaster^.NA , NAG_Action , NAS_MoveMode , MM_Skim ); PrepAction( GB , Target , NAV_Stop ); SetNAtt( Target^.NA , NAG_Action , NAS_MoveMode , MM_Skim ); PrepAction( GB , Target , Target_Move_Action ); SetSkillsAtLevel( P1 , Skill_Level ); SetSkillsAtLevel( P2 , Skill_Level ); repeat DoAttack( GB , Attacker , Target , Tar_X , Tar_Y , 0 , AtOp ); StripNAtt( AMaster , NAG_Condition ); StripNAtt( AMaster , NAG_Damage ); StripNAtt( AMaster , NAG_WeaponModifier ); StripNAtt( AMaster , NAG_StatusEffect ); DeleteAllClouds( GB ); Inc( N ); until ( N >= 50 ) or not GearActive( Target ); Inc( Histiogram[ N ] ); Total := Total + N; { Delete the damage from the target for the next runthrough. } StripNAtt( FindRoot( Target ) , NAG_Condition ); StripNAtt( FindRoot( Target ) , NAG_Damage ); StripNAtt( FindRoot( Target ) , NAG_WeaponModifier ); StripNAtt( FindRoot( Target ) , NAG_StatusEffect ); end; { Determine the TimesToKill and MedianToKill. } TTK := Total div Num_Trials; N := 1; for t := 1 to 50 do if Histiogram[ T ] > Histiogram[ N ] then N := T; StoreSAtt( Results, FullGearName( Attacker ) + ': Mean ' + BStr( TTK ) + ', Median ' + Bstr( N ) + ' ($' + BStr( GearValue( Attacker ) ) + ')' ); end; Procedure TestThings( GB: GameBoardPtr; AMaster , Hand , Target: GearPtr; TestWeapon: String ); { Test the requested weapon. } var Attacker: GearPtr; begin Attacker := LoadNewSTC( TestWeapon ); if Attacker = Nil then begin StoreSAtt( results, TestWeapon + ' not found!' ); end else begin InsertInvCom( Hand , Attacker ); TestThisWeapon( GB , AMaster , Attacker , Target ); RemoveGear( Hand^.InvCom , Attacker ); writeln( TestWeapon + ' done.' ); end; end; Procedure TestDC( GB: GameBoardPtr; AMaster , Hand , Target: GearPtr ); { Test the requested weapon. } var Attacker: GearPtr; T: LongInt; { Times To Kill } begin Attacker := LoadNewSTC( 'LAS-10' ); if Attacker <> Nil then begin InsertInvCom( Hand , Attacker ); for t := 1 to 5 do begin Attacker^.V := t * 5; SetSAtt( Attacker^.SA , 'name ' ); SetSAtt( Attacker^.SA , 'type <>' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'name ' ); SetSAtt( Attacker^.SA , 'type ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'name ' ); SetSAtt( Attacker^.SA , 'type ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'name ' ); SetSAtt( Attacker^.SA , 'type ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); StoreSAtt( Results , ' ' ); end; RemoveGear( Hand^.InvCom , Attacker ); end; end; Procedure TestBV( GB: GameBoardPtr; AMaster , Hand , Target: GearPtr ); { Test the requested weapon. } var Attacker: GearPtr; T: LongInt; { Times To Kill } begin Attacker := LoadNewSTC( 'LAS-10' ); if Attacker <> Nil then begin InsertInvCom( Hand , Attacker ); Attacker^.V := 24; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.V := 12; Attacker^.Stat[ STAT_BurstValue ] := 1; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.V := 8; Attacker^.Stat[ STAT_BurstValue ] := 2; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.V := 6; Attacker^.Stat[ STAT_BurstValue ] := 3; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.V := 4; Attacker^.Stat[ STAT_BurstValue ] := 5; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.V := 3; Attacker^.Stat[ STAT_BurstValue ] := 7; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); RemoveGear( Hand^.InvCom , Attacker ); end; end; Procedure TestAttributes( GB: GameBoardPtr; AMaster , Hand , Target: GearPtr ); { Test the requested weapon. } var Attacker: GearPtr; T: LongInt; { Times To Kill } begin Attacker := LoadNewSTC( 'SC-9' ); if Attacker <> Nil then begin InsertInvCom( Hand , Attacker ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_BurstValue ] := 2; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_BurstValue ] := 4; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_BurstValue ] := 7; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); { Attacker^.Stat[ STAT_Accuracy ] := 2; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_Accuracy ] := 5; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_Accuracy ] := -2; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_Accuracy ] := -5; SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_Accuracy ] := 0; SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); Attacker^.Stat[ STAT_Accuracy ] := 5; SetSAtt( Attacker^.SA , 'type ' ); SetSAtt( Attacker^.SA , 'name ' ); TestThisWeapon( GB , AMaster , Attacker , Target ); } RemoveGear( Hand^.InvCom , Attacker ); end; end; var GB: GameBoardPtr; Scene,Hand,AMaster,Target,P1,P2,CP: GearPtr; begin { For the attacker and defender, load some nice SAN-D1 Daums. } { Install a generic pilot in each. } AMaster := LoadNewItem( 'SAN-D1 Daum' ); CP := SeekGear( AMaster , GG_CockPit , 0 ); if CP <> Nil then begin P1 := LoadNewNPC( 'Arena Pilot' , FALSE ); { SetNAtt( P1^.NA , NAG_Skill , NAS_SpotWeakness , 10 ); } SetSkillsAtLevel( P1 , 50 ); SetNAtt( P1^.NA , NAG_Intrinsic , NAS_Integral , 1 ); InsertSubCom( CP , P1 ); end; Target := SeekGearByName( AMaster , 'Bolt Cannon' )^.Parent; if Target <> Nil then begin Hand := Target^.Parent; while Hand^.InvCom <> Nil do begin Target := Hand^.InvCom; RemoveGear( Hand^.InvCom , Target ); end; end else begin writeln( 'ERROR: No Bolt Cannon found.' ); end; Target := LoadNewItem( 'AD26c Vadel' ); { Target := LoadNewSTC( 'ATTACKTEST-TARGET' );} CP := SeekGear( Target , GG_CockPit , 0 ); if CP <> Nil then begin P2 := LoadNewNPC( 'Arena Pilot' , FALSE ); SetNAtt( P2^.NA , NAG_Intrinsic , NAS_Integral , 1 ); InsertSubCom( CP , P2 ); end; Scene := LoadNewSTC( 'SCENE_EmptyBuilding' ); GB := RandomMap( Scene ); AppendGear( GB^.Meks , AMaster ); AppendGear( GB^.Meks , Target ); SetNAtt( AMaster^.NA , NAG_Location , NAS_X , Tar_X - 5 ); SetNAtt( AMaster^.NA , NAG_Location , NAS_Y , Tar_Y ); SetNAtt( Target^.NA , NAG_Location , NAS_X , Tar_X ); SetNAtt( Target^.NA , NAG_Location , NAS_Y , Tar_Y ); results := Nil; { TestDC( GB , AMaster , Hand , Target ); StoreSAtt( results , ' ' ); TestBV( GB , AMaster , Hand , Target ); StoreSAtt( results , ' ' ); } TestAttributes( GB , AMaster , Hand , Target ); StoreSAtt( results , ' ' ); { TestThings( GB , AMaster , Hand , Target , 'MBAZ-17' ); TestThings( GB , AMaster , Hand , Target , 'PAR-2' ); TestThings( GB , AMaster , Hand , Target , 'PAR-6' ); TestThings( GB , AMaster , Hand , Target , 'PAR-13' ); TestThings( GB , AMaster , Hand , Target , 'PHS-8' ); TestThings( GB , AMaster , Hand , Target , 'PHS-25' ); TestThings( GB , AMaster , Hand , Target , 'GR-12' ); TestThings( GB , AMaster , Hand , Target , 'GR-24' ); TestThings( GB , AMaster , Hand , Target , 'MAC-4' ); TestThings( GB , AMaster , Hand , Target , 'RG-8' ); TestThings( GB , AMaster , Hand , Target , 'RG-16' ); TestThings( GB , AMaster , Hand , Target , 'MAC-2' ); TestThings( GB , AMaster , Hand , Target , 'VC-5' ); TestThings( GB , AMaster , Hand , Target , 'SC-9' ); TestThings( GB , AMaster , Hand , Target , 'MB-7' ); TestThings( GB , AMaster , Hand , Target , 'MRIF-5' ); } StoreSAtt( results , 'Attacker Skill = ' + BStr( NAttValue( P1^.NA , NAG_Skill , NAS_MechaGunnery ) ) ); StoreSAtt( results , 'Defender Skill = ' + BStr( NAttValue( P2^.NA , NAG_Skill , NAS_MechaPiloting ) ) ); SaveStringList( 'atest_out.txt' , results ); SaveStringList( 'atest_out2.txt' , Skill_Roll_History ); DisposeSAtt( results ); DisposeMap( GB ); end. gearhead-2-0.701/backpack.pp000066400000000000000000002313551321074026100155250ustar00rootroot00000000000000unit backpack; { This unit handles both the inventory display and the } { FieldHQ interface, which uses many of the same things. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale,ghchars, {$IFDEF ASCII} vidgfx; {$ELSE} sdlgfx; {$ENDIF} const TRIGGER_GetItem = 'GET'; Skill_Use_Trigger: Array [1..NumSkill] of String = ( 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'CLUE_SURVIVAL', 'CLUE_REPAIR', 'CLUE_MEDICINE', 'USE', 'USE', 'USE', 'USE', 'USE', 'USE', 'CLUE_SCIENCE', 'USE', 'CLUE_CODEBREAKING', 'CLUE_MYSTICISM', 'USE', 'USE', 'CLUE_INSIGHT', 'USE' ); Function DefaultAtOp( Weapon: GearPtr ): Integer; Procedure GatherFieldHQ( GB: GameBoardPtr ); Procedure GivePartToPC( var LList: GearPtr; Part, PC: GearPtr ); Procedure GivePartToPC( GB: GameBoardPtr; Part, PC: GearPtr ); {$IFNDEF ASCII} Procedure SelectColors( M: GearPtr; Redrawer: RedrawProcedureType ); Procedure SelectSprite( M: GearPtr; Redrawer: RedrawProcedureType ); {$ENDIF} Function DoFieldRepair( GB: GameBoardPtr; PC , Item: GearPtr; Skill: Integer ): Boolean; Function Handless( Mek: GearPtr ): Boolean; Function CanBeExtracted( Item: GearPtr ): Boolean; Procedure ExtractMechaPart( var LList,Item: GearPtr ); Function ShakeDown( GB: GameBoardPtr; Part: GearPtr; X,Y: Integer ): LongInt; Procedure PCGetItem( GB: GameBoardPtr; PC: GearPtr ); Procedure PCTradeItems( GB: GameBoardPtr; PC,Target: GearPtr ); Procedure EatItem( GB: GameBoardPtr; TruePC , Item: GearPtr ); Procedure FHQ_SelectMechaForPilot( GB: GameBoardPtr; NPC: GearPtr ); Procedure ArenaHQBackpack( Source,BPPC: GearPtr; BasicRedraw: RedrawProcedureType ); Procedure LancemateBackpack( GB: GameBoardPtr; PC,NPC: GearPtr; BasicRedraw: RedrawProcedureType ); Procedure BackpackMenu( GB: GameBoardPtr; PC: GearPtr; StartWithInv: Boolean; BasicRedraw: RedrawProcedureType ); Procedure MechaPartEditor( GB: GameBoardPtr; var LList: GearPtr; PC,Mek: GearPtr; BasicRedraw: RedrawProcedureType ); Procedure MechaPartBrowser( Mek: GearPtr; RDP: RedrawProcedureType ); Procedure MysteryPartBrowser( Mek: GearPtr; RDP: RedrawProcedureType ); Procedure BrowseDesignFile( List: GearPtr; RDP: RedrawProcedureType ); Procedure FHQ_ThisWargearWasSelected( GB: GameBoardPtr; var LList: GearPtr; PC,M: GearPtr; BasicRedrawer: RedrawProcedureType ); Procedure PCDoPerformance( GB: GameBoardPtr; PC: GearPtr ); Procedure StartPerforming( GB: GameBoardPtr; PC: GearPtr ); Procedure UsableGearMenu( GB: GameBoardPtr; PC: GearPtr ); implementation uses ability,action,arenacfe,arenascript,gearutil,ghholder, ghmodule,ghprop,ghswag,interact,menugear,rpgdice,skilluse,texutil, description,ghweapon,ui4gh,narration,specialsys,ghsupport, ghintrinsic,effects,targetui,ghsensor,customization, {$IFDEF ASCII} vidmap,vidmenus,vidinfo; {$ELSE} colormenu,sdlmap,sdlmenus,sdlinfo; {$ENDIF} var ForceQuit: Boolean; EqpRPM,InvRPM: RPGMenuPtr; MenuA,MenuB: RPGMenuPtr; BP_Source: GearPtr; { Gear to appear in the INFO menu. } BP_SeekSibs: Boolean; { TRUE if the menu lists sibling gears; FALSE if it lists child gears. } BP_ActiveMenu: RPGMenuPtr; { The active menu. Used to determine the gear to show info about. } BP_GB: GameBoardPtr; BP_Redraw: RedrawProcedureType; MPB_Redraw: RedrawProcedureType; { Mecha Part Browser redraw procedure. } { Since the mecha part browser may be called } { from the main menu, the mecha editor, or } { a dozen other places it needs to specify } { a redrawer. } Function DefaultAtOp( Weapon: GearPtr ): Integer; { Return the default Attack Options value for the weapon selected. } var atop,PVal: Integer; Ammo: GearPtr; begin AtOp := 0; PVal := WeaponBVSetting( Weapon ); if ( Weapon^.G = GG_Weapon ) then begin if ( ( Weapon^.S = GS_Ballistic ) or ( Weapon^.S = GS_BeamGun ) ) and ( Weapon^.Stat[ STAT_BurstValue ] > 0 ) then begin if PVal = BV_Max then begin AtOp := Weapon^.Stat[ STAT_BurstValue ]; end else if PVal = BV_Half then begin AtOp := Weapon^.Stat[ STAT_BurstValue ] div 2; if AtOp < 1 then AtOp := 1; end else if PVal = BV_Quarter then begin AtOp := Weapon^.Stat[ STAT_BurstValue ] div 4; if AtOp < 1 then AtOp := 1; end; end else if Weapon^.S = GS_Missile then begin Ammo := LocateGoodAmmo( Weapon ); if Ammo = Nil then begin AtOp := 0; end else if PVal = BV_Max then begin AtOp := Ammo^.Stat[ STAT_AmmoPresent ] - 1; if AtOp < 0 then AtOp := 0; end else if PVal = BV_Half then begin AtOp := ( Ammo^.Stat[ STAT_AmmoPresent ] div 2 ) - 1; if AtOp < 0 then AtOp := 0; end else if PVal = BV_Quarter then begin AtOp := ( Ammo^.Stat[ STAT_AmmoPresent ] div 4 ) - 1; if AtOp < 0 then AtOp := 0; end; end; end; DefaultAtOp := atop; end; Procedure PlainRedraw; { Miscellaneous menu redraw procedure. } begin if BP_GB <> Nil then CombatDisplay( BP_GB ); end; Procedure MiscProcRedraw; { Miscellaneous menu redraw procedure. The Eqp display will be shown; } { the INV display won't be. } var N: Integer; Part: GearPtr; begin BP_Redraw; DrawBPBorder; GameMsg( MsgString( 'BACKPACK_Directions' ) , ZONE_BackpackInstructions , InfoHilight ); if ( BP_ActiveMenu <> Nil ) and ( BP_Source <> Nil ) then begin N := CurrentMenuItemValue( BP_ActiveMenu ); if N > 0 then begin if BP_SeekSibs then Part := RetrieveGearSib( BP_Source , N ) else Part := LocateGearByNumber( BP_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( BP_GB , Part , ZONE_ItemsInfo ); end; end; end; if EqpRPM <> Nil then begin DisplayMenu( EqpRPM , Nil ); end; end; Procedure InstallRedraw; { Redrawer for installing a part into a mecha. } begin BP_Redraw; DrawBPBorder; BrowserInterfaceInfo( BP_GB , BP_Source , ZONE_ItemsInfo ); if EqpRPM <> Nil then begin DisplayMenu( EqpRPM , Nil ); end; end; Procedure EqpRedraw; { Show Inventory, select Equipment. } var N: Integer; Part: GearPtr; begin BP_Redraw; DrawBPBorder; DisplayMenu( InvRPM , Nil ); GameMsg( MsgString( 'BACKPACK_Directions' ) , ZONE_BackpackInstructions , InfoHilight ); if ( BP_ActiveMenu <> Nil ) and ( BP_Source <> Nil ) then begin N := CurrentMenuItemValue( BP_ActiveMenu ); if N > 0 then begin if BP_SeekSibs then Part := RetrieveGearSib( BP_Source , N ) else Part := LocateGearByNumber( BP_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( BP_GB , Part , ZONE_ItemsInfo ); end; end; end; end; Procedure ThisItemRedraw; { A specific item was selected, and its location stored in BP_Source. } begin BP_Redraw; DrawBPBorder; GameMsg( MsgString( 'BACKPACK_Directions' ) , ZONE_BackpackInstructions , InfoHilight ); if BP_Source <> Nil then BrowserInterfaceInfo( BP_GB , BP_Source , ZONE_ItemsInfo ); if EqpRPM <> Nil then begin DisplayMenu( EqpRPM , Nil ); end; end; Procedure GetItemRedraw; begin CombatDisplay( BP_GB ); DrawGetItemBorder; end; Procedure ThisWargearRedraw; { A specific item was selected, and its location stored in BP_Source. } begin BP_Redraw; SetupFHQDisplay; if BP_Source <> Nil then BrowserInterfaceInfo( BP_GB , BP_Source , ZONE_ItemsInfo ); end; Procedure MechaPartEditorRedraw; { Show Inventory, select Equipment. } var Part: GearPtr; begin BP_Redraw; SetupFHQDisplay; if ( BP_ActiveMenu <> Nil ) and ( BP_Source <> Nil ) then begin if BP_SeekSibs then Part := RetrieveGearSib( BP_Source , CurrentMenuItemValue( BP_ActiveMenu ) ) else Part := LocateGearByNumber( BP_Source , CurrentMenuItemValue( BP_ActiveMenu ) ); if Part <> Nil then begin BrowserInterfaceInfo( BP_GB , Part , ZONE_ItemsInfo ); end; end; end; Procedure PartBrowserRedraw; { Redraw the screen for the part browser. } var Part: GearPtr; begin if MPB_Redraw <> Nil then MPB_Redraw; SetupFHQDisplay; if ( BP_ActiveMenu <> Nil ) and ( BP_Source <> Nil ) then begin if BP_SeekSibs then Part := RetrieveGearSib( BP_Source , CurrentMenuItemValue( BP_ActiveMenu ) ) else Part := LocateGearByNumber( BP_Source , CurrentMenuItemValue( BP_ActiveMenu ) ); if Part <> Nil then begin BrowserInterfaceInfo( BP_GB , Part , ZONE_ItemsInfo ); end; end; end; Procedure MysteryBrowserRedraw; { Redraw the screen for the mystery display. } { This is the screen that shows no real information when the PC doesn't have } { the correct software to view a target. } begin if MPB_Redraw <> Nil then MPB_Redraw; SetupFHQDisplay; if ( BP_Source <> Nil ) then begin BrowserInterfaceMystery( BP_Source , ZONE_ItemsInfo ); end; end; Procedure TradeItemsRedraw; { Trade Items menu redraw procedure. The MenuA and MenuB will both be shown. } var N: Integer; Part: GearPtr; begin CombatDisplay( BP_GB ); DrawBPBorder; GameMsg( MsgString( 'BACKPACK_Directions' ) , ZONE_BackpackInstructions , InfoHilight ); DisplayMenu( MenuA , Nil ); DisplayMenu( MenuB , Nil ); if ( BP_ActiveMenu <> Nil ) and ( BP_Source <> Nil ) then begin N := CurrentMenuItemValue( BP_ActiveMenu ); if N > 0 then begin Part := RetrieveGearSib( BP_Source^.InvCom , N ); if Part <> Nil then begin BrowserInterfaceInfo( BP_GB , Part , ZONE_ItemsInfo ); end; end; end; end; Procedure GatherFieldHQ( GB: GameBoardPtr ); { The PC wants to open his FieldHQ. Look through the current city and gather } { up everything belonging to the PC team. Deposit these gears on the gameboard. } Procedure GatherFromScene( S: GearPtr ); { Gather any gears belonging to the PC from this scene. } { Move them to the gameboard. } var M,M2: GearPtr; begin M := S^.InvCom; while M <> Nil do begin M2 := M^.Next; if ( M^.G >= 0 ) and ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then begin DelinkGear( S^.InvCom , M ); DeployGear( GB , M , False ); end; M := M2; end; end; Procedure SearchAlongPath( SList: GearPtr ); { Search along this path for scenes. } begin while SList <> Nil do begin if SList^.G = GG_Scene then begin GatherFromScene( SList ); SearchAlongPath( SList^.SubCom ); end; SList := SList^.Next; end; end; var City: GearPtr; begin City := FindRootScene( GB^.Scene ); if City <> Nil then begin GatherFromScene( City ); SearchAlongPath( City^.SubCom ); end; end; Procedure GivePartToPC( var LList: GearPtr; Part, PC: GearPtr ); { Give the specified part to the PC. If the part cannot be } { held by the PC, store it so that it can be recovered using } { the FieldHQ Wargear Explorer. } { The part should be delinked already. } var P2,Pilot: GearPtr; begin if PC^.G = GG_Mecha then Pilot := LocatePilot( PC ) else Pilot := Nil; if ( Part^.G = GG_Set ) then begin while Part^.SubCom <> Nil do begin P2 := Part^.SubCom; DelinkGear( Part^.SubCom , P2 ); GivePartToPC( LList , P2 , PC ); end; while Part^.InvCom <> Nil do begin P2 := Part^.InvCom; DelinkGear( Part^.InvCom , P2 ); GivePartToPC( LList , P2 , PC ); end; end else if ( Pilot <> Nil ) and IsLegalInvCom( Pilot , Part ) then begin StripNAtt( Part , NAG_Location ); StripNAtt( Part , NAG_EpisodeData ); InsertInvCom( Pilot , Part ); end else if ( PC <> Nil ) and IsLegalInvCom( PC , Part ) then begin StripNAtt( Part , NAG_Location ); StripNAtt( Part , NAG_EpisodeData ); InsertInvCom( PC , Part ); end else begin { If the PC can't carry this equipment, } { stick it off the map. } SetNAtt( Part^.NA , NAG_Location , NAS_Team , 1 ); SetNAtt( Part^.NA , NAG_Location , NAS_X , 0 ); SetNAtt( Part^.NA , NAG_Location , NAS_Y , 0 ); AppendGear( LList , Part ); end; end; Procedure GivePartToPC( GB: GameBoardPtr; Part, PC: GearPtr ); { Call the above procedure, with GB^.Meks as the LList. } begin GivePartToPC( GB^.Meks , Part , PC ); end; {$IFNDEF ASCII} Procedure SelectColors( M: GearPtr; Redrawer: RedrawProcedureType ); { The player wants to change the colors for this part. Make it so. } { The menu will be placed in the Menu area; assume the redrawer will } { show whatever changes are made here. } var portraitname,oldcolor,newcolor: String; begin portraitname := InfoImageName( M ); oldcolor := SAttValue( M^.SA , 'SDL_Colors' ); if M^.G = GG_Character then begin newcolor := SelectColorPalette( colormenu_mode_character , portraitname , oldcolor , 100 , 150 , 0, Redrawer ); end else if M^.G = GG_Mecha then begin newcolor := SelectColorPalette( colormenu_mode_mecha , portraitname , oldcolor , 100 , 150 , 0, Redrawer ); end else begin newcolor := SelectColorPalette( colormenu_mode_allcolors , portraitname , oldcolor , 100 , 150 , 0, Redrawer ); end; SetSAtt( M^.SA , 'SDL_Colors <' + newcolor + '>' ); end; Procedure SelectSprite( M: GearPtr; Redrawer: RedrawProcedureType ); { The player wants to change the colors for sprite for this character. } { The menu will be placed in the Menu area; assume the redrawer will } { show whatever changes are made here. } var RPM: RPGMenuPtr; fname: String; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharViewMenu ); if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Female then begin BuildFileMenu( RPM , Graphics_Directory + 'cha_f_*.*' ); end else if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin BuildFileMenu( RPM , Graphics_Directory + 'cha_m_*.*' ); end else begin BuildFileMenu( RPM , Graphics_Directory + 'cha_*_*.*' ); end; AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); fname := SelectFile( RPM , Redrawer ); if fname <> '' then begin SetSAtt( M^.SA , 'SDL_SPRITE <' + fname + '>' ); end; DisposeRPGMenu( RPM ); end; {$ENDIF} Procedure AddRepairOptions( RPM: RPGMenuPtr; PC,Item: GearPtr ); { Check the object in question, then add options to the } { provided menu if the item is in need of repairs which the } { PC can provide. Repair items will be numbered 100 + RSN } var N: Integer; begin PC := LocatePilot( PC ); if PC <> Nil then begin for N := 1 to NumSkill do begin { The repair option will only be added to the menu if: } { - The PC has the required skill. } { - The item is in need of repair (using this skill). } if ( SkillMan[N].Usage = USAGE_Repair ) and ( NAttValue( PC^.NA , NAG_Skill , N ) > 0 ) and ( RepairNeededBySkill( Item , N ) > 0 ) then begin AddRPGMenuItem( RPM , MsgString( 'BACKPACK_Repair' ) + MSgString( 'SKILLNAME_' + BStr( N ) ) , 100 + N ); end; end; end; end; Function DoFieldRepair( GB: GameBoardPtr; PC , Item: GearPtr; Skill: Integer ): Boolean; { The PC is going to use one of the repair skills. Call the } { standard procedure, then print output. } { Return TRUE if repair fuel found, or FALSE otherwise. } var msg: String; Dmg0,DDmg,T: LongInt; SFX_Check: Array [1..Num_Status_FX] of Boolean; RepairFuelFound,NoStatusCured: Boolean; begin { Record the initial state of the repair target. } Dmg0 := AmountOfDamage( Item , True ); for t := 1 to Num_Status_FX do SFX_Check[ t ] := HasStatus( Item , T ); RepairFuelFound := UseRepairSkill( GB , PC , Item , Skill ); if NAttValue( PC^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam then begin msg := ReplaceHash( MsgString( 'PCREPAIR_UseSkill' ) , MsgString( 'SkillName_' + BStr( Skill ) ) ); end else begin msg := ReplaceHash( MsgString( 'NPCREPAIR_UseSkill' ) , PilotName( PC ) ); msg := ReplaceHash( msg , MsgString( 'SkillName_' + BStr( Skill ) ) ); end; msg := ReplaceHash( msg , GearName( Item ) ); { Report the final state of the repair target. } DDmg := Dmg0 - AmountOfDamage( Item , True ); { Inform the user of the success. } if ( Item^.G = GG_Character ) and Destroyed( Item ) then begin msg := msg + ' ' + ReplaceHash( MsgString( 'PCREPAIR_DEAD' ) , GearName( Item ) ); end else if not RepairFuelFound then begin msg := msg + ' ' + MsgString( 'PCREPAIR_NoRepairFuel' ); end else if DDmg > 0 then begin msg := msg + ' ' + ReplaceHash( MsgString( 'PCREPAIR_Success' ) , BStr( DDmg ) ); end; { Assume TRUE unless proven FALSE. } NoStatusCured := True; for t := 1 to Num_Status_FX do begin if SFX_Check[ t ] and not HasStatus( Item , T ) then begin { This status effect was cured. Add it to the list. } NoStatusCured := FALSE; end; end; { If no damage was healed and no status was cured, this was a big waste of time. } if ( DDMg = 0 ) and NoStatusCured then msg := msg + ' ' + MsgString( 'PCREPAIR_Failure' ) else if NotDestroyed( Item ) and not NoStatusCured then msg := msg + ' ' + ReplaceHash( MsgString( 'STATUS_Remove' ) , GearName( Item ) ); DialogMsg( msg ); DoFieldRepair := RepairFuelFound; end; Function ShakeDown( GB: GameBoardPtr; Part: GearPtr; X,Y: Integer ): LongInt; { This is the workhorse for this function. It does the } { dirty work of separating inventory from (former) owner. } var cash: LongInt; SPart: GearPtr; { Sub-Part } begin { Start by removing the cash from this part. } cash := NAttValue( Part^.NA , NAG_Experience , NAS_Credits ); SetNAtt( Part^.NA , NAG_Experience , NAS_Credits , 0 ); SetNAtt( Part^.NA , NAG_EpisodeData , NAS_Ransacked , 1 ); { Remove all InvComs, and place them on the map. } While Part^.InvCom <> Nil do begin SPart := Part^.InvCom; DelinkGear( Part^.InvCom , SPart ); { If this invcom isn't destroyed, put it on the } { ground for the PC to pick up. Otherwise delete it. } if NotDestroyed( SPart ) then begin SetNAtt( SPart^.NA , NAG_Location , NAS_X , X ); SetNAtt( SPart^.NA , NAG_Location , NAS_Y , Y ); SPart^.Next := GB^.Meks; GB^.Meks := SPart; end else begin DisposeGear( SPart ); end; end; { Shake down this gear's subcoms. } SPart := Part^.SubCOm; while SPart <> Nil do begin if SPart^.G <> GG_Cockpit then cash := cash + ShakeDown( GB , SPart , X , Y ); SPart := SPart^.Next; end; ShakeDown := Cash; end; Function Ransack( GB: GameBoardPtr; X,Y: Integer ): LongInt; { Yay! Loot and pillage! This function has two purposes: } { first, it separates all Inventory gears from any non-operational } { masters standing in this tile. Secondly, it collects the } { money from all those non-operational masters and returns the } { total amount as the function result. } var it: LongInt; Mek: GearPtr; begin it := 0; Mek := GB^.Meks; while Mek <> Nil do begin { If this is a broken-down master, check to see if it's } { one we want to pillage. } if IsMasterGear( Mek ) and not GearOperational( Mek ) then begin { We will ransack this gear if it's in the correct location. } if ( NAttValue( Mek^.NA , NAG_Location , NAS_X ) = X ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_Y ) = Y ) then begin it := it + ShakeDown( GB , Mek , X , Y ); end; end else if ( Mek^.G = GG_MetaTerrain ) and ( ( Mek^.Stat[ STAT_Lock ] = 0 ) or Destroyed( Mek ) ) then begin { Metaterrain gets ransacked if it's unlocked, } { or wrecked. } if ( NAttValue( Mek^.NA , NAG_Location , NAS_X ) = X ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_Y ) = Y ) then begin it := it + ShakeDown( GB , Mek , X , Y ); end; end; Mek := Mek^.Next; end; Ransack := it; end; Function Handless( Mek: GearPtr ): Boolean; { Return TRUE if Mek either has no hands or can't use its hands } { at the moment (say, because it's transformed into tank mode). } { Return TRUE if Mek has hands and they are in perfect working order. } var Hand: GearPtr; begin Hand := SeekActiveIntrinsic( Mek , GG_Holder , GS_Hand ); if Hand = Nil then Handless := True else Handless := not InGoodModule( Hand ); end; Function SelectVisibleItem( GB: GameBoardPtr; PC: GearPtr; X,Y: Integer ): GearPtr; { Attempt to select a visible item from gameboard tile X,Y. } { If more than one item is present, prompt the user for which one } { to pick up. } var N,T: Integer; RPM: RPGMenuPtr; begin { First count the number of items in this spot. } N := NumVisibleItemsAtSpot( GB , X , Y ); { If it's just 0 or 1, then our job is simple... } if N = 0 then begin SelectVisibleItem := Nil; end else if N = 1 then begin SelectVisibleItem := FindVisibleItemAtSpot( GB , X , Y ); { If it's more than one, better create a menu and let the user } { pick one. } end else if N > 1 then begin DialogMsg( MsgString( 'GET_WHICH_ITEM?' ) ); RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_GetItemMenu ); for t := 1 to N do begin AddRPGMenuItem( RPM , GearName( GetVisibleItemAtSpot( GB , X , Y , T ) ) , T ); end; BP_GB := GB; N := SelectMenu( RPM , @GetItemRedraw ); DisposeRPGMenu( RPM ); if N > -1 then begin SelectVisibleItem := GetVisibleItemAtSpot( GB , X , Y , N ); end else begin SelectVisibleItem := Nil; end; end; end; Procedure PCGetItem( GB: GameBoardPtr; PC: GearPtr ); { The PC will attempt to pick up something lying on the ground. } var Cash,NID: LongInt; P: Point; item: GearPtr; IsHandless: Boolean; begin IsHandless := Handless( PC ); if IsHandless and not IsSafeArea( GB ) then begin { Start by checking something that other RPGs would } { just assume- does the PC have any hands? } DialogMsg( MsgString( 'HANDLESS_PICKUP' ) ); end else begin P := GearCurrentLocation( PC ); { Before attempting to get an item, ransack whatever } { fallen enemies lie in this spot. } Cash := Ransack( GB , P.X , P.Y ); { Perform an immediate vision check- without it, items } { freed by the Ransack procedure above will remain unseen. } VisionCheck( GB , PC ); Item := SelectVisibleItem( GB , PC , P.X , P.Y ); if Item <> Nil then begin if IsLegalInvCom( PC , Item ) then begin DelinkGear( GB^.Meks , Item ); { Clear the item's location values. } StripNAtt( Item , NAG_Location ); InsertInvCom( PC , Item ); { Clear the home, to prevent wandering items. } SetSAtt( Item^.SA , 'HOME <>' ); if ( PC^.G = GG_Mecha ) and IsHandless then begin DialogMsg( ReplaceHash( MsgString( 'YOU_STRAP_?' ) , GearName( Item ) ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'YOU_GET_?' ) , GearName( Item ) ) ); end; NID := NAttValue( Item^.NA , NAG_Narrative , NAS_NID ); if NID <> 0 then SetTrigger( GB , TRIGGER_GetItem + BStr( NID ) ); end else if Cash = 0 then begin DialogMsg( ReplaceHash( MsgString( 'CANT_GET_?' ) , GearName( Item ) ) ); end; end else if Cash = 0 then begin DialogMSG( 'No item found.' ); end; if Cash > 0 then begin DialogMsg( ReplaceHash( MsgString( 'YouFind$' ) , BStr( Cash ) ) ); AddNAtt( LocatePilot( PC )^.NA , NAG_Experience , NAS_Credits , Cash ); end; { Picking up an item takes time. } { More time if you're doing it without hands. } if IsHandless then begin WaitAMinute( GB , PC , ReactionTime( PC ) * 3 ); end else begin WaitAMinute( GB , PC , ReactionTime( PC ) ); end; end; end; Procedure PCTradeItems( GB: GameBoardPtr; PC,Target: GearPtr ); { The PC will attempt to trade items with TARGET. } Procedure SetupMenu( RPM: RPGMenuPtr; M: GearPtr ); { The setup procedure for both menus is the same, so here } { it is. } begin BuildSiblingMenu( RPM , M^.InvCom ); RPMSortAlpha( RPM ); { If the menu is empty, add a message saying so. } If RPM^.NumItem < 1 then AddRPGMenuItem( RPM , '[no inventory items]' , -1 ) else AlphaKeyMenu( RPM ); { Add the menu keys. } AddRPGMenuKey(RPM,'/',-2); end; Procedure TransferItem( Source , Item , Destination: GearPtr ); { If possible, move ITEM from SOURCE to DESTINATION. } var NID: LongInt; begin if IsLegalInvCom( Destination , Item ) then begin { Clear the item's location values. } StripNAtt( Item , NAG_Location ); DelinkGear( Source^.InvCom , Item ); InsertInvCom( Destination , Item ); { Clear the home, to prevent wandering items. } SetSAtt( Item^.SA , 'HOME <>' ); if Destination = PC then begin DialogMsg( ReplaceHash( MsgString( 'YOU_GET_?' ) , GearName( Item ) ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'YOU_PUT_?' ) , GearName( Item ) ) ); end; NID := NAttValue( Item^.NA , NAG_Narrative , NAS_NID ); if NID <> 0 then SetTrigger( GB , TRIGGER_GetItem + BStr( NID ) ); if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'CANT_GET_?' ) , GearName( Item ) ) ); end; end; var item: GearPtr; PCMenu,TarMenu: RPGMenuPtr; EscMenu,UseTarInv: Boolean; N: Integer; begin { Error check. } { PC and Target must be non-nil; they must also be separate entities, or else } { weird things will result. } if ( PC = Nil ) or ( Target = Nil ) or ( FindRoot( PC ) = FindRoot( Target ) ) then Exit; { Initialize variables. } BP_GB := GB; UseTarInv := True; { Keep going back and forth between the PC and the target until } { the player hits ESCape. } EscMenu := False; repeat { Create the two menus. } PCMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_EqpMenu ); MenuA := PCMenu; SetupMenu( PCMenu , PC ); TarMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); MenuB := TarMenu; SetupMenu( TarMenu , Target ); { Determine which of the two menus is going to be our active one, } { based on the UseTarInv variable. } if UseTarInv then begin BP_ActiveMenu := TarMenu; BP_Source := Target; end else begin BP_ActiveMenu := PCMenu; BP_Source := PC; end; N := SelectMenu( BP_ActiveMenu , @TradeItemsRedraw ); if N > 0 then begin { An item has been selected. Find it, then attempt to swap from } { target to PC or vice versa. } if UseTarInv then begin Item := RetrieveGearSib( Target^.InvCom , N ); TransferItem( Target , Item , PC ); end else begin Item := RetrieveGearSib( PC^.InvCom , N ); TransferItem( PC , Item , Target ); end; end else if N = -1 then begin { An Escape has been recieved. Quit this procedure. } EscMenu := True; end else if N = -2 then begin { A menu swap has been requested. Change the active menu. } UseTarInv := Not UseTarInv; end; { Dispose the two menus. } DisposeRPGMenu( PCMenu ); DisposeRPGMenu( TarMenu ); until EscMenu; end; Procedure CreateInvMenu( PC: GearPtr ); { Allocate the Inventory menu and fill it up with the PC's inventory. } begin if InvRPM <> Nil then DisposeRPGMenu( InvRPM ); InvRPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); InvRPM^.Mode := RPMNoCleanup; BuildInventoryMenu( InvRPM , PC , False ); RPMSortAlpha( InvRPM ); { If the menu is empty, add a message saying so. } If InvRPM^.NumItem < 1 then AddRPGMenuItem( InvRPM , '[no inventory items]' , -1 ) else AlphaKeyMenu( InvRPM ); { Add the menu keys. } AddRPGMenuKey(InvRPM,'/',-2); end; Procedure CreateEqpMenu( PC: GearPtr ); { Allocate the equipment menu and fill it up with the PC's gear. } begin if EqpRPM <> Nil then DisposeRPGMenu( EqpRPM ); EqpRPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_EqpMenu ); EqpRPM^.Mode := RPMNoCleanup; BuildEquipmentMenu( EqpRPM , PC ); { If the menu is empty, add a message saying so. } If EqpRPM^.NumItem < 1 then AddRPGMenuItem( EqpRPM , '[no equipped items]' , -1 ); { Add the menu keys. } AddRPGMenuKey(EqpRPM,'/',-2); end; Procedure UpdateBackpack( PC: GearPtr ); { Redo all the menus, and display them on the screen. } begin CreateInvMenu( PC ); CreateEqpMenu( PC ); end; Procedure UnequipItem( GB: GameBoardPtr; var LList: GearPtr; PC , Item: GearPtr ); { Delink ITEM from its parent, and stick it in the general inventory. } begin { First, delink Item from its parent. } DelinkGear( Item^.Parent^.InvCom , Item ); { HOW'D YA LIKE THEM CARROT DOTS, EH!?!? } { Next, link ITEM into the general inventory, if possible. } GivePartToPC( LList , Item , PC ); { Unequipping takes time. } if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Procedure UnequipFrontend( GB: GameBoardPtr; var LList: GearPtr; PC , Item: GearPtr ); { Simply unequip the provided item. } { PRECOND: PC and ITEM had better be correct, dagnabbit... } begin DialogMsg( ReplaceHash( MsgString( 'BACKPACK_Do_Unequip' ) , GearName( Item ) ) ); UnequipItem( GB , LList , PC , Item ); end; Procedure EjectAmmo( GB: GameBoardPtr; var LList: GearPtr; PC , Item: GearPtr ); { Remove all ammo from this item. } Procedure RemoveThisAmmo( Ammo: GearPtr ); { Remove ammo from wherever it is, then give it to the PC. } { Ammo must be a subcom!!! } begin { First, delink Ammo from its parent. } DelinkGear( Ammo^.Parent^.SubCom , Ammo ); { Next, link Ammo into the general inventory, if possible. } GivePartToPC( LList , Ammo , PC ); DialogMsg( ReplaceHash( MsgString( 'BACKPACK_Do_EjectAmmo' ) , GearName( Ammo ) ) ); end; Procedure CheckForAmmoToEject( IList: GearPtr ); { Check this list for ammo to eject, and also all subcoms. } var I2: GearPtr; begin while IList <> Nil do begin I2 := IList^.Next; if IList^.G = GG_Ammo then begin RemoveThisAmmo( IList ); end else begin CheckForAmmoToEject( IList^.SubCom ); end; IList := I2; end; end; begin { Start the search going. } CheckForAmmoToEject( Item^.SubCom ); { Unequipping takes time. } if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Procedure EjectSoftware( GB: GameBoardPtr; var LList: GearPtr; PC , Item: GearPtr ); { Remove all software from this item. } Procedure RemoveThisSoftware( Soft: GearPtr ); { Remove Soft from wherever it is, then give it to the PC. } { Soft must be a subcom!!! } begin { First, delink Soft from its parent. } DelinkGear( Soft^.Parent^.SubCom , Soft ); { Next, link Ammo into the general inventory, if possible. } GivePartToPC( LList , Soft , PC ); DialogMsg( ReplaceHash( MsgString( 'BACKPACK_Do_EjectSoftware' ) , GearName( Soft ) ) ); end; Procedure CheckForSoftwareToEject( IList: GearPtr ); { Check this list for software to eject, and also all subcoms. } var I2: GearPtr; begin while IList <> Nil do begin I2 := IList^.Next; if IList^.G = GG_Software then begin RemoveThisSoftware( IList ); end else begin CheckForSoftwareToEject( IList^.SubCom ); end; IList := I2; end; end; begin { Start the search going. } CheckForSoftwareToEject( Item^.SubCom ); { Unequipping takes time. } if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Function CanBeExtracted( Item: GearPtr ): Boolean; { Return TRUE if the listed part can be extracted from a mecha, } { or FALSE if it cannot normally be extracted. } begin if ( Item^.G = GG_Support ) or ( Item^.G = GG_Cockpit ) or IsMasterGear( Item ) or ( Item^.Parent = Nil ) or ( Item^.Parent^.Scale = 0 ) or ( Item^.G = GG_Modifier ) then begin CanBeExtracted := False; end else if ( Item^.G = GG_Module ) and ( Item^.S = GS_Body ) then begin CanBeExtracted := False; end else if Item^.G = GG_SOftware then begin { Software can't be extracted; it must be uninstalled. } CanBeExtracted := False; end else if SeekGear( Item , GG_Cockpit , 0 , False ) <> Nil then begin { If the item contains the cockpit, it can't be extracted. } CanBeExtracted := False; end else begin CanBeExtracted := Not PartHasIntrinsic( Item , NAS_Integral ); end; end; Function CanBeInstalled( Item: GearPtr ): Boolean; { Return TRUE if the listed part can maybe be installed in a mecha } { with the Mecha Engineering skill, or FALSE if it definitely can't. } begin if ( Item = Nil ) or ( Item^.G = GG_Ammo ) or ( Item^.G = GG_Shield ) or ( Item^.G = GG_ExArmor ) or ( Item^.G = GG_Tool ) or ( Item^.G = GG_RepairFuel ) or ( Item^.G = GG_Consumable ) or ( Item^.G = GG_WeaponAddOn ) or ( Item^.G = GG_Software ) then begin CanBeInstalled := False; end else begin CanBeInstalled := True; end; end; Procedure ExtractMechaPart( var LList,Item: GearPtr ); { Remove this part from the mecha. } begin DelinkGear( LList , Item ); { If this is a variable form module, assume the primary form. } if ( Item^.G = GG_Module ) and ( Item^.Stat[ STAT_VariableModuleForm ] <> 0 ) then begin Item^.S := Item^.Stat[ STAT_PrimaryModuleForm ]; end; end; Function ExtractItem( GB: GameBoardPtr; TruePC , PC: GearPtr; var Item: GearPtr ): Boolean; { As of GH2 all attempts to extract an item will be successful. } { The only question is whether the part will be destroyed in the process, } { or whether some other bad effect will take place. } { Note that pulling a gear out of its mecha may well wreck it } { beyond any repair! Therefore, after this call, ITEM might no } { longer exist... i.e. it may equal NIL. } { This function returns TRUE if the item is okay, or FALSE if it was destroyed. } var it: Boolean; SkRoll,WreckTarget: Integer; Slot: GearPtr; begin Slot := Item^.Parent; { First, calculate the skill target. } if Item^.G = GG_Module then begin WreckTarget := 6; if Slot^.G = GG_Mecha then WreckTarget := WreckTarget + Slot^.V - Item^.V; end else begin WreckTarget := ComponentComplexity( Item ) + 10 - UnscaledMaxDamage( Item ); WreckTarget := WreckTarget + SubComComplexity( Slot ) - ComponentComplexity( Slot ); end; if WreckTarget < 5 then WreckTarget := 5; SkRoll := SkillRoll( GB , TruePC , NAS_MechaEngineering , STAT_Knowledge , WreckTarget , 0 , True , True ); if GB <> Nil then begin AddMentalDown( TruePC , 1 ); WaitAMinute( GB , TruePC , ReactionTime( TruePC ) * 5 ); end; { Decide whether to extract the part and keep it, or just wreck it trying } { to get it out. } if SkRoll > WreckTarget then begin { First, delink Item from its parent. } ExtractMechaPart( Item^.Parent^.SubCom , Item ); { Try to stick as invcom of parent. } GivePartToPC( GB , Item , PC ); it := True; end else begin RemoveGear( Item^.Parent^.SubCom , Item ); Item := Nil; it := False; end; ExtractItem := it; end; Procedure ExtractFrontend( GB: GameBoardPtr; TruePC , PC , Item: GearPtr ); { Simply remove the provided item. } { PRECOND: PC and ITEM had better be correct, dagnabbit... } var name: String; begin name := GearName( Item ); if GearActive( PC ) then begin DialogMsg( MsgString( 'EXTRACT_NOTACTIVE' ) ); end else begin if ExtractItem( GB , TruePC , PC , Item ) then begin DialogMsg( ReplaceHash( MsgString( 'EXTRACT_OK' ) , name ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'EXTRACT_WRECK' ) , name ) ); end; end; end; Procedure EquipItem( GB: GameBoardPtr; var LList: GearPtr; PC , Slot , Item: GearPtr ); { This is the real equipping procedure. Stuff ITEM into SLOT. } { As noted in TheRules.txt, any nonmaster gear can only have one } { item of any particular "G" type equipped at a time. So, if } { SLOT already has equipment of type ITEM^.G, unequip that and } { stuff it into PC's general inventory. } var I2,I3: GearPtr; begin { First, check for already equipped items. } I2 := Slot^.InvCom; while I2 <> Nil do begin I3 := I2^.Next; { This next step might delink I2, so... } if ( I2^.G = Item^.G ) or ( Slot^.G = GG_Holder ) then begin UnequipItem( GB , LList , PC , I2 ); end; I2 := I3; end; { Next, delink Item from PC. } DelinkGear( PC^.InvCom , Item ); { Next, link ITEM into SLOT. } InsertInvCom( Slot , Item ); { Equipping an item takes time. } if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Procedure EquipItemFrontend( GB: GameBoardPtr; var LList: GearPtr; PC , Item: GearPtr ); { Assign ITEM to a legal equipment slot. Move it from the } { general inventory into its new home. } var EI_Menu: RPGMenuPtr; N: Integer; W,L: Integer; begin { Build the slot selection menu. } EI_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); BuildSlotMenu( EI_Menu , PC , Item ); AlphaKeyMenu( EI_Menu ); if EI_Menu^.NumItem < 1 then AddRPGMenuItem( EI_Menu , ReplaceHash( MsgString( 'BACKPACK_CantEquip' ) , GearName( Item ) ) , -1 ); { Select a slot for the item to go into. } BP_Source := Item; N := SelectMenu( EI_Menu , @ThisItemRedraw); DisposeRPGMenu( EI_Menu ); { If a slot was selected, pass that info on to the workhorse. } if N <> -1 then begin DialogMsg( ReplaceHash( MsgString( 'BACKPACK_Do_Equip' ) , GearName( Item ) ) ); EquipItem( GB , LList , PC , LocateGearByNumber( PC , N ) , Item ); if Item^.G = GG_Weapon then begin W := Firing_Weight( Item , DefaultAtOp( Item ) ); L := Firing_Weight_Limit( PC ); if W > ( L * 3 ) then begin DialogMsg( MsgString( 'BACKPACK_Weapon_TooHeavy' ) ); end else if W > L then begin DialogMsg( MsgString( 'BACKPACK_Weapon_Need2Hands' ) ); end; end; end; end; Function InstallItem( GB: GameBoardPtr; TruePC , Slot: GearPtr; var Item: GearPtr ): Boolean; { Attempt the skill rolls needed to install ITEM into the } { requested slot. } { ITEM should be linked as an inventory gear, and may be deleted by this function. } { Return TRUE if the install was successful, or FALSE otherwise. } Procedure ResortInventory( Part: GearPtr ); { Take all the inventory from PART and move it to FindMaster(Slot) } var M,I: GearPtr; begin M := FindMaster( Slot ); if M <> Nil then begin while Part^.InvCom <> Nil do begin I := Part^.InvCom; DelinkGear( Part^.InvCom , I ); InsertInvCom( M , I ); end; end else begin DisposeGear( Part^.InvCom ); end; end; Procedure ShakeDownItem( LList: GearPtr ); { Remove all InvComs from this list, and for all children as well. } begin while LList <> Nil do begin if LList^.InvCom <> Nil then ResortInventory( LList ); ShakeDownItem( LList^.SubCom ); LList := LList^.Next; end; end; var SlotCom,ItemCom,UsedCom,HardLimit: Integer; WreckTarget,SkRoll: Integer; begin { Error Check - no circular references! } if ( FindGearIndex( Item , Slot ) <> -1 ) then begin DialogMsg( ReplaceHash( MsgString( 'INSTALL_FAIL' ) , GearName( Item ) ) ); Exit( False ); end; { Also, can't engineer things when you're exhausted. } if IsMasterGear( TruePC ) and ( CurrentMental( TruePC ) < 1 ) then begin DialogMsg( ReplaceHash( MsgString( 'INSTALL_FAIL' ) , GearName( Item ) ) ); Exit( False ); end; { Can't install into a personal-scale slot. } if Slot^.Scale = 0 then begin DialogMsg( ReplaceHash( MsgString( 'INSTALL_FAIL' ) , GearName( Item ) ) ); Exit( False ); end; SlotCom := ComponentComplexity( Slot ); HardLimit := SlotCom; ItemCom := ComponentComplexity( Item ); UsedCom := SubComComplexity( Slot ); { If the INNOVATION talent is known, increase the HardLimit. } if GB <> Nil then begin if TeamHasTalent( GB , NAV_DefPlayerTeam , NAS_Innovation ) then HardLimit := HardLimit + 5; end else begin if HasTalent( TruePC , NAS_Innovation ) then HardLimit := HardLimit + 5; end; if (( UsedCom + ItemCom ) > HardLimit ) and not IsMasterGear( Slot ) then begin DialogMsg( MSgString( 'INSTALL_NoRoom' ) ); Exit( False ); end; { The WreckTarget is the target number that must be beat } { in order to avoid accidentally destroying the part... } if ( Item^.G = GG_Module ) then begin WreckTarget := 3 + NumSiblingGears( Slot^.SubCom ) - Item^.V; end else if Item^.G = GG_MoveSys then begin WreckTarget := 5 + ItemCom; end else if ( Item^.G = GG_Modifier ) then begin WreckTarget := 10 + ItemCom; end else if ( UnscaledMaxDamage( Item ) < 1 ) or ( Item^.Scale < Slot^.Scale ) then begin WreckTarget := 10; end else begin WreckTarget := 10 - UnscaledMaxDamage( Item ); end; { The insertion target is easier for heavy items, harder for lightened ones. } WreckTarget := WreckTarget - NAttValue( Item^.NA , NAG_GearOps , NAS_MassAdjust ); if WreckTarget < 3 then WreckTarget := 3; { If the SLOT is going to be overstuffed, better raise the } { number of successes and the target number drastically. } if ( ( ItemCom + UsedCom ) > SlotCom ) and ( Not IsMasterGear( Slot ) ) then begin WreckTarget := WreckTarget + 2 + UsedCom + ItemCom - SlotCom + ItemCom * 5 div SlotCom; end; if GB <> Nil then WaitAMinute( GB , TruePC , ReactionTime( TruePC ) * 5 ); SkRoll := SkillRoll( GB , TruePC , NAS_MechaEngineering , STAT_Knowledge , WreckTarget , 0 , True , True ); if SkRoll >= WreckTarget then begin DialogMsg( ReplaceHash( MsgString( 'INSTALL_OK' ) , GearName( Item ) ) ); { Install the item. } ResortInventory( Item ); ShakeDownItem( Item^.SubCom ); DelinkGear( Item^.Parent^.InvCom , Item ); InsertSubCom( Slot , Item ); end else begin DialogMsg( ReplaceHash( MsgString( 'INSTALL_WRECK' ) , GearName( Item ) ) ); RemoveGear( Item^.Parent^.InvCom , Item ); Item := Nil; end; if ( GB <> Nil ) and IsMasterGear( TruePC ) then begin AddMentalDown( TruePC , 1 ); end; InstallItem := SkRoll >= WreckTarget; end; Procedure InstallFrontend( GB: GameBoardPtr; TruePC , PC , Item: GearPtr ); { Assign ITEM to a legal equipment slot. Move it from the } { general inventory into its new home. } var EI_Menu: RPGMenuPtr; N: Integer; begin { Error check- can't install into an active master. } if GearActive( PC ) then begin DialogMsg( MsgString( 'INSTALL_NOTACTIVE' ) ); Exit; end; { Build the slot selection menu. } EI_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); BuildSubMenu( EI_Menu , PC , Item , True ); if EI_Menu^.NumItem < 1 then AddRPGMenuItem( EI_Menu , '[cannot install ' + GearName( Item ) + ']' , -1 ); { Select a slot for the item to go into. } DialogMsg( ReplaceHash( MsgSTring( 'BACKPACK_InstallInfo' ) , GearName( Item ) ) ); BP_Source := Item; N := SelectMenu( EI_Menu , @InstallRedraw); DisposeRPGMenu( EI_Menu ); { If a slot was selected, pass that info on to the workhorse. } if N <> -1 then begin { Store the name here, since the item might get destroyed } { during the installation process. } InstallItem( GB , TruePC , LocateGearByNumber( PC , N ) , Item ); end; end; Procedure InstallAmmo( GB: GameBoardPtr; PC , Gun , Ammo: GearPtr ); { Place the ammunition gear into the gun. } var A,A2: GearPtr; begin { To start with, unload any ammo currently in the gun. } A := Gun^.SubCom; while A <> Nil do begin A2 := A^.Next; if A^.G = GG_Ammo then begin DelinkGear( Gun^.SubCom , A ); InsertInvCom( PC , A ); end; A := A2; end; { Delink the magazine from wherever it currently resides. } if IsInvCom( Ammo ) then begin DelinkGear( Ammo^.Parent^.InvCom , Ammo ); end else if IsSubCom( Ammo ) then begin DelinkGear( Ammo^.Parent^.SubCom , Ammo ); end; { Stick the new magazine into the gun. } InsertSubCom( Gun , Ammo ); { Loading a gun takes time. } if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Procedure InstallAmmoFrontend( GB: GameBoardPtr; PC , Item: GearPtr ); { Assign ITEM to a legal projectile weapon. Move it from the } { general inventory into its new home. } var IA_Menu: RPGMenuPtr; Gun: GearPtr; N: Integer; begin { Build the slot selection menu. } IA_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); BuildSubMenu( IA_Menu , PC , Item , False ); if IA_Menu^.NumItem < 1 then AddRPGMenuItem( IA_Menu , '[no weapon for ' + GearName( Item ) + ']' , -1 ); { Select a slot for the item to go into. } N := SelectMenu( IA_Menu , @MiscProcRedraw); DisposeRPGMenu( IA_Menu ); { If a slot was selected, pass that info on to the workhorse. } if N <> -1 then begin Gun := LocateGearByNumber( PC , N ); DialogMsg( 'You load ' + GearName( Item ) + ' into ' + GearName( Gun ) + '.' ); InstallAmmo( GB , PC , Gun , Item ); end; end; Procedure DropFrontEnd( GB: GameBoardPtr; var LList: GearPtr; PC , Item: GearPtr ); { How to drop an item: Make sure PC is a root-level gear. } { Delink ITEM from its current location. } { Copy PC's location variables to ITEM. } { Install ITEM as the next sibling of PC. } begin { Make sure PC is at root level... } PC := FindRoot( PC ); { Delink ITEM from its parent... } DelinkGear( Item^.Parent^.InvCom , Item ); { Copy the location variables to ITEM... } SetNAtt( Item^.NA , NAG_Location , NAS_X , NAttValue( PC^.NA , NAG_Location , NAS_X ) ); SetNAtt( Item^.NA , NAG_Location , NAS_Y , NAttValue( PC^.NA , NAG_Location , NAS_Y ) ); if ( GB <> Nil ) and not OnTheMap( GB , PC ) then SetNAtt( Item^.NA , NAG_Location , NAS_Team , NAV_DefPlayerTeam ); { Install ITEM into LList... } AppendGear( LList , Item ); { Do display stuff. } DialogMsg( ReplaceHash( MsgString( 'BACKPACK_Do_Drop' ) , GearName( Item ) ) ); end; Procedure TradeFrontend( GB: GameBoardPtr; PC , Item, LList: GearPtr ); { Assign ITEM to a different master. Move it from the } { general inventory of PC into its new home. } const Unsafe_Transfer_Range = 10; var TI_Menu: RPGMenuPtr; M: GearPtr; Team,N: Integer; X,Y: Integer; Function Transferable_To ( Dest: GearPtr ) : Boolean; var DTeam, DX, DY : Integer; begin If Dest = PC then Exit(False); { Team check. This could probably be simplified -- How could the source Master's team be other than DefPlayer or Lancemate anyway? } DTeam := NAttValue( Dest^.NA , NAG_Location , NAS_Team ); if DTeam = NAV_LancemateTeam then DTeam := NAV_DefPlayerTeam; if DTeam <> Team then Exit(False); if X = 0 then Exit(True); {safe area case} {we're now in the unsafe area case, check for adjacency} DX := NAttValue( Dest^.NA, NAG_Location, NAS_X); DY := NAttValue( Dest^.NA, NAG_Location, NAS_Y); if not OnTheMap(GB,DX,DY) then Exit(False); {cannot transfer to offmap stuff} Transferable_To := ( Range(X,Y,DX,DY) <= Unsafe_Transfer_Range ); end; begin if ( GB = Nil ) or IsSafeArea( GB ) then begin X := 0; Y := 0; end else begin DialogMsg( MsgString( 'TRANSFER_UNSAFE' ) ); X := NAttValue(PC^.NA, NAG_Location, NAS_X); Y := NAttValue(PC^.NA, NAG_Location, NAS_Y); end; { Build the slot selection menu. } TI_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); N := 1; M := LList; Team := NAttValue( PC^.NA , NAG_Location , NAS_Team ); if Team = NAV_LancemateTeam then Team := NAV_DefPlayerTeam; { This menu should contain all the masters from LList which } { belong to Team 1. } while M <> Nil do begin if ( GB = Nil ) and IsMasterGear( M ) and ( M <> PC ) then begin AddRPGMenuItem( TI_Menu , TeamMateName( M ) , N ); end else if IsMasterGear( M ) and Transferable_To(M) then begin AddRPGMenuItem( TI_Menu , GearName( M ) , N ); end; M := M^.Next; Inc( N ); end; RPMSortAlpha( TI_Menu ); AlphaKeyMenu( TI_Menu ); if TI_Menu^.NumItem < 1 then AddRPGMenuItem( TI_Menu , MsgString( 'CANCEL' ) , -1 ); { Select a slot for the item to go into. } BP_Source := LList; BP_SeekSibs := True; BP_ActiveMenu := TI_Menu; DialogMSG( ReplaceHash( MsgString( 'FHQ_SelectDestination' ), GearName( Item ) ) ); N := SelectMenu( TI_Menu , @MiscProcRedraw); DisposeRPGMenu( TI_Menu ); { If a slot was selected, pass that info on to the workhorse. } if N <> -1 then begin M := RetrieveGearSib( LList , N ); if IsLegalInvCom( M , Item ) then begin DelinkGear( Item^.Parent^.InvCom , Item ); InsertInvCom( M , Item ); DialogMsg( MsgString( 'BACKPACK_ItemTraded' ) ); end else begin DialogMsg( MsgString( 'BACKPACK_NotTraded' ) ); end; end; end; Procedure FHQ_AssociatePilotMek( PC , M , LList: GearPtr ); { Associate the mecha with the pilot. } begin AssociatePilotMek( LList , PC , M ); DialogMsg( ReplaceHash( MsgString( 'FHQ_AssociatePM' ) , GearName( PC ) ) ); end; Procedure FHQ_AssociateRedraw; { Do a redraw for the Field HQ. } var Part: GearPtr; begin CombatDisplay( BP_GB ); SetupFHQDisplay; if ( BP_ActiveMenu <> Nil ) and ( BP_Source <> Nil ) then begin Part := RetrieveGearSib( BP_Source , CurrentMenuItemValue( BP_ActiveMenu ) ); if Part <> Nil then begin BrowserInterfaceInfo( BP_GB , Part , ZONE_ItemsInfo ); end; end; end; Procedure FHQ_SelectPilotForMecha( GB: GameBoardPtr; Mek: GearPtr ); { Select a pilot for the mecha in question. } { Pilots must be characters- they must either belong to the default } { player team or, if they're lancemates, they must have a CID. } { This is to prevent the PC from dominating some sewer rats and } { training them to be pilots. } var RPM: RPGMenuPtr; N: Integer; M: GearPtr; begin BP_GB := GB; BP_Source := GB^.Meks; DialogMsg( ReplaceHash( MsgString( 'FHQ_SP4M_Prompt' ) , FullGearName( Mek ) ) ); { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); BP_ActiveMenu := RPM; M := GB^.Meks; N := 1; while M <> Nil do begin if M^.G = GG_Character then begin if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and ( NAttValue( M^.NA , NAG_Personal , NAS_CID ) <> 0 ) then begin AddRPGMenuItem( RPM , GearName( M ) , N ); end else if NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam then begin AddRPGMenuItem( RPM , GearName( M ) , N ); end; end; M := M^.Next; Inc( N ); end; RPMSortAlpha( RPM ); AddRPGMenuItem( RPM , MSgString( 'EXIT' ) , -1 ); { Get a selection from the menu. } n := SelectMenu( RPM , @FHQ_AssociateRedraw ); DisposeRPGMenu( RPM ); if N > 0 then begin M := RetrieveGearSib( GB^.Meks , N ); FHQ_AssociatePilotMek( M , Mek , GB^.Meks ); end; end; Procedure FHQ_SelectMechaForPilot( GB: GameBoardPtr; NPC: GearPtr ); { Select a pilot for the mecha in question. } { Pilots must be characters- they must either belong to the default } { player team or, if they're lancemates, they must have a CID. } { This is to prevent the PC from dominating some sewer rats and } { training them to be pilots. } var RPM: RPGMenuPtr; N: Integer; M: GearPtr; begin BP_GB := GB; BP_Source := GB^.Meks; DialogMsg( ReplaceHash( MsgString( 'FHQ_SM4P_Prompt' ) , GearName( NPC ) ) ); { Error check- only characters can pilot mecha! Pets can't. } if ( NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) = 0 ) and ( NAttValue( NPC^.NA , NAG_Location , NAS_Team ) <> NAV_DefPlayerTeam ) then begin DialogMsg( ReplaceHash( MsgString( 'FHQ_SMFP_NoPets' ) , GearName( NPC ) ) ); Exit; end; { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); BP_ActiveMenu := RPM; M := GB^.Meks; N := 1; while M <> Nil do begin if ( M^.G = GG_Mecha ) and ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then begin AddRPGMenuItem( RPM , GearName( M ) , N ); end; M := M^.Next; Inc( N ); end; RPMSortAlpha( RPM ); AddRPGMenuItem( RPM , MSgString( 'EXIT' ) , -1 ); { Get a selection from the menu. } n := SelectMenu( RPM , @FHQ_AssociateRedraw ); DisposeRPGMenu( RPM ); if N > 0 then begin M := RetrieveGearSib( GB^.Meks , N ); FHQ_AssociatePilotMek( NPC , M , GB^.Meks ); end; end; Procedure UseScriptItem( GB: GameBoardPtr; TruePC, Item: GearPtr; T: String ); { This item has a script effect. Exit the backpack and use it. } begin if SAttValue( Item^.SA , T ) <> '' then begin { Announce the intention. } DialogMsg( ReplaceHash( MsgString( 'BACKPACK_Script_' + T ) , GearName( Item ) ) ); { Using items takes time... } WaitAMinute( GB , TruePC , ReactionTime( TruePC ) ); { ...and also exits the backpack. } ForceQuit := True; CombatDisplay( GB ); { Finally, trigger the script. } TriggerGearScript( GB , Item , T ); end else begin { Announce the lack of a valid script. } DialogMsg( ReplaceHash( MsgString( 'BACKPACK_CannotUseScript' ) , GearName( Item ) ) ); end; end; Procedure UseSkillOnItem( GB: GameBoardPtr; TruePC, Item: GearPtr ); { The PC will have the option to use a CLUE-type skill on this } { item, maybe to gain some new information, activate an effect, } { or whatever else. } var SkMenu: RPGMenuPtr; T: Integer; msg: String; begin SkMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); { Add the usable skills. } for t := 1 to NumSkill do begin { In order to be usable, it must be a CLUE type skill, } { and the PC must have ranks in it. } if ( SkillMan[ T ].Usage = USAGE_Clue ) and ( TeamHasSkill( GB , NAV_DefPlayerTeam , T ) or TeamHasTalent( GB , NAV_DefPlayerTeam , NAS_JackOfAll ) ) then begin msg := ReplaceHash( MsgString( 'BACKPACK_ClueSkillPrompt' ) , MsgString( 'SKILLNAME_' + BStr( T ) ) ); msg := ReplaceHash( msg , GearName( Item ) ); AddRPGMenuItem( SkMenu , msg , T ); end; end; RPMSortAlpha( SkMenu ); AddRPGMenuItem( SkMenu , MsgSTring( 'BACKPACK_CancelSkillUse' ) , -1 ); BP_GB := GB; BP_Source := Item; T := SelectMenu( SkMenu , @ThisItemRedraw); DisposeRPGMenu( SkMenu ); if T <> -1 then begin UseScriptItem( GB , TruePC , Item , Skill_Use_Trigger[ T ] ); end; end; Procedure EatItem( GB: GameBoardPtr; TruePC , Item: GearPtr ); { The PC wants to eat this item. Give it a try. } var effect: String; begin TruePC := LocatePilot( TruePC ); if TruePC = Nil then begin DialogMsg( ReplaceHash( MsgString( 'BACKPACK_CantBeEaten' ) , GearName( Item ) ) ); end else if ( NAttValue( TruePC^.NA , NAG_Condition , NAS_Hunger ) > ( Item^.V div 2 ) ) or ( Item^.V = 0 ) then begin { Show a message. } DialogMsg( ReplaceHash( ReplaceHash( MsgString( 'BACKPACK_YouAreEating' ) , GearName( TruePC ) ) , GearName( Item ) ) ); { Eating takes time... } WaitAMinute( GB , TruePC , ReactionTime( TruePC ) * GearMass( Item ) + 1 ); { ...and also exits the backpack. } ForceQuit := True; { Locate the PC's Character record, then adjust hunger values. } AddNAtt( TruePC^.NA , NAG_Condition , NAS_Hunger , -Item^.V ); AddMoraleDmg( TruePC , -( Item^.Stat[ STAT_MoraleBoost ] * FOOD_MORALE_FACTOR ) ); { Invoke the item's effect, if any. } if Item^.Stat[ STAT_FoodEffectType ] <> 0 then begin effect := ReplaceHash( Food_Effect_String[ Item^.Stat[ STAT_FoodEffectType ] ] , BStr( Item^.Stat[ STAT_FoodEffectMod ] ) ); EffectFrontEnd( GB , TruePC , effect , '' ); end; { Apply the item's SkillXP, if any. } if Item^.Stat[ STAT_FoodSkillXP ] <> 0 then begin if DoleSkillExperience( TruePC , Item^.Stat[ STAT_FoodSkillXP ] , Item^.Stat[ STAT_FoodSkillXPAmount ] ) then begin DialogMsg( ReplaceHash( MsgString( 'BACKPACK_FoodSkillBoost' ) , GearName( Item ) ) ); end; end; { Destroy the item, if appropriate. } if IsInvCom( Item ) then begin RemoveGEar( Item^.Parent^.InvCom , Item ); end else if IsSubCom( Item ) then begin RemoveGEar( Item^.Parent^.SubCom , Item ); end; end else begin DialogMsg( MsgString( 'BACKPACK_NotHungry' ) ); end; end; Procedure SwitchQuickFire( Item: GearPtr ); { Swap the quickfire prefs for this item. } var QFP: Integer; { Quick Fire Priority } begin QFP := ( NAttValue( Item^.NA, NAG_WeaponModifier, NAS_QuickFire ) + 3 ) mod 4; SetNAtt( Item^.NA, NAG_WeaponModifier, NAS_QuickFire , QFP ); end; Procedure InstallSoftware( GB: GameBoardPtr; PC , SW: GearPtr ); { Attempt to install some software into a computer. This is how it's } { going to work: First pick a computer to install into. If a computer } { was selected, see if it has enough free space for the requested } { software. If it doesn't, prompt to uninstall some programs. If after } { the purge there's enough room for the requested program install it. } Function SelectComputer: GearPtr; { Attempt to select a computer. } var RPM: RPGMenuPtr; N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); BuildGearMenu( RPM , PC , GG_Computer ); BP_Source := PC; BP_SeekSibs := False; BP_ActiveMenu := RPM; if RPM^.NumItem < 1 then AddRPGMenuItem( RPM , '[no computer for ' + GearName( SW ) + ']' , -1 ); { Select a computer, and get rid of the menu. } N := SelectMenu( RPM , @MiscProcRedraw); DisposeRPGMenu( RPM ); if N > 0 then begin SelectComputer := LocateGearByNumber( PC , N ); end else begin SelectComputer := Nil; end; end; Procedure FreeSoftwareSpace( Compy: GearPtr ); { Unload programs from this computer until SW can be installed, } { or the user cancels. } var RPM: RPGMenuPtr; S: GearPtr; N: Integer; begin DialogMsg( ReplaceHash( MsgString( 'BACKPACK_FreeSoftwareSpace' ) , GearName( SW ) ) ); repeat RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); BP_Source := Compy^.SubCom; BP_SeekSibs := True; BP_ActiveMenu := RPM; { Add the software to the menu. } N := 1; S := Compy^.SubCom; while S <> nil do begin AddRPGMenuItem( RPM , GearName( S ) , N ); Inc( N ); S := S^.Next; end; { Select from the menu. } N := SelectMenu( RPM , @MiscProcRedraw); DisposeRPGMenu( RPM ); { If a software program was selected, uninstall it and place } { it in the inventory. } if N > -1 then begin S := RetrieveGearSib( Compy^.SubCom , N ); DelinkGear( Compy^.SubCom , S ); InsertInvCom( PC , S ); end; until ( N = -1 ) or IsLegalSubCom( Compy , SW ); end; var Compy: GearPtr; begin BP_GB := GB; Compy := SelectComputer; if ( Compy <> Nil ) then begin if ( ZetaGigs( Compy ) >= ZetaGigs( SW ) ) then begin if not IsLegalSubCom( Compy , SW ) then FreeSoftwareSpace( Compy ); if IsLegalSubCom( Compy , SW ) then begin DelinkGear( SW^.Parent^.InvCom , SW ); InsertSubCom( Compy , SW ); DialogMsg( ReplaceHash( MsgString( 'BACKPACK_InstallSoftware' ) , GearName( SW ) ) ); if GB <> Nil then WaitAMinute( GB , PC , ReactionTime( PC ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'BACKPACK_ComputerTooFull' ) , GearName( SW ) ) ); end; end else begin { This computer is too small to install this program. } DialogMsg( ReplaceHash( MsgString( 'BACKPACK_ComputerTooSmall' ) , GearName( SW ) ) ); end; end; end; Procedure PCDoPerformance( GB: GameBoardPtr; PC: GearPtr ); { The PC is playing an instrument. Whee! Check how many positive } { reactions the PC scores. The PC might also earn money, if the } { public response is positive enough. } var Target: GearPtr; Success: LongInt; msg: String; begin { Select a target for this performance. } Target := SelectPerformanceTarget( GB , PC ); msg := ReplaceHash( MsgString( 'PERFORMANCE_Base' ) , GearName( PC ) ); { If we have a target, then perform. } if Target <> Nil then begin { Call the performance procedure to find out how well the } { player has done. } Success := UsePerformance( GB , PC , Target ); { Print an appropriate message. } if Success > 0 then begin { Good show! The PC made some money as a busker. } msg := msg + ' ' + ReplaceHash( MsgString( 'PERFORMANCE_DidWell' + BStr( Random( 3 ) ) ) , BStr( Success ) ); end else if Success < 0 then begin { The PC flopped. No money made, and possibly damage } { to his reputation. } msg := msg + ' ' + MsgString( 'PERFORMANCE_Horrible' + BStr( Random( 3 ) ) ); end; end else begin msg := msg + ' ' + MsgString( 'PERFORMANCE_NoAudience' ); SetNAtt( PC^.NA , NAG_Location , NAS_SmartAction , 0 ); end; DialogMsg( msg ); end; Procedure StartPerforming( GB: GameBoardPtr; PC: GearPtr ); { Start performing on a musical instrument. This procedure will set } { up the continuous action. } begin if ( PC = Nil ) or ( PC^.G <> GG_Character ) then Exit; SetNAtt( PC^.NA , NAG_Location , NAS_SmartCount , 4 ); SetNAtt( PC^.NA , NAG_Location , NAS_SmartAction , NAV_UseSkill ); SetNAtt( PC^.NA , NAG_Location , NAS_SmartSkill , NAS_Performance ); { ...and also exit the backpack. } ForceQuit := True; PCDoPerformance( GB , PC ); WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Procedure ThisItemWasSelected( GB: GameBoardPtr; var LList: GearPtr; TruePC , PC , Item: GearPtr ); { TruePC is the primary character, who may be doing repairs } { and stuff. } { PC is the current master being examined, which may well be } { a mecha belonging to the TruePC rather than the TruePC itself. } { LList is a list of mecha and other things which may or may not } { belong to the same team as TruePC et al. } { Item is the piece of wargear currently being examined. } { BP_Redraw must have been set some time before this procedure was called. } var TIWS_Menu: RPGMenuPtr; N: Integer; begin N := 0; repeat TIWS_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); if ( Item^.G = GG_Tool ) and ( Item^.S = NAS_Performance ) and ( GB <> Nil ) then AddRPGMenuItem( TIWS_Menu , ReplaceHash( MsgString( 'BACKPACK_UseInstrument' ) , GearName( Item ) ) , -9 ); if ( Item^.G = GG_Consumable ) and ( GB <> Nil ) then AddRPGMenuItem( TIWS_Menu , ReplaceHash( MsgString( 'BACKPACK_EatItem' ) , GearName( Item ) ) , -10 ); if ( Item^.G = GG_Software ) and IsInvCom( Item ) then AddRPGMenuItem( TIWS_Menu , ReplaceHash( MsgString( 'BACKPACK_TIWS_InstallSoftware' ) , GearName( Item ) ) , -12 ); if ( GB <> Nil ) and ( SATtValue( Item^.SA , 'USE' ) <> '' ) then AddRPGMenuItem( TIWS_Menu , ReplaceHash( MsgString( 'BACKPACK_UseItemScript' ) , GearName( Item ) ) , -11 ); if ( Item^.G = GG_Ammo ) and IsInvCom( Item ) then AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_LoadAmmo' ) , -5 ); if IsInvCom( Item ) then begin if Item^.Parent = PC then begin AddRPGMenuItem( TIWS_Menu , ReplaceHash( MsgString( 'BACKPACK_EquipItem' ) , GearName( Item ) ) , -2 ); if ( FindMaster( Item ) <> Nil ) and ( FindMaster( Item )^.G = GG_Mecha ) and CanBeInstalled( Item ) then begin AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_Install' ) + GearName( Item ) , -8 ); end; end else begin AddRPGMenuItem( TIWS_Menu , ReplaceHash( MsgString( 'BACKPACK_UnequipItem' ) , GearName( Item ) ) , -3 ); end; if ( LList <> Nil ) then AddRPGMenuItem ( TIWS_Menu , MsgString( 'BACKPACK_TradeItem' ) , -6 ); AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_DropItem' ) , -4 ); end else if ( FindMaster( Item ) <> Nil ) and ( FindMaster( Item )^.G = GG_Mecha ) and CanBeExtracted( Item ) then begin AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_Remove' ) + GearName( Item ) , -7 ); end; if ( SeekSubsByG( Item^.SubCom , GG_Ammo ) <> Nil ) and not IsMasterGear( Item ) then AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_EjectAmmo' ) , 4 ); if ( SeekSubsByG( Item^.SubCom , GG_Software ) <> Nil ) and not IsMasterGear( Item ) then AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_EjectSoftware' ) , 5 ); if GB <> Nil then AddRepairOptions( TIWS_Menu , TruePC , Item ); if ( Item^.G = GG_Weapon ) or ( ( Item^.G = GG_Ammo ) and ( Item^.S = GS_Grenade ) ) then begin AddRPGMenuItem( TIWS_Menu, ReplaceHash( MsgString( 'BACKPACK_QF' + BStr( NAttValue( Item^.NA, NAG_WeaponModifier, NAS_QuickFire ) ) ), GearName( Item ) ), 2 ); if NAttValue( Item^.NA , NAG_WeaponModifier , NAS_SafetySwitch ) = 0 then begin AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_EngageSafety' ) , 3 ); end else begin AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_DisengageSafety' ) , 3 ); end; end; if GB <> Nil then AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_UseSkillOnItem' ) , 1 ); AddRPGMenuItem( TIWS_Menu , MsgString( 'BACKPACK_ExitTIWS' ) , -1 ); { Restore the menu item in case this isn't the first iteration. } SetItemByValue( TIWS_Menu , N ); BP_GB := GB; BP_Source := Item; N := SelectMenu( TIWS_Menu , @ThisItemRedraw ); DisposeRPGMenu( TIWS_Menu ); if N > 100 then begin DoFieldRepair( GB , TruePC , Item , N-100 ); end else begin case N of 5: EjectSoftware( GB , LList , PC , Item ); 4: EjectAmmo( GB , LList , PC , Item ); 3: SetNAtt( Item^.NA , NAG_WeaponModifier , NAS_SafetySwitch , 1 - NAttValue( Item^.NA , NAG_WeaponModifier , NAS_SafetySwitch ) ); 2: SwitchQuickFire( Item ); 1: UseSkillOnItem( GB , TruePC , Item ); -2: EquipItemFrontend( GB , LList , PC , Item ); -3: UnequipFrontEnd( GB , LList , PC , Item ); -4: DropFrontEnd( GB , LList , PC , Item ); -5: InstallAmmoFrontEnd( GB , PC , Item ); -6: TradeFrontEnd( GB , PC, Item , LList ); -7: ExtractFrontEnd( GB , TruePC , PC , Item ); -8: InstallFrontEnd( GB , TruePC , PC , Item ); -9: StartPerforming( GB , PC ); -10: EatItem( GB , PC , Item ); -11: UseScriptItem( GB , TruePC , Item , 'USE' ); -12: InstallSoftware( GB , PC , Item ); { Install Software } end; end; until ( N < 0 ) or ForceQuit; end; Function DoInvMenu( GB: GameBoardPtr; var LList: GearPtr; PC,M: GearPtr ): Boolean; { Return TRUE if the user selected Quit. } { M is the MASTER whose inventory we're examining. In a normal case, when } { the PC is examining his own stuff, then M = PC. } var N: Integer; begin Repeat BP_GB := GB; BP_Source := M; BP_SeekSibs := False; BP_ActiveMenu:= InvRPM; N := SelectMenu( INVRPM , @MiscProcRedraw); { If an item was selected, pass it along to the appropriate } { procedure. } if N > 0 then begin ThisItemWasSelected( GB , LList , PC , M , LocateGearByNumber( M , N ) ); { Restore the display. } UpdateBackpack( M ); end; until ( N < 0 ) or ForceQuit; DoInvMenu := N=-1; end; Function DoEqpMenu( GB: GameBoardPtr; var LList: GearPtr; PC,M: GearPtr ): Boolean; { Return TRUE if the user selected Quit. } { M is the MASTER whose inventory we're examining. In a normal case, when } { the PC is examining his own stuff, then M = PC. } var N: Integer; begin Repeat BP_GB := GB; BP_Source := M; BP_SeekSibs := False; BP_ActiveMenu:= EqpRPM; N := SelectMenu( EqpRPM , @EqpRedraw); { If an item was selected, pass it along to the appropriate } { procedure. } if N > 0 then begin ThisItemWasSelected( GB , LList , PC , M , LocateGearByNumber( M , N ) ); { Restore the display. } UpdateBackpack( M ); end; until ( N < 0 ) or ForceQuit; DoEqpMenu := N=-1; end; Procedure RealBackpack( GB: GameBoardPtr; var LList: GearPtr; PC,M: GearPtr; StartWithInv: Boolean; BasicRedraw: RedrawProcedureType ); { This is the backpack routine which should allow the player to go } { through all the stuff in his/her inventory, equip items, drop them, } { reload weapons, and whatnot. It is based roughly upon the procedures } { from DeadCold. } { GB = The gameboard; may be nil. } { LList = The list of stuff surrounding M; where things go when dropped. } { PC = The controller of the party; the primary PC. } { M = The model whose backpack we're dealing with. } var QuitBP: Boolean; begin { Set up the display. } ForceQuit := False; BP_Redraw := BasicRedraw; { Initialize menus to NIL, then create them. } InvRPM := Nil; EqpRPM := Nil; UpdateBackpack( M ); repeat if StartWithInv then begin QuitBP := DoInvMenu( GB , LList , PC , M ); end else begin QuitBP := DoEqpMenu( GB , LList , PC , M ); end; { If we have not been ordered to exit the loop, we must } { have been ordered to switch menus. } StartWithInv := Not StartWithInv; until QuitBP or ForceQuit; DisposeRPGMenu( InvRPM ); DisposeRPGMenu( EqpRPM ); end; Procedure ArenaHQBackpack( Source,BPPC: GearPtr; BasicRedraw: RedrawProcedureType ); { Open the backpack menu for a member of this arena unit. } begin RealBackpack( Nil , Source^.SubCom , Source , BPPC , True , BasicRedraw ); end; Procedure LancemateBackpack( GB: GameBoardPtr; PC,NPC: GearPtr; BasicRedraw: RedrawProcedureType ); { This is a header for the REALBACKPACK function. } begin RealBackPack( GB , GB^.Meks , PC , NPC , True , BasicRedraw ); end; Procedure BackpackMenu( GB: GameBoardPtr; PC: GearPtr; StartWithInv: Boolean; BasicRedraw: RedrawProcedureType ); { This is a header for the REALBACKPACK function. } begin RealBackPack( GB , GB^.Meks , PC , PC , StartWithInv , BasicRedraw ); end; Procedure MechaPartEditor( GB: GameBoardPtr; var LList: GearPtr; PC,Mek: GearPtr; BasicRedraw: RedrawProcedureType ); { This procedure may be used to browse through all the various } { bits of a mecha and examine each one individually. } { LList is the list of mecha of which MEK is a sibling. If any item gets removed } { from Mek but can't be placed in the general inventory, it will be put there. } var RPM: RPGMenuPtr; N,I: Integer; begin { Set up the display. } DrawBPBorder; I := 0; Repeat RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); BP_ActiveMenu := RPM; BuildGearMenu( RPM , Mek ); if I > 0 then SetItemByPosition( RPM , I ); AddRPGMenuItem( RPM , 'Exit Editor' , -1 ); BP_Redraw := BasicRedraw; BP_GB := GB; BP_Source := Mek; BP_SeekSibs := False; N := SelectMenu( RPM , @MechaPartEditorRedraw); I := RPM^.SelectItem; DisposeRPGMenu( RPM ); if N > -1 then begin ThisItemWasSelected( GB , LList , PC , Mek , LocateGearByNumber( Mek , N ) ); end; until N = -1; end; Procedure MechaPartBrowser( Mek: GearPtr; RDP: RedrawProcedureType ); { This procedure may be used to browse through all the various } { bits of a mecha and examine each one individually. } var RPM: RPGMenuPtr; N: Integer; begin MPB_Redraw := RDP; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); BuildGearMenu( RPM , Mek ); AddRPGMenuItem( RPM , 'Exit Browser' , -1 ); Repeat BP_Source := Mek; BP_SeekSibs := False; BP_ActiveMenu := RPM; N := SelectMenu( RPM , @PartBrowserRedraw ); until N = -1; DisposeRPGMenu( RPM ); end; Procedure MysteryPartBrowser( Mek: GearPtr; RDP: RedrawProcedureType ); { Like the above procedure, but provide no actual info. } { This procedure is used when the PC attempts to inspect a target, } { but lacks the proper identification software. } var RPM: RPGMenuPtr; begin MPB_Redraw := RDP; BP_Source := Mek; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); AddRPGMenuItem( RPM , '????' , -1 ); SelectMenu( RPM , @MysteryBrowserRedraw ); DisposeRPGMenu( RPM ); end; Procedure BrowseDesignFile( List: GearPtr; RDP: RedrawProcedureType ); { Choose one of the sibling gears from LIST and display its properties. } var BrowseMenu: RPGMenuPtr; Part: GearPtr; N: Integer; begin { Create the menu. } BrowseMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); { Add each of the gears to the menu. } BuildSiblingMenu( BrowseMenu , List ); RPMSortAlpha( BrowseMenu ); AddRPGMenuItem( BrowseMenu , ' Cancel' , -1 ); repeat MPB_Redraw := RDP; BP_Source := List; BP_SeekSibs := True; BP_ActiveMenu := BrowseMenu; { Select a gear. } N := SelectMenu( BrowseMenu, @PartBrowserRedraw ); if N > -1 then begin Part := RetrieveGearSib( List , N ); MechaPartBrowser( Part , RDP ); if Part^.G = GG_Theme then CheckTheme( Part ); end; until N = -1; DisposeRPGMenu( BrowseMenu ); end; Procedure FHQ_Transfer( var LList: GearPtr; PC,Item: GearPtr ); { An item has been selected. Allow it to be transferred to } { one of the team's master gears. } var RPM: RPGMenuPtr; M: GearPtr; N,Team: Integer; begin { Create the menu. } RPM := CreateRPGMenu( MenuItem, MenuSelect, ZONE_FieldHQMenu ); M := LList; N := 1; Team := NAttValue( PC^.NA , NAG_LOcation , NAS_Team ); while M <> Nil do begin if ( ( NAttValue( M^.NA , NAG_LOcation , NAS_Team ) = Team ) or ( NAttValue( M^.NA , NAG_LOcation , NAS_Team ) = NAV_LancemateTeam ) ) and IsMasterGear( M ) and IsLegalInvcom( M , Item ) then begin AddRPGMenuItem( RPM , TeamMateName( M ) , N ); end; M := M^.Next; Inc( N ); end; { Sort the menu, then add an exit option. } RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); AddRPGMenuItem( RPM , MsgString( 'FHQ_ReturnToMain' ) , -1 ); { Get a menu selection, then exit the menu. } BP_Source := LList; BP_SeekSibs := True; BP_ActiveMenu := RPM; DialogMSG( ReplaceHash( MsgString( 'FHQ_SelectDestination' ), GearName( Item ) ) ); N := SelectMenu( RPM , @PartBrowserRedraw ); //N := SelectMenu( RPM , @MiscProcRedraw ); DisposeRPGMenu( RPM ); if N > -1 then begin M := RetrieveGearSib( LList , N ); DelinkGear( LList , Item ); InsertInvCom( M , Item ); DialogMSG( MsgString( 'FHQ_ItemMoved' ) ); end else begin DialogMSG( MsgString( 'Cancelled' ) ); end; end; Procedure Rename_Mecha( GB: GameBoardPtr; NPC: GearPtr ); { Enter a new name for NPC. } var name: String; begin name := GetStringFromUser( ReplaceHash( MsgString( 'FHQ_Rename_Prompt' ) , GearName( NPC ) ) , @PlainRedraw ); if name <> '' then SetSAtt( NPC^.SA , 'name <' + name + '>' ); end; Procedure FHQ_ThisWargearWasSelected( GB: GameBoardPtr; var LList: GearPtr; PC,M: GearPtr; BasicRedrawer: RedrawProcedureType ); { A mecha has been selected by the PC from the FHQ main menu. } { Offer up all the different choices of things the PC can } { do with mecha - select pilot, repair, check inventory, etc. } var RPM: RPGMenuPtr; N: Integer; begin repeat { Create the FHQ menu. } RPM := CreateRPGMenu( MenuItem, MenuSelect, ZONE_FieldHQMenu ); RPM^.Mode := RPMNoCleanup; if IsMasterGear( M ) then begin if IsSafeArea( GB ) or OnTheMap( GB , M ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_GoBackpack' ) , 1 ); end else if IsSafeArea( GB ) then begin AddRPGMenuItem( RPM , MsgString( 'FHQ_Transfer' ) , -3 ); end; if IsSafeArea( GB ) then AddRepairOptions( RPM , PC , M ); if M^.G = GG_Mecha then begin AddRPGMenuItem( RPM , MsgString( 'FHQ_SelectMecha' ) , 2 ); AddRPGMenuItem( RPM , MsgString( 'FHQ_Rename' ) , 6 ); end; if IsSafeArea( GB ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_PartEditor' ) , 4 ); if M^.G = GG_Mecha then AddRPGMenuItem( RPM , MsgString( 'FHQ_EditColor' ) , 5 ); AddRPGMenuItem( RPM , MsgString( 'FHQ_ReturnToMain' ) , -1 ); { Get a selection from the menu, then dispose of it. } BP_GB := GB; BP_Source := M; BP_Redraw := BasicRedrawer; MPB_Redraw := BasicRedrawer; N := SelectMenu( RPM , @ThisWargearRedraw ); DisposeRPGMenu( RPM ); if N > 100 then begin { A repair option must have been selected. } DoFieldRepair( GB , PC , M , N-100 ); end else begin case N of 1: RealBackpack( GB , LList , PC , M , False , BasicRedrawer ); 2: FHQ_SelectPilotForMecha( GB , M ); -3: FHQ_Transfer( LList , PC , M ); 4: MechaPartEditor( GB , LList , PC , M , @PlainRedraw ); {$IFNDEF ASCII} 5: SelectColors( M , BasicRedrawer ); {$ENDIF} 6: Rename_Mecha( GB , M ); end; end; until N < 0; {$IFNDEF ASCII} CleanSpriteList; {$ENDIF} end; Procedure UsableGearMenu( GB: GameBoardPtr; PC: GearPtr ); { The PC is about to invoke a usable gear. Take a look and see } { which effects are available, then invoke one of them. } var RPM: RPGMenuPtr; N: Integer; Part: GearPtr; begin BP_GB := GB; BP_Source := PC; BP_Redraw := @PlainRedraw; BP_SeekSibs := False; RPM := CreateRPGMenu( MenuItem, MenuSelect, ZONE_FieldHQMenu ); BuildGearMenu( RPM , PC , GG_Usable ); AlphaKeyMenu( RPM ); RPMSortAlpha( RPM ); AddRPGMenuItem( RPM , MsgString( 'Cancel' ) , -1 ); BP_ActiveMenu := RPM; N := SelectMenu( RPM , @MechaPartEditorRedraw ); DisposeRPGMenu( RPM ); Part := LocateGearByNumber( PC , N ); if Part <> Nil then begin if Part^.S = GS_Transformation then begin if CanDoTransformation( GB , PC , Part ) then begin DoTransformation( GB , PC , Part , True ); end else begin DialogMsg( MsgString( 'TRANSFORM_NotNow' ) ); end; end else if Part^.S = GS_LongRangeScanner then begin if LongRangeScanEPCost( GB, Part ) > EnergyPoints( FindRoot( Part ) ) then begin DialogMsg( MsgString( 'LONGRANGESCAN_NoPower' ) ); end else if CanLRScanHere( GB, Part ) then begin DoLongRangeScan( GB , PC , Part ); end else begin DialogMsg( MsgString( 'LONGRANGESCAN_NotNow' ) ); end; end; end; end; end. gearhead-2-0.701/beancounter.pas000066400000000000000000000105111321074026100164240ustar00rootroot00000000000000Program beancounter; { This program will examine the character development subplots and produce a graph of Motivation x Attitude, showing how many options exist for each combination. It's named for the Beanpole, the 12 episode initial series of the core story. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} uses gears,narration,gearutil,texutil,mpbuilder; const Propp_M: Array [0..Num_XXR_Motivations] of Integer = ( 0, 1,2,7,3,4, 5,6,8 ); Propp_A: Array [0..Num_XXR_Attitudes] of Integer = ( 0, 11, 1, 2, 12, 10, 3, 4, 5, 14, 13, 6, 7, 8, 9 ); Procedure CountTheBeans( var ResultList: SAttPtr; base_context: String; npc_ident: Char; LList: GearPtr ); { Given the listed base_context, find out how many components there are } { in LList which match each Attitude/Motivation combo of the NPC identified } { by NPC_Ident. Store the results as a nicely formatted graph in } { ResultList. } var A,M: Integer; { Attitude, Motivation counters. } MA_Results: Array [0..Num_XXR_Motivations,0..Num_XXR_Attitudes] of Integer; context: String; ShoppingList: NAttPtr; begin { Clear the results array. } for A := 0 to Num_XXR_Attitudes do begin for M := 0 to Num_XXR_Motivations do begin MA_Results[ M , A ] := 0; end; end; { Start checking the combos. } for A := 0 to Num_XXR_Attitudes do begin for M := 0 to Num_XXR_Motivations do begin { Generate the context for this combo. } Context := base_context + ' ' + npc_ident + ':M.' + XXR_Motivation[ Propp_M[ M ] ] + ' ' + npc_ident + ':A.' + XXR_Attitude[ Propp_A[ A ] ]; { Generate a shopping list for this context. } ShoppingList := CreateComponentList( LList , Context ); { Store the number of legal components, delete the } { shopping list. } MA_Results[ M , A ] := NumNAtts( ShoppingList ); DisposeNAtt( ShoppingList ); end; end; { We should have all the results. Store them in a nicely formatted } { table, just like we used to make all the time on the C64. Yay! } { Start with the motivation key. } context := ' '; for M := 0 to Num_XXR_Motivations do context := context + ' ' + XXR_Motivation[ Propp_M[ M ] ]; StoreSAtt( ResultList , Context ); { Now, the data cells. } for A := 0 to Num_XXR_Attitudes do begin context := XXR_Attitude[ Propp_A[ A ] ] + ' '; for M := 0 to Num_XXR_Motivations do begin if MA_Results[ M , A ] > 0 then begin context := context + ' ' + WideStr( MA_Results[ M , A ] , 3 ); end else begin context := context + ' - '; end; end; StoreSAtt( ResultList , Context ); end; end; const CS_Enemy_Chardev = '*:CS_MIX_Confrontation *:CS_StopNPCMission&IsEnemyNPC *:CS_MechaEncounter *:CS_GatherInformation *:CS_FetchItem &Beancounter E:++ F:++'; var ResultList: SAttPtr; begin ResultList := Nil; StoreSAtt( ResultList , 'Core Story Enemy Chardev' ); CountTheBeans( ResultList , CS_Enemy_Chardev + ' !Hi' , 'E' , Sub_Plot_List ); StoreSAtt( ResultList , ' ' ); StoreSAtt( ResultList , 'Core Story Confrontation Chardev' ); CountTheBeans( ResultList , '*:CS_MIX_Confrontation E:++ F:++ !Hi' , '1' , Sub_Plot_List ); StoreSAtt( ResultList , ' ' ); StoreSAtt( ResultList , 'Rookie Enemy Chardev' ); CountTheBeans( ResultList , CS_Enemy_Chardev + ' !Ne' , 'E' , Sub_Plot_List ); StoreSAtt( ResultList , ' ' ); StoreSAtt( ResultList , 'Lancemate NonCom Chardev' ); CountTheBeans( ResultList , '*LM_NonComCharDev *LM_PersonalJob &BeanCounter !Ne !Lo !Md !Hi !Ex 1:++ 1:TRAIN 1:NOFAC' , '1' , Sub_Plot_List ); SaveStringList( 'out.txt' , ResultList ); DisposeSAtt( ResultList ); end. gearhead-2-0.701/chargen.pp000066400000000000000000001604021321074026100153670ustar00rootroot00000000000000unit chargen; { This unit contains the nuts and bolts of the GearHead } { character generator. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} { ******************** } { *** THE EGG *** } { ******************** } { EGG^.G = GG_Set } { EGG^.S = NA } { EGG^.V = NA } { } { Egg Subcoms: The PC and their mecha. } { Egg Invcoms: All the NPCs connected to the PC. } { } { The Character Creator returns an "Egg" containing not only the PC, but also } { said PC's life experience and social network. When the egg is imported into } { an RPG campaign these elements are placed and initialized. } { } { } interface uses gears; Const RC_DirList = Series_Directory + OS_Search_Separator + OS_Current_Directory; BaseStatPts = 80; MaxStartingSkill = 5; MaxStartingStat = 20; var Jobs_List,Family_List,Bio_List: GearPtr; Goal_List,Focus_List: GearPtr; Hometown_List: GearPtr; Function CharacterCreator( Fac: Integer ): GearPtr; Function RandomNPC( Adv: GearPtr; Fac,Hometown: Integer ): GearPtr; implementation uses gearutil,ghchars,texutil,ui4gh,description,gearparser,playwright, ability,wmonster,dos,locale,menugear, {$IFDEF ASCII} vidgfx,vidinfo,vidmenus; {$ELSE} colormenu,sdlgfx,sdlinfo,sdlmenus; {$ENDIF} type SkillArray = Array [1..NumSkill] of Integer; var RCPC: GearPtr; RCPromptMessage,RCDescMessage,RCCaption: String; Procedure RandCharRedraw; { Redraw the screen for SDL. } begin ClrScreen; if RCPC <> Nil then CharacterDisplay( RCPC , Nil, ZONE_CharGenChar ); InfoBox( ZONE_CharGenDesc ); InfoBox( ZONE_CharGenPrompt ); InfoBox( ZONE_CharGenCaption ); InfoBox( ZONE_CharGenMenu ); GameMsg( RCDescMessage , ZONE_CharGenDesc , InfoGreen ); GameMsg( RCPromptMessage , ZONE_CharGenPrompt , InfoGreen ); if RCCaption <> '' then CMessage( RCCaption , ZONE_CharGenCaption , InfoGreen ); end; Procedure EasyStatPoints( PC: GearPtr; StatPt: Integer ); { Allocate the stat points for the PC mostly randomly, making sure there are no } { obvious deficiencies. } const NumBaseLineTypes = 7; BaseLineName: Array [1..NumBaseLineTypes] of String = ( 'ACADE|MEDIC','CORPO|TRADE','LABOR','MEDIA|POLIT','MILIT', 'THIEF','CRAFT' ); BaseLineStats: Array [0..NumBaseLineTypes,1..NumGearStats] of Byte = ( { Ref Bod Spd Per Cra Ego Kno Cha } ( 8, 8, 8, 8, 8, 8, 8, 8 ), ( 7, 5, 7, 8, 10, 8, 12, 8 ), {Professor} ( 8, 7, 8, 8, 8, 8, 8, 9 ), {Corporate} ( 8, 10, 8, 6, 9, 8, 6, 8 ), {Labor} ( 7, 8, 7, 6, 7, 10, 7, 12 ), {Celeb} ( 10, 10, 10, 10, 7, 7, 6, 6 ), {Soldier} ( 8, 5, 10, 10, 10, 6, 8, 8 ), {Thief} ( 7, 7, 7, 10, 11, 6, 9, 7 ) {Tech} ); var Job_Desig: String; T,BL: Integer; { BL = BaseLine, determined by job. } begin { Start by determining the baseline stats for this character. Those are going } { to depend upon the job designation. } Job_Desig := SAttValue( PC^.SA , 'JOB_DESIG' ); BL := 0; if ( Job_Desig <> '' ) then begin for t := 1 to NumBaseLineTypes do begin if AStringHasBString( BaseLineName[ T ] , Job_Desig ) then begin BL := T; Break; end; end; end; { Copy over the baseline values, and reduce the number of free stat points } { appropriately. } for t := 1 to NumGearStats do begin PC^.Stat[ T ] := BaseLineStats[ BL , T ]; StatPt := StatPt - BaseLineStats[ BL , T ]; end; { Spend remaining stat points randomly. } if StatPt > 0 then RollStats( PC , StatPt ); end; Procedure ClearSkillArray( var PCSkills: SkillArray ); { Clear the skill array. Seems simple enough. } var T: Integer; begin { Zero out the base skill values. } for t := 1 to NumSkill do begin PCSkills[ T ] := 0; end; end; Function CGPCHasSkill( PC: GearPtr; const PCSkills: SkillArray; Skill: Integer ): Boolean; { Return TRUE if the character being generated has this skill, or FALSE otherwise. } { Don't count the hidden skills, which everyone has. } begin CGPCHasSkill := ( not SkillMan[ Skill ].Hidden ) and ( ( NAttValue( PC^.NA , NAG_Skill , Skill ) > 0 ) or ( PCSkills[ Skill ] > 0 ) ); end; Function NumPickedSkills( PC: GearPtr; const PCSkills: SkillArray ): Integer; { Return the number of skills this character knows. } var SkT,NPS: Integer; begin NPS := 0; for SkT := 1 to NumSkill do begin if CGPCHasSkill( PC , PCSkills , SkT ) then Inc( NPS ); end; NumPickedSkills := NPS; end; Function CanIncreaseSkill( SkillVal, SkillPt: Integer ): Boolean; { Return TRUE if this skill value can be increased given the remaining number of } { skill points, or FALSE if it can't be. } { To increase a skill by one rank, one must spend SkillVal skill points. } { For instance, to increase a skill from Rank 3 to Rank 4 one would have to } { spend 3 skill points. } begin CanIncreaseSkill := ( SkillVal < MaxStartingSkill ) and ( SkillPt >= SkillVal ) and ( SkillPt > 0 ); end; Procedure CGImproveSkill( var PCSkills: SkillArray; Skill: Integer; var SkillPt: Integer ); { Improve this skill, reducing SkillPt by the appropriate amount. } begin if PCSkills[ Skill ] = 0 then begin Dec( SkillPt ); end else begin SkillPt := SkillPt - PCSkills[ Skill ]; end; Inc( PCSkills[ Skill ] ); end; Procedure SpendSkillPointsRandomly( PC: GearPtr; var PCSkills: SkillArray; SkillPt: Integer ); { Spend all remaining skill points randomly. Maybe purchase some new skills, } { if appropriate. At the end of the process, leftover skill points will be } { converted to XP at a rate of 100XP per skill point. } { The SkillArray will not be combined back into the PC; the calling procedure } { must do that itself. } Function NumIncreasableSkills: Integer; { Return the number of known skills which may be increased given the number } { of free skill points. } var Skill,Total: Integer; begin Total := 0; For Skill := 1 to NumSkill do if CGPCHasSkill( PC , PCSkills , Skill ) and CanIncreaseSkill( PCSkills[ Skill ] , SkillPt ) then Inc( Total ); NumIncreasableSkills := Total; end; Function NumFreeSkillSlots: Integer; { Return the number of free skill slots. } { Note that this procedure assumes that all characters will want to learn } { the six basic combat skills, so the skill slots equal the number of regular } { skill slots minus ten minus the number of noncombat skills known. } var SkT,NPS: Integer; begin NPS := 0; for SkT := 7 to NumSkill do begin if CGPCHasSkill( PC , PCSkills , SkT ) then Inc( NPS ); end; NumFreeSkillSlots := NumberOfSkillSlots( PC ) - 6 - NPS; end; Procedure AddNewSkill; { Try to add a new skill to this PC. } { Usually we'll add one of the generic skills that absolutely any character } { might know, but sometimes we'll go all freaky and give out something like } { Biotech or Acrobatics. } const NumBeginnerSkills = 10; BeginnerSkills: Array [1..NumBeginnerSkills] of Byte = ( NAS_Awareness, NAS_Initiative, NAS_Repair, NAS_Medicine, NAS_ElectronicWarfare, NAS_SpotWeakness, NAS_Conversation, NAS_MechaEngineering, NAS_Insight, NAS_Taunt ); var Skill: Integer; begin if Random( 8 ) = 1 then begin Skill := Random( NumSkill ) + 1; end else begin Skill := BeginnerSkills[ Random( NumBeginnerSkills ) + 1 ]; end; if ( not SkillMan[ Skill ].Hidden ) and ( not CGPCHasSkill( PC , PCSkills , Skill ) ) then begin CGImproveSkill( PCSkills , Skill , SkillPt ); end; end; Procedure ImproveExistingSkill( N: Integer ); { Improve the N'th improvable skill known by this PC. } var Skill: Integer; begin Skill := 1; while ( Skill <= NumSkill ) and ( N > 0 ) do begin if CGPCHasSkill( PC , PCSkills , Skill ) and CanIncreaseSkill( PCSkills[ Skill ] , SkillPt ) and ( not SkillMan[ Skill ].Hidden ) then begin Dec( N ); if N = 0 then begin CGImproveSkill( PCSkills , Skill , SkillPt ); end; end; Inc( Skill ); end; end; var tries,NumSkill,NumSlot: Integer; begin tries := 0; while ( SkillPt > 0 ) and ( Tries < 10000 ) do begin NumSkill := NumIncreasableSkills; NumSlot := NumFreeSkillSlots; if ( NumSlot > ( Random( 50 ) + 1 ) ) then begin { Add a new skill. } AddNewSkill; end else if NumSkill > 0 then begin { Improve an existing skill. } ImproveExistingSkill( Random( NumSkill ) + 1 ); end else begin { Add a new skill. } AddNewSkill; end; Inc( Tries ); end; { Convert remaining skill points into experience points. } if SkillPt > 0 then AddNAtt( PC^.NA , NAG_Experience , NAS_TotalXP , SkillPt * 100 ); end; Procedure RecordSkills( PC: GearPtr; const PCSkills: SkillArray ); { Record the purchased skills in the PC record. } var T: Integer; begin for T := 1 to NumSkill do AddNAtt( PC^.NA , NAG_Skill , T , PCSkills[T] ); end; Procedure AllocateSkillPoints( PC: GearPtr; SkillPt: Integer ); { Distribute the listed number of points out to the PC. } var RPM: RPGMenuPtr; PCSkills: SkillArray; T,SkNum: Integer; Function SkillSelectorMsg( N: Integer ): String; var msg: String; begin msg := MsgString( 'SkillName_' + BStr( N ) ); {$IFNDEF ASCII} while TextLength( Game_Font , msg ) < ( ZONE_CharGenMenu.W - 50 ) do msg := msg + ' '; {$ELSE} while Length( msg ) < 20 do msg := msg + ' '; {$ENDIF} msg := msg + BStr( NAttValue( PC^.NA , NAG_Skill , N ) + PCSkills[ N ] ); SkillSelectorMsg := msg; end; Function FreeSkillSlots: Integer; { Return the number of free skill slots. } begin FreeSkillSlots := NumberOfSkillSlots( PC ) - NumPickedSkills( PC , PCSkills ); end; begin ClearSkillArray( PCSkills ); { Create the menu & set up the display. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); RPM^.Mode := RPMNoCleanup; {$IFDEF ASCII} RCDescMessage := MsgString( 'RANDCHAR_SkillDesc' ); RCPromptMessage := ''; RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenPrompt ); {$ELSE} RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_SkillPrompt' ); RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenDesc ); {$ENDIF} for t := 1 to NumSkill do begin if ( not SkillMan[ T ].Hidden ) then AddRPGMenuItem( RPM , SkillSelectorMsg( T ) , T , SkillDescription( T ) ); end; RPMSortAlpha( RPM ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_ASPDone' ) , -2 ); RPM^.dtexcolor := InfoGreen; { Add RPGKeys for the left and right buttons, since these will be } { used to spend & retrieve points. } {$IFNDEF ASCII} AddRPGMenuKey( RPM , RPK_Right , 1 ); AddRPGMenuKey( RPM , RPK_Left , -1 ); {$ELSE} AddRPGMenuKey( RPM , KeyMap[ KMC_East ].KCode , 1 ); AddRPGMenuKey( RPM , KeyMap[ KMC_West ].KCode , -1 ); {$ENDIF} repeat RCCaption := ReplaceHash( MsgString( 'RANDCHAR_ASPPrompt' ) , BStr( SkillPt ) ); RCCaption := ReplaceHash( RCCaption , BStr( FreeSkillSlots ) ); T := SelectMenu( RPM , @RandCharRedraw ); if ( T > 0 ) and ( SkillPt > 0 ) then begin { Increase Skill } { Figure out which skill we're changing... } SkNum := RPMLocateByPosition(RPM , RPM^.selectitem )^.value; if ( SkNum > 0 ) and ( SkNum <= NumSkill ) and CanIncreaseSkill( PCSkills[ SkNum ] , SkillPt ) then begin if CGPCHasSkill( PC , PCSkills , SkNum ) or ( FreeSkillSlots > 0 ) then begin CGImproveSkill( PCSkills, SkNum , SkillPt ); { Replace the message line. } RPMLocateByPosition(RPM , RPM^.selectitem )^.msg := SkillSelectorMsg( SkNum ); end; end; end else if ( T = -1 ) then begin { Decrease Skill } { Figure out which skill we're changing... } SkNum := RPMLocateByPosition(RPM , RPM^.selectitem )^.value; { Only decrease if the skill > 0... } if ( SkNum > 0 ) and ( SkNum <= NumSkill ) and ( PCSkills[ SkNum ] > 0 ) then begin if PCSkills[ SkNum ] = 1 then begin Inc( SkillPt ); end else begin SkillPt := SkillPt + PCSkills[ SkNum ] - 1; end; Dec( PCSkills[ SkNum ] ); { Replace the message line. } RPMLocateByPosition(RPM , RPM^.selectitem )^.msg := SkillSelectorMsg( SkNum ); end; end; until T = -2; { Spend remaining skill points randomly. } if SkillPt > 0 then SpendSkillPointsRandomly( PC , PCSkills , SkillPt ); { Copy temporary values into the PC record. } RecordSkills( PC , PCSkills ); { Get rid of the menu. } DisposeRPGMenu( RPM ); end; Procedure RandomSkillPoints( PC: GearPtr; SkillPt: Integer; IsNPC: Boolean ); { Allocate out some sensible skill points to hopefully keep this beginning character } { alive. } { Step One: Decide on primary skills for this character. } { Step Two: Pass remaining points on to the random skill allocator. } const PointsForLevel: Array [1..5] of Byte = ( 1,2,4,7,11 ); Function CheckLevel( L: Integer ): Integer; { If the requested skill level is too great for the } { number of skill points posessed, reduce it. } begin if SkillPt < 1 then Exit( 0 ); while SkillPt < PointsForLevel[ L ] do Dec( L ); CheckLevel := L; end; var t,L: Integer; PCSkills: SkillArray; begin ClearSkillArray( PCSkills ); { First give decent Mecha Piloting and Dodge scores. } t := CheckLevel( Random( 2 ) + 4 ); PCSkills[ NAS_MechaPiloting ] := T; SkillPt := SkillPt - PointsForLevel[ t ]; t := CheckLevel( Random( 2 ) + 4 ); PCSkills[ NAS_Dodge ] := T; SkillPt := SkillPt - PointsForLevel[ t ]; { Give the guaranteed skill. } { PCs automatically get Conversation. } t := CheckLevel( Random( 3 ) + 1 ); if not IsNPC then begin PCSkills[ NAS_Conversation ] := T; SkillPt := SkillPt - PointsForLevel[ t ]; end; { Add combat skills. } { The default character will get all combat skills. } for t := 1 to Num_Basic_Combat_Skills do begin L := CheckLevel( 2 + Random( 2 ) ); PCSkills[ T ] := L; if L > 0 then SkillPt := SkillPt - PointsForLevel[ L ]; end; { Spend remaining skill points randomly. } if SkillPt > 0 then SpendSkillPointsRandomly( PC , PCSkills , SkillPt ); { Copy temporary values into the PC record. } RecordSkills( PC , PCSkills ); end; Function SelectMode: Integer; { Prompt the user for a mode selection. } var RPM: RPGMenuPtr; G: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_SMOp0' ) , 0 ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_SMOp1' ) , 1 ); RCPromptMessage := MsgString( 'RANDCHAR_SMPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_SMDesc' ); RCCaption := ''; G := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); SelectMode := G; end; Function SelectGender: Integer; { Prompt the user for a gender selection. } var RPM: RPGMenuPtr; G: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); AddRPGMenuItem( RPM , MsgString( 'GenderName_0' ) , NAV_Male ); AddRPGMenuItem( RPM , MsgString( 'GenderName_1' ) , NAV_Female ); AddRPGMenuItem( RPM , MsgString( 'GenderName_2' ) , NAV_Nonbinary ); RCDescMessage := MsgString( 'RANDCHAR_SGDesc' ); RCPromptMessage := MsgString( 'RANDCHAR_SGPrompt' ); RCCaption := ''; G := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); SelectGender := G; end; Function SelectAge: Integer; { Prompt the user for character age. } var RPM: RPGMenuPtr; T: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); for t := -4 to 10 do begin AddRPGMenuItem( RPM , BStr( T + 20 ) + ' years old' , T ); end; RCDescMessage := MsgString( 'RANDCHAR_SADesc' ); RCPromptMessage := MsgString( 'RANDCHAR_SAPrompt' ); RCCaption := ''; T := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); SelectAge := T; end; Procedure StoreHomeTownDataInPC( PC,City: GearPtr ); { Store the information for this PC's home town in the character record. } var msg: String; Fac: GearPtr; begin StoreSAtt( PC^.SA , 'HOMETOWN <' + GearName( City ) + '>' ); StoreSAtt( PC^.SA , 'HOMETOWN_FACTIONS <' + SAttValue( City^.SA , 'FACTIONS' ) + '>' ); msg := SAttValue( City^.SA , 'TYPE' ) + ' ' + SAttValue( City^.SA , 'DESIG' ); Fac := SeekCurrentLevelGear( Factions_List , GG_Faction , NAttValue( City^.NA , NAG_Personal , NAS_FactionID ) ); if Fac <> Nil then begin msg := msg + ' ' + SAttValue( Fac^.SA , 'DESIG' ); StoreSAtt( PC^.SA , 'HOMETOWN_GOVERNMENT <' + SAttValue( Fac^.SA , 'DESIG' ) + '>' ); end; StoreSAtt( PC^.SA , 'HOMETOWN_CONTEXT <' + msg + '>' ); end; Procedure SelectHomeTown( PC: GearPtr; CanEdit: Boolean; ForceFac: Integer ); { Select the PC's home town. Store the home town information in the PC } { string attributes. } { If ForceFac is nonzero, the generated PC must belong to this faction or } { no faction at all. So, only allow cities where this faction is active. } var City,Fac: GearPtr; N: Integer; RPM: RPGMenuPtr; FacDesig: String; begin if ForceFac <> 0 then begin Fac := SeekCurrentLevelGear( Factions_List , GG_Faction , ForceFac ); FacDesig := SAttValue( Fac^.SA , 'DESIG' ); end; { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); {$IFDEF ASCII} AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCDescMessage := MsgString( 'RANDCHAR_CityDesc' ); RCPromptMessage := ''; RCCaption := MsgString( 'RANDCHAR_CityPrompt' ); {$ELSE} RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_CityPrompt' ); RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenDesc ); {$ENDIF} { Add all faction-legal HOMETOWNs to the menu. } N := 1; City := Hometown_List; while City <> Nil do begin if ( ForceFac = 0 ) or AStringHasBString( SAttValue( City^.SA , 'FACTIONS' ) , FacDesig ) then begin AddRPGMenuItem( RPM , GearName( City ) , N , SAttValue( City^.SA , 'DESC' ) ); end; Inc( N ); City := City^.Next; end; RPMSortAlpha( RPM ); if RPM^.NumItem < 1 then begin { We've got ourselves an empty menu. Just pick a hometown randomly. } N := Random( NumSiblingGears( Hometown_List ) ) + 1; end else if CanEdit then begin { Can edit - allow the PC to select a home town. } N := SelectMenu( RPM , @RandCharRedraw ); if N = -1 then begin N := RPMLocateByPosition( RPM , Random( RPM^.NumItem ) + 1 )^.value; end; end else begin { Can't edit- select a home town randomly. } N := RPMLocateByPosition( RPM , Random( RPM^.NumItem ) + 1 )^.value; end; DisposeRPGMenu( RPM ); { Store the data for this city. } City := RetrieveGearSib( Hometown_List , N ); if City <> Nil then begin StoreHomeTownDataInPC( PC , City ); end; end; Function FilterList( Source: GearPtr; const PContext: String ): GearPtr; { Create a list of things based on the context provided. } { Yeah, I know, real specific there... } var it,J: GearPtr; Context: String; begin { Add a GENERAL tag to the context. Everybody gets a GENERAL tag. } context := 'GENERAL ' + PContext; it := Nil; { Go through the jobs list and copy everything that matches the context. } J := Source; while J <> Nil do begin if StringMatchWeight( Context , SAttValue( J^.SA , 'REQUIRES' ) ) > 0 then begin AppendGear( it , CloneGear( J ) ); end; J := J^.Next; end; { Return the finished list. } FilterList := it; end; Procedure ApplyJobModifiers( PC, Job: GearPtr ); { Given the provided job, apply its bonuses and whatnot to the } { provided PC. } var N,T: Integer; begin { Copy over the details and bonuses from this job. } { Each job will give a +1 bonus to a number of skills, and also some starting } { cash. The more skills given, the less money the PC starts with. } N := 0; for t := 1 to NumSkill do begin if NAttValue( Job^.NA , NAG_Skill , T ) <> 0 then begin AddNAtt( PC^.NA , NAG_Skill , T , 1 ); inc( N ); end; end; if N > 3 then N := 3; AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , 45000 * ( 3 - N ) ); { Copy the personality traits. } for t := 1 to Num_Personality_Traits do begin AddReputation( PC , T , NAttValue( Job^.NA , NAG_CharDescription , -T ) ); end; SetNAtt( PC^.NA , NAG_Personal , NAS_FactionID , NAttValue( Job^.NA , NAG_Personal , NAS_FactionID ) ); SetSAtt( PC^.SA , 'JOB <' + SAttValue( Job^.SA , 'NAME' ) + '>' ); SetSAtt( PC^.SA , 'JOB_DESIG <' + SAttValue( Job^.SA , 'DESIG' ) + '>' ); end; Procedure GenerateFamilyHistory( Egg , PC: GearPtr; CanEdit: Boolean ); { Generate the PC's personal history up to this point. This step will } { determine the following information: } { - Starting skill XP bonuses } { - the PC's parentage (or lack thereof) } { - the PC's starting XXRan context + enemy faction } { - NPCs for the PC's egg (family, friends, and otherwise) } { - The PC's personal conflict } const Parental_XP = 210; var RPM: RPGMenuPtr; LegalJobList,Fam,BioEvent: GearPtr; N: Integer; Context,Bio1: String; { Procedures block. } Procedure ApplyParentalBonus( Job: GearPtr ); var N,T: Integer; begin { Error check - Job might be NIL. } if Job = Nil then begin AddNAtt( PC^.NA , NAG_Experience , NAS_TotalXP , ( Parental_XP * 2 ) div 3 ); Exit; end; { See what's in there. } { We have to make two passes- one to see how many skills there are, } { then a second one to apply the experience. } N := 0; { Count skills. } for t := 1 to NumSkill do begin if NAttValue( Job^.NA , NAG_SKill , T ) <> 0 then begin Inc( N ); end; end; { Apply bonuses. } if N > 0 then begin for t := 1 to NumSkill do begin if NAttValue( Job^.NA , NAG_SKill , T ) <> 0 then begin AddNAtt( PC^.NA , NAG_Experience , NAS_Skill_XP_Base + t , Parental_XP div N ); end; end; end else begin AddNAtt( PC^.NA , NAG_Experience , NAS_TotalXP , ( Parental_XP * 2 ) div 3 ); end; end; Procedure ApplyBiographyEvent( Bio: GearPtr ); { Apply the changes brought about by this biography event. } var Base,Changes: String; T: Integer; Jobs: GearPtr; begin { An empty biography has no effect. } if Bio = Nil then begin AddNAtt( PC^.NA , NAG_Experience , NAS_TotalXP , ( Parental_XP * 4 ) div 3 ); Exit; end; { Copy over the personality traits from the biography event. } for t := 1 to Num_Personality_Traits do begin AddNAtt( PC^.NA , NAG_CharDescription , -T , NAttValue( Bio^.NA , NAG_CharDescription , -T ) ); end; { Copy the changes to the PC's context. } Base := SAttValue( PC^.SA , 'CONTEXT' ); Changes := SAttValue( Bio^.SA , 'CONTEXT' ); AlterDescriptors( Base , Changes ); SetSAtt( PC^.SA , 'CONTEXT <' + Base + '>' ); { Copy over the egg attributes, if appropriate. } Base := SAttValue( Bio^.SA , 'CONFLICT' ); if Base <> '' then SetSAtt( Egg^.SA , 'CONFLICT <' + Base + '>' ); { Apply the bonuses from the jobs. } { Only two jobs may be applied as bonuses. } Jobs := Bio^.SubCom; T := 2; while ( Jobs <> Nil ) and ( T > 0 ) do begin ApplyParentalBonus( Jobs ); Dec( T ); Jobs := Jobs^.Next; end; if T > 0 then begin AddNAtt( PC^.NA , NAG_Experience , NAS_TotalXP , ( Parental_XP * T * 2 ) div 3 ); end; { Copy over the background NPCs. } while Bio^.InvCom <> Nil do begin Jobs := Bio^.InvCom; DelinkGear( Bio^.InvCom , Jobs ); InsertInvCom( Egg , Jobs ); end; end; Procedure InitBackground( BGGear: GearPtr ); { Initialize this background gear and any NPCs it } { requests. Initialize the description. } var desc: String; NPC,Job1: GearPtr; N: Integer; begin if BGGear = Nil then Exit; desc := SAttValue( BGGear^.SA , 'DESC' ); ReplacePat( desc , '%job%' , SAttValue( PC^.SA , 'JOB' ) ); if NAttValue( PC^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin ReplacePat( desc , '%sd%' , MsgString( 'SON' ) ); end else begin ReplacePat( desc , '%sd%' , MsgString( 'DAUGHTER' ) ); end; { Initialize the NPCs requested by this event. If they are a } { mentor, store their jobs as subcoms of BGGear } NPC := BGGear^.invcom; N := 1; while NPC <> Nil do begin { Assign the NPC a name, and ititialize its age. } SetSAtt( NPC^.SA , 'NAME <' + RandomName + '>' ); AddNAtt( NPC^.NA , NAG_CharDescription , NAS_DAge , NAttValue( PC^.NA , NAG_CharDescription , NAS_DAge ) + Random( 4 ) - Random( 4 ) ); { Select a job. } Job1 := SelectRandomGear( LegalJobList ); if NAttValue( NPC^.NA , NAG_CharDescription , NAS_IsMentor ) <> 0 then begin InsertSubCom( BGGear , CloneGear( Job1 ) ); end; ApplyJobModifiers( NPC , Job1 ); { Alter the DESC } ReplacePat( desc , '%job' + BStr( N ) + '%' , GearName( Job1 ) ); ReplacePat( desc , '%name' + BStr( N ) + '%' , GearName( NPC ) ); { Allocate stat and skill points. } EasyStatPoints( NPC , 105 ); RandomSkillPoints( NPC , 50 , True ); Inc( N ); NPC := NPC^.Next; end; SetSAtt( BGGear^.SA , 'DESC <' + desc + '>' ); end; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_FHAccept' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_FHDecline' ) , -1 ); if CanEdit then begin {$IFDEF ASCII} RCPromptMessage := ''; RCDescMessage := MsgString( 'RANDCHAR_FHDesc' ); RCCaption := MsgString( 'RANDCHAR_FHPrompt' ); {$ELSE} RCPromptMessage := MsgString( 'RANDCHAR_FHPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_FHDesc' ); RCCaption := ''; {$ENDIF} end; Context := SAttValue( PC^.SA , 'HOMETOWN_CONTEXT' ) + ' ' + SAttValue( PC^.SA , 'JOB_DESIG' ) + ' ' + SAttValue( PC^.SA , 'CG_FACDESIG' ); LegalJobList := FilterList( Jobs_List , Context ); Fam := Nil; BioEvent := Nil; repeat { Decide upon the family history here, giving skill points } { and whatever. } { Start with a random component. } if Fam <> Nil then DisposeGear( Fam ); if BioEvent <> Nil then DisposeGear( BioEvent ); { Roll the family type for the PC. } Fam := CloneGear( FindNextComponent( Family_List , 'GENERAL ' + Context ) ); InitBackground( Fam ); bio1 := SAttValue( Fam^.SA , 'DESC' ); { Roll the biography event for the PC. } if Random( 3 ) <> 1 then begin BioEvent := CloneGear( FindNextComponent( Bio_List , 'GENERAL ' + Context + ' ' + SAttValue( Fam^.SA , 'CONTEXT' ) ) ); if BioEvent <> Nil then begin InitBackground( BioEvent ); bio1 := bio1 + ' ' + SAttValue( BioEvent^.SA , 'DESC' ); end; end; AtoAn( bio1 ); { Display the created biography for the user. } SetSAtt( PC^.SA , 'BIO1 <' + Bio1 + '>' ); { Decide whether to accept or decline this family history. } if CanEdit then begin N := SelectMenu( RPM , @RandCharRedraw ); end else begin N := 1; end; until N = 1; ApplyBiographyEvent( Fam ); DisposeGear( Fam ); ApplyBiographyEvent( BioEvent ); DisposeGear( BioEvent ); SetSAtt( PC^.SA , 'BIO1 <' + Bio1 + '>' ); DisposeRPGMenu( RPM ); DisposeGear( LegalJobList ); end; Procedure SelectJobAndFaction( PC: GearPtr; CanEdit: Boolean; ForceFac: Integer ); { Select a job for the PC. } { Based on this job, select a faction. } function NeedsFaction( Job: GearPtr ): Boolean; { Return TRUE if the provided job absolutely must have a faction } { associated with it, or FALSE otherwise. } begin NeedsFaction := AStringHasBString( SAttValue( Job^.SA , 'SPECIAL' ) , 'NeedsFaction' ); end; Function JobFitsFaction( Job,Faction: GearPtr ): Boolean; { Return TRUE if this job fits this faction, or FALSE otherwise. } begin JobFitsFaction := AStringHasBString( SAttValue( Faction^.SA , 'JOBS' ) , SAttValue( Job^.SA , 'Desig' ) ); end; Function CreateFactionList( Loc_Factions: String; Job: GearPtr ): GearPtr; { Create a list of legal factions for the PC to choose from. } { It must be a faction featured in the PC's home town, and it must } { be hiring people on the PC's job path. } { If ForceFac is nonzero, it must be that faction. } var it,F: GearPtr; begin if ForceFac <> 0 then begin it := CloneGear( SeekCurrentLevelGear( Factions_List , GG_Faction , ForceFac ) ); end else begin it := Nil; F := Factions_List; while F <> Nil do begin if AStringHasBString( Loc_Factions , SAttValue( F^.SA , 'DESIG' ) ) and JobFitsFaction( Job , F ) then begin AppendGear( it , CloneGear( F ) ); end; F := F^.Next; end; end; CreateFactionList := it; end; Procedure DoExtraFacFilter( Fac: GearPtr; var LegalJobList: GearPtr ); { The PC must belong to a specific faction or no faction at all. } { If any of these jobs have a preset faction or require a faction } { but can't be taken by the available faction, they get deleted from } { the list. That's an awful run-on sentance but I was busy all day } { making kimchi. } var J,J2: GearPtr; FID: Integer; begin J := LegalJobList; while J <> Nil do begin J2 := J^.Next; FID := NAttValue( J^.NA , NAG_Personal , NAS_FactionID ); if ( FID <> 0 ) and ( FID <> ForceFac ) then begin RemoveGear( LegalJobList , J ); end else if NeedsFaction( J ) and not JobFitsFaction( J , Fac ) then begin RemoveGear( LegalJobList , J ); end; J := J2; end; end; Function JobDescription( Job: GearPtr ): String; { Return a description for this job: This will be its category } { and its list of skills. } var msg: String; S,N: Integer; begin { Start with the job category. } msg := '(' + SAttValue( Job^.SA , 'DESIG' ) + ') '; { Add the skills. } N := 0; for S := 1 to NumSkill do begin if NAttValue( Job^.NA , NAG_Skill , S ) <> 0 then begin if N > 0 then msg := msg + ', '; msg := msg + MsgString( 'SkillName_' + BStr( S ) ); inc( N ); end; end; JobDescription := msg; end; var RPM: RPGMenuPtr; LegalJobList,Job,LegalFactionList,F: GearPtr; Context: String; N: Integer; { Procedures block. } begin Context := SAttValue( PC^.SA , 'HOMETOWN_CONTEXT' ); LegalJobList := FilterList( Jobs_List , Context ); if ForceFac <> 0 then DoExtraFacFilter( SeekCurrentLevelGear( Factions_List , GG_Faction , ForceFac ) , LegalJobList ); if CanEdit then begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); {$IFDEF ASCII} AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCPromptMessage := ''; RCDescMessage := MsgString( 'RANDCHAR_JobDesc' ); RCCaption := MsgString( 'RANDCHAR_JobPrompt' ); {$ELSE} RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_JobPrompt' ); RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenDesc ); {$ENDIF} { Fill the menu. } Job := LegalJobList; N := 1; while Job <> Nil do begin AddRPGMenuItem( RPM , GearName( Job ) , N , JobDescription( Job ) ); Inc( N ); Job := Job^.Next; end; RPMSortAlpha( RPM ); { Select an item from the menu. } N := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); { Locate the Job gear selected. If no job was selected, pick one randomly. } if N > -1 then begin Job := RetrieveGearSib( LegalJobList , N ); end else begin Job := SelectRandomGear( LegalJobList ); end; end else begin Job := SelectRandomGear( LegalJobList ); end; ApplyJobModifiers( PC , Job ); { Copy the changes to the PC's context. } Context := SAttValue( PC^.SA , 'CONTEXT' ) + ' C:' + SAttValue( Job^.SA , 'DESIG' ); SetSAtt( PC^.SA , 'CONTEXT <' + Context + '>' ); { Next, see about a faction. Some jobs have factions assigned to them... } { For instance, if your job is "Knight", you'll start as a member of the } { Silver Knights. The designation of your job and your home town will } { determine what factions you can join. You are also free to not join a } { faction, unless your job indicates that it requires a faction choice. } if NAttValue( Job^.NA , NAG_Personal , NAS_FactionID ) <> 0 then begin { This job comes with a pre-assigned faction. } SetNAtt( PC^.NA , NAG_Personal , NAS_FactionID , NAttValue( Job^.NA , NAG_Personal , NAS_FactionID ) ); end else if CanEdit or NeedsFaction( Job ) then begin { This job can maybe have a faction assigned. } LegalFactionList := CreateFactionList( SAttValue( PC^.SA , 'HOMETOWN_FACTIONS' ) , Job ); if CanEdit and ( LegalFactionList <> Nil ) then begin { Create the menus. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); {$IFDEF ASCII} AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCPromptMessage := ''; RCCaption := MsgString( 'RANDCHAR_FactionPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_FactionDesc' ); {$ELSE} AttachMenuDesc( RPM , ZONE_CharGenDesc ); RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_FactionPrompt' ); RCCaption := ''; {$ENDIF} { Add the factions. } F := LegalFactionList; while F <> Nil do begin AddRPGMenuItem( RPM , GearName( F ) , F^.S , SAttValue( F^.SA , 'DESC' ) ); F := F^.Next; end; RPMSortAlpha( RPM ); { If this job absolutely requires a faction, don't add the "NoFac" option } { to the menu. } if not NeedsFaction( Job ) then AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_NoFactionPlease' ) , -1 ); { If there are any factions in the menu, select one. } if RPM^.NumItem > 1 then begin N := SelectMenu( RPM , @RandCharRedraw ); end else N := RPM^.FirstItem^.Value; F := SeekCurrentLevelGear( LegalFactionList , GG_Faction , N ); if ( F = Nil ) and NeedsFaction( Job ) then F := SelectRandomGear( LegalFactionList ); { Get rid of the menu. } DisposeRPGMenu( RPM ); end else if NeedsFaction( Job ) or ( Random( 3 ) = 1 ) then begin F := SelectRandomGear( LegalFactionList ); end; { Apply the bonuses for this faction. } if F <> Nil then begin SetNAtt( PC^.NA , NAG_Personal , NAS_FactionID , F^.S ); AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , 50000 ); SetSAtt( PC^.SA , 'CG_FacDesig <' + SAttValue( F^.SA , 'DESIG' ) + '>' ); end; { Get rid of the factions list. } DisposeGear( LegalFactionList ); end; DisposeGear( LegalJobList ); end; Procedure AllocateStatPoints( PC: GearPtr; StatPt: Integer ); { Distribute the listed number of points out to the PC. } var RPM: RPGMenuPtr; PCStats: Array [1..NumGearStats] of Integer; T: Integer; Function StatSelectorMsg( N: Integer ): String; var msg: String; begin msg := MsgString( 'StatName_' + BStr( N ) ); {$IFNDEF ASCII} while TextLength( Game_Font , msg ) < ( ZONE_CharGenMenu.W - 50 ) do msg := msg + ' '; {$ELSE} while Length( msg ) < 12 do msg := msg + ' '; {$ENDIF} msg := msg + BStr( PCStats[ N ] + PC^.Stat[ N ] ); StatSelectorMsg := msg; end; begin { Zero out the base stat line, and make sure minimum values are met. } for t := 1 to NumGearStats do begin PCStats[ T ] := 0; if PC^.Stat[ T ] < 1 then begin PC^.Stat[ T ] := 1; Dec( StatPt ); end; end; { Create the menu & set up the display. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); RPM^.Mode := RPMNoCleanup; {$IFDEF ASCII} AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCPromptMessage := ''; RCDescMessage := MsgString( 'RANDCHAR_ASPDesc' ); RCCaption := ''; {$ELSE} RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_StatPrompt' ); RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenDesc ); {$ENDIF} for t := 1 to NumGearStats do begin AddRPGMenuItem( RPM , StatSelectorMsg( T ) , 1 , MsgString( 'STATDESC_' + BStr( T ) ) ); end; AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_ASPDone' ) , 2 ); RPM^.dtexcolor := InfoGreen; { Add RPGKeys for the left and right buttons, since these will be } { used to spend & retrieve points. } {$IFNDEF ASCII} AddRPGMenuKey( RPM , RPK_Right , 1 ); AddRPGMenuKey( RPM , RPK_Left , -1 ); {$ELSE} AddRPGMenuKey( RPM , KeyMap[ KMC_East ].KCode , 1 ); AddRPGMenuKey( RPM , KeyMap[ KMC_West ].KCode , -1 ); {$ENDIF} repeat RCCaption := ReplaceHash( MsgString( 'RANDCHAR_SelectStatsCap' ) , BStr( StatPt ) ); T := SelectMenu( RPM , @RandCharRedraw ); if ( T = 1 ) and ( RPM^.selectitem <= NumGearStats ) and ( StatPt > 0 ) then begin { Increase Stat } { Only do this if the stat is currently below the max value. } if PCStats[ RPM^.selectitem ] < MaxStartingStat then begin { Only do this if the player has enough points to do so... } if ( StatPt > 1 ) or ( PCStats[ RPM^.selectitem ] < NormalMaxStatValue ) then begin { Increase the stat. } Inc( PCStats[ RPM^.selectitem ] ); { Decrease the free stat points. Take away 2 if } { this stat has been improved to the normal maximum. } Dec( StatPt ); if PCStats[ RPM^.selectitem ] > NormalMaxStatValue then Dec( StatPt ); { Replace the message line. } RPMLocateByPosition(RPM , RPM^.selectitem )^.msg := StatSelectorMsg( RPM^.selectitem ); end; end; end else if ( T = -1 ) and ( RPM^.selectitem <= NumGearStats ) then begin { Decrease Stat } if PCStats[ RPM^.selectitem ] > 0 then begin { Decrease the stat. } Dec( PCStats[ RPM^.selectitem ] ); { Increase the free stat points. Give back 2 if } { this stat has been improved to the normal maximum. } Inc( StatPt ); if PCStats[ RPM^.selectitem ] >= NormalMaxStatValue then Inc( StatPt ); { Replace the message line. } RPMLocateByPosition(RPM , RPM^.selectitem )^.msg := StatSelectorMsg( RPM^.selectitem ); end; end; until T = 2; { Copy temporary values into the PC record. } for T := 1 to NumGearStats do PC^.Stat[T] := PC^.Stat[T] + PCStats[T]; { Spend remaining stat points randomly. } if StatPt > 0 then RollStats( PC , StatPt ); { Get rid of the menu. } DisposeRPGMenu( RPM ); end; Procedure SelectATalent( PC: GearPtr ); { The PC needs to select a talent. Create a list of all the } { legally available talents, then have the PC select one. } var RPM: RPGMenuPtr; T: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); { Add the legal talents. } for t := 1 to NumTalent do begin if CanLearnTalent( PC , T ) then AddRPGMenuItem( RPM , MsgString( 'TALENT' + BStr( T ) ) , T , MsgString( 'TALENTDESC' + BStr( T ) ) ); end; RPM^.Mode := RPMNoCancel; RPMSortAlpha( RPM ); ALphaKeyMenu( RPM ); {$IFDEF ASCII} RCCaption := MsgString( 'RANDCHAR_TalentPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_TalentDesc' ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCPromptMessage := ''; {$ELSE} RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_TalentPrompt' ); RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenDesc ); {$ENDIF} T := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); ApplyTalent( PC , T ); end; Procedure SelectRandomTalent( PC: GearPtr ); { Select one of the generic talents for this PC. } begin { If the PC has high Martial Arts skill, assign either Kung Fu or Hap Ki Do. } if CanLearnTalent( PC , NAS_KungFu ) and ( Random( 3 ) <> 1 ) then begin ApplyTalent( PC , NAS_KungFu ); end else if CanLearnTalent( PC , NAS_HapKiDo ) then begin ApplyTalent( PC , NAS_HapKiDo ); end else if CanLearnTalent( PC , NAS_Ninjitsu ) and ( Random( 2 ) <> 1 ) then begin ApplyTalent( PC , NAS_Ninjitsu ); end else if CanLearnTalent( PC , NAS_HardAsNails ) and ( Random( 2 ) <> 1 ) then begin ApplyTalent( PC , NAS_HardAsNails ); end else if CanLearnTalent( PC , NAS_Camaraderie ) and ( Random( 2 ) <> 1 ) then begin ApplyTalent( PC , NAS_Camaraderie ); end else if CanLearnTalent( PC , NAS_JackOfAll ) and ( Random( 2 ) <> 1 ) then begin ApplyTalent( PC , NAS_JackOfAll ); end else if CanLearnTalent( PC , NAS_Sniper ) and ( Random( 2 ) <> 1 ) then begin ApplyTalent( PC , NAS_Sniper ); end else if CanLearnTalent( PC , NAS_BusinessSense ) and ( Random( 2 ) <> 1 ) then begin ApplyTalent( PC , NAS_BusinessSense ); end else if CanLearnTalent( PC , NAS_StuntDriving ) and ( Random( 3 ) <> 1 ) then begin ApplyTalent( PC , NAS_StuntDriving ); end else if CanLearnTalent( PC , NAS_Bishounen ) and ( Random( 5 ) <> 1 ) then begin ApplyTalent( PC , NAS_Bishounen ); { At the very end, if no other talents can be learned, apply one of the two } { generic talents which don't have any pre-requisites. } end else if ( Random( 5 ) = 1 ) and CanLearnTalent( PC , NAS_Polymath ) then begin ApplyTalent( PC , NAS_Polymath ); end else begin ApplyTalent( PC , NAS_Idealist ); end; end; Procedure SelectMecha( Egg,PC: GearPtr; CanEdit: Boolean ); { Select a mecha for the PC to start with. } const BaseMechaAllowance = 250000; MaxMechaAllowance = 350000; var Factions: String; MechaList,MList,Mek: GearPtr; Fac: GearPtr; MVP,cash,N: LongInt; RPM: RPGMenuPtr; SRec: SearchRec; begin { Determine what mechas the PC can use. } Factions := 'GENERAL ' + SATtValue( PC^.SA , 'HOMETOWN_GOVERNMENT' ); Fac := SeekCurrentLevelGear( Factions_List , GG_Faction , NAttValue( PC^.NA , NAG_Personal , NAS_FactionID ) ); if Fac <> Nil then factions := factions + ' ' + SAttValue( Fac^.SA , 'DESIG' ); { Determine the maximum value of a mecha to select. This is modified upwards } { if the PC has a lot of money. } MVP := BaseMechaAllowance; cash := NAttValue( PC^.NA , NAG_Experience , NAS_Credits ); if Cash > 10000 then MVP := BaseMechaAllowance + ( Cash div 2 ) - 5000; if MVP > MaxMechaAllowance then MVP := MaxMechaAllowance; { Generate the mecha shopping list. } MechaList := Nil; { Start the search process going... } FindFirst( Design_Directory + Default_Search_Pattern , AnyFile , SRec ); { As long as there are files which match our description, } { process them. } While DosError = 0 do begin { Load this mecha design file from disk. } MList := LoadFile( SRec.Name , Design_Directory ); { Search through it for mecha. } Mek := MList; while Mek <> Nil do begin if ( Mek^.G = GG_Mecha ) then begin if ( GearValue( Mek ) <= MVP ) and PartMatchesCriteria( SAttValue( Mek^.SA , 'TYPE' ) , All_Terrain_Designations ) and PartAtLeastOneMatch( SAttValue( Mek^.SA , 'FACTIONS' ) , Factions ) then begin AppendGear( MechaList , CloneGear( Mek ) ); end; end; Mek := Mek^.Next; end; { Dispose of the list. } DisposeGear( MList ); { Look for the next file in the directory. } FindNext( SRec ); end; { Select a mecha. } { The exact method is gonna depend on whether or not the PC can edit. } if CanEdit then begin { Allocate the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); RPM^.Mode := RPMNoCancel; {$IFDEF ASCII} RCCaption := MsgString( 'RANDCHAR_MechaPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_MechaDesc' ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCPromptMessage := ''; {$ELSE} RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_MechaPrompt' ); RCCaption := ''; AttachMenuDesc( RPM , ZONE_CharGenDesc ); {$ENDIF} { Add the mecha to the menu. } Mek := MechaList; N := 1; while Mek <> Nil do begin AddRPGMenuItem( RPM , FullGearName( Mek ) , N , SAttValue( Mek^.SA , 'DESC' ) ); Inc( N ); Mek := Mek^.Next; end; { Select one of them. } N := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); Mek := RetrieveGearSib( MechaList , N ); end else begin Mek := SelectRandomGear( MechaList ); end; { Attach a copy of the selected mecha to the egg. } if Mek <> Nil then begin Mek := CloneGear( Mek ); InsertSubCom( Egg , Mek ); if GearValue( mek ) > BaseMechaAllowance then begin AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , BaseMechaAllowance - GearValue( Mek ) ); end; end; { Dispose of the shopping list. } DisposeGear( MechaList ); RCCaption := ''; end; Procedure SetTraits( PC: GearPtr ); { Set some personality traits for the PC. } Procedure DoTraitType( MasterList: GearPtr ); { Do the menu for this trait type. } var RPM: RPGMenuPtr; T: GearPtr; Base,Changes: String; N: Integer; begin { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); RPM^.Mode := RPMNoCancel; { Add the traits. } T := MasterList; N := 1; while T <> Nil do begin AddRPGMenuItem( RPM , GearName( T ) , N ); T := T^.Next; Inc( N ); end; RPMSortAlpha( RPM ); { Get a menu selection. } N := SelectMenu( RPM , @RandCharRedraw ); DisposeRPGMenu( RPM ); { Locate the trait selected, and copy over its stuff. } T := RetrieveGearSib( MasterList , N ); if T <> Nil then begin { Copy over the personality traits from the biography event. } for N := 1 to Num_Personality_Traits do begin AddReputation( PC , N , NAttValue( T^.NA , NAG_CharDescription , -N ) ); end; { Copy the changes to the PC's context. } Base := SAttValue( PC^.SA , 'CONTEXT' ); Changes := SAttValue( T^.SA , 'CONTEXT' ); AlterDescriptors( Base , Changes ); SetSAtt( PC^.SA , 'CONTEXT <' + Base + '>' ); end; end; begin {$IFDEF ASCII} RCDescMessage := MsgString( 'RANDCHAR_TraitDesc' ); RCPromptMessage := MsgString( 'RANDCHAR_FocusPrompt' ); {$ELSE} RCDescMessage := MsgString( 'RANDCHAR_FocusPrompt' ); RCPromptMessage := MsgString( 'RANDCHAR_TraitPrompt' ); {$ENDIF} DoTraitType( Focus_List ); {$IFDEF ASCII} RCPromptMessage := MsgString( 'RANDCHAR_GoalPrompt' ); {$ELSE} RCDescMessage := MsgString( 'RANDCHAR_GoalPrompt' ); {$ENDIF} DoTraitType( Goal_List ); end; {$IFNDEF ASCII} Procedure SelectColors( PC: GearPtr; CanEdit: Boolean ); { Select colors for this character. } var sdl_colors: String; begin RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_SelectColorsPrompt' ); if CanEdit then sdl_colors := SelectColorPalette( colormenu_mode_character, SAttValue( PC^.SA , 'SDL_PORTRAIT' ), SAttValue( PC^.SA , 'SDL_COLORS' ), 100, 150, 0, @RandCharRedraw ) else sdl_colors := RandomColorString( CS_Clothing ) + ' ' + RandomColorString( CS_Skin ) + ' ' + RandomColorString( CS_Hair ); { Record the colors. } SetSAtt( PC^.SA , 'SDL_Colors <' + sdl_colors + '>' ); end; Procedure SelectSprite( PC: GearPtr ); { Select a sprite for the PC's portrait. } var RPM: RPGMenuPtr; PList: SAttPtr; P,N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_NextPicture' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_LastPicture' ) , 2 ); AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_AcceptPicture' ) , -1 ); if NAttValue( PC^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin PList := CreateFileList( Graphics_Directory + 'por_m_*.*' ); end else if NAttValue( PC^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Female then begin PList := CreateFileList( Graphics_Directory + 'por_f_*.*' ); end else begin PList := CreateFileList( Graphics_Directory + 'por_*_*.*' ); end; RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_PicturePrompt' ); RCCaption := ''; P := Random( NumSAtts( PList ) ) + 1; repeat CleanSpriteList; SetSAtt( PC^.SA , 'SDL_PORTRAIT <' + RetrieveSAtt( PList , P )^.Info + '>' ); N := SelectMenu( RPM , @RandCharRedraw ); if N = 1 then begin Inc( P ); if P > NumSatts( PList ) then P := 1; end else if N = 2 then begin Dec( P ); if P < 1 then P := NumSatts( PList ); end; until N = -1; DisposeSAtt( PList ); DisposeRPGMenu( RPM ); end; {$ENDIF} Procedure SetRomance( PC: GearPtr ); { Decide on the sort of NPCs this PC is interested in (in that way). } var RPM: RPGMenuPtr; N: Integer; begin { Create the menu and set up the display. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_CharGenMenu ); RCPromptMessage := MsgString( 'RANDCHAR_RomPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_RomDesc' ); { Add the romance options to the menu. } for N := 0 to 3 do begin AddRPGMenuItem( RPM , MsgString( 'RANDCHAR_RomOp' + BStr(N)) , N ); end; RPMSortAlpha( RPM ); N := SelectMenu( RPM , @RandCharRedraw ); if N = -1 then N := 0; SetNAtt( PC^.NA , NAG_CharDescription, NAS_RomanceType, N ); { Get rid of the menu. } DisposeRPGMenu( RPM ); end; Procedure ReputationCompensation( PC: GearPtr ); { If the PC starts the game as Wangtta, Villainous, or Criminal, better } { give him some bonuses to make up for all the #@@!$! he's gonna get } { once the campaign starts. } var Trait,Pts: Integer; begin Pts := 0; { Start by checking heroism. } Trait := NAttValue( PC^.NA , NAG_CharDescription , NAS_Heroic ); if Trait < -10 then begin Pts := Abs( Trait ); end else if Trait < 0 then begin Pts := 5; end; { Next, check law. } Trait := NAttValue( PC^.NA , NAG_CharDescription , NAS_Lawful ); if Trait < -10 then begin Pts := Pts + 10; end else if Trait < 0 then begin Pts := Pts + 1; end; { Finally, check renown. } Trait := NAttValue( PC^.NA , NAG_CharDescription , NAS_Renowned ); if Trait < -10 then begin Pts := Pts + 7; end else if Trait < 0 then begin Pts := Pts + 5; end; while Pts > 0 do begin if Random( 2 ) = 1 then begin { Experience award. } AddNAtt( PC^.NA , NAG_Experience, NAS_TotalXP , Random( 5 ) ); end else begin { Cash award. } AddNAtt( PC^.NA , NAG_Experience, NAS_Credits , Random( 200 ) ); end; Dec( Pts ); end; end; Function CharacterCreator( Fac: Integer ): GearPtr; { This is my brand-spankin' new character generator. It is meant } { to emulate the interactive way in which characters are generated } { for such games as Mekton and Traveller. } { The character may be limited to a certain faction, in which case } { FAC will be non-zero. } const MODE_Regular = 1; MODE_Easy = 0; var Egg,PC: GearPtr; M: Integer; N,StatPt,SkillPt: LongInt; name: String; begin RCPC := Nil; M := SelectMode; if M = -1 then Exit( Nil ); { Start by allocating the PC record. } Egg := NewGear( Nil ); PC := NewGear( Nil ); InsertSubCom( Egg , PC ); PC^.G := GG_Character; InitGear( PC ); StatPt := 90; SkillPt := 50; {$IFNDEF ASCII} SetSAtt( PC^.SA, 'SDL_PORTRAIT ' ); SetSAtt( PC^.SA, 'SDL_COLORS <' + RandomColorString(CS_Clothing) + ' ' + RandomColorString(CS_Skin) + ' ' + RandomColorString(CS_Hair) + '>' ); {$ENDIF} { First select gender, keeping in mind that the selection may be } { cancelled. } N := SelectGender; if N = -1 then begin DisposeGear( PC ); Exit( Nil ); end else begin SetNAtt( PC^.NA , NAG_CharDescription , NAS_Gender , N ); end; { Next select age. } if M = MODE_Regular then begin N := SelectAge; SetNAtt( PC^.NA , NAG_CharDescription , NAS_DAge , N ); CharacterDisplay( PC , Nil , ZONE_CharGenChar ); end else begin N := Random( 10 ) - Random( 5 ); SetNAtt( PC^.NA , NAG_CharDescription , NAS_DAge , N ); end; { Adjust cash & free skill points based on Age. } AddNAtt( PC^.NA , NAG_Experience , NAS_TotalXP , ( N + 5 ) * 25 ); AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , 150000 - N * 5000 + Random( 1000 ) ); RCPC := PC; { Next, select home town. } if M = MODE_Regular then begin SelectHomeTown( PC , True , Fac ); end else begin SelectHomeTown( PC , False , Fac ); end; SelectJobAndFaction( PC , M = MODE_Regular , Fac ); GenerateFamilyHistory( Egg , PC , M = MODE_Regular ); { Allocate stat points. } if M = MODE_Regular then begin AllocateStatPoints( PC , StatPt ); CharacterDisplay( PC , Nil, ZONE_CharGenChar ); end else begin EasyStatPoints( PC , StatPt ); end; { Allocate skill points. } if M = MODE_Regular then begin AllocateSkillPoints( PC , SkillPt ); CharacterDisplay( PC , Nil, ZONE_CharGenChar ); end else begin RandomSkillPoints( PC , SkillPt , False ); end; { Select a talent. } if M = MODE_Regular then begin SelectATalent( PC ); end else begin SelectRandomTalent( PC ); end; SetRomance( PC ); SelectMecha( Egg , PC , M = MODE_Regular ); { Set personality traits. } if M = MODE_Regular then begin SetTraits( PC ); CharacterDisplay( PC , Nil, ZONE_CharGenChar ); end; { The background and so forth may have started the PC with a bad reputation. } { If this has happened, give the PC some XP and cash to compensate. } ReputationCompensation( PC ); {$IFNDEF ASCII} { In SDLMode, before selecting a name, finalize the portrait. } SelectSprite( PC ); SelectColors( PC , M = MODE_Regular ); {$ENDIF} { Select a name. } { If no name is entered, this cancels character creation. } name := GetStringFromUser( MsgString( 'RANDCHAR_GetName' ) , @RandCharRedraw ); if Name <> '' then begin SetSAtt( PC^.SA , 'NAME <'+name+'>'); if PC^.Next <> Nil then SetSAtt( PC^.Next^.SA , 'PILOT <' + name + '>' ); CharacterDisplay( PC , Nil, ZONE_CharGenChar ); end else begin DisposeGear( Egg ); end; { Clear the screen, and return the PC. } CharacterCreator := Egg; end; Function RandomNPC( Adv: GearPtr; Fac,Hometown: Integer ): GearPtr; { Create a random character, using most of the same materials as available } { for a regular character. } var NPC,City: GearPtr; begin NPC := NewGear( Nil ); NPC^.G := GG_Character; InitGear( NPC ); { Set a random gender and age. } SetNAtt( NPC^.NA , NAG_CharDescription , NAS_Gender , Random( 2 ) ); SetNAtt( NPC^.NA , NAG_CharDescription , NAS_DAge , Random( 10 ) - Random( 5 ) ); { If no home town was provided, select one randomly. } if HomeTown = 0 then begin SelectHomeTown( NPC , False , Fac ); end else begin City := SeekGear( Adv , GG_Scene , HomeTown , False ); if ( City <> Nil ) and ( City^.G = GG_Scene ) then begin { City := FindRootScene( City );} StoreHomeTownDataInPC( NPC , City ); end else begin SelectHomeTown( NPC , False , Fac ); end; end; { Select a job and maybe even a faction. } SelectJobAndFaction( NPC , False , Fac ); { Allocate stats and skills. } EasyStatPoints( NPC , 100 ); RandomSkillPoints( NPC , 50 , True ); { Set this NPC as a combatant. } SetNAtt( NPC^.NA , NAG_CharDescription , NAS_IsCombatant , 1 ); { Remove the character context, since it will get in the way later. } SetSAtt( NPC^.SA , 'CONTEXT <>' ); { Return the result. } RandomNPC := NPC; end; Procedure TrimTheAtlas(); { Remove everything from the atlas that isn't a hometown. } var City,C2: GearPtr; begin City := Hometown_List; while City <> Nil do begin C2 := City^.Next; if not( ( City^.G = GG_Scene ) and AStringHasBString( SAttValue( City^.SA , 'TYPE' ) , 'HOMETOWN' ) ) then begin { This isn't a home town. Delete it. } RemoveGear( Hometown_List , City ); end else begin { This is a home town. Delete its children. } DisposeGear( City^.SubCom ); DisposeGear( City^.InvCom ); end; City := C2; end; end; initialization Jobs_List := AggregatePattern( 'CG_JOBS_*.txt' , Series_Directory ); Family_List := LoadRandomSceneContent( 'CG_FAMILY_*.txt' , Series_Directory ); Bio_List := LoadRandomSceneContent( 'CG_BIO_*.txt' , Series_Directory ); Goal_List := AggregatePattern( 'CG_GOAL_*.txt' , Series_Directory ); Focus_List := AggregatePattern( 'CG_FOCUS_*.txt' , Series_Directory ); Hometown_List := AggregatePattern( 'ATLAS_*.txt' , Series_Directory ); TrimTheAtlas(); finalization DisposeGear( Jobs_List ); DisposeGear( Family_List ); DisposeGear( Bio_List ); DisposeGear( Goal_List ); DisposeGear( Focus_List ); DisposeGear( Hometown_List ); end. gearhead-2-0.701/colormenu.pp000066400000000000000000000423251321074026100157660ustar00rootroot00000000000000unit colormenu; { This unit contains the color selector menu code. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,sdl,ui4gh,sdlgfx; const colormenu_mode_allcolors = 0; colormenu_mode_character = 1; colormenu_mode_mecha = 2; Num_Color_Sets = 6; CS_Clothing = 1; CS_Skin = 2; CS_Hair = 3; CS_PrimaryMecha = 4; CS_SecondaryMecha = 5; CS_Detailing = 6; type ColorDesc = Record name: String; rgb: TSDL_Color; cs: Array [1..Num_Color_Sets] of Boolean; end; var Available_Colors: Array of ColorDesc; Num_Available_Colors: Integer; Num_Colors_Per_Set: Array [1..Num_Color_Sets] of Integer; Function SelectColorPalette( init_mode: Integer; image_name,color_palette: String; image_width,image_height,anim_frames: Integer; Redrawer: RedrawProcedureType ): String; Function RandomColorString( ColorSet: Integer ): String; implementation uses texutil; const Swatch_Columns = 20; Swatch_Rows = 5; Swatch_Width = 16; Swatch_Height = 24; cm_panel_width = 600; cm_panel_height = 456; cm_window_dx = -cm_panel_width div 2; cm_window_dy = -230; cm_image_x_offset = 20; cm_image_y_offset = 20; { Relative positioning of the color selection boxes. } cm_swatchzone_x_offset = 250; cm_swatchzone_y_start = 11; cm_swatchzone_height = 145; { Offset from the upper left corner of the selection box } { where the swatch areas start. } off_swatches_x = 3; off_swatches_y = 19; ZONE_colormenu_base: DynamicRect = ( dx: cm_window_dx; dy: cm_window_dy; w: cm_panel_width; h: cm_panel_height; anchor: ANC_MIDDLE ); ZONE_colormenu_sprite: DynamicRect = ( dx: cm_image_x_offset+cm_window_dx; dy: cm_window_dy + cm_image_y_offset; w: 211; h: 308; anchor: ANC_MIDDLE ); ZONE_colorselectionboxes: Array [1..3] of DynamicRect = ( ( dx: cm_window_dx + cm_swatchzone_x_offset; dy: cm_window_dy + cm_swatchzone_y_start; w: 600; h: 145; anchor: ANC_MIDDLE ), ( dx: cm_window_dx + cm_swatchzone_x_offset; dy: cm_window_dy + cm_swatchzone_y_start + cm_swatchzone_height; w: 600; h: 145; anchor: ANC_MIDDLE ), ( dx: cm_window_dx + cm_swatchzone_x_offset; dy: cm_window_dy + cm_swatchzone_y_start + 2 * cm_swatchzone_height; w: 600; h: 145; anchor: ANC_MIDDLE ) ); ZONE_swatch_area: Array [1..3] of DynamicRect = ( ( dx: cm_window_dx + cm_swatchzone_x_offset + off_swatches_x; dy: cm_window_dy + cm_swatchzone_y_start + off_swatches_y; w: Swatch_Width * Swatch_Columns; h: Swatch_Height * Swatch_Rows; anchor: ANC_MIDDLE ), ( dx: cm_window_dx + cm_swatchzone_x_offset + off_swatches_x; dy: cm_window_dy + cm_swatchzone_y_start + cm_swatchzone_height + off_swatches_y; w: Swatch_Width * Swatch_Columns; h: Swatch_Height * Swatch_Rows; anchor: ANC_MIDDLE ), ( dx: cm_window_dx + cm_swatchzone_x_offset + off_swatches_x; dy: cm_window_dy + cm_swatchzone_y_start + 2 * cm_swatchzone_height + off_swatches_y; w: Swatch_Width * Swatch_Columns; h: Swatch_Height * Swatch_Rows; anchor: ANC_MIDDLE ) ); { ZONE_colormenu_base: TSDL_Rect = ( x: cm_window_x; y: cm_window_y; w: cm_panel_width; h: cm_panel_height ); ZONE_colormenu_sprite: TSDL_Rect = ( x: cm_window_x + cm_image_x_offset; y: cm_window_y + cm_image_y_offset; w: 211; h: 308 ); ZONE_colorselectionboxes: Array [1..3] of TSDL_Rect = ( ( x: cm_window_x + cm_swatchzone_x_offset; y: cm_window_y + cm_swatchzone_y_start; w: 600; h: 145 ), ( x: cm_window_x + cm_swatchzone_x_offset; y: cm_window_y + cm_swatchzone_y_start + cm_swatchzone_height; w: 600; h: 145 ), ( x: cm_window_x + cm_swatchzone_x_offset; y: cm_window_y + cm_swatchzone_y_start + 2 * cm_swatchzone_height; w: 600; h: 145 ) ); ZONE_swatch_area: Array [1..3] of TSDL_Rect = ( ( x: cm_window_x + cm_swatchzone_x_offset + off_swatches_x; y: cm_window_y + cm_swatchzone_y_start + off_swatches_y; w: Swatch_Width * Swatch_Columns; h: Swatch_Height * Swatch_Rows ), ( x: cm_window_x + cm_swatchzone_x_offset + off_swatches_x; y: cm_window_y + cm_swatchzone_y_start + cm_swatchzone_height + off_swatches_y; w: Swatch_Width * Swatch_Columns; h: Swatch_Height * Swatch_Rows ), ( x: cm_window_x + cm_swatchzone_x_offset + off_swatches_x; y: cm_window_y + cm_swatchzone_y_start + 2 * cm_swatchzone_height + off_swatches_y; w: Swatch_Width * Swatch_Columns; h: Swatch_Height * Swatch_Rows ) ); } var colormenu_ReDrawer: RedrawProcedureType; colormenu_imagename, colormenu_imagepalette: String; colormenu_imagewidth, colormenu_imageheight: Integer; colormenu_animframes: Integer; cm_panel,cm_bits: SensibleSpritePtr; colormenu_colorset: Array [1..3] of Integer; { What color set is being used in each selection box? } colormenu_currentpen: Array [1..3] of Integer; { What pen is being used in each selection box? } colormenu_penswap: Array [1..3] of String; { The swap strings for each channel, separated. } colormenu_rowoffset: Array [1..3] of Integer; { Controls the positioning of the color swatches in each box. } colormenu_channel, colormenu_curs_x, colormenu_curs_y: Integer; { Where is the cursor? } Procedure DrawColorSelectionBox( Z: TSDL_Rect; CSet, CPen, CTop, Curs_X, Curs_Y: Integer ); { Draw this color selection box in the provided zone. } { CSet: The color set being used. } { CPen: The currently selected pen, or -1 for an unknown color. } { CTop: The first row to be displayed, starting from 0. } const off_palettename_x = 3; off_palettename_y = 3; off_colorname_x = 122; var MyDest: TSDL_Rect; T,X,Y,N: Integer; myswatch: SensibleSpritePtr; begin MyDest := Z; { Display the palette type. } { If the color set is out of range, set it to "0" for all colors. } MyDest.X := MyDest.X + off_palettename_x; MyDest.Y := MyDest.Y + off_palettename_y; if ( CSet >= 0 ) and ( CSet <= Num_Color_Sets ) then QuickText( MsgString( 'ColorSet_' + BStr( CSet ) ) , MyDest , StdBlack, Game_Font ) else CSet := 0; { Display the pen name. } MyDest.X := Z.X + off_colorname_x; if ( CPen >= 0 ) and ( CPen < Num_Available_Colors ) then QuickText( Available_Colors[ CPen ].name , MyDest , InfoHiLight, Game_Font ) else QuickText( '???' , MyDest , InfoHiLight, Game_Font ); { Display the swatches. } { T will cycle through all colors; N will count the number of colors displayed so far. } N := 0; MyDest.W := Swatch_Width - 2; MyDest.H := Swatch_Height - 2; for t := 0 to ( Num_Available_Colors - 1 ) do begin { Figure out whether or not we should display this color for this palette. } if ( CSet = 0 ) or Available_Colors[ t ].cs[ CSet ] then begin { Alright, this is one of the ones we're supposed to display. } { Figure out its position, based on N and CTop. } X := N mod Swatch_Columns; Y := ( N div Swatch_Columns ) - CTop; if ( Y >= 0 ) and ( Y < Swatch_Rows ) then begin { Display the color first, then the cursor and selection check. } MyDest.X := Z.X + off_swatches_x + X * Swatch_Width + 1; MyDest.Y := Z.Y + off_swatches_y + Y * Swatch_Height + 1; {SDL_FillRect( game_screen , @MyDest , SDL_MapRGB( Game_Screen^.Format , Available_Colors[ T ].rgb.R , Available_Colors[ T ].rgb.G , Available_Colors[ T ].rgb.B ) );} myswatch := LocateSprite('color_menu_swatch.png',BStr( Available_Colors[ T ].rgb.R) +' '+ Bstr(Available_Colors[ T ].rgb.G)+' '+BStr(Available_Colors[ T ].rgb.B)+' 0 0 0 0 0 0',Swatch_Width,Swatch_Height); DrawSprite( myswatch, MyDest, 0 ); MyDest.X := MyDest.X - 1; MyDest.Y := MyDest.Y - 1; if ( X = Curs_X ) and ( Y = Curs_Y ) then begin { This is where the cursor is. Draw the selector. } DrawSprite( cm_bits , MyDest , 0 ); end; if T = CPen then begin { This is the selected color. Draw a checkmark in the box. } DrawSprite( cm_bits , MyDest , 1 ); end; end; Inc( N ); end; end; end; Procedure ColorMenuRedraw; { Do the redraw for the color menu. Yay! } var MySprite: SensibleSpritePtr; MyDest: TSDL_Rect; t: Integer; begin { If a redraw procedure has been specified, call it. } if colormenu_ReDrawer <> Nil then colormenu_ReDrawer; { Display the panel. } MyDest := ZONE_colormenu_base.GetRect(); ClearExtendedBorder( MyDest ); SDL_FillRect( game_screen , @MyDest , SDL_MapRGB( Game_Screen^.Format , PlayerBlue.R , PlayerBlue.G , PlayerBlue.B ) ); DrawSprite( cm_panel , MyDest , 0 ); { Display the sprite we're editing. } MySprite := LocateSprite( colormenu_imagename, colormenu_imagepalette, colormenu_imagewidth, colormenu_imageheight ); MyDest := ZONE_colormenu_sprite.GetRect(); if MySprite <> Nil then begin MyDest.X := MyDest.X + ( MyDest.W div 2 ) - ( colormenu_imagewidth div 2 ); MyDest.Y := MyDest.Y + ( MyDest.H div 2 ) - ( colormenu_imageheight div 2 ); if colormenu_animframes > 0 then begin t := Animation_Phase div 5 mod colormenu_animframes; end else begin t := 0; end; DrawSprite( MySprite , MyDest , t ); end; { Display the instructions. } { Display the three color swatch areas. } for t := 1 to 3 do begin if t = colormenu_channel then begin DrawColorSelectionBox( ZONE_colorselectionboxes[t].GetRect(), colormenu_colorset[ t ], colormenu_currentpen[ t ], colormenu_rowoffset[ t ], colormenu_Curs_X, colormenu_Curs_Y ); end else begin DrawColorSelectionBox( ZONE_colorselectionboxes[t].GetRect(), colormenu_colorset[ t ], colormenu_currentpen[ t ], colormenu_rowoffset[ t ], -1, -1 ); end; end; end; Function GetColorAtSpot( Channel, Curs_X, Curs_Y: Integer ): Integer; { Determine which color is being shown at the provided location. If there's no color there, } { return -1. } var t,N,it,x,y: Integer; begin N := 0; it := -1; for t := 0 to ( Num_Available_Colors - 1 ) do begin { Figure out whether or not this color is being displayed. } if ( colormenu_colorset[ Channel ] = 0 ) or Available_Colors[ t ].cs[ colormenu_colorset[ Channel ] ] then begin { Alright, this is one of the ones being displayed. } { Figure out its position, based on N and colormenu_rowoffset. } X := N mod Swatch_Columns; Y := ( N div Swatch_Columns ) - colormenu_rowoffset[ Channel ]; if ( X = Curs_X ) and ( Y = Curs_Y ) then begin it := t; Break; end; Inc( N ); end; end; GetColorAtSpot := it; end; Procedure ProcessMouseHit; { Alright, so the mouse button has just been pressed. Figure out if it hit anything } { interesting and maybe change one of the color pens. } var MyHit: TSDL_Rect; T,C,HitX,HitY: Integer; begin { There are three color channels to worry about. See if it hit any of those. } for t := 1 to 3 do begin MyHit := ZONE_swatch_area[t].GetRect(); if ( Mouse_X >= MyHit.X ) and ( Mouse_Y >= MyHit.Y ) and ( Mouse_X < ( MyHit.X + MyHit.W ) ) and ( Mouse_Y < ( MyHit.Y + MyHit.H ) ) then begin { Alright, we're in the hit box. We've definitely hit a swatch, if there's one at this position. } { Determine HitX and HitY. } HitX := ( Mouse_X - MyHit.X ) div Swatch_Width; HitY := ( Mouse_Y - MyHit.Y ) div Swatch_Height; { Now the question becomes, is there a color at this area? } colormenu_channel := T; colormenu_curs_x := HitX; colormenu_curs_y := HitY; C := GetColorAtSpot( T , HitX , HitY ); if C <> -1 then begin colormenu_currentpen[ T ] := C; colormenu_penswap[ t ] := BStr( Available_Colors[ C ].rgb.r ) + ' ' + BStr( Available_Colors[ C ].rgb.g ) + ' ' + BStr( Available_Colors[ C ].rgb.b ); end; end; end; end; Function SelectColorPalette( init_mode: Integer; image_name,color_palette: String; image_width,image_height, anim_frames: Integer; Redrawer: RedrawProcedureType ): String; { Select a color palette for this image name. } { init_mode tells what initial palettes to use. } var T,tt: Integer; A: Char; tmp_string: String; tmp_color: TSDL_Color; begin { Store all the values that we've been given. } colormenu_imagename := image_name; colormenu_imagewidth := image_width; colormenu_imageheight := image_height; colormenu_animframes := anim_frames; colormenu_ReDrawer := Redrawer; { Initialize the selection variables. } colormenu_channel := 1; colormenu_curs_x := 0; colormenu_curs_y := 0; for t := 1 to 3 do begin if init_mode = colormenu_mode_character then begin colormenu_colorset[ t ] := t; end else if init_mode = colormenu_mode_mecha then begin colormenu_colorset[ t ] := t + 3; end else begin colormenu_colorset[ t ] := 0; end; { Determine if this color in the provided default is a standard color or not. } tmp_string := ExtractWord( color_palette ); colormenu_penswap[ t ] := tmp_string; tmp_color.R := ExtractValue( tmp_string ); tmp_string := ExtractWord( color_palette ); colormenu_penswap[ t ] := colormenu_penswap[ t ] + ' ' + tmp_string; tmp_color.G := ExtractValue( tmp_string ); tmp_string := ExtractWord( color_palette ); colormenu_penswap[ t ] := colormenu_penswap[ t ] + ' ' + tmp_string; tmp_color.B := ExtractValue( tmp_string ); colormenu_currentpen[ t ] := -1; for tt := 0 to ( Num_Available_Colors - 1 ) do begin if ( Available_Colors[ tt ].rgb.r = tmp_color.r ) and ( Available_Colors[ tt ].rgb.g = tmp_color.g ) and ( Available_Colors[ tt ].rgb.b = tmp_color.b ) then begin colormenu_currentpen[ t ] := tt; end; end; colormenu_rowoffset[ t ] := 0; end; { I think we're ready to begin polling for input. } repeat colormenu_imagepalette := colormenu_penswap[1] + ' ' + colormenu_penswap[2] + ' ' + colormenu_penswap[3]; ColorMenuRedraw; DoFlip; a := RPGKey; if a = #8 then begin a := #27; end else if a = RPK_MouseButton then begin { Crap, mouse input!!! I wonder if I can simply copy and paste the code from } { the first Cosplay program? } ProcessMouseHit; end; until a = #27; SelectColorPalette := colormenu_imagepalette; end; Procedure LoadColorList; { Load the standard colors from disk, and convert them to the colormenu format. } var CList,C: SAttPtr; T,tt: Integer; msg: String; begin { Begin by loading the definitions from disk. } CList := LoadStringList( Data_Directory + 'sdl_colors.txt' ); { Clear the Num_Colors_Per_Set array. } for t := 1 to Num_Color_Sets do begin Num_Colors_Per_Set[ t ] := 0; end; { Now that we know how many colors we're dealing with, we can size the } { colors array to the perfect dimensions. } Num_Available_Colors := NumSAtts( CList ); SetLength( Available_Colors , Num_Available_Colors ); { Copy the data into the array. } C := CList; T := 0; while C <> Nil do begin msg := RetrieveAPreamble( C^.Info ); if Length( msg ) < 8 then msg := msg + '------:ERROR'; Available_Colors[ t ].name := Copy( msg , 8 , 255 ); for tt := 1 to Num_Color_Sets do begin Available_Colors[ t ].cs[tt] := msg[tt] = '+'; if Available_Colors[ t ].cs[tt] then Inc( Num_Colors_Per_Set[ tt ] ); end; msg := RetrieveAString( C^.Info ); Available_Colors[ t ].rgb.r := ExtractValue( msg ); Available_Colors[ t ].rgb.g := ExtractValue( msg ); Available_Colors[ t ].rgb.b := ExtractValue( msg ); C := C^.Next; Inc( T ); end; { Get rid of the color definitions. } DisposeSAtt( CList ); end; Function RandomColorString( ColorSet: Integer ): String; { Select a random color string belonging to the provided color set. } var N,T,it: Integer; begin { Make sure we've been given a valid color set, and that there are } { colors in the set. } if ( ColorSet < 1 ) or ( ColorSet > Num_Color_Sets ) or ( Num_Colors_Per_Set[ ColorSet ] < 1 ) then Exit( '100 100 100' ); { Select one of the colors at random, then find it. } N := Random( Num_Colors_Per_Set[ ColorSet ] ); T := 0; it := -1; while ( it = -1 ) and ( T < Num_Available_Colors ) do begin if Available_Colors[ t ].cs[ ColorSet ] then begin Dec( N ); if N = -1 then begin it := T; end; end; Inc( T ); end; if it <> -1 then begin RandomColorString := BStr( Available_Colors[ it ].rgb.r ) + ' ' + BStr( Available_Colors[ it ].rgb.g ) + ' ' + BStr( Available_Colors[ it ].rgb.b ); end else begin { Use bright purple, as a warning that a bug has occurred. } RandomColorString := '255 0 255'; end; end; initialization LoadColorList; cm_bits := LocateSprite( 'color_menu_bits.png' , '', Swatch_Width , Swatch_Height ); cm_panel := LocateSprite( 'color_menu.png' , '', cm_panel_width , cm_panel_height ); finalization end. gearhead-2-0.701/cosplay2.pas000066400000000000000000000052131321074026100156560ustar00rootroot00000000000000unit cosplay2; {$LONGSTRINGS ON} interface uses gears,ui4gh,sdlgfx,sdlmenus,colormenu; Procedure Cosplay(); implementation Procedure RedrawOpening; { The opening menu redraw procedure. } begin ClrScreen; InfoBox( ZONE_Menu ); end; Procedure BrowseByType( FPat: String; width,height,frames,ColorMode: Integer ); { Browse the images by file pattern and color mode. } var FileMenu: RPGMenuPtr; SpriteName: String; begin FileMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); if FPat = '' then begin { We want all the mecha. All of them!!! } BuildFileMenu( FileMenu , Graphics_Directory + 'aer_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'ara_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'btr_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'gca_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'ger_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'ghu_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'hov_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'orn_*.png' ); BuildFileMenu( FileMenu , Graphics_Directory + 'zoa_*.png' ); end else BuildFileMenu( FileMenu , Graphics_Directory + FPat ); RPMSortAlpha( FileMenu ); AddRPGMenuItem( FileMenu, MsgString( 'EXIT' ), -1 ); SpriteName := ''; repeat SpriteName := SelectFile( FileMenu , @RedrawOpening ); if SpriteName <> '' then SelectColorPalette( ColorMode, SpriteName, '200 0 0 200 200 0 0 200 0', width, height, frames, @ClrScreen ); until SpriteName = ''; DisposeRPGMenu( FileMenu ); end; Procedure Cosplay(); var FileMenu: RPGMenuPtr; N: Integer; begin FileMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); AddRPGMenuItem( FileMenu , 'Browse Portraits' , 1 ); AddRPGMenuItem( FileMenu , 'Browse Mecha' , 2 ); AddRPGMenuItem( FileMenu , 'Browse Monsters' , 3 ); AddRPGMenuItem( FileMenu , 'Browse All' , 4 ); repeat N := SelectMenu( FileMenu , @RedrawOpening ); case N of 1: BrowseByType( 'por_*.png' , 100, 150, 0, colormenu_mode_character ); 2: BrowseByType( '' , 64, 64, 8, colormenu_mode_mecha ); 3: BrowseByType( 'monster_*.png' , 64, 64, 8, colormenu_mode_allcolors ); 4: BrowseByType( '*.png' , 211, 308, 0, colormenu_mode_allcolors ); end; until N = -1; DisposeRPGMenu( FileMenu ); end; end. gearhead-2-0.701/customization.pp000066400000000000000000000507401321074026100166730ustar00rootroot00000000000000unit customization; { This unit handles the customization of mecha. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears; Const WT_SmallGun = 1; WT_BigGun = 2; WT_MissileLauncher = 3; WT_MeleeWeapon = 4; Function MechaModPoints( NPC: GearPtr ): Integer; Function WeaponThemeClass( W: GearPtr ): Integer; Procedure MechaMakeover( Mek: GearPtr; Skill,Theme,MP: Integer ); Procedure ShopkeeperModifyMek( NPC,Mek: GearPtr ); Procedure CheckTheme( Theme: GearPtr ); implementation uses ghchars,gearutil,gearparser,ghintrinsic,effects,ghweapon,ability,ui4gh, texutil,rpgdice, {$IFDEF ASCII} vidgfx; {$ELSE} sdlgfx; {$ENDIF} Function MechaModPoints( NPC: GearPtr ): Integer; { Return the number of modification points this NPC should be given, based } { on his renown and Mecha Engineering skill. } var MP: Integer; begin MP := NAttValue( NPC^.NA , NAG_CharDescription , NAS_Renowned ) div 10; if MP < 1 then MP := 1; MP := MP + ( ( NAttValue( NPC^.NA , NAG_Skill , NAS_MechaEngineering ) + 2 ) div 3 ); MechaModPoints := MP; end; Function WeaponThemeClass( W: GearPtr ): Integer; { Return the weapon type of this gear. } begin if ( W^.S = GS_Melee ) or ( W^.S = GS_EMelee ) then begin WeaponThemeClass := WT_MeleeWeapon; end else if W^.S = GS_Missile then begin WeaponThemeClass := WT_MissileLauncher; end else if W^.V > 10 then begin WeaponThemeClass := WT_BigGun; end else begin WeaponThemeClass := WT_SmallGun; end; end; Procedure MechaMakeover( Mek: GearPtr; Skill,Theme,MP: Integer ); { Original title of this procedure was going to be "PimpMyMecha", } { but the word "pimp" is awfully overused these days, isn't it? } var PartsBox: GearPtr; OriginalMV,NumMassAdjusts: Integer; Function SwapParts( OldPart, NewPart: GearPtr ): Boolean; { Attempt to swap OldPart with NewPart. If the exchange is impossible } { for any reason, return FALSE and dispose of NewPart. Otherwise return TRUE. } var Slot: GearPtr; begin Slot := OldPart^.Parent; if IsInvCom( OldPart ) then begin { This part is an invcom. Remove it, then insert the NewPart. } DelinkGear( Slot^.InvCom , OldPart ); { Attempt to insert NewPart. } if IsLegalInvCom( Slot , NewPart ) then begin InsertInvCom( Slot , NewPart ); DisposeGear( OldPart ); SwapParts := True; end else begin { Can't insert the new part. Just stick the old one back. } InsertInvCom( Slot , OldPart ); DisposeGear( NewPart ); SwapParts := False; end; end else if IsSubCom( OldPart ) then begin { This part is an subcom. Remove it, then insert the NewPart. } DelinkGear( Slot^.SubCom , OldPart ); if IsLegalSubCom( Slot , NewPart ) then begin InsertSubCom( Slot , NewPart ); DisposeGear( OldPart ); SwapParts := True; end else begin { Can't insert the new part. Just stick the old one back. } InsertSubCom( Slot , OldPart ); DisposeGear( NewPart ); SwapParts := False; end; { If the part to swap isn't an invcom or a subcom, that's a major FUBAR. } end else SwapParts := False; end; Procedure CheckManeuverScore; { The maneuverability score of the mecha shouldn't get any worse than } { it started. } Function ComponentExcessMass( Part: GearPtr ): LongInt; { Return the excess mass points of this component. } { What's an excess mass point? In excess of 1. Weight reduction } { should not take any item to Mass:0, so ignore those gears. } var it: LongInt; begin it := ComponentMass( Part ) - 1; if it < 0 then it := 0; ComponentExcessMass := it; end; Function NumExcessMassPoints( LList: GearPtr ): LongInt; { Add up all the excess mass points in this linked list. } { For the point of brevity, ignore gears of a smaller scale } { than the Mek itself. } var total: LongInt; begin total := 0; while LList <> Nil do begin if ( LList^.Scale >= Mek^.Scale ) then begin total := total + ComponentExcessMass( LList ); total := total + NumExcessMassPoints( LList^.SubCom ); total := total + NumExcessMassPoints( LList^.InvCom ); end; if IsMasterGear( LList ) then LList := Nil else LList := LList^.Next; end; NumExcessMassPoints := total; end; Function GetExcessMassPoint( LList: GearPtr; var N: Integer ): GearPtr; { Return the gear that has excess mass point N, as if all } { the excess mass points were individually numbered. I think maybe } { I shouldn't be writing comments in summer when I feel dizzy but } { I'm sure you all know what I mean. Still, that sentance looks } { messy. Hi Peter. } var it: gearPtr; begin it := Nil; while ( LList <> Nil ) and ( it = Nil ) do begin if ( LList^.Scale >= Mek^.Scale ) then begin N := N - ComponentExcessMass( LList ); if N < 0 then it := LList; if it = Nil then it := GetExcessMassPoint( LList^.SubCom , N ); if it = Nil then it := GetExcessMassPoint( LList^.InvCom , N ); end; if IsMasterGear( LList ) then LList := Nil else LList := LList^.Next; end; GetExcessMassPoint := it; end; var EMP: Integer; Part: GearPtr; begin { As long as our maneuverability is too high, reduce the mass from } { parts randomly. } While ( MechaManeuver( Mek ) < OriginalMV ) and ( MP > 0 ) do begin EMP := NumExcessMassPoints( Mek ); if EMP > 0 then begin EMP := Random( EMP ); Part := GetExcessMassPoint( Mek , EMP ); if Part <> Nil then begin inc( NumMassAdjusts ); if ( NumMassAdjusts mod 5 ) = 0 then Dec( MP ); AddNAtt( Part^.NA , NAG_GearOps , NAS_MassAdjust , -1 ); if XXRAN_DEBUG then DialogMsg( 'Reducing mass of ' + GearName( Part ) + '.' ); end else begin DialogMsg( 'ERROR: Part ' + BStr( EMP ) + '/' + BStr( NumExcessMassPoints( Mek ) ) + ' not found in customization/CheckManeuverScore' ); Break; end; end else break; end; end; Procedure AddNewWeapon; { Add a new weapon to the mecha. } Function SeekMountingPoint( LList: GearPtr ): GearPtr; { Try to find an empty mounting point along this list } { or through its equal-scale subcoms. } var it: GearPtr; begin it := Nil; while ( LList <> Nil ) and ( it = Nil ) do begin if LList^.Scale >= Mek^.Scale then begin if ( LList^.G = GG_Holder ) and ( LList^.InvCom = Nil ) then it := LList else it := SeekMountingPoint( LList^.SubCom ); end; LList := LList^.Next; end; SeekMountingPoint := it; end; Function GetNewWeaponToInstall: GearPtr; { Locate a new weapon to install; pick the smallest weapon } { listed of a randomly selected category. } var WT,Tries: Integer; Part,it: GearPtr; PartValue,ItValue: LongInt; begin WT := Random( 4 ) + 1; it := Nil; Tries := 0; while ( it = Nil ) and ( Tries < 4 ) do begin Part := PartsBox^.InvCom; while Part <> Nil do begin if ( Part^.G = GG_Weapon ) and ( WeaponThemeClass( Part ) = WT ) then begin { We've found a weapon of the appropriate type. Compare } { it against IT to see whether or not to choose it. } PartValue := GearValue( Part ); if ( it = Nil ) then begin it := Part; ItValue := GearValue( Part ); end else if ( PartValue < ItValue ) then begin it := Part; ItValue := PartValue; end; end; Part := Part^.Next; end; WT := WT + 1; if WT = 5 then WT := 1; Inc( Tries ); end; GetNewWeaponToInstall := it; end; Function SeekEmptiestModule: GearPtr; { Search through the modules of this mecha, looking for one that has } { the least amount of junk already installed. } var M,it: GearPtr; MSpace,ItSpace: Integer; begin M := Mek^.SubCom; it := Nil; itSpace := 0; while M <> Nil do begin if ( M^.G = GG_Module ) and ( M^.Scale >= mek^.Scale ) then begin { This module is one we'll have to consider. Find out how } { much space it has for installing new things. } if it = Nil then begin it := M; ItSpace := ComponentComplexity( M ) - SubComComplexity( M ); end else begin MSpace := ComponentComplexity( M ) - SubComComplexity( M ); if MSpace > ItSpace then begin it := M; ItSpace := MSpace; end; end; end; M := M^.Next; end; SeekEmptiestModule := it; end; var Weapon,Slot: GearPtr; { The new weapon, and the spot to put it. } begin { Step one: Find a weapon to use. } Weapon := GetNewWeaponToInstall; if Weapon = Nil then begin if XXRAN_DEBUG then DialogMsg( 'Error- No weapon found for install.' ); Exit; end else if XXRAN_DEBUG then DialogMsg( 'Installing new ' + GearName( Weapon ) + '.' ); { Step two: Find a place to stick it. } Slot := SeekMountingPoint( Mek^.SubCOm ); if Slot <> Nil then begin if IsLegalInvCom( Slot , Weapon ) then InsertInvCom( Slot , CloneGear( Weapon ) ) else if XXRAN_DEBUG then DialogMsg( 'Install ' + GearName( Weapon ) + ' failed.' ); end else begin Slot := SeekEmptiestModule; if ( Slot <> Nil ) and IsLegalSubCom( Slot , Weapon ) then InsertSubCom( Slot , CloneGear( Weapon ) ) else if XXRAN_DEBUG then DialogMsg( 'Install ' + GearName( Weapon ) + ' failed.' ); end; end; Procedure UpgradeWeapons; { Replace a weapon on this mecha with an even better weapon taken } { from the parts box. } Function WUWeight( Part: GearPtr ): LongInt; { How much do I want to change this weapon? I want to change the } { most expensive weapons first, but preference is strongly given } { to weapons mounted as InvComs. } var it: LongInt; begin it := GearValue( Part ); if ( Part^.Parent <> Nil ) and ( Part^.Parent^.G = GG_Holder ) then it := it * 3; WUWeight := it; end; Function LocateWeaponToUpgrade( LList,Best: GearPtr ): GearPtr; { Search through this list, its invcoms and subcoms, looking } { for a weapon to upgrade. } var ASkill1,ASkill2: Integer; begin while LList <> Nil do begin if ( LList^.G = GG_Weapon ) and ( NAttValue( LList^.NA , NAG_EpisodeData , NAS_WeaponUpgrades ) <> -1 ) and ( not PartHasIntrinsic( LList , NAS_Integral ) ) then begin if Best = Nil then begin { Better to have some weapon than no weapon. } Best := LList; end else if NAttValue( LList^.NA , NAG_EpisodeData , NAS_WeaponUpgrades ) < NAttValue( Best^.NA , NAG_EpisodeData , NAS_WeaponUpgrades ) then begin { The weapon which has been upgraded the least } { will always be selected. } Best := LList; end else if NAttValue( LList^.NA , NAG_EpisodeData , NAS_WeaponUpgrades ) = NAttValue( Best^.NA , NAG_EpisodeData , NAS_WeaponUpgrades ) then begin ASkill1 := AttackSkillNeeded( Best ); ASkill2 := AttackSkillNeeded( LList ); if ( ASkill2 = Skill ) and ( ASkill1 <> Skill ) then begin { Always go with a weapon that uses the preferred skill. } Best := LList; end else if ( ASkill1 = ASkill2 ) and ( ASkill1 = Skill ) and ( WUWeight( LList ) > WUWeight( Best ) ) then begin { Both weapons use the preferred skill- modify the most expensive first. } Best := LList; end else if ( ASkill1 <> Skill ) and ( WUWeight( LList ) > WUWeight( Best ) ) then begin { Neither weapon uses the preferred skill- take the most expensive. } Best := LList; end; end; end; { Check along the subcoms and invcoms. } if LList^.G <> GG_Cockpit then begin Best := LocateWeaponToUpgrade( LList^.SubCom , Best ); Best := LocateWeaponToUpgrade( LList^.InvCom , Best ); end; LList := LList^.Next; end; LocateWeaponToUpgrade := Best; end; Function SelectReplacementWeapon( WeaponToReplace: GearPtr ): GearPtr; { Searching along PartsBox^.InvCom, look for a weapon of the same } { type as WeaponToReplace but more expensive. Return the cheapest } { such weapon found. } var WT: Integer; { Weapon type to match. } Part,It: GearPtr; MinValue,PartValue,ItValue: LongInt; begin WT := WeaponThemeClass( WeaponToReplace ); MinValue := GearValue( WeaponToReplace ); It := Nil; ItValue := 0; Part := PartsBox^.InvCom; while Part <> Nil do begin if ( Part^.G = GG_Weapon ) and ( WeaponThemeClass( Part ) = WT ) then begin { We've found a weapon of the appropriate type. Compare } { it against IT to see whether or not to choose it. } PartValue := GearValue( Part ); if ( it = Nil ) and ( PartValue > MinValue ) then begin it := Part; ItValue := GearValue( Part ); end else if ( PartValue > MinValue ) and ( PartValue < ItValue ) then begin it := Part; ItValue := GearValue( Part ); end; end; Part := Part^.Next; end; SelectReplacementWeapon := it; end; var WeaponToReplace,NewWeapon: GearPtr; begin repeat WeaponToReplace := LocateWeaponToUpgrade( Mek^.SubCom , Nil ); if WeaponToReplace = Nil then break; { Step two- locate a new weapon from the parts list which: } { 1) is of the same basic type as the weapon being replaced, } { 2) has a value greater than that of the weapon being replaced } { but smaller than any other weapon matching 1. } NewWeapon := SelectReplacementWeapon( WeaponToReplace ); { If no NewWeapon can be found for the current WeaponToReplace, } { mark this weapon as unreplacable and attempt another. } if NewWeapon = Nil then SetNAtt( WeaponToReplace^.NA , NAG_EpisodeData , NAS_WeaponUpgrades , -1 ); until ( WeaponToReplace <> Nil ) and ( NewWeapon <> Nil ); { If either WeaponToReplace or NewWeapon are Nil, call the AddNewWeapon upgrade } { instead. } if ( WeaponToReplace = Nil ) or ( NewWeapon = Nil ) then begin AddNewWeapon; exit; end; { Record the WeaponUpgrades total. } NewWeapon := CloneGear( NewWeapon ); SetNAtt( NewWeapon^.NA , NAG_EpisodeData , NAS_WeaponUpgrades , NAttValue( WeaponToReplace^.NA , NAG_EpisodeData , NAS_WeaponUpgrades ) + 1 ); if XXRAN_DEBUG then DialogMsg( 'Replacing ' + GearName( WeaponToReplace ) + ' with ' + GearName( NewWeapon ) + '.' ); { Step three- perform the swap. } { Record the number of upgrades + 1 in the new weapon. } SwapParts( WeaponToReplace , NewWeapon ); end; Procedure ReDesignate; { Since this mecha has been extensively modified, it can't keep the } { same old factory designation it once had. Let's give it a name to } { indicate its new abilities... a name even StrongBad would be proud } { of. } { The new designation will consist of an adjective + a noun. Each } { theme should have a list of 5 adjectives and 5 nouns specifically } { for this purpose. One or the other of the words may be taken from } { a general list instead. } var NormComp: Integer; Desig: String; begin NormComp := Random( 3 ); if NormComp = 1 then Desig := MSgString( 'CUSTOMIZATION_ADJECTIVE_' + BStr( Random( 5 ) ) ) else Desig := SAttValue( PartsBox^.SA , 'CUSTOMIZATION_ADJECTIVE_' + BStr( Random( 5 ) ) ); if NormComp = 2 then Desig := Desig + ' ' + MSgString( 'CUSTOMIZATION_NOUN_' + BStr( Random( 5 ) ) ) else Desig := Desig + ' ' + SAttValue( PartsBox^.SA , 'CUSTOMIZATION_NOUN_' + BStr( Random( 5 ) ) ); SetSAtt( Mek^.SA , 'DESIG <' + Desig + '>' ); end; begin { Start by locating the parts list. } { If Theme = 0 then select a random theme. } if Theme = 0 then begin PartsBox := SelectRandomGear( Mecha_Theme_List ); end else begin { The actual parts we want will be invcoms of this gear. } PartsBox := SeekCurrentLevelGear( Mecha_Theme_List , Mecha_Theme_List^.G , Theme ); end; if PartsBox = Nil then begin Exit; end; { Record the original maneuverability score of the mecha. If this changes, we } { need to lose some weight. } OriginalMV := MechaManeuver( Mek ); NumMassAdjusts := 0; while MP > 0 do begin if Random( 20 ) = 1 then begin AddNewWeapon; end else begin UpgradeWeapons; end; Dec( MP ); { After all modifications, check to make sure the MV is as good as it ever was. } CheckManeuverScore; end; { Give this mecha a new designation. } ReDesignate; end; Procedure ShopkeeperModifyMek( NPC,Mek: GearPtr ); { The NPC is going to modify this mecha for sale in his/her shop. } { Decide on a random theme and a number of modification points, then send everything } { to the above procedure. } { Note that if the NPC makes a bad roll, it's quite possible for the mecha to leave this } { procedure without being modified at all. } var SkRoll,MP: Integer; Theme: GearPtr; begin { Make a skill roll against the size of the mecha. If it succeeds, then } { we'll have some modification points to work with. } SkRoll := RollStep( SkillValue( NPC , NAS_MechaEngineering , STAT_Knowledge ) ) - ( Mek^.S + 4 ); if SkRoll > 0 then begin { Okay, we have some points. Now, pick a theme. } Theme := SelectRandomGear( Mecha_Theme_List ); MP := ( SkRoll + 2 ) div 3; if MP > 9 then MP := 9; { Send all the info to the MechaMakeover procedure. } MechaMakeover( Mek , 0 , Theme^.S , MP ); end; end; Procedure CheckTheme( Theme: GearPtr ); { Check this theme. See if it has all the required weapons. } { There are four weapon types and about 10 cost classes. } const Cost_Max: Array [ 1..4 , 0..10 ] of LongInt = ( ( 0 , 30000 , 45000 , 65000 , 90000 , 120000 , 160000 , 210000 , 270000 , 340000 , 420000 ), { Small Guns } ( 0 , 30000 , 45000 , 65000 , 90000 , 120000 , 160000 , 210000 , 270000 , 340000 , 420000 ), { Big Guns } ( 0 , 30000 , 45000 , 65000 , 90000 , 120000 , 160000 , 210000 , 270000 , 340000 , 420000 ), { Missile Launchers } ( 0 , 10000 , 15000 , 25000 , 40000 , 60000 , 90000 , 130000 , 180000 , 240000 , 310000 ) { Melee Weapons } ); var Memo: SAttPtr; { List of comments. } Procedure CheckTypeRank( WP_Type,WP_Rank: Integer ); { Check this weapon type/rank to make sure one appropriate weapon is present. } { If more than one is present, that's an error. If less than one is present, that's } { an error too. } var W: GearPtr; N: Integer; Cost: LongInt; begin W := Theme^.InvCom; N := 0; while W <> Nil do begin if WeaponThemeClass( W ) = WP_Type then begin Cost := GearValue( W ); if ( Cost > Cost_Max[ WP_Type , WP_Rank - 1 ] ) and ( Cost <= Cost_Max[ WP_Type , WP_Rank ] ) then Inc( N ); end; W := W^.Next; end; { Store any errors in MEMO. } if N = 0 then begin StoreSAtt( Memo , 'ERROR: Type ' + BStr( WP_Type ) + ' Rank ' + BStr( WP_Rank ) + ' has no weapon.' ); end else if N > 1 then begin StoreSAtt( Memo , 'ERROR: Type ' + BStr( WP_Type ) + ' Rank ' + BStr( WP_Rank ) + ' has ' + BStr( N ) + ' weapons.' ); W := Theme^.InvCom; while W <> Nil do begin if WeaponThemeClass( W ) = WP_Type then begin Cost := GearValue( W ); if ( Cost > Cost_Max[ WP_Type , WP_Rank - 1 ] ) and ( Cost <= Cost_Max[ WP_Type , WP_Rank ] ) then begin if ( WP_Type = WT_MissileLauncher ) and ( W^.SubCom <> Nil ) then StoreSAtt( Memo , ' ->' + FullGearName( W^.SubCom ) + ' [' + BStr( W^.SubCom^.Stat[ STAT_AmmoPresent ] ) + ']' ) else StoreSAtt( Memo , ' ->' + GearName( W ) ); end; end; W := W^.Next; end; end; end; var C,R: Integer; begin Memo := Nil; for C := 1 to 4 do begin for R := 1 to 10 do begin CheckTypeRank( C , R ); end; end; if Memo <> Nil then begin {$IFDEF ASCII} MoreText( Memo , 1 ); {$ELSE} MoreText( Memo , 1 , Nil ); {$ENDIF} DisposeSAtt( Memo ); end; end; end. gearhead-2-0.701/description.pp000066400000000000000000000510261321074026100163040ustar00rootroot00000000000000unit description; { This unit provides the descriptions for gears. Its purpose is to } { explain to the player exactly what a given item does, and how it } { differs from other items. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses texutil,ui4gh,gears,gearutil,movement,locale; Function WeaponDescription( GB: GameBoardPtr; Weapon: GearPtr ): String; Function ExtendedDescription( GB: GameBoardPtr; Part: GearPtr ): String; Function MechaDescription( Mek: GearPtr ): String; Function TimeString( ComTime: LongInt ): String; Function JobAgeGenderDesc( NPC: GearPtr ): String; Function SkillDescription( N: Integer ): String; Function MechaPilotName( Mek: GearPtr ): String; Function TeamMateName( M: GearPtr ): String; Function RenownDesc( Renown: Integer ): String; implementation uses ghmodule,ghweapon,ghsensor,ghsupport,ghmovers,ghguard,ghswag,ghholder,ghchars, ability,ghintrinsic,interact,ghmecha; Function BasicWeaponDesc( Weapon: GearPtr ): String; {Supply a default name for this particular weapon.} begin {Convert the size of the weapon to a string.} if Weapon^.G = GG_Weapon then begin BasicWeaponDesc := DCName( WeaponDC( Weapon ) , Weapon^.Scale ) + ' ' + MsgString( 'WEAPONNAME_' + BStr( Weapon^.S ) ); end else begin BasicWeaponDesc := DCName( WeaponDC( Weapon ) , Weapon^.Scale ); end; end; Function WeaponDescription( GB: GameBoardPtr; Weapon: GearPtr ): String; { Create a description for this weapon. } var Master,Ammo: GearPtr; desc,AA: String; S,M,L: Integer; begin { Take the default name for the weapon from the WeaponName } { function in ghweapon. } desc := BasicWeaponDesc( Weapon ) + ' (' + MsgSTring( 'STATABRV_' + BStr( Weapon^.Stat[ STAT_AttackStat ] ) ) + ')'; if Weapon^.G = GG_Weapon then begin Master := FindMaster( Weapon ); Ammo := LocateGoodAmmo( Weapon ); if ( Weapon^.S = GS_Missile ) and ( Ammo <> Nil ) then begin desc := BasicWeaponDesc( Ammo ); end; if Master <> Nil then begin if Master^.Scale <> Weapon^.Scale then begin desc := desc + ' SF:' + BStr( Weapon^.Scale ); end; end else if Weapon^.Scale > 0 then begin desc := desc + ' SF:' + BStr( Weapon^.Scale ); end; AA := WeaponAttackAttributes( Weapon ); if (Weapon^.S = GS_Ballistic) or (Weapon^.S = GS_BeamGun) or ( Weapon^.S = GS_Missile ) then begin S := ScaleRange( WeaponRange( Nil , Weapon , RANGE_Short ) , Weapon^.Scale ); M := ScaleRange( WeaponRange( Nil , Weapon , RANGE_Medium ) , Weapon^.Scale ); L := ScaleRange( WeaponRange( Nil , Weapon , RANGE_Long ) , Weapon^.Scale ); if HasAttackAttribute( AA , AA_LineAttack ) then begin desc := desc + ' RNG:' + BStr( S ) + '-' + BStr( L ); end else begin desc := desc + ' RNG:' + BStr( S ) + '-' + BStr( M ) + '-' + BStr( L ); end; end else if HasAttackAttribute( AA , AA_Extended ) then begin desc := desc + ' RNG:' + BStr( ScaleRange( 2 , Weapon^.Scale ) ); end; if Weapon^.S <> GS_Missile then begin desc := desc + ' ACC:' + SgnStr( Weapon^.Stat[STAT_Accuracy] ); end else if Ammo <> Nil then begin desc := desc + ' ACC:' + SgnStr( Ammo^.Stat[STAT_Accuracy] ); end; desc := desc + ' SPD:' + BStr( Weapon^.Stat[STAT_Recharge] ); if (Weapon^.S = GS_Ballistic) or (Weapon^.S = GS_BeamGun) then begin if Weapon^.Stat[ STAT_BurstValue ] > 0 then desc := desc + ' BV:' + BStr( Weapon^.Stat[ STAT_BurstValue ] + 1 ); end; if (Weapon^.S = GS_Ballistic) or (Weapon^.S = GS_Missile) then begin if Ammo <> Nil then begin desc := desc + ' ' + BStr( AmmoRemaining( Weapon ) ) + '/' + BStr( Ammo^.Stat[ STAT_AmmoPresent] ) + 'a'; end else begin desc := desc + MsgString( 'AMMO-EMPTY' ); end; end else if ( Weapon^.S = GS_BeamGun ) or ( Weapon^.S = GS_EMelee ) then begin desc := desc + ' EP:' + BStr( EnergyCost( Weapon ) ) + '/' + BStr( EnergyPoints( FindMasterOrRoot( Weapon ) ) ); end; if HasAttackAttribute( AA , AA_Mystery ) then begin desc := desc + ' ???'; end else begin if AA <> '' then begin desc := desc + ' ' + UpCase( AA ); end; end; if SAttValue( Weapon^.SA , 'CALIBER' ) <> '' then desc := desc + ' ' + SAttValue( Weapon^.SA , 'CALIBER' ); end else if Weapon^.G = GG_Ammo then begin AA := WeaponAttackAttributes( Weapon ); if Weapon^.S = GS_Grenade then begin desc := desc + ' RNG:T'; if Weapon^.Stat[ STAT_BurstValue ] > 0 then desc := desc + ' BV:' + BStr( Weapon^.Stat[ STAT_BurstValue ] + 1 ); end; desc := desc + ' ' + BStr( Weapon^.Stat[STAT_AmmoPresent] - NAttValue( Weapon^.NA , NAG_WeaponModifier , NAS_AmmoSpent ) ) + '/' + BStr( Weapon^.Stat[ STAT_AmmoPresent] ) + 'a'; if HasAttackAttribute( AA , AA_Mystery ) then begin desc := desc + ' ???'; end else begin if AA <> '' then begin desc := desc + ' ' + UpCase( AA ); end; end; end; desc := desc + ' ARC:' + MsgString( 'WEAPONINFO_ARC' + BStr( WeaponArc( Weapon ) ) ); WeaponDescription := desc; end; Function WAODescription( Weapon: GearPtr ): String; { Create a description for this weapon. } var desc,AA: String; begin { Take the default name for the weapon from the WeaponName } { function in ghweapon. } desc := MsgString( 'WAO_' + BStr( Weapon^.S ) ); if Weapon^.V <> 0 then desc := desc + ' DC:' + SgnStr( Weapon^.V ); if Weapon^.Stat[ STAT_Range ] <> 0 then desc := desc + ' RNG:' + SgnStr( Weapon^.Stat[ STAT_Range ] ); if Weapon^.Stat[ STAT_Accuracy ] <> 0 then desc := desc + ' ACC:' + SgnStr( Weapon^.Stat[ STAT_Accuracy ] ); if Weapon^.Stat[ STAT_Recharge ] <> 0 then desc := desc + ' SPD:' + SgnStr( Weapon^.Stat[ STAT_Recharge ] ); AA := WeaponAttackAttributes( Weapon ); if HasAttackAttribute( AA , AA_Mystery ) then begin desc := desc + ' ???'; end else if AA <> '' then begin desc := desc + ' ' + UpCase( AA ); end; WAODescription := desc; end; Function MoveSysDescription( Part: GearPtr ): String; { Return a description of the size/type of this movement } { system. } begin MoveSysDescription := MsgString( 'MoveSys_Class' ) + ' ' + BStr( Part^.V ) + ' ' + MoveSysName( Part ); end; Function ModifierDescription( Part: GearPtr ): String; { Return a description for this modifier gear. } var it: String; T: Integer; begin if Part^.S = GS_StatModifier then begin it := ''; for t := 1 to NumGearStats do begin if Part^.Stat[ T ] <> 0 then begin if it <> '' then it := it + ', '; it := it + SgnStr( Part^.Stat[ T ] ) + ' ' + MsgString( 'STATNAME_' + BStr( T ) ); end; end; end else if Part^.S = GS_SkillModifier then begin it := MsgString( 'SKILLNAME_' + Bstr( Part^.Stat[ STAT_SkillToModify ] ) ); it := it + ' ' + SgnStr( Part^.Stat[ STAT_SkillModBonus ] ); end; if it <> '' then it := it + '.'; ModifierDescription := it; end; Function ShieldDescription( Part: GearPtr ): String; { Return a description of the size/type of this movement } { system. } begin ShieldDescription := MsgString( 'Shield_Desc' ) + SgnStr( Part^.Stat[ STAT_ShieldBonus ] ); end; Function ToolDescription( Part: GearPtr ): String; { Return a description of the size/type of this movement } { system. } var msg: String; begin if Part^.S > 0 then begin msg := SgnStr( Part^.V ) + ' ' + MsgString( 'SKILLNAME_' + Bstr( Part^.S ) ); end else begin msg := SgnStr( Part^.V ) + ' ' + MsgString( 'TALENT' + Bstr( Abs( Part^.S ) ) ); end; ToolDescription := msg; end; Function IntrinsicsDescription( Part: GearPtr ): String; { Return a list of all the intrinsics associated with this part. } var T: Integer; it: String; begin it := ''; { Start by adding the armor type, if appropriate. } T := NAttValue( Part^.NA , NAG_GearOps , NAS_ArmorType ); if T <> 0 then it := MsgString( 'ARMORTYPE_' + BStr( T ) ); { We're only interested if the intrinsics are attached directly } { to this part. } for t := 1 to NumIntrinsic do begin if NAttValue( Part^.NA , NAG_Intrinsic , T ) <> 0 then begin if it = '' then begin it := MsgString( 'INTRINSIC_' + BStr( T ) ); end else begin it := it + ', ' + MsgString( 'INTRINSIC_' + BStr( T ) ); end; end; end; IntrinsicsDescription := it; end; Function ArmorDescription( Part: GearPtr ): String; { Return a description of this armor's stat modifiers, if any. } var it: String; T: Integer; begin it := ArmorName( Part ); for t := 1 to NumGearStats do begin if Part^.Stat[t] <> 0 then begin it := it + ', +' + BStr( Part^.Stat[t] div 10 ) + '.' + BStr( Part^.Stat[t] mod 10 ) + ' ' + MsgString( 'STATNAME_' + BStr( T ) ); end; end; ArmorDescription := it; end; Function RepairFuelDescription( Part: GearPtr ): String; { Return a description for this repair fuel. } begin RepairFuelDescription := MsgString( 'REPAIRTYPE_' + BStr( Part^.S ) ) + ' ' + BStr( Part^.V ) + ' DP'; end; Function PowerSourceDescription( Part: GearPtr ): String; { Return a description of the size of this power source. } var msg: String; begin msg := ReplaceHash( MsgString( 'PowerSource_Desc' ) , BStr( Part^.V ) ); PowerSourceDescription := msg; end; Function ComputerDescription( Part: GearPtr ): String; { Return a description of this computer. } var msg: String; ZG,SWZG: Integer; SW: GearPtr; begin msg := ReplaceHash( MsgString( 'Computer_Desc' ) , BStr( Part^.V ) ); ZG := ZetaGigs( Part ); SWZG := 0; SW := Part^.SubCom; while SW <> Nil do begin SWZG := SWZG + ZetaGigs( SW ); SW := SW^.Next; end; msg := ReplaceHash( msg , BStr( ZG - SWZG ) ); msg := ReplaceHash( msg , BStr( ZG ) ); ComputerDescription := Msg; end; Function SoftwareDescription( Part: GearPtr ): String; { Return a description of this software's function. } var msg: String; begin msg := SgnStr( Part^.V ) + ' '; case Part^.Stat[ STAT_SW_Type ] of S_MVBoost: msg := msg + ReplaceHash( MsgString( 'SOFTWARE_MVBOOST_DESC' ) , Bstr( Part^.Stat[ STAT_SW_Param ] ) ); S_TRBoost: msg := msg + ReplaceHash( MsgString( 'SOFTWARE_TRBOOST_DESC' ) , Bstr( Part^.Stat[ STAT_SW_Param ] ) ); S_SpeedComp: msg := msg + ReplaceHash( MsgString( 'SOFTWARE_SPEEDCOMP_DESC' ) , Bstr( Part^.Stat[ STAT_SW_Param ] ) ); S_Information: msg := MsgSTring( 'SOFTWARE_INFORMATION_' + Bstr( Part^.Stat[ STAT_SW_Param ] ) ); else msg := msg + MsgString( 'SOFTWARE_MISC_DESC' ); end; msg := msg + '; ' + BStr( ZetaGigs( Part ) ) + ' ZeG'; SoftwareDescription := msg; end; Function UsableDescription( Part: GearPtr ): String; { Return a description for this usable gear. } begin if Part^.S = GS_Transformation then begin UsableDescription := MsgString( 'USABLENAME_1' ) + ': ' + MsgString( 'FORMNAME_' + BStr( Part^.V ) ); end else begin UsableDescription := MsgString( 'USABLE_CLASS' ) + ' ' + BStr( Part^.V ) + ' ' + MsgString( 'USABLENAME_' + BStr( Part^.S ) ); end; end; Function CharaDescription( PC: GearPtr ): String; { Return a description of this character. For now, the description will } { just be a list of the character's talents. } var T: Integer; msg: String; begin msg := ''; for t := 1 to NumTalent do begin if NAttValue( PC^.NA , NAG_Talent , T ) <> 0 then begin if msg = '' then msg := MsgSTring( 'TALENT' + BStr( T ) ) else msg := msg + ', ' + MsgSTring( 'TALENT' + BStr( T ) ); end; end; CharaDescription := msg; end; Function ExtendedDescription( GB: GameBoardPtr; Part: GearPtr ): String; { Provide an extended description telling all about the } { attributes of this particular item. } var it,IntDesc: String; SC: GearPtr; begin { Error check first. } if Part = Nil then Exit( '' ); { Start examining the part. } it := ''; if ( Part^.G = GG_Weapon ) then begin it := WeaponDescription( GB , Part ); end else if Part^.G = GG_Mecha then begin it := MechaDescription( Part ); end else if Part^.G = GG_RepairFuel then begin it := RepairFuelDescription( Part ); end else if ( Part^.G = GG_Ammo ) then begin it := WeaponDescription( GB , Part ); end else if Part^.G = GG_MoveSys then begin it := MoveSysDescription( Part ); end else if Part^.G = GG_Modifier then begin it := ModifierDescription( Part ); end else if Part^.G = GG_Character then begin it := CharaDescription( Part ); end else if Part^.G = GG_Tool then begin it := ToolDescription( Part ); SC := Part^.SubCom; while ( SC <> Nil ) do begin it := it + '; ' + ExtendedDescription( GB , SC ); SC := SC^.Next; end; end else if Part^.G = GG_PowerSource then begin it := PowerSourceDescription( Part ); end else if Part^.G = GG_COmputer then begin it := ComputerDescription( Part ); end else if Part^.G = GG_Software then begin it := SoftwareDescription( Part ); end else if Part^.G = GG_ExArmor then begin it := ArmorDescription( Part ); SC := Part^.SubCom; while ( SC <> Nil ) do begin it := it + '; ' + ExtendedDescription( GB , SC ); SC := SC^.Next; end; end else if Part^.G = GG_Shield then begin it := ShieldDescription( Part ); SC := Part^.SubCom; while ( SC <> Nil ) do begin it := it + '; ' + ExtendedDescription( GB , SC ); SC := SC^.Next; end; end else if Part^.G = GG_WeaponAddOn then begin it := WAODescription( Part ); SC := Part^.SubCom; while ( SC <> Nil ) do begin it := it + '; ' + ExtendedDescription( GB , SC ); SC := SC^.Next; end; end else if Part^.G = GG_Support then begin it := ReplaceHash( MsgString( 'SupportDesc' ) , BStr( Part^.V ) ); end else if Part^.G = GG_Usable then begin it := UsableDescription( Part ); end else if Part^.G <> GG_Module then begin SC := Part^.SubCom; while ( SC <> Nil ) do begin if it = '' then it := ExtendedDescription( GB , SC ) else it := it + '; ' + ExtendedDescription( GB , SC ); SC := SC^.Next; end; end else begin { This is a module, as determined by the above clause. } if Part^.Stat[ STAT_VariableModuleForm ] <> 0 then it := MsgString( 'VariableModule' ) + ' ' + MsgString( 'MODULENAME_' + BStr( Part^.Stat[ STAT_PrimaryModuleForm ] ) ) + '/' + MsgString( 'MODULENAME_' + BStr( Part^.Stat[ STAT_VariableModuleForm ] ) ); end; IntDesc := IntrinsicsDescription( Part ); if IntDesc <> '' then begin if it = '' then it := IntDesc else it := it + ', ' + IntDesc; end; ExtendedDescription := it; end; Function MechaDescription( Mek: GearPtr ): String; { Return a text description of this mecha's technical points. } var it,i2: String; MM,MMS,MaxSpeed,FullSpeed: Integer; CanMove: Boolean; Engine: GearPtr; begin it := MassString( Mek ) + ' ' + MsgString( 'FORMNAME_' + BStr( Mek^.S ) ); it := it + ' ' + 'MV:' + SgnStr(MechaManeuver(Mek)); it := it + ' ' + 'TR:' + SgnStr(MechaTargeting(Mek)); it := it + ' ' + 'SE:' + SgnStr(MechaSensorRating(Mek)); MM := CountActiveParts( Mek , GG_Holder , GS_Hand ); if MM > 0 then begin it := it + ' ' + MsgString( 'MEKDESC_Hands' ) + ':' + BStr( MM ); end; MM := CountActiveParts( Mek , GG_Holder , GS_Mount ); if MM > 0 then begin it := it + ' ' + MsgString( 'MEKDESC_Mounts' ) + ':' + BStr( MM ); end; CanMove := False; MaxSpeed := 0; for MM := 1 to NumMoveMode do begin MMS := BaseMoveRate( Nil , Mek , MM ); FullSpeed := AdjustedMoveRate( Nil , Mek , MM , NAV_FullSpeed ); if FullSpeed > MaxSpeed then MaxSpeed := FullSpeed; if MMS > 0 then begin CanMove := True; { Add a description for this movemode. } if MM = MM_Fly then begin { Check to see whether the mecha can } { fly or just jump. } if JumpTime( Nil , Mek ) = 0 then begin it := it + ' ' + MsgString( 'MoveModeName_' + BStr( MM ) ) + ':' + BStr( MMS ); end else begin it := it + ' ' + MsgString( 'MEKDESC_Jump' ) + ':' + BStr( JumpTime( Nil , Mek ) ) + 's'; end; end else begin it := it + ' ' + MsgString( 'MoveModeName_' + BStr( MM ) ) + ':' + BStr( MMS ); end; end; end; if MaxSpeed > 0 then it := it + ' Max:' + BStr( MaxSpeed ); if Mek^.Stat[ STAT_MechaTrait ] <> 0 then begin it := it + ' ' + MsgString( 'MECHATRAIT_' + BStr( Mek^.Stat[ STAT_MechaTrait ] ) ); end; Engine := SeekGear( Mek , GG_Support , GS_Engine ); if Engine <> Nil then begin i2 := MsgString( 'MEKDESC_ENGINE' + Bstr( Engine^.Stat[ STAT_EngineSubtype ] ) ); if i2 <> '' then it := it + ' ' + i2; end; { Add warnings for different conditions. } if not CanMove then begin it := it + ' ' + MsgString( 'MEKDESC_Immobile' ); end; if Destroyed( Mek ) then begin it := it + ' ' + MsgString( 'MEKDESC_Destroyed' ); end; if SeekGear(mek,GG_CockPit,0) = Nil then begin it := it + ' ' + MsgString( 'MEKDESC_NoCockpit' ); end; MechaDescription := it; end; Function TimeString( ComTime: LongInt ): String; { Create a string to express the time listed in COMTIME. } var msg: String; S,M,H,D: LongInt; { Seconds, Minutes, Hours, Days } begin S := ComTime mod 60; M := ( ComTime div 60 ) mod 60; H := ( ComTime div AP_Hour ) mod 24; D := ComTime div AP_Day; msg := Bstr( H ) + ':' + WideStr( M , 2 ) + ':' + WideStr( S , 2 ) + MsgString( 'CLOCK_days' ) + BStr( D ); TimeString := msg; end; Function JobAgeGenderDesc( NPC: GearPtr ): String; { Return the Job, Age, and Gender of the provided character in } { a nicely formatted string. } var msg,job: String; R: Integer; begin R := NAttValue( NPC^.NA , NAG_CharDescription , NAS_DAge ) + 20; if R > 0 then msg := BStr( R ) else msg := '???'; msg := msg + ' year old'; if NAttValue( NPC^.NA, NAG_CharDescription, NAS_Gender) <> NAV_Undefined then begin msg := msg + ' ' + LowerCase( MsgString( 'GenderName_' + BStr( NAttValue( NPC^.NA , NAG_CharDescription , NAS_Gender ) ) ) ); end; job := SAttValue( NPC^.SA , 'JOB' ); if job <> '' then msg := msg + ' ' + LowerCase( job ); { Check the NPC's relationship with the PC. } r := NAttValue( NPC^.NA , NAG_Relationship , 0 ); if R > 0 then begin job := MsgString( 'RELATIONSHIP_' + BStr( R ) ); if job <> '' then msg := msg + ', ' + job; end; msg := msg + '.'; JobAgeGenderDesc := msg; end; Function SkillDescription( N: Integer ): String; { Return a description for this skill. The main text is taken } { from the messages.txt file, plus the name of the stat which } { governs this skill. } var msg: String; begin msg := ''; { Error check- only provide description for a legal skill } { number. Otherwise just return an empty string. } if ( N >= 1 ) and ( N <= NumSkill ) then begin msg := MsgString( 'SKILLDESC_' + BStr( N ) ); end; SkillDescription := msg; end; Function MechaPilotName( Mek: GearPtr ): String; { Return the name of the mecha and the pilot, together. } var Msg,PName: String; begin msg := GearName( Mek ); if Mek^.G = GG_Mecha then begin PName := PilotName( Mek ); if PName <> msg then msg := msg + ' (' + PName + ')'; end; MechaPilotName := msg; end; Function TeamMateName( M: GearPtr ): String; { Return the name of this team-mate. If the team-mate is a mecha, } { also return the name of its pilot if appropriate. } var msg,pname: String; begin msg := FullGearName( M ); if M^.G = GG_Mecha then begin pname := SAttValue( M^.SA , 'pilot' ); if pname <> '' then msg := msg + ' (' + pname + ')'; end; TeamMateName := msg; end; Function RenownDesc( Renown: Integer ): String; { Return a description for the provided renown. } begin if Renown > 80 then begin RenownDesc := MsgString( 'AHQRANK_5' ); end else if Renown > 60 then begin RenownDesc := MsgString( 'AHQRANK_4' ); end else if Renown > 40 then begin RenownDesc := MsgString( 'AHQRANK_3' ); end else if Renown > 20 then begin RenownDesc := MsgString( 'AHQRANK_2' ); end else begin RenownDesc := MsgString( 'AHQRANK_1' ); end; end; end. gearhead-2-0.701/design/000077500000000000000000000000001321074026100146655ustar00rootroot00000000000000gearhead-2-0.701/design/Argos.txt000066400000000000000000000035361321074026100165100ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ROLE_COMET ROLE_MAQUI ROLE_FCOMS sub Head Armor 4 Mass -2 sub Sensor 9 mass -2 end Torso Armor 4 Mass -2 sub CPit Armor 2 mass -1 Flight 5 Sensor 4 ECM 3 STC LAS-5 name type end Storage name Armor 4 mass -1 sub MLauncher 8 sub STC SWM-2 magazine 40 end MLauncher 1 sub STC PLM-9 magazine 1 end Mount name inv MLauncher 8 sub STC SWM-2 magazine 40 end end end Storage name Armor 4 mass -1 sub MLauncher 8 sub STC SWM-2 magazine 40 end MLauncher 1 sub STC PLM-9 magazine 1 end Mount name inv MLauncher 8 sub STC SWM-2 magazine 40 end end end Arm name Size 2 Armor 4 Mass -1 sub Hand name inv STC MAC-4 end Flight 2 end Arm name Size 2 Armor 4 Mass -1 sub Hand name Flight 2 end inv Shield 4 DefBonus 1 end Leg name Size 5 Armor 4 Mass -1 sub Mount name inv MLauncher 12 sub STC HR-10 magazine 12 end end Flight 5 end Leg name Size 5 Armor 4 Mass -1 sub Mount name inv MLauncher 12 sub STC HR-10 magazine 12 end end Flight 5 end end gearhead-2-0.701/design/Atgeir.txt000066400000000000000000000036561321074026100166530ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ROLE_HOELL ROLE_FCOMS ROLE_PRIVA sub Head size 3 Armor 4 Mass -3 sub Sensor 5 ECM 4 STC LAS-3 name type end inv HeadArmor 2 mass -2 end Torso Armor 4 Mass -3 sub CPit Armor 1 HoverJet 6 Engine 4 Armor 1 HighPerformance Gyro Armor 1 Mount name Mount name Sensor 2 end inv BodyArmor 3 Mass -3 end Arm name Armor 3 Mass -2 sub Hand name inv Melee 8 name Acc 1 Speed 3 type end Mount name inv Gun 6 name caliber <65mm caseless> Acc 1 Speed 3 Range 7 Magazine 50 sub Ammo 6 caliber <65mm caseless> mass -4 end end HeavyActuator 2 mass -1 end inv ArmArmor 2 mass -2 end Arm name Armor 3 Mass -2 sub Hand name inv end Mount name inv end HeavyActuator 2 mass -1 end inv ArmArmor 2 mass -2 Shield 5 DefBonus 2 mass -4 sub MLauncher 3 sub STC PLM-9 magazine 3 end end end Leg name Armor 4 Mass -2 sub HoverJet 3 OverCharger 2 end inv LegArmor 2 mass -2 sub HoverJet 1 end end Leg name Armor 4 Mass -2 sub HoverJet 3 OverCharger 2 end inv LegArmor 2 mass -2 sub HoverJet 1 end end end gearhead-2-0.701/design/Bargol.txt000066400000000000000000000041471321074026100166420ustar00rootroot00000000000000Battroid 7 Name Desig type sdl_sprite <> SDL_PORTRAIT <> Desc factions ROLE_REDMA sub Head size 6 Armor 8 Mass -8 sub CPit Armor 2 mass -1 Sensor 6 mass -1 ECM 6 mass -1 STC LAS-5 name Speed 3 type end Torso Armor 8 Mass -10 sub Sensor 4 Engine 7 Armor 2 Mass -1 HighOutput Computer 5 mass -5 sub Software 2 name S_MVBoost S_BoostScale 2 Software 3 name S_TRBoost S_BoostScale 2 end PowerSource 4 mass -4 Mount Name inv STC RG-16 end Mount Name inv mlauncher 10 sub Rockets 25 name magazine 4 range 8 type end end HeavyActuator 3 end Arm Name Size 8 Armor 8 Mass -10 sub Hand name Mount name inv Melee 15 name type desc UsesSpeed Speed 2 Mass -9 end BeamGun 10 name Range 5 type Integral HeavyActuator 2 end Arm Name Size 8 Armor 8 Mass -10 sub Hand name Mount name inv end BeamGun 13 Name Type Integral mass -4 Speed 2 BV 2 Range 7 HeavyActuator 2 end Leg Name size 6 Armor 6 Mass -8 sub HoverJet 7 end Leg Name size 6 Armor 6 Mass -8 sub HoverJet 7 end Leg Name size 6 Armor 6 Mass -8 sub HoverJet 7 end Leg Name size 6 Armor 6 Mass -8 sub HoverJet 7 end end gearhead-2-0.701/design/BlueBird.txt000066400000000000000000000014251321074026100171200ustar00rootroot00000000000000HoverFighter 3 name desig type SDL_SPRITE SDL_PORTRAIT desc factions ROLE_AEGSF sub Torso Armor 3 sub CPit Sensor 4 SpaceFlight 2 end Turret Size 1 Armor 1 sub Mount name inv STC LAS-10 end end Storage name size 2 Armor 2 sub MLauncher 4 sub STC LR-1 Magazine 40 end SpaceFlight 2 end Storage name size 2 Armor 2 sub MLauncher 4 sub STC LR-1 Magazine 40 end SpaceFlight 2 end end gearhead-2-0.701/design/BuruBuru.txt000066400000000000000000000331551321074026100172100ustar00rootroot00000000000000Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 ROLE_FCOMS ROLE_MAQUI ROLE_RISHI ROLE_BOHEM ROLE_PRIVA sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end end Arm Name mass 3 Armor 4 sub Hand inv STC SC-9 end end Arm Name mass 3 Armor 4 sub Hand inv Melee 5 Name Acc 1 end end Leg Name mass 3 Armor 4 sub HoverJet 4 end Leg Name mass 3 Armor 4 sub HoverJet 4 end end Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 ROLE_FCOMS ROLE_RISHI ROLE_MAQUI ROLE_BOHEM sub Head Armor 4 mass 1 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 1 sub CPit Armor 2 Mount name inv MLauncher 6 sub STC LR-1 Magazine 60 end end HeavyActuator 3 end Arm Name Armor 4 sub Hand inv STC SC-6 end HeavyActuator 1 Melee 1 Name Acc 1 end Arm Name Armor 4 sub Hand inv Melee 8 Name end STC MAC-2 HeavyActuator 1 end Leg Name mass 1 Armor 4 sub HoverJet 4 end Leg Name mass 1 Armor 4 sub HoverJet 4 end end Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -5 ROLE_MAQUI ROLE_SILKN sub Head Armor 4 Mass 1 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC HR-10 Magazine 4 end end ECM 2 PowerSource 4 mass -4 end Arm Name Armor 4 Mass 1 sub Hand inv STC PAR-2 end Mount name inv STC SML-5 end PowerSource 1 Mass -1 end Arm Name Armor 4 Mass 1 sub Hand Mount name PowerSource 1 Mass -1 end inv Shield 3 DefBonus 1 end Leg Name mass 1 Armor 4 sub HoverJet 4 end Leg Name mass 1 Armor 4 sub HoverJet 4 end end Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 ROLE_RISHI ROLE_ROCKE sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv STC MAC-4 end SpaceFlight 3 end Arm Name mass 3 Armor 4 sub Hand inv STC GBAZ-17 end end Arm Name mass 3 Armor 4 sub Hand inv EMelee 10 Name Mass 1 end end Leg Name mass 2 Armor 4 sub HoverJet 4 end Leg Name mass 2 Armor 4 sub HoverJet 4 end end Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 ROLE_FCOMS ROLE_L5LAW ROLE_BOHEM sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC LR-1 Magazine 40 end end end Arm Name mass 3 Armor 4 sub Hand inv STC SC-9 end end Arm Name mass 3 Armor 3 sub Hand end inv ArmArmor 2 sub Melee 4 name end Shield 2 DefBonus -1 end Leg Name mass 3 Armor 4 sub HoverJet 4 end Leg Name mass 3 Armor 4 sub HoverJet 4 end end Battroid 5 Name Desig type SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 factions ROLE_CRIHN ROLE_REDMA ROLE_PRIVA sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end Mount name ECM 1 end Arm Name mass 3 Armor 3 sub Hand inv STC MB-7 end end inv ArmArmor 2 sub Melee 2 name end end Arm Name mass 3 Armor 3 sub Hand inv Melee 7 name Speed 3 Mass -3 Acc 1 end end inv ArmArmor 2 sub Melee 2 name end end Leg Name mass 3 Armor 4 sub HoverJet 4 end Leg Name mass 3 Armor 4 sub HoverJet 4 end end Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 ROLE_L5LAW sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end end Arm Name mass 3 Armor 4 sub Hand inv STC SC-6 end end Arm Name mass 3 Armor 4 sub Hand inv Melee 5 Name Acc 1 end end inv Shield 5 name DefBonus 1 sub STC MAC-2 end end Leg Name mass 3 Armor 4 sub HoverJet 4 end Leg Name mass 3 Armor 4 sub HoverJet 4 end end %% End Urban Buru Buru Battroid 5 Name Desig type SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 factions ROLE_MAQUI ROLE_FCOMS ROLE_L5LAW ROLE_CRIHN ROLE_REDMA ROLE_PRIVA ROLE_ROCKE sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end end Arm Name mass 3 Armor 4 sub Hand inv STC GR-24 end end Arm Name mass 3 Armor 4 sub Hand end inv Shield 3 name DefBonus -1 sub STC MAC-2 end end Leg Name mass 3 Armor 4 sub HoverJet 4 end Leg Name mass 3 Armor 4 sub HoverJet 4 end end %% End Heavyfoot Buru Buru Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 sub Head Armor 4 mass 1 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 1 sub CPit Armor 2 Mount name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end HeavyActuator 3 end Arm Name Armor 4 sub Hand inv Gun 5 name caliber <70mm caseless> mass -4 range 4 acc -2 BV 4 magazine 100 sub ammo 5 caliber <70mm caseless> end end HeavyActuator 1 Melee 1 Name Acc 1 end Arm Name Armor 4 sub Hand inv melee 18 name type end STC MAC-2 HeavyActuator 1 end Leg Name mass 1 Armor 4 sub HoverJet 4 end Leg Name mass 1 Armor 4 sub HoverJet 4 end end %% End Devil BuruBuru Battroid 5 Name Desig type SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 factions ROLE_CRIHN sub Head Armor 4 mass 1 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 1 sub CPit Armor 2 Mount name inv MLauncher 6 sub STC HR-10 Magazine 6 end end HeavyActuator 3 ECM 2 end Arm Name Armor 4 sub Hand inv Gun 5 name caliber <70mm caseless> mass -4 range 5 BV 3 magazine 100 sub ammo 5 caliber <70mm caseless> end end HeavyActuator 1 Melee 1 Name Acc 1 end Arm Name Armor 4 sub Hand STC MAC-2 HeavyActuator 1 end inv Shield 2 DefBonus 1 sub Melee 12 name Integral end end Leg Name mass 1 Armor 4 sub HoverJet 4 end Leg Name mass 1 Armor 4 sub HoverJet 4 end end %% End Revenge BuruBuru Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT SDL_MESH SDL_SKIN desc Legality -15 sub Head Armor 4 mass 2 sub Sensor 4 STC LAS-3 type Integral end Torso Armor 4 mass 3 sub CPit Armor 2 Mount name inv MLauncher 2 sub STC HR-10 name type Acc -1 magazine 2 end end end Arm Name mass 3 Armor 4 sub Hand inv Gun 3 Name Range 3 caliber Speed 4 BV 4 Acc -1 Magazine 120 sub Ammo 3 caliber end end end Arm Name mass 3 Armor 4 sub Hand inv melee 7 name type acc -1 end end Leg Name mass 3 Armor 4 sub HoverJet 4 end Leg Name mass 3 Armor 4 sub HoverJet 4 end end %% End Mason Buru Buru gearhead-2-0.701/design/Century.txt000066400000000000000000000021671321074026100170650ustar00rootroot00000000000000Battroid 4 Name Desig type factions %%sdl_portrait desc sub Head Armor 4 sub CPit Armor 2 STC LAS-5 name type Integral Sensor 5 end Torso Armor 4 sub MLauncher 8 Name sub Rockets 2 Name Magazine 40 Range 12 Speed 1 end Computer 1 sub Software 1 name S_TRBoost S_BoostScale 2 end end Arm Name Armor 4 sub gun 10 name range 5 Speed 3 type caliber <165mm caseless> integral Magazine 20 sub Ammo 10 caliber <165mm caseless> end end Arm Name Armor 4 sub Hand inv STC LAS-5 name Range 4 Recharge 2 end end Leg Name Armor 4 Leg Name Armor 4 end gearhead-2-0.701/design/Chameleon.txt000066400000000000000000000130611321074026100173220ustar00rootroot00000000000000Battroid 3 Name Desig type SDL_PORTRAIT desc factions ROLE_AEGIS ROLE_AEGSF sub Head Size 2 Armor 2 sub Sensor 6 mass -1 end Torso Armor 2 sub CPit Armor 1 Gyro Armor 1 BeamGun 2 Name Range 3 Acc 2 BeamGun 2 Name Range 3 Acc 2 Computer 2 sub Software 1 name S_MVBoost S_BoostScale 2 Software 1 name S_TRBoost S_BoostScale 2 end end Arm Name Size 4 Armor 2 sub Hand name inv BeamGun 1 Name Range 5 Acc 1 Speed 6 BV 2 end end Arm Name Size 4 Armor 2 sub Hand name end inv Shield 3 DefBonus 2 end Leg Name Size 4 Armor 2 sub Wheels 3 end Leg Name Size 4 Armor 2 sub Wheels 3 end end Battroid 3 Name Desig type SDL_PORTRAIT desc factions ROLE_AEGIS ROLE_AEGSF sub Head Size 2 Armor 2 sub Sensor 6 mass -1 end Torso Armor 2 sub CPit Armor 1 Gyro Armor 1 BeamGun 2 Name Range 3 Acc 2 BeamGun 2 Name Range 3 Acc 2 Computer 2 sub Software 1 name S_MVBoost S_BoostScale 2 Software 1 name S_TRBoost S_BoostScale 2 end end Arm Name Size 4 Armor 2 sub Hand name inv STC GR-12 Acc 2 Speed 2 Desig end Mount name inv MLauncher 3 sub STC GHM-15 Magazine 2 end end end Arm Name Size 4 Armor 2 sub Hand name Mount name inv MLauncher 3 sub STC LR-1 Magazine 30 end end end inv Shield 3 DefBonus 2 end Leg Name Size 4 Armor 2 sub Wheels 3 end Leg Name Size 4 Armor 2 sub Wheels 3 end end Battroid 3 Name Desig type desc SDL_PORTRAIT factions ROLE_RISHI ROLE_AEGSF sub Head Size 2 Armor 2 sub Sensor 6 mass -1 end Torso Armor 2 sub CPit Armor 1 Gyro Armor 1 Computer 2 sub Software 1 name S_MVBoost S_BoostScale 2 Software 1 name S_TRBoost S_BoostScale 2 end BeamGun 2 Name Range 3 Acc 2 BeamGun 2 Name Range 3 Acc 2 end Storage name size 3 armor 2 mass -1 sub Flight 8 end Arm Name Size 4 Armor 2 sub Hand name inv BeamGun 3 Name Range 6 Acc 1 Speed 3 end end Arm Name Size 4 Armor 2 sub Hand name end inv Shield 3 DefBonus 2 end Leg Name Size 4 Armor 2 sub Wheels 3 end Leg Name Size 4 Armor 2 sub Wheels 3 end end Battroid 3 Name Desig type SDL_PORTRAIT desc factions ROLE_AEGIS ROLE_AEGSF sub Head Size 2 Armor 3 sub Sensor 6 mass -1 end Torso Armor 3 mass -1 sub CPit Armor 1 Gyro Armor 1 Flight 6 Integral Computer 2 sub Software 1 name S_MVBoost S_BoostScale 2 Software 1 name S_TRBoost S_BoostScale 2 end end inv BodyArmor 2 sub Flight 2 Integral end end Arm Name Size 4 Armor 3 mass -1 sub Hand name inv BeamGun 5 name Acc 1 Mass -3 Range 5 end Mount name inv MLauncher 3 sub STC GHM-15 Magazine 2 end end end Arm Name Size 4 Armor 3 mass -1 sub Hand name inv Melee 4 name Acc 2 mass -2 end Mount name inv MLauncher 3 sub STC LR-1 Magazine 30 end end end inv Shield 3 DefBonus 2 end Leg Name Size 4 Armor 3 mass -1 sub Wheels 3 Flight 3 Integral end Leg Name Size 4 Armor 3 mass -1 sub Wheels 3 Flight 3 Integral end end gearhead-2-0.701/design/Chimentero.txt000066400000000000000000000062221321074026100175250ustar00rootroot00000000000000Battroid 7 Name Desig desc type %%%sdl_portrait sdl_sprite factions ROLE_AEGIS ROLE_AEGSF sub Storage Armor 6 Name mass -3 sub HoverJet 9 Mount Name inv BeamGun 8 Name Acc -1 Range 6 BV 5 Speed 1 end Computer 5 mass -4 sub Software 5 name S_TRBoost S_BoostScale 2 end end inv StorageArmor 3 Mass -5 sub MLauncher 2 sub STC LM-20 magazine 1 end end end Storage Armor 6 Name mass -3 sub HoverJet 9 Mount Name inv BeamGun 8 Name Acc -1 Range 6 BV 5 Speed 1 end Computer 5 mass -6 sub Software 3 name S_MVBoost S_BoostScale 2 Software 4 name S_SpeedComp S_BoostScale 2 end end inv StorageArmor 3 Mass -5 sub MLauncher 2 sub STC LM-20 magazine 1 end end end Torso Armor 7 Mass -9 sub CPit Armor 2 Mass -1 Sensor 9 mass -3 Gyro Armor 2 Mass -1 Engine Armor 2 Mass -1 BeamGun 14 Name type mass -3 Integral Acc 1 Speed 1 Range 7 Mount Name inv MLauncher 12 sub STC GM-10 Magazine 12 end end Mount Name inv MLauncher 18 sub STC HFR-3 Magazine 60 end end end Arm Size 5 Armor 7 Mass -7 Name sub Hand Mount Name inv Gun 20 Name Type Acc -2 BV 4 Range 5 Speed 1 Mass -7 caliber <120mm ferrous> Magazine 50 sub Ammo 20 Mass -5 caliber <120mm ferrous> end end MLauncher 4 sub STC LM-20 Magazine 2 end end Arm Size 5 Armor 7 Mass -7 Name sub Hand Mount Name inv Gun 20 Name Type Acc -2 BV 4 Range 5 Speed 1 Mass -7 caliber <120mm ferrous> Magazine 50 sub Ammo 22 Mass -5 caliber <120mm ferrous> end end MLauncher 4 sub STC LM-20 magazine 2 end end Leg Size 8 Armor 7 Mass -8 Name sub HoverJet 5 MLauncher 4 sub STC LM-20 magazine 2 end end Leg Size 8 Armor 7 Mass -8 Name sub HoverJet 5 MLauncher 4 sub STC LM-20 magazine 2 end end Tail Size 3 Armor 5 Mass -4 sub Melee 10 Name Speed 3 Acc 2 mass -8 Integral end end gearhead-2-0.701/design/Cogan.txt000066400000000000000000000034451321074026100164630ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ROLE_ROCKE sub Head size 2 Armor 3 Mass -1 VariableStorage sub Sensor 5 STC LAS-3 name type Integral mass -2 ECM 2 end Torso Armor 4 Mass -2 sub CPit Armor 1 BeamGun 7 name range 5 type Integral mass -4 Transform:Aerofighter name SDL_SPRITE2 <> CUTE_SPRITE2 <> Integral Mount name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end Flight 5 end inv BodyArmor 2 mass -2 end Arm name Armor 3 size 3 Mass -1 VariableStorage sub Hand name inv Gun 5 Scale 2 name caliber <70mm self-propelled> Speed 2 Range 6 Acc 1 mass -3 BV 2 Magazine 60 sub Ammo 5 caliber <70mm self-propelled> end end Flight 3 end Arm name size 3 Armor 3 Mass -1 VariableStorage sub Hand name inv EMelee 6 name end Flight 3 end Leg name size 5 Armor 4 Mass -2 VariableWing sub Mount name inv MLauncher 2 sub stc HWM-4 magazine 4 end end Flight 5 end Leg name Size 5 Armor 4 Mass -2 VariableWing sub Mount name inv MLauncher 2 sub stc HWM-4 magazine 4 end end Flight 5 end end gearhead-2-0.701/design/Corraich.txt000066400000000000000000000042021321074026100171560ustar00rootroot00000000000000AeroFighter 6 Name Desig SDL_Sprite <> SDL_Portrait <> type desc FACTIONS ROLE_PRIVA ROLE_CRIHN ROLE_REDMA sub torso Armor 6 mass -4 antibeam sub CPit Armor 1 Gyro Armor 1 Engine Armor 1 HighPerformance Sensor 6 ECM 5 Mount Name inv STC RG-16 name Speed 3 mass -7 end Mount Name inv Gun 8 name range 6 Speed 3 mass -3 caliber <30cm explosive cannister> Magazine 12 sub Ammo 8 caliber <30cm explosive cannister> type end end Computer 4 % Salvaged from a Thorshammer. Really, that's the main reason they can sell % those mecha in the dusty ring. mass -4 sub Software 2 name S_MVBoost S_BoostScale 2 Software 2 name S_TRBoost S_BoostScale 2 end Flight 4 end Storage name size 4 Armor 4 antibeam mass -3 Integral sub STC VC-5 name range 4 OverCharger 9 end Storage name size 4 Armor 4 antibeam mass -3 Integral sub STC VC-5 name range 4 OverCharger 9 end Wing Name Armor 5 antibeam mass -3 sub Mount Name inv MLauncher 16 sub STC SWM-5 name magazine 32 type end end Flight 6 end Wing Name Armor 5 antibeam mass -3 sub Mount Name inv MLauncher 16 sub STC SWM-5 name magazine 32 type end end Flight 6 end end gearhead-2-0.701/design/Crown.txt000066400000000000000000000034371321074026100165250ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite SDL_Portrait Desig desc TYPE FACTIONS ROLE_SILKN sub Head Armor 3 mass -2 sub Sensor 7 mass -1 STC LAS-3 mass -2 name type end Torso Armor 4 mass -2 sub CPit Armor 2 mass -1 Mount name inv MLauncher 2 sub STC HWM-4 Magazine 4 end end Mount name inv MLauncher 2 sub STC HWM-4 Magazine 4 end end Sensor 3 ECM 3 Computer 5 mass -7 sub Software 1 name S_SpeedComp S_BoostScale 2 Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 end end Storage size 4 Armor 4 mass -2 sub Mount name Flight 3 ECM 6 mass -1 LongRangeScanner 4 end Arm Name Size 3 Armor 4 mass -2 sub Mount name inv EMelee 5 Name type end Hand inv STC PAR-2 end PowerSource 2 mass -2 end Arm Name Size 3 Armor 4 mass -2 sub mount name Hand PowerSource 2 mass -2 end inv Shield 4 DefBonus 2 end Leg Name Armor 2 mass -1 sub Flight 5 end Leg Name Armor 2 mass -1 sub Flight 5 end end gearhead-2-0.701/design/DaoDeoji.txt000066400000000000000000000051351321074026100171100ustar00rootroot00000000000000Battroid 8 Name Desig SDL_Sprite <> SDL_Portrait <> Desc TYPE FACTIONS ROLE_CRIHN ROLE_BOHEM Sub Head Armor 4 Size 4 Mass -4 sub Sensor 5 STC LAS-3 type Integral STC MAC-4 end inv HeadArmor 5 Mass -6 end Torso Armor 7 Mass -7 sub Sensor 1 CPit Armor 1 Computer 4 mass -4 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 end Gyro Armor 1 Engine 8 Armor 1 HighPerformance HeavyActuator 12 mass -6 Integral ECM 5 end inv BodyArmor 7 Mass -7 sub Flight 4 end end Arm Name Size 9 Armor 7 Mass -6 sub Hand Name inv STC RG-8 end Mount Name inv BeamGun 10 name Range 5 Type mass -2 end HeavyActuator 6 mass -3 PowerSource 2 mass -2 end inv ArmArmor 7 Mass -7 sub Melee 12 name type mass -7 end end Arm Name Size 9 Armor 7 Mass -6 sub Hand Name Mount Name inv Gun 16 name caliber <80cm self-propelled> range 6 mass -10 Magazine 20 sub Ammo 16 mass -3 caliber <80cm self-propelled> type end end HeavyActuator 6 mass -3 PowerSource 2 mass -2 end inv ArmArmor 7 Mass -7 sub Melee 12 name type mass -7 end end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 12 sub Rockets 8 name type Scale 2 Range 8 Magazine 15 end end Flight 8 end inv LegArmor 7 Mass -7 sub Flight 4 end end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 12 sub Rockets 8 name type Scale 2 Range 8 Magazine 15 end end Flight 8 end inv LegArmor 7 Mass -7 sub Flight 4 end end end gearhead-2-0.701/design/Daum.txt000066400000000000000000000100241321074026100163110ustar00rootroot00000000000000Battroid 5 Name Desig SDL_Sprite SDL_PORTRAIT desc type factions ROLE_MAQUI ROLE_CRIHN ROLE_L5LAW ROLE_FCOMS ROLE_RISHI sub Head Armor 3 mass -1 sub Sensor 5 STC LAS-5 name type Integral mass -1 end inv HeadArmor 2 Mass -1 end torso Armor 3 mass -1 sub CPit Armor 2 Sensor 1 Mount Name Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end Computer 3 mass -3 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end HoverJet 3 end inv BodyArmor 3 Mass -2 end Arm Name Armor 3 mass -1 sub Hand inv Gun 2 Name Range 6 caliber <25mm Ferrous> Speed 3 BV 4 Acc -1 Magazine 250 sub Ammo 2 caliber <25mm Ferrous> end end Mount Name end inv ArmArmor 2 Mass -1 end Arm Name Armor 3 mass -1 sub Hand Mount Name inv Melee 7 Name Acc 1 end end inv ArmArmor 2 Mass -1 EShield 5 DefBonus -1 end Leg Name Armor 3 sub HoverJet 4 Mount Name end inv LegArmor 2 Mass -1 sub HoverJet 1 end end Leg Name Armor 3 sub HoverJet 4 Mount Name end inv LegArmor 2 Mass -1 sub HoverJet 1 end end end Battroid 5 Name Desig SDL_Sprite SDL_PORTRAIT desc type factions ROLE_MAQUI ROLE_CRIHN ROLE_FCOMS ROLE_PRIVA ROLE_RISHI ROLE_BOHEM sub Head Armor 3 mass -1 sub Sensor 5 STC LAS-5 name type Integral mass -1 end inv HeadArmor 2 Mass -1 end torso Armor 3 mass -1 sub CPit Armor 2 Sensor 1 Mount Name Mount Name inv MLauncher 8 sub STC GM-10 Magazine 8 end end Computer 3 mass -3 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end HoverJet 3 end inv BodyArmor 3 Mass -2 end Arm Name Armor 3 mass -1 sub Hand inv STC MBAZ-17 end Mount Name end inv ArmArmor 2 Mass -1 end Arm Name Armor 3 mass -1 sub Hand Mount Name inv Melee 7 Name Acc 1 end end inv ArmArmor 2 Mass -1 EShield 5 DefBonus -1 end Leg Name Armor 3 sub HoverJet 4 Mount Name end inv LegArmor 2 Mass -1 sub HoverJet 1 end end Leg Name Armor 3 sub HoverJet 4 Mount Name end inv LegArmor 2 Mass -1 sub HoverJet 1 end end end gearhead-2-0.701/design/Dora.txt000066400000000000000000000015751321074026100163230ustar00rootroot00000000000000Gerwalk 3 name desig sdl_sprite sdl_portrait desc type factions Legality -20 ROLE_COMET ROLE_HOELL ROLE_MUGLE sub torso armor 2 sub CPit Mount name inv BeamGun 3 name range 4 Acc 1 end Sensor 3 SpaceFlight 3 end arm name size 1 armor 1 sub Hand end arm name size 1 armor 1 sub Hand end storage size 1 name armor 2 sub SpaceFlight 4 end end gearhead-2-0.701/design/Eggman.txt000066400000000000000000000072731321074026100166350ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ROLE_BOHEM ROLE_MUGLE sub Head size 5 Armor 4 Mass -2 sub Sensor 6 mass -1 CPit Armor 1 Computer 2 mass -2 sub Software 2 name S_SpeedComp S_BoostScale 2 Software 1 name S_TRBoost S_BoostScale 2 end ECM 2 end inv HeadArmor 2 Mass -1 end Torso Armor 4 Mass -2 sub HeavyActuator 4 STC LAS-5 mass -3 Sensor 1 end inv BodyArmor 2 Mass -1 end Storage Size 1 Armor 3 Mass -1 sub Mount name inv MLauncher 4 sub STC ICM-5 Magazine 8 end end Flight 3 end Arm name Armor 3 Mass -1 sub Hand name inv STC GR-12 end Mount name inv Melee 7 name desc type mass -2 end HeavyActuator 2 end inv ArmArmor 1 end Arm name Armor 3 Mass -1 sub Hand name Mount name HeavyActuator 2 end inv Shield 4 DefBonus 1 mass -2 ArmArmor 1 end Leg name Armor 4 Mass -1 sub Mount name inv MLauncher 4 sub STC GM-10 Magazine 4 end end Flight 4 end inv LegArmor 1 end Leg name Armor 4 Mass -1 sub Mount name inv MLauncher 4 sub STC GM-10 Magazine 4 end end Flight 4 end inv LegArmor 1 end end Battroid 4 Name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ROLE_BOHEM sub Head size 5 Armor 4 Mass -2 sub Sensor 6 mass -1 CPit Armor 1 Computer 2 mass -2 sub Software 2 name S_SpeedComp S_BoostScale 2 Software 1 name S_TRBoost S_BoostScale 2 end ECM 2 end inv HeadArmor 2 Mass -1 end Torso Armor 4 Mass -2 sub HeavyActuator 4 STC LAS-5 mass -3 Sensor 1 end inv BodyArmor 2 Mass -1 end Storage Size 1 Armor 3 Mass -1 sub Mount name inv MLauncher 4 sub STC ICM-5 Magazine 8 end end Flight 3 end Arm name Armor 3 Mass -1 sub Hand name inv STC RG-8 end Mount name inv Melee 7 name desc type mass -2 end HeavyActuator 2 end inv ArmArmor 1 end Arm name Armor 3 Mass -1 sub Hand name Mount name HeavyActuator 2 end inv Shield 4 DefBonus 1 mass -2 ArmArmor 1 end Leg name Armor 4 Mass -1 sub Mount name inv MLauncher 4 sub STC GM-10 Magazine 4 end end Flight 4 end inv LegArmor 1 end Leg name Armor 4 Mass -1 sub Mount name inv MLauncher 4 sub STC GM-10 Magazine 4 end end Flight 4 end inv LegArmor 1 end end gearhead-2-0.701/design/Eyre.txt000066400000000000000000000047611321074026100163420ustar00rootroot00000000000000Battroid 5 Name Desig type factions ROLE_HOELL ROLE_FCOMS ROLE_BOHEM SDL_Sprite SDL_PORTRAIT desc Legality -10 sub Head Armor 4 mass 2 mass -1 sub Sensor 5 STC LAS-3 type end Torso Armor 4 mass 3 sub CPit Armor 1 ECM 2 HoverJet 2 end Arm Name mass 3 Armor 4 sub Hand inv STC SC-6 end Mount name inv EMelee 6 name Acc -1 end end Arm Name mass 3 Armor 4 sub Hand Mount name end inv Shield 4 DefBonus 1 end Storage Name size 2 Armor 4 sub Mount name inv MLauncher 6 sub STC LR-1 magazine 60 end end HoverJet 5 end Storage Name size 2 Armor 4 sub Mount name inv MLauncher 6 sub STC LR-1 magazine 60 end end HoverJet 5 end end Battroid 5 Name Desig type factions ROLE_HOELL ROLE_BOHEM SDL_Sprite SDL_PORTRAIT desc Legality -10 sub Head Armor 4 mass 2 mass -1 sub Sensor 5 STC LAS-3 type end Torso Armor 4 mass 3 sub CPit Armor 1 ECM 2 HoverJet 2 end Arm Name mass 3 Armor 4 sub Hand inv STC RG-8 end Mount name inv EMelee 6 name end end Arm Name mass 3 Armor 4 sub Hand Mount name end inv Shield 4 DefBonus 1 end Storage Name size 2 Armor 4 sub Mount name inv MLauncher 4 sub STC SWM-2 magazine 20 end end HoverJet 5 end Storage Name size 2 Armor 4 sub Mount name inv MLauncher 4 sub STC SWM-2 magazine 20 end end HoverJet 5 end end gearhead-2-0.701/design/Fenris.txt000066400000000000000000000023541321074026100166600ustar00rootroot00000000000000Zoanoid 4 Name Desig factions SDL_Sprite %%SDL_PORTRAIT type desc sub Head Size 5 Armor 4 Mass -3 sub CPit Armor 1 Sensor 4 Melee 9 Name type Acc 1 Speed 1 Integral mass -4 end Torso Armor 4 Mass -6 sub MLauncher 3 sub STC LR-1 name Acc 0 Magazine 30 end Computer 1 sub Software 1 name S_TRBoost S_BoostScale 2 end Overcharger 4 end Leg Name Size 5 Armor 4 Mass -5 sub Mount Name inv STC MAC-4 end Melee 5 Acc 2 Speed 3 Name mass -2 Integral HeavyActuator 4 end Leg Name Size 5 Armor 4 Mass -5 sub Mount Name inv STC MAC-4 end Melee 5 Acc 2 Speed 3 Name mass -2 Integral HeavyActuator 4 end Tail Size 3 Armor 4 Mass -3 Integral sub HeavyActuator 4 Integral end end gearhead-2-0.701/design/Galah.txt000066400000000000000000000074231321074026100164500ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite SDL_Portrait Desig desc TYPE FACTIONS ROLE_MAQUI ROLE_COMET ROLE_FCOMS sub Head size 3 Armor 3 Mass -1 sub Sensor 6 mass -1 end Torso Armor 4 Mass -2 sub CPit Armor 1 Flight 6 Mount name Mount name inv MLauncher 1 sub STC HWM-4 Magazine 2 end end ECM 2 end Arm name Armor 3 Mass -1 sub Hand name inv Beamgun 4 name Acc 1 Range 6 Recharge 3 end PowerSource 2 mass -2 end Arm name Armor 3 Mass -1 sub Hand name PowerSource 2 mass -2 end inv Shield 2 end Leg name Armor 4 Mass -1 sub Mount name Flight 4 end Leg name Armor 4 Mass -1 sub Mount name Flight 4 end end Battroid 4 Name SDL_Sprite SDL_Portrait Desig desc TYPE FACTIONS ROLE_COMET ROLE_L5LAW sub Head size 3 Armor 3 Mass -1 sub Sensor 6 mass -1 end Torso Armor 4 Mass -2 sub CPit Armor 1 Flight 6 Mount name Mount name inv MLauncher 1 sub STC HWM-4 Magazine 2 end end ECM 2 end inv BodyArmor 2 end Arm name Armor 3 Mass -1 sub Hand name inv Beamgun 4 name Acc 1 Range 6 Recharge 3 sub EMelee 6 name Integral end end PowerSource 2 mass -2 end Arm name Armor 3 Mass -1 sub Hand name PowerSource 2 mass -2 end inv Shield 2 DefBonus 2 end Leg name Armor 4 Mass -1 sub Mount name Flight 4 end Leg name Armor 4 Mass -1 sub Mount name Flight 4 end end Battroid 4 Name SDL_Sprite SDL_Portrait Desig desc TYPE FACTIONS ROLE_L5LAW sub Head size 3 Armor 3 Mass -1 sub Sensor 6 mass -1 end Torso Armor 4 Mass -2 sub CPit Armor 1 Flight 6 Mount name Mount name inv MLauncher 1 sub STC HWM-4 Magazine 2 end end ECM 2 end Arm name Armor 3 Mass -1 sub Hand name inv STC PAR-2 name sub EMelee 6 name type Integral end end PowerSource 2 mass -2 end Arm name Armor 3 Mass -1 sub Hand name PowerSource 2 mass -2 end inv Shield 3 name DefBonus 2 end Leg name Armor 4 Mass -1 sub Mount name Flight 4 end Leg name Armor 4 Mass -1 sub Mount name Flight 4 end end gearhead-2-0.701/design/Gaunt.txt000066400000000000000000000030551321074026100165070ustar00rootroot00000000000000Battroid 2 Name SDL_Sprite <> SDL_Portrait Desig desc TYPE FACTIONS ROLE_REDMA sub Head size 1 Armor 2 Mass -1 sub Sensor 5 ECM 3 end Torso Armor 3 Mass -1 sub CPit Armor 1 Mount name inv MLauncher 4 sub STC AAM-8 Magazine 5 end end Engine HighPerformance end Arm name Armor 2 Mass -1 sub Hand name inv Gun 5 name caliber <70mm self-propelled> Range 5 BV 4 mass -4 Magazine 100 sub Ammo 5 caliber <70mm self-propelled> mass -8 MLauncher 1 Integral sub Rockets 1 name magazine 6 range 6 type end end end Mount name inv EMelee 5 name end end Arm name Armor 2 Mass -1 sub Hand name inv end Mount name inv end end inv Shield 1 DefBonus 2 end Wing name Size 3 Armor 2 mass -1 sub Flight 2 OverCharger 2 end Wing name Size 3 Armor 2 mass -1 sub Flight 2 OverCharger 2 end Leg name Armor 3 Mass -1 sub Flight 3 end Leg name Armor 3 Mass -1 sub Flight 3 end end gearhead-2-0.701/design/Gigas.txt000066400000000000000000000022011321074026100164530ustar00rootroot00000000000000AeroFighter 7 Name Desig SDL_Sprite desc type factions ROLE_AEGIS ROLE_AEGSF sub Torso Armor 7 Mass -9 sub CPit Armor 2 mass -1 Sensor 6 mass -1 Computer 2 mass -1 sub Software 2 name S_TRBoost S_BoostScale 2 end Mount Name inv MLauncher 12 sub STC SWM-2 Magazine 60 end end STC LAS-5 Flight 8 end Wing Name Armor 7 Mass -9 sub Mount Name inv MLauncher 2 sub STC NUKE-20 end end Flight 7 end Wing Name Armor 7 Mass -9 sub Mount Name inv MLauncher 2 sub STC NUKE-20 end end Flight 7 end Storage Name Size 3 Armor 6 Mass -3 sub STC PAR-6 Flight 5 end Storage Name Size 3 Armor 6 Mass -3 sub STC PAR-6 Flight 5 end end gearhead-2-0.701/design/Gorilla.txt000066400000000000000000000017671321074026100170320ustar00rootroot00000000000000Battroid 5 Name desig type desc sdl_portrait factions ROLE_CRIHN sub Head Armor 5 sub CPit Armor 1 Sensor 4 end Torso Armor 5 sub Mount name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end HoverJet 2 end Arm Name size 6 Armor 5 sub Hand inv STC MAC-4 end Melee 14 name type Integral HoverJet 1 end Arm Name size 6 Armor 5 sub Hand Melee 14 name type Integral HoverJet 1 end Leg Name size 4 Armor 4 sub HoverJet 3 end Leg Name size 4 Armor 4 sub HoverJet 3 end end gearhead-2-0.701/design/Haiho.txt000066400000000000000000000024441321074026100164620ustar00rootroot00000000000000Battroid 3 Name SDL_Sprite SDL_Portrait SDL_Mesh SDL_Skin Desig desc TYPE FACTIONS ROLE_MUGLE ROLE_ROCKE ROLE_BOHEM sub Torso Armor 3 Mass -2 Sub CPit Sensor 5 HeavyActuator 6 End Inv BodyArmor 2 End Arm name Armor 3 Size 4 Mass -2 sub Hand name inv Melee 10 name type end Mount name inv end HeavyActuator 3 end inv ArmArmor 2 end Arm name Armor 3 Size 4 Mass -2 sub Hand name inv end Mount name inv Beamgun 5 name Range 4 Acc -1 Speed 2 end HeavyActuator 3 end inv ArmArmor 2 end Leg name Armor 3 Mass -1 sub Flight 4 end inv LegArmor 2 end Leg name Armor 3 Mass -1 sub Flight 4 end inv LegArmor 2 end end gearhead-2-0.701/design/Hariseng.txt000066400000000000000000000027531321074026100171750ustar00rootroot00000000000000Battroid 6 Name Desig SDL_Sprite desc <> type factions sub Head Size 3 Armor 4 Mass -3 sub Sensor 4 Computer 2 mass -1 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end end inv HeadArmor 3 Mass -3 end Torso Armor 6 Mass -3 sub CPit Armor 2 mass -1 Gun 8 Name caliber Type Range 5 Mass -2 Magazine 20 sub Ammo 8 caliber Type end HeavyActuator 4 mass -1 end inv BodyArmor 6 Mass -6 end Arm Name Size 4 Armor 5 Mass -3 sub Hand inv Melee 16 Name mass -2 Type end STC MAC-4 end inv ArmArmor 4 Mass -4 sub MLauncher 2 sub STC FRM-6 magazine 3 end end end Arm Name Size 4 Armor 5 Mass -3 sub Hand STC MAC-4 end inv EShield 5 Name DefBonus -1 ArmArmor 4 Mass -4 sub MLauncher 2 sub STC FRM-6 magazine 3 end end end Leg Name Armor 5 mass -3 sub HeavyActuator 7 Mass -5 end inv LegArmor 4 mass -4 end Leg Name Armor 5 mass -3 sub HeavyActuator 7 Mass -5 end inv LegArmor 4 mass -4 end end gearhead-2-0.701/design/Harkney.txt000066400000000000000000000037011321074026100170300ustar00rootroot00000000000000HoverFighter 6 name desig type desc sdl_portrait factions ROLE_CRIHN sub Torso name Armor 4 sub CPit Armor 1 Engine 6 HighPerformance Armor 2 Gyro Armor 1 Sensor 6 ECM 4 Flight 5 Computer 3 sub Software 2 name S_TRBoost S_BoostScale 2 Software name S_Information SInfo_MechaDex end Mount name inv STC LAS-3 name type end Mount name inv STC PAR-2 end end inv BodyArmor 4 fits name end Turret size 5 Armor 3 sub Gun 20 Scale 2 name caliber <500mm ferrous ball> Range 7 Recharge 1 type Mass -4 Magazine 15 sub Ammo 20 caliber <500mm ferrous ball> end end inv TurretArmor 5 end Storage name size 4 Armor 3 sub MLauncher 6 Scale 2 sub Rockets 15 name Range 6 type Magazine 4 end Flight 4 end Storage name size 4 Armor 3 sub MLauncher 6 Scale 2 sub Rockets 15 name Range 6 type Magazine 4 end Flight 4 end Wing name size 3 Armor 3 sub Flight 3 Mount name inv MLauncher 16 sub STC GM-10 Magazine 16 name end end end Wing name size 3 Armor 3 sub Flight 3 Mount name inv MLauncher 16 sub STC ICM-5 Magazine 32 end end end end gearhead-2-0.701/design/IceWind.txt000066400000000000000000000056431321074026100167600ustar00rootroot00000000000000Battroid 3 Name Desig type factions desc SDL_Portrait SDL_SPRITE ROLE_SILKN ROLE_ROCKE sub Head Size 1 Armor 3 sub Sensor 5 end Torso Armor 3 sub CPit STC LAS-5 STC LAS-5 HoverJet 3 end inv BodyArmor 2 Fits end Arm Name Armor 3 Size 2 sub Hand inv EMelee 5 Name Acc 1 end Mount Name inv Gun 1 Name Acc -1 Range 5 BV 4 caliber <16mm ferrous> Magazine 60 sub Ammo 1 caliber <16mm ferrous> end end end Arm Name Armor 3 Size 2 sub Hand Mount Name end inv EShield 3 DefBonus 1 end Leg Name Size 4 Armor 3 sub HoverJet 3 Mount Name inv MLauncher 2 sub STC LR-1 Magazine 20 end end end inv LegArmor 2 Fits end Leg Name Size 4 Armor 3 sub HoverJet 3 Mount Name inv MLauncher 2 sub STC LR-1 Magazine 20 end end end inv LegArmor 2 Fits end end Battroid 3 Name Desig type desc SDL_Portrait SDL_SPRITE factions ROLE_REDMA ROLE_SILKN ROLE_ROCKE sub Head Size 1 Armor 3 sub Sensor 5 end Torso Armor 3 sub CPit STC LAS-5 STC LAS-5 HoverJet 3 end inv BodyArmor 2 Fits end Arm Name Armor 3 Size 2 sub Hand inv Gun 8 Name caliber <30mm ferrous> Acc 1 Range 6 Speed 3 Magazine 30 sub Ammo 8 caliber <30mm ferrous> end end Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end end Arm Name Armor 3 Size 2 sub Hand Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end end Leg Name Size 4 Armor 3 sub HoverJet 3 Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end end Leg Name Size 4 Armor 3 sub HoverJet 3 Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end end end gearhead-2-0.701/design/Joust.txt000066400000000000000000000014021321074026100165270ustar00rootroot00000000000000Battroid 5 Name Desig desc type factions sdl_portrait Legality -5 sub Head Size 3 Armor 4 sub CPit Sensor 5 end torso Armor 4 sub Mount name inv MLauncher 3 sub STC SWM-2 magazine 15 end end end Arm Name Armor 3 sub Hand inv EMelee 6 Name end end Arm Name Armor 3 sub Hand inv STC maC-4 end end Leg Name Armor 4 sub HoverJet 4 end Leg Name Armor 4 sub HoverJet 4 end end gearhead-2-0.701/design/Kraken.txt000066400000000000000000000041621321074026100166440ustar00rootroot00000000000000AeroFighter 10 Name Desig SDL_Sprite <> SDL_Portrait <> type FACTIONS desc ROLE_CRIHN sub torso Armor 9 mass -10 sub CPit Armor 1 Gyro Armor 1 Engine Armor 1 HighPerformance Computer 7 mass -4 sub Software 5 name S_TRBoost S_BoostScale 2 Software 4 name S_SpeedComp S_BoostScale 2 end Sensor 6 ECM 6 Mount Name inv STC PHS-8 end Mount Name inv MLauncher 2 sub STC NUKE-20 magazine 1 end end Gun 25 Scale 2 name caliber <500mm ferrous ball> Range 8 Recharge 2 BV 2 type Integral mass -10 Magazine 60 sub Ammo 25 caliber <500mm ferrous ball> end end inv BodyArmor 4 mass -5 end Wing Name Size 6 Armor 7 mass -8 HighTier sub Mount Name inv MLauncher 24 mass -1 sub STC SWM-5 magazine 48 type end end SpaceFlight 6 end inv WingArmor 2 mass -3 end Wing Name Size 6 Armor 7 mass -8 HighTier sub Mount Name inv MLauncher 24 mass -1 sub STC SWM-5 magazine 48 type end end SpaceFlight 6 end inv WingArmor 2 mass -3 end Wing Name Size 6 Armor 7 mass -8 LowTier sub Mount Name inv STC PAR-2 end SpaceFlight 6 end inv WingArmor 2 mass -3 end Wing Name Size 6 Armor 7 mass -8 LowTier sub Mount Name inv STC VC-5 name mass -9 BV 5 range 5 end SpaceFlight 6 end inv WingArmor 2 mass -3 end end gearhead-2-0.701/design/Largo.txt000066400000000000000000000052111321074026100164710ustar00rootroot00000000000000Battroid 6 Name Desig SDL_Sprite <> SDL_Portrait <> desc sdl_portrait <> factions type ReflexSystem ROLE_L5LAW sub Head Armor 5 mass -6 sub Sensor 8 mass -1 ECM 5 EMelee 8 name type Acc 1 Speed 3 end inv HeadArmor 4 Mass -4 end Torso Armor 6 mass -6 sub CPit Armor 2 mass -1 Computer 4 mass -6 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 Software 2 name S_SpeedComp S_BoostScale 2 Software 1 name desc Category Factions S_Information SInfo_MechaDex end Mount Name inv STC PAR-13 end Transform:Zoanoid name <> SDL_SPRITE2 <> CUTE_SPRITE2 <> Integral HeavyActuator 8 Integral Mass -6 end inv BodyArmor 6 Mass -6 sub Flight 4 Integral end end Arm Name Armor 5 mass -5 VariableLeg sub Hand inv STC MAC-4 mass -4 end Mount Name inv MLauncher 4 sub STC GM-10 magazine 4 end end Flight 3 HeavyActuator 3 Mass -2 Integral end inv ArmArmor 4 mass -4 sub Flight 3 Integral end end Arm Name Armor 5 mass -5 VariableLeg sub Hand inv Melee 10 name type speed 1 acc 1 mass -5 end Mount Name inv MLauncher 4 sub STC GM-10 magazine 4 end end Flight 3 HeavyActuator 3 mass -2 Integral end inv ArmArmor 4 mass -4 sub Flight 3 Integral end end Leg Name Armor 5 mass -5 sub Flight 4 Mount Name inv MLauncher 6 sub STC HWM-4 magazine 15 end end HeavyActuator 3 mass -2 Integral end inv LegArmor 4 Mass -4 sub Flight 3 Integral end end Leg Name Armor 5 mass -5 sub Flight 4 Mount Name inv MLauncher 6 sub STC HWM-4 magazine 15 end end HeavyActuator 3 mass -2 Integral end inv LegArmor 4 Mass -4 sub Flight 3 Integral end end end gearhead-2-0.701/design/Luna2.txt000066400000000000000000000057641321074026100164230ustar00rootroot00000000000000Battroid 4 Name Desig desc type factions ROLE_AEGIS ROLE_AEGSF ROLE_RISHI sdl_portrait sub Head Size 3 Armor 3 mass -2 sub Sensor 7 mass -2 end torso Armor 4 mass -2 sub CPit Sensor 5 Computer 4 mass -6 sub Software 3 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end HoverJet 2 end Arm Name Size 5 Armor 4 mass -2 sub mount NAme inv MLauncher 10 sub STC HR-10 Magazine 10 end end Hand inv BeamGun 7 Name Range 7 Acc 3 end Melee 10 name type mass -6 Integral end Arm Name Size 5 Armor 4 mass -2 sub mount name inv MLauncher 10 sub STC HR-10 Magazine 10 end end Hand inv end Melee 10 name type mass -6 Integral end Leg Name Size 4 Armor 3 mass -2 sub HoverJet 5 end Leg Name Size 4 Armor 3 mass -2 sub HoverJet 5 end end Battroid 4 Name Desig desc type factions ROLE_AEGIS ROLE_AEGSF sdl_portrait sub Head Size 3 Armor 3 mass -2 sub Sensor 7 mass -2 end torso Armor 4 mass -3 sub CPit Sensor 5 ECM 3 Computer 4 mass -6 sub Software 3 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end HoverJet 2 end Arm Name Size 5 Armor 4 mass -2 sub mount NAme inv MLauncher 9 sub STC GHM-15 Magazine 6 end end Hand inv BeamGun 7 Name Range 7 Acc 3 end Melee 10 name type mass -6 Integral end Arm Name Size 5 Armor 4 mass -2 sub mount name inv MLauncher 8 sub STC AAM-8 Magazine 10 end end Hand inv end Melee 10 name type mass -6 Integral end inv Shield 2 DefBonus 1 end Leg Name Size 4 Armor 3 mass -2 sub HoverJet 5 end Leg Name Size 4 Armor 3 mass -2 sub HoverJet 5 end end gearhead-2-0.701/design/Mebsy.txt000066400000000000000000000045251321074026100165130ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait Desig desc TYPE FACTIONS ROLE_MAQUI ROLE_FCOMS ROLE_PRIVA ROLE_RISHI ROLE_BOHEM sub Head size 1 Armor 3 sub Sensor 4 end Torso Armor 4 mass 1 sub CPit end Arm name size 3 Armor 3 sub Hand name inv Gun 5 name caliber <25mm ferrous> speed 3 Range 6 Magazine 40 sub Ammo 5 caliber <25mm ferrous> end end end Arm name size 3 Armor 3 sub Hand name end inv Shield 2 end Leg name Armor 3 mass 1 sub Mount name inv MLauncher 1 sub STC LR-1 magazine 10 end end SpaceFlight 4 end Leg name Armor 3 mass 1 sub Mount name inv MLauncher 1 sub STC LR-1 magazine 10 end end SpaceFlight 4 end end Battroid 4 Name SDL_Sprite <> SDL_Portrait Desig desc TYPE FACTIONS ROLE_BOHEM sub Head size 1 Armor 3 sub Sensor 4 end Torso Armor 4 mass 1 sub CPit end Arm name size 3 Armor 3 sub Hand name inv STC GBAZ-17 end end Arm name size 3 Armor 3 sub Hand name end inv Shield 2 end Leg name Armor 3 mass 1 sub Mount name inv MLauncher 1 sub STC LR-1 magazine 10 end end SpaceFlight 4 end Leg name Armor 3 mass 1 sub Mount name inv MLauncher 1 sub STC LR-1 magazine 10 end end SpaceFlight 4 end end gearhead-2-0.701/design/Neko.txt000066400000000000000000000031021321074026100163160ustar00rootroot00000000000000Gerwalk 4 Name Desig SDL_Sprite SDL_Portrait desc type factions ROLE_ROCKE ROLE_REDMA sub torso Armor 3 Mass -4 sub Mount Name CPit Sensor 7 mass -1 Computer 3 mass -1 sub Software 1 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 end STC LAS-3 Name BV 4 Type end Arm Name Size 2 Armor 3 Mass -4 sub Hand Name inv STC PAR-6 end end Arm Name Size 2 Armor 3 Mass -4 sub Hand Name end inv Shield 3 DefBonus -1 end Wing Name Armor 2 size 3 Mass -2 sub Mount Name inv MLauncher 6 sub STC ICM-5 Magazine 12 end end ArcJet 3 end Wing Name Armor 2 size 3 Mass -2 sub Mount Name inv MLauncher 6 sub STC ICM-5 Magazine 12 end end ArcJet 3 end Leg Name Armor 2 Mass -3 sub Mount Name Flight 3 end Leg Name Armor 2 Mass -3 sub Mount Name Flight 3 end end gearhead-2-0.701/design/Ovaknight.txt000066400000000000000000000055121321074026100173630ustar00rootroot00000000000000Battroid 7 Name Desig type factions sdl_sprite SDL_PORTRAIT <> Desc sub Head Size 5 Armor 7 Mass -5 sub CPit Armor 2 mass -1 Sensor 5 Computer 3 mass -2 sub Software 3 name S_TRBoost S_BoostScale 2 end ECM 4 end inv HeadArmor 5 Mass -7 end Torso Armor 7 Mass -9 sub Engine 7 Armor 2 Mass -1 HighOutput BeamGun 15 Name Type Integral Acc 1 Speed 1 Range 7 Computer 2 mass -2 sub Software 4 name S_SpeedComp S_BoostScale 2 end Mount Name inv STC VC-5 mass -9 end Mount Name inv STC VC-5 mass -9 end STC LAS-5 Name Type mass -1 Sensor 3 end inv BodyArmor 8 Mass -12 end Arm Name Size 6 Armor 6 Mass -3 sub Hand end inv ArmArmor 5 Mass -8 sub Gun 5 name Type caliber <30mm ferrous> Acc -1 Range 7 BV 3 mass -5 Magazine 60 sub Ammo 5 caliber <30mm ferrous> Type mass -2 end end end Arm Name Size 6 Armor 6 Mass -3 sub Hand end inv ArmArmor 5 Mass -8 sub Gun 5 name Type caliber <30mm ferrous> Acc -1 Range 7 BV 3 mass -5 Magazine 60 sub Ammo 5 caliber <30mm ferrous> Type mass -2 end end end Leg Name size 8 Armor 4 Mass -7 sub PowerSource 2 mass -3 HeavyActuator 5 mass -4 end inv LegArmor 3 Mass -5 end Leg Name size 8 Armor 4 Mass -7 sub PowerSource 2 mass -3 HeavyActuator 5 mass -4 end inv LegArmor 3 Mass -5 end Leg Name size 8 Armor 4 Mass -7 sub PowerSource 2 mass -3 HeavyActuator 5 mass -4 end inv LegArmor 3 Mass -5 end Leg Name size 8 Armor 4 Mass -7 sub PowerSource 2 mass -3 HeavyActuator 5 mass -4 end inv LegArmor 3 Mass -5 end Leg Name size 8 Armor 4 Mass -7 sub PowerSource 2 mass -3 HeavyActuator 5 mass -4 end inv LegArmor 3 Mass -5 end Leg Name size 8 Armor 4 Mass -7 sub PowerSource 2 mass -3 HeavyActuator 5 mass -4 end inv LegArmor 3 Mass -5 end end gearhead-2-0.701/design/PC_Earth.txt000066400000000000000000000154431321074026100170620ustar00rootroot00000000000000% ************************ % *** MELEE WEAPONS *** % ************************ Melee 6 Name desc Type category factions SDL_PORTRAIT legality -10 Speed 3 Melee 12 Name desc category factions SDL_PORTRAIT legality 5 % Battle-axes aren't illegal, but you'll be more likely to find one in a rowdy area... Mass -2 Melee 8 Name Type Mass -3 speed 3 desc category factions SDL_PORTRAIT Melee 10 Name Type Mass -3 desc category factions SDL_PORTRAIT legality 5 Melee 15 Name Type Mass -7 desc category factions SDL_PORTRAIT legality 5 % **************************** % *** MISSILE WEAPONS *** % **************************** Gun 8 Name desig <7-Z> desc <> sdl_portrait <> caliber <5mm auto> category factions type legality 5 Mass -7 Range 6 Speed 3 BV 5 magazine 120 sub Ammo 8 name <5mm Auto Clip> caliber <5mm auto> SDL_PORTRAIT end % *************** % *** ARMOR *** % *************** Set name desc Category Factions legality -10 inv BodyArmor 3 Name desc Category Factions legality -10 ArmArmor 3 Name desc Category Factions Mass -3 legality -5 LegArmor 3 Name desc Category Factions Mass -3 legality -5 ArmArmor 3 Name desc Category Factions Mass -3 legality -5 LegArmor 3 Name desc Category Factions Mass -3 legality -5 end Set name desc Category Factions legality -5 inv BodyArmor 5 Name desc Category Factions legality -5 ArmArmor 5 Name desc Category Factions mass -2 legality -5 ArmArmor 5 Name desc Category Factions mass -2 legality -5 LegArmor 5 Name desc Category Factions mass -2 legality -5 LegArmor 5 Name desc Category Factions mass -2 legality -5 end Set name desc Category Factions inv BodyArmor 4 Name desc Category Factions Mass -6 legality -5 ArmArmor 3 Name desc Category Factions Mass -4 legality -5 LegArmor 3 Name desc Category Factions Mass -4 legality -5 ArmArmor 3 Name desc Category Factions Mass -4 legality -5 LegArmor 3 Name desc Category Factions Mass -4 legality -5 end Set name desc Category Factions legality -10 inv HeadArmor 4 Name SDL_PORTRAIT desc Category Factions Mass -5 legality -10 BodyArmor 6 Name desc SDL_PORTRAIT Category Factions Mass 1 legality -10 ArmArmor 4 Name desc SDL_PORTRAIT Category Factions Mass -5 legality -10 LegArmor 4 Name desc SDL_PORTRAIT Category Factions Mass -5 legality -10 ArmArmor 4 Name desc SDL_PORTRAIT Category Factions Mass -5 legality -10 LegArmor 4 Name desc SDL_PORTRAIT Category Factions Mass -5 legality -10 end HeadArmor 1 Name desc Category Factions SDL_PORTRAIT Mass -1 legality -20 HeadArmor 3 Name desc Category Factions SDL_PORTRAIT Mass -5 Memo Phone legality -5 HeadArmor 5 Name desc Category Factions SDL_PORTRAIT Mass -7 Memo legality -5 % ************************ % *** WEAPON ADD-ONS *** % ************************ MeleeAddOn 0 Name desc Category Factions sub BeamGun 8 Name desc Range 5 Acc -1 Type end gearhead-2-0.701/design/PC_Equipment.txt000066400000000000000000001463161321074026100177720ustar00rootroot00000000000000% GearHead PC Scale Equipment % In this file are the items used by all factions and throughout % the solar system. See the other PC_* files for items used in % specific locations. %% SAMPLE LEGALITY LEVELS %% -50 : Absolutely legal everywhere %% -25 : Should be legal %% -10 : Tools that can be used as weapons %% -5 : Tasers and other non-deadly weapons %% 0 : Most adventuring equipment %% 5 : Advanced Weapons %% 10 : Heavy Weapons, Combat Armor %% 25 : Contraband in most places %% 50 : GARU serum, Hunter-X pods %% Earth generally has a tolerance of 10, meaning that any items with a Legality %% score of 10 or less are freely available. Most space colonies have a tolerance %% of between 0 and 5, meaning that heavy weapons are not available but most "light" %% adventuring equipment is. Luna has a tolerance of -15, meaning that weapons %% cannot legally be purchased at all. % ************************ % *** MELEE WEAPONS *** % ************************ Melee 2 Name Type desc sdl_portrait category factions legality -10 Acc 1 Speed 5 RepairTool 1 name desc sdl_portrait factions category legality -15 sub Melee 7 name sdl_portrait Speed 3 Mass -3 end SurvivalTool 1 Name desc sdl_portrait factions category legality -5 sub Melee 3 name Acc 1 Speed 5 Mass -2 end Melee 4 name Type desc SDL_PORTRAIT category factions Mass -3 Melee 8 name Type desc SDL_PORTRAIT category factions Legality 15 Acc 1 Mass -7 Melee 3 Name Type desc category factions SDL_PORTRAIT legality -10 Acc 1 Speed 4 Mass -2 Melee 5 Name desc sdl_portrait category factions legality -10 Acc 1 Speed 4 Mass -2 Melee 5 Name desc UsesBody category factions SDL_PORTRAIT legality -10 Mass 2 Speed 1 Melee 4 Name desc UsesBody category factions SDL_PORTRAIT legality -15 Melee 7 Name desc category factions SDL_PORTRAIT legality -5 Acc 1 Speed 4 Mass -3 Melee 6 Name desc category factions type SDL_PORTRAIT legality -5 Speed 3 UsesBody Melee 8 Name desc type category factions SDL_PORTRAIT legality 0 UsesBody Melee 8 Name desc category factions SDL_PORTRAIT legality 0 Acc 2 Speed 4 Mass -5 Melee 5 Name desc type category factions SDL_PORTRAIT legality 0 Acc 3 Speed 5 Mass -3 Melee 9 Name Type Acc 1 Mass -4 Speed 3 desc category factions SDL_PORTRAIT legality 0 Melee 2 Name Type Mass -1 Speed 1 desc category factions SDL_PORTRAIT legality -10 Melee 17 Name Type UsesReflexes Mass -9 Acc -1 desc category factions SDL_PORTRAIT legality 5 EMelee 19 Name Type SDL_PORTRAIT Mass 12 Acc -1 desc category factions legality 0 inv PowerSource 4 mass -1 name end EMelee 19 Name Type SDL_PORTRAIT Mass 8 Acc 1 Speed 3 desc category factions legality 15 inv PowerSource 4 mass -1 name end EMelee 14 Name Type SDL_PORTRAIT legality 10 UsesReflexes Mass 3 Acc 1 Speed 4 desc category factions inv PowerSource 4 mass -2 name end Melee 14 Name Type Mass -9 legality 10 Acc 2 Speed 3 desc category factions SDL_PORTRAIT Melee 9 Name Type Acc 2 legality 20 Speed 4 Mass -6 desc sdl_portrait category factions Melee 10 Name Type Acc 1 legality 5 Speed 6 Mass -6 desc sdl_portrait category factions Melee 22 Name Type UsesReflexes legality 10 Mass -15 desc category factions SDL_PORTRAIT Melee 2 Name Acc -2 Speed 4 UsesSpeed Type desc category factions legality -10 SDL_PORTRAIT EMelee 3 Name Speed 5 desc category factions SDL_PORTRAIT legality -5 inv PowerSource 1 mass -1 name end % **************************** % *** BALLISTIC WEAPONS *** % **************************** Gun 5 Name desig desc sdl_portrait category factions caliber <5mm rifle> Mass -1 Range 9 Acc 1 Speed 3 magazine 30 sub Ammo 8 name <5mm Rifle Clip> caliber <5mm rifle> SDL_PORTRAIT end Gun 6 Name desig desc sdl_portrait category factions caliber <5mm rifle> Range 7 Acc 1 Magazine 30 Legality -5 sub Ammo 8 name <5mm Rifle Clip> caliber <5mm rifle> SDL_PORTRAIT end Gun 6 Name desig desc sdl_portrait caliber <5mm rifle> category factions legality 0 Mass -3 Range 6 Speed 5 magazine 40 sub Ammo 8 name <5mm Rifle Clip> caliber <5mm rifle> category factions SDL_PORTRAIT end Gun 6 Name desig desc sdl_portrait caliber <5mm rifle> category factions legality 5 Mass -4 Range 6 Speed 4 BV 2 magazine 60 sub Ammo 8 name <5mm Rifle Clip> caliber <5mm rifle> category factions SDL_PORTRAIT end Gun 12 name desc <> caliber <18mm shell> category factions SDL_PORTRAIT mass -4 Range 5 Acc 1 Recharge 3 Legality 5 magazine 16 sub Ammo 12 name <18mm Shotgun Clip> caliber <18mm shell> type category factions SDL_PORTRAIT end Gun 15 name desc <> caliber <18mm shell> category factions SDL_PORTRAIT mass -4 Range 5 Acc 1 Recharge 5 Legality 10 magazine 32 sub Ammo 15 name <18mm Shotgun Clip> caliber <18mm shell> type category factions SDL_PORTRAIT end Gun 12 name desc <> caliber <18mm shell> category factions SDL_PORTRAIT mass -4 Range 5 Acc 1 Recharge 3 BV 1 Legality 15 magazine 24 sub Ammo 12 name <18mm Shotgun Clip> caliber <18mm shell> type category factions SDL_PORTRAIT end Gun 5 Name desig desc Mass -3 Range 3 caliber <.32 revolver> category factions SDL_PORTRAIT Legality -15 Magazine 6 sub Ammo 5 name desc category factions caliber <.32 revolver> SDL_PORTRAIT end Gun 8 Name desig desc Mass -6 Range 3 speed 6 caliber <.32 revolver> category factions SDL_PORTRAIT Legality -10 Magazine 6 sub Ammo 8 name desc caliber <.32 revolver> SDL_PORTRAIT end Gun 17 Name desig desc Type caliber <12mm auto> category factions Mass -9 Range 7 Speed 4 BV 7 Magazine 80 UsesBody Legality 15 sub Ammo 17 Mass -3 caliber <12mm auto> end % ******************** % *** BEAM GUNS *** % ******************** BeamGun 8 Name desig desc sdl_portrait category factions Range 5 Mass -6 Acc 1 Speed 3 Legality 5 inv PowerSource 4 name mass -2 end BeamGun 20 Name desig desc sdl_portrait category factions Range 8 Mass -8 Acc 1 Speed 2 Legality 10 inv PowerSource 4 name mass -2 end % *************************** % *** MISSILE LAUNCHERS *** % *************************** % ************************ % *** WEAPON ADD-ONS *** % ************************ MeleeAddOn 0 Name desc Category Factions Type Acc 1 Mass 1 legality -5 GunAddOn 0 Name desc Category Factions Range 2 GunAddOn 0 Name desc Category Factions Acc 1 GunAddOn 0 Name desc Category Factions Speed 1 GunAddOn 0 Name desc Category Factions Acc 1 Speed 1 GunAddOn 0 Name desc Category Factions Mass -1 sub Melee 3 Name desc Mass -1 end GunAddOn 0 Name desc Category Factions legality 5 sub Gun 8 Name desc Mass -7 Integral Range 6 caliber <20mm cannister> UsesPerception Magazine 5 sub Ammo 8 name desc caliber <20mm cannister> Type end end MeleeAddOn 0 Name desc Category Factions Acc 1 legality -10 MeleeAddOn 0 Name desc Category Factions Speed 2 legality -10 MeleeAddOn 0 Name desc Category Factions Mass -1 sub Melee 3 Name desc Mass -2 end legality -5 MeleeAddOn 1 Name desc Category Factions Type MeleeAddOn 1 Name desc Category Factions Type MeleeAddOn 1 Name desc Category Factions Type legality 25 MeleeAddOn 0 Name desc Category Factions Type Acc 1 MeleeAddOn 0 Name desc Category Factions Type Speed 1 legality 10 MeleeAddOn 2 Name desc Category Factions Type % ****************** % *** GRENADES *** % ****************** Grenade 20 Name desc sdl_portrait Type Category Factions legality -5 Grenade 10 Name desc sdl_portrait Category Factions Type legality -5 Grenade 20 Name desc sdl_portrait Category Factions Type legality 10 Grenade 18 Name desc sdl_portrait Category Factions Type legality 0 Grenade 15 Name desc sdl_portrait Category Factions Type legality 5 Grenade 12 Name desc sdl_portrait Category Factions Type legality 10 Grenade 13 Name desc sdl_portrait Category Factions Type legality 50 Grenade 16 Name desc sdl_portrait Category Factions Type legality 10 Grenade 10 Name desc sdl_portrait Category Factions Type Mass -1 legality 5 Grenade 14 Name desc sdl_portrait Category Factions Type Grenade 12 Name desc sdl_portrait Category Factions Type Grenade 18 Name desc sdl_portrait Category Factions Type Legality 5 Grenade 8 Name desc sdl_portrait Category Factions Type Grenade 7 Name desc sdl_portrait Category Factions Type legality 5 Grenade 5 Name desc Category Factions SDL_PORTRAIT Mass -1 BV 4 Type legality -5 UsesSpeed Grenade 13 Name desc Category Factions SDL_PORTRAIT Mass -1 BV 9 Type UsesSpeed % *************** % *** ARMOR *** % *************** BodyArmor 1 Name desc sdl_portrait Category Factions Mass 3 legality -25 BodyArmor 2 Name desc SDL_PORTRAIT Category Factions Mass -3 legality -15 HeadArmor 2 Name desc sdl_portrait Category Factions Mass -3 legality -25 Set name desc Category Factions Legality -25 inv HeadArmor 1 name desc Category SDL_PORTRAIT Mass -1 Sealed Hardened BodyArmor 1 name desc Category SDL_PORTRAIT Mass -1 Sealed sub SpaceFlight 1 end ArmArmor 1 name desc Category SDL_PORTRAIT Mass -1 Sealed LegArmor 1 name desc SDL_PORTRAIT Category Mass -1 Sealed ArmArmor 1 name desc Category SDL_PORTRAIT Mass -1 Sealed LegArmor 1 name desc Category SDL_PORTRAIT Mass -1 Sealed end Set name desc Category Factions Legality -25 inv HeadArmor 2 name desc Category SDL_PORTRAIT Mass -3 Sealed Hardened Phone BodyArmor 2 name desc Category SDL_PORTRAIT Mass -3 Sealed sub SpaceFlight 2 end ArmArmor 2 name desc Category SDL_PORTRAIT Mass -3 Sealed LegArmor 2 name desc Category SDL_PORTRAIT Mass -3 Sealed sub SpaceFlight 1 end ArmArmor 2 name desc Category SDL_PORTRAIT Mass -3 Sealed LegArmor 2 name desc Category SDL_PORTRAIT Mass -3 Sealed sub SpaceFlight 1 end end Set name desc Category Factions legality -25 inv BodyArmor 1 Name desc Category Factions legality -25 Mass -1 ArmArmor 1 Name desc Category Mass -1 legality -25 ArmArmor 1 Name desc Category Mass -1 legality -25 LegArmor 1 Name desc Category Mass -1 legality -25 LegArmor 1 Name desc Category Mass -1 legality -25 end Set name desc Category Factions Legality 10 inv HeadArmor 5 name desc Mass -5 Sealed Phone Email Memo BodyArmor 5 name desc Mass -6 Legality 10 Sealed Body 10 sub HeavyActuator 3 end ArmArmor 5 name desc Mass -5 Sealed Body 5 sub HeavyActuator 3 end ArmArmor 5 name desc Mass -5 Sealed Body 5 sub HeavyActuator 3 end LegArmor 5 name desc Mass -5 Sealed Body 5 sub SpaceFlight 3 end LegArmor 5 name desc Mass -5 Sealed Body 5 sub SpaceFlight 3 end end BodyArmor 2 Name desc sdl_portrait Category Factions Mass -1 legality -5 BodyArmor 5 Name desc sdl_portrait Category Factions Mass -1 legality -5 HeadArmor 2 Name desc Category Factions SDL_PORTRAIT Mass -3 legality -30 HeadArmor 2 Name desc Category Factions SDL_PORTRAIT Mass -3 legality -10 HeadArmor 3 Name desc Category Factions SDL_PORTRAIT Mass -5 Memo legality -10 ArmArmor 1 Name desc Category Factions sdl_Portrait Mass -1 legality -25 LegArmor 1 Name desc Category Factions sdl_Portrait Mass -1 legality -25 ArmArmor 2 Name desc Category Factions sdl_Portrait Mass -3 legality -20 LegArmor 2 Name desc Category Factions sdl_Portrait Mass -3 legality -20 ArmArmor 2 Name desc Category Factions Mass -1 legality 5 sub Melee 5 Mass -4 Speed 1 Name desc end LegArmor 2 Name desc Category Factions Mass -1 legality 5 sub Melee 5 Mass -4 Speed 1 Name desc end % ***************** % *** SHIELDS *** % ***************** Shield 3 Name desc Category Factions legality -5 Shield 2 Name desc Category Factions DefBonus -3 legality -5 Shield 4 Name desc Category Factions DefBonus -2 Shield 6 Name desc Category Factions Shield 5 Name desc sdl_portrait Category Factions DefBonus 2 Shield 5 Name desc Category Factions legality 5 sub Gun 6 Name caliber <5mm rifle> factions Mass -3 Range 7 Speed 5 magazine 40 sub Ammo 6 name <5mm Rifle Clip> caliber <5mm rifle> category factions end end % ********************* % *** ELECTRONICS *** % ********************* Computer 1 name desc Category Factions SDL_PORTRAIT Memo Email Mass -1 legality -25 Computer 1 name desc Category Factions SDL_PORTRAIT Memo Email News legality -15 Mass -1 Treasure name desc sdl_portrait Category Factions Memo Email Phone legality -10 Treasure name desc sdl_portrait Category Factions Memo Email Phone News legality -10 Treasure name desc sdl_portrait Category Factions Memo Email Phone News Personadex legality -10 Treasure Mass 1 name desc sdl_portrait Category Factions Memo Email Phone legality -15 BodyHarness 1 name desc Category Factions Phone legality -10 mass -1 HeadHarness 1 name desc Category Factions Phone legality -10 mass -1 ArmHarness 2 name Desc Category Factions Memo Email News legality -15 sub Computer 3 mass -5 PowerSource 1 mass -1 end HeadHarness 2 name Desc Category Factions SDL_PORTRAIT Memo legality -15 sub Computer 3 mass -5 PowerSource 1 mass -1 end % ****************** % *** SOFTWARE *** % ****************** Software 1 name Category Factions S_TRBoost S_BoostScale 2 Software 2 name Category Factions S_TRBoost S_BoostScale 2 Software 1 name Category Factions S_MVBoost S_BoostScale 2 Software 2 name Category Factions S_MVBoost S_BoostScale 2 Software 1 name Category Factions S_SpeedComp S_BoostScale 2 Software 2 name Category Factions S_SpeedComp S_BoostScale 2 Software 3 name Category Factions S_SpeedComp S_BoostScale 2 Software 2 name Category Factions S_SpeedComp S_BoostScale 0 Software 1 name desc Category Factions S_Information SInfo_CreatureDex Legality -25 Software 1 name desc Category Factions S_Information SInfo_RobotDex Legality -15 Software 1 name desc Category Factions S_Information SInfo_SynthDex Legality 15 Software 1 name desc Category Factions S_Information SInfo_MechaDex Legality -10 % *********************** % *** POWER SOURCES *** % *********************** PowerSource 1 name Category Factions Legality -50 PowerSource 2 name Category Factions Legality -50 PowerSource 4 name Category Factions Legality -50 PowerSource 6 name Category Factions Legality -50 PowerSource 1 name mass -1 Category Factions Legality -25 PowerSource 2 name mass -2 Category Factions Legality -25 PowerSource 4 name mass -4 Category Factions Legality -25 PowerSource 2 name mass -3 Category Factions Legality -15 PowerSource 4 name mass -6 Category Factions Legality -15 BodyHarness 8 name desc Category Factions Legality 10 sub PowerSource 8 mass -12 PowerSource 8 mass -12 end BodyHarness 4 name desc Category Factions sub PowerSource 8 mass -9 end BodyHarness 5 name desc Category Factions Legality -5 sub PowerSource 10 mass -8 end ArmHarness 3 name desc Category Factions Legality -10 sub PowerSource 6 mass -9 end % ***************************** % *** MUSICAL INSTRUMENTS *** % ***************************** Instrument 0 name desc Category Factions Legality -50 Instrument 0 name desc Category Factions sdl_portrait Legality -50 Instrument 1 name desc Category Factions sdl_portrait Legality -50 Mass 1 Instrument 1 name desc Category Factions sdl_portrait Legality -50 Mass 1 Instrument 1 name desc Category Factions sdl_portrait Legality -50 Mass 1 Instrument 2 name desc sdl_portrait Category Factions Legality -50 % *************************** % *** ADVENTURING TOOLS *** % *************************** AcrobatTool 2 name desc Category Factions Legality -50 AcrobatTool 2 name desc Category Factions Legality 0 sub Melee 12 name UsesSpeed mass -12 type end HolySymbol 1 name desc <> Category Factions Legality -50 InsightTool 1 name desc Category Factions Legality -50 SDL_PORTRAIT LockPick 0 name desc Category factions Legality -5 LockPick 1 name desc Category Factions Legality 5 LockPick 2 name desc Category Factions Legality 15 ScienceTool 3 name desc Category Factions Legality -10 sub Computer 2 mass -2 PowerSource 2 mass -2 end % ********************* % *** REPAIR FUEL *** % ********************* MedicineFuel 30 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 MedicineFuel 60 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 MedicineFuel 90 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 MedicineFuel 120 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 MedicineFuel 150 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 MedicineFuel 300 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 MedicineFuel 600 Name desc Category Factions SDL_PORTRAIT Mass 1 legality -50 RepairFuel 10 Name desc Category Factions Mass 1 legality -50 RepairFuel 50 Name desc Category Factions Mass 1 legality -50 RepairFuel 75 Name desc Category Factions Mass 1 legality -50 RepairFuel 100 Name desc Category Factions Mass 1 legality -50 RepairFuel 170 Name desc Category Factions Mass 1 legality -50 RepairFuel 750 Name desc Category Factions Mass 3 legality -50 RepairFuel 100 Name desc Category Factions Mass 2 legality -50 RepairFuel 125 Name desc Category Factions Mass 1 legality -50 RepairFuel 50 Name desc Category Factions Mass 1 legality -50 RepairFuel 500 Name desc Category Factions Mass 3 legality -20 RepairFuel 1500 Name desc Category Factions Mass 4 legality -10 RepairFuel 3000 Name desc Category Factions Mass 5 legality -10 BiotechFuel 500 Name desc Category Factions Mass 1 legality 0 % ********************* % *** CONSUMABLES *** % ********************* Food 50 Name FoodMorale -2 Mass -3 desc Category Factions SDL_PORTRAIT legality -50 Food 50 Name Mass -3 desc Category Factions SDL_PORTRAIT legality -50 Food 50 Name Mass -3 FoodMorale 1 desc Category Factions SDL_PORTRAIT legality -50 Food 50 Name desc Category Factions SDL_PORTRAIT legality -50 Food 40 Name Mass -6 desc Category Factions SDL_PORTRAIT legality -50 Food 40 Name Mass -3 FoodMorale -2 desc Category Factions SDL_PORTRAIT legality -50 Food 20 Name Mass -2 FoodMorale -1 desc Category Factions SDL_PORTRAIT legality -50 Food 20 Name Mass -2 FoodMorale 1 desc Category Factions SDL_PORTRAIT legality -50 Set name desc Category Factions legality -10 SDL_PORTRAIT inv STC SurvivalBar desig <> STC SurvivalBar desig <> STC SurvivalBar desig <> end Food 5 Name FoodMorale 2 desc Category Factions legality -15 SDL_PORTRAIT Set name desc Category Factions SDL_PORTRAIT legality -15 inv STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> STC PILL_Nutrient desig <> end Set name desc Category Factions SDL_PORTRAIT legality -10 inv STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> end Set name desc Category Factions SDL_PORTRAIT legality -10 inv STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> STC PILL_QuickFix desig <> end Set name desc Category Factions SDL_PORTRAIT legality 0 inv STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> end Set name desc Category Factions SDL_PORTRAIT legality 0 inv STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> STC PILL_Regen desig <> end Set name desc Category Factions SDL_PORTRAIT legality -10 inv STC PILL_Antidote desig <> STC PILL_Antidote desig <> STC PILL_Antidote desig <> STC PILL_Antidote desig <> STC PILL_Antidote desig <> end Set name desc Category Factions SDL_PORTRAIT legality -10 inv STC PILL_Antibiotic desig <> STC PILL_Antibiotic desig <> STC PILL_Antibiotic desig <> STC PILL_Antibiotic desig <> STC PILL_Antibiotic desig <> end Food 5 Name FoodFX:CauseStatus FoodMod:Stone FoodMorale 3 desc Category Factions SDL_PORTRAIT legality -5 % ******************* % *** CYBERWARE *** % ******************* StatCyberware Name desc <(SKELETON) Repair and reinforcement for damaged bones.> CyberSlot Category Factions Body 1 Mass 25 legality -20 StatCyberware Name desc <(SKELETON) A structurally-strengthened skeleton.> CyberSlot Category Factions Body 3 Speed -1 Mass 25 legality 10 StatCyberware Name desc <(SKELETON) Does away with the weight caused by organically dense bones.> Category Factions CyberSlot Body -1 Speed 2 Reflexes 1 legality 5 StatCyberware Name desc <(SPINE) Does away with the fragile nature of a human's organic spine.> Category Factions CyberSlot Body 3 Ego 3 Speed -1 Mass 16 legality 5 StatCyberware Name desc <(SPINE) A replacement for an injured spine.> Category Factions CyberSlot Body 2 Speed -1 Mass 2 legality -20 StatCyberware Name desc <(SPINE) Replaces many of the spinal nerves with fiber-optic data transmission wires.> Category Factions CyberSlot Speed 2 Craft 1 Reflexes 1 Knowledge 1 Body -1 Mass 10 Legality 10 StatCyberware Name desc <(BRAIN) Takes advantage of certain predator instincts found in animals of prey.> Category Factions CyberSlot Body 1 Reflexes 4 Speed 3 Perception 2 Ego 1 Knowledge -3 Charm -1 Mass 15 legality 25 StatCyberware Name desc <(MUSCULATURE) Replaces conventional muscles with a technologically-improved biomaterial.> Category Factions CyberSlot Body 3 Mass 20 legality 10 StatCyberware Name desc <(MUSCULATURE) Muscles tuned for quick reactions.> Category Factions CyberSlot Reflexes 2 Speed 2 Mass 20 legality 15 StatCyberware Name desc <(MUSCULATURE) Synthetic reinforcements for the augmentation or repair of muscles.> Category Factions CyberSlot Body 1 Mass 20 legality -10 StatCyberware Name desc <(EYES) These eyes include resolution enhancers.> Category Factions CyberSlot Perception 1 Charm -1 Mass 1 legality -5 StatCyberware Name desc <(EYES) These eyes include resolution enhancers.> Category Factions CyberSlot Reflexes 1 Perception 2 Charm -1 Mass 1 legality 10 StatCyberware Name desc <(EYES) A pair of rather charming eyes.> Category Factions CyberSlot Perception 1 Charm 1 Mass 1 legality -5 SkillCyberware Name desc <(EYES) Provides highlighting of personal-scale targets.> Category Factions CyberSlot Type SkillModRangedCombat SkillModAmount 1 Mass 1 legality 10 StatCyberware Name desc <(EARS) Improves the perception of sound.> Category Factions CyberSlot Perception 1 Mass 1 legality -10 SkillCyberware Name desc <(EARS) Ears used by some performance judges to dissect and analyse music.> Category Factions CyberSlot SkillModPerformance SkillModAmount 3 Mass 1 legality -5 StatCyberware Name desc <(HEART) A heavy-duty replacement for a conventional organic heart.> Category Factions CyberSlot Body 2 Mass 2 legality -25 SkillCyberware Name desc <(BRAINSTEM) Interacts with a piloted mecha and provides neural feedback to the pilot.> Category Factions CyberSlot SkillModMechaPiloting SkillModAmount 3 Mass 1 legality 10 StatCyberware Name desc <(BRAIN) Provides direct links between some parts of the brain.> Category Factions CyberSlot Knowledge 1 Mass 1 legality -5 StatCyberware Name desc <(BRAIN) Removes some of the processing burden from the human brain.> Category Factions CyberSlot Knowledge 2 Mass 1 legality 5 StatCyberware Name desc <(BRAIN) Tunes the mind to detect tiny inputs that would otherwise go unnoticed.> Category Factions CyberSlot Perception 3 Mass 2 legality 5 StatCyberware Name desc <(BRAIN) Works with the existing synapses of the brain to greatly boost data storage and retrieval capabilities.> Category Factions CyberSlot Knowledge 3 Mass 1 legality 10 % *********************** % *** MISCELLANEOUS *** % *********************** set name desc factions Category scale 2 Legality -20 inv Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT desc Legality -15 sub Torso Armor 4 mass 3 sub CPit Armor 2 Mount name end end Head size 5 Armor 4 mass 3 scale 2 sub Sensor 4 STC LAS-3 type end Arm size 5 Name mass 3 Armor 4 scale 2 sub Hand end Arm size 5 Name mass 3 Armor 4 scale 2 sub Hand end Leg size 5 Name mass 3 Armor 4 scale 2 sub HoverJet 5 end Leg size 5 Name mass 3 Armor 4 scale 2 sub HoverJet 5 end end gearhead-2-0.701/design/PC_Exotic.txt000066400000000000000000000042271321074026100172500ustar00rootroot00000000000000%% %% Exotic items are difficult to find. They're strange, different... %% not necessarily better than normal items, but possibly better for %% certain characters. Exotic weapons will often use unusual stats. %% % ************************ % *** MELEE WEAPONS *** % ************************ Melee 8 name desc factions category legality -5 mass -4 UsesEgo Acc 1 Speed 3 sub BeamGun 6 Name desc Range 5 Mass -5 Acc 1 Speed 3 UsesEgo Integral inv PowerSource 2 name mass -2 end end Melee 12 name desc mass -10 Speed 5 factions category legality -5 type UsesCharm Melee 7 name mass -6 Speed 4 factions category legality -10 type UsesCharm Melee 9 Name desc category factions SDL_PORTRAIT legality -5 Acc 1 Speed 4 Mass -5 sub Gun 8 Name Mass -6 Range 5 speed 3 caliber <.32 revolver> Integral Magazine 9 sub Ammo 8 name desc caliber <.32 revolver> SDL_PORTRAIT end end % **************************** % *** BALLISTIC WEAPONS *** % **************************** Gun 10 name desig desc sdl_portrait <> category factions caliber <20cm bolt> Mass -5 Range 8 Acc 1 Speed 3 magazine 20 sub Ammo 10 name caliber <20cm bolt> SDL_PORTRAIT <> end gearhead-2-0.701/design/PC_L5Pat.txt000066400000000000000000000715461321074026100167520ustar00rootroot00000000000000%% L5 Pattern Equipment File %% %% Contains equipment normally found in the L5 region, including gear %% used by the L5 Alliance, Comet Metalworks, and the other factions %% operating in the region. % ************************ % *** MELEE WEAPONS *** % ************************ Melee 12 name Speed 3 Mass -7 desc category factions SDL_PORTRAIT Legality 5 Melee 20 Name Speed 6 UsesSpeed Acc -2 Type Mass -18 desc category factions SDL_PORTRAIT legality 15 Melee 9 Name Acc -2 mass -7 Speed 5 UsesSpeed Type desc category factions SDL_PORTRAIT legality 5 Melee 10 Name Type Acc 1 legality 5 Speed 6 Mass -9 desc sdl_portrait category factions Melee 8 Name Acc -2 mass -6 Speed 5 UsesSpeed Type desc category factions SDL_PORTRAIT legality 10 Melee 14 Name Type UsesReflexes Acc 1 Speed 8 Mass -10 desc sdl_portrait category factions Legality 5 Melee 10 Name Type Acc 1 Speed 5 Mass -8 desc sdl_portrait category factions Melee 16 Name Type UsesReflexes Acc 1 Speed 5 Mass -8 desc sdl_portrait category factions % **************************** % *** BALLISTIC WEAPONS *** % **************************** Gun 6 Name desig desc sdl_portrait caliber <5mm auto> category factions legality 5 Mass -3 Range 6 Speed 4 BV 3 magazine 80 sub Ammo 6 name <5mm Auto Clip> caliber <5mm auto> category factions SDL_PORTRAIT end Gun 7 Name desig desc sdl_portrait caliber <5mm auto> type category factions legality 5 Mass -5 Range 7 Acc 2 Speed 3 BV 4 magazine 100 sub Ammo 7 name <5mm Auto Clip> caliber <5mm auto> SDL_PORTRAIT end Gun 9 Name desig desc sdl_portrait <> caliber <8mm self-propelled> category factions legality 5 Mass -6 Range 7 Acc 4 Speed 5 magazine 50 sub Ammo 9 name caliber <8mm self-propelled> SDL_PORTRAIT type end Gun 9 Name desig desc sdl_portrait <> caliber <8mm self-propelled> category factions legality 0 Mass -8 Range 4 Acc 4 Speed 5 magazine 20 sub Ammo 9 name caliber <8mm self-propelled> SDL_PORTRAIT type end Gun 9 Name desig desc sdl_portrait <> caliber <8mm self-propelled> category factions legality 15 Mass -4 Range 8 Acc 4 Speed 5 BV 4 type magazine 150 sub Ammo 9 name caliber <8mm self-propelled> SDL_PORTRAIT type end Gun 6 name desig category factions desc caliber SDL_PORTRAIT Acc 1 Range 4 Speed 3 Legality -5 Mass -4 magazine 40 sub Ammo 6 name caliber type mass -1 SDL_PORTRAIT end Gun 4 name desig category Factions desc sdl_portrait caliber <2mm ferrous> Acc 1 Range 5 BV 2 Speed 4 Legality 5 Mass -2 magazine 90 sub Ammo 4 name <2mm Ferrous Clip> caliber <2mm ferrous> mass -1 SDL_PORTRAIT end Gun 4 Name desig Type category factions desc sdl_portrait caliber <2mm ferrous> UsesPerception Mass -8 Acc 1 Range 8 BV 8 Speed 3 legality 5 magazine 450 sub Ammo 4 name <2mm Ferrous Clip> caliber <2mm ferrous> mass -4 SDL_PORTRAIT end Gun 4 Name desig Type category factions desc sdl_portrait caliber <2mm ferrous> UsesPerception Mass -8 Acc 1 Range 9 BV 8 Speed 4 legality 10 magazine 450 sub Ammo 4 name <2mm Ferrous Clip> caliber <2mm ferrous> mass -4 SDL_PORTRAIT end Gun 5 Name desig desc category factions caliber <6mm pistol> sdl_portrait Mass -2 Range 3 Acc 2 Speed 4 legality -5 magazine 12 sub Ammo 5 name <6mm Pistol Clip> caliber <6mm pistol> SDL_PORTRAIT end Gun 3 name desc desig caliber <5mm auto> category factions SDL_PORTRAIT Mass -3 Range 3 Acc 1 Speed 5 BV 3 magazine 40 sub Ammo 3 name <5mm Automatic Clip> caliber <5mm auto> SDL_PORTRAIT end Gun 3 name desc desig caliber <5mm auto> category factions SDL_PORTRAIT Mass -3 Range 5 Acc 1 Speed 5 BV 5 Legality 0 magazine 90 sub Ammo 3 name <5mm Automatic Clip> caliber <5mm auto> SDL_PORTRAIT end Gun 15 name desc desig caliber <22mm cone> category factions sdl_sprite Range 8 Speed 3 Legality 5 Magazine 20 sub Ammo 15 name caliber <22mm cone> type end Gun 24 name desc desig caliber <60mm rocket> category factions sdl_sprite Range 9 acc -1 Speed 2 Legality 10 mass -14 Magazine 12 sub Ammo 24 name caliber <60mm rocket> type end % ******************** % *** BEAM GUNS *** % ******************** BeamGun 3 name Desig desc sdl_portrait category factions Mass -2 Range 3 Speed 3 legality -5 inv PowerSource 1 name mass -1 end BeamGun 5 Name desig desc sdl_portrait category factions Mass -4 Range 3 Acc 1 Speed 5 legality 0 inv PowerSource 2 name end BeamGun 7 Name desig desc sdl_portrait category factions type Mass -6 Range 3 Acc 1 Speed 5 legality 5 inv PowerSource 2 name end BeamGun 10 Name desig desc sdl_portrait category factions Range 5 Mass -7 Acc 2 Speed 3 Legality 5 inv PowerSource 4 name mass -2 end BeamGun 9 Name desig desc sdl_portrait category factions type Range 5 Mass -6 Acc 1 Speed 3 Legality 10 inv PowerSource 4 name mass -2 end BeamGun 4 Name Desig desc category factions type sdl_portrait Mass -3 Range 3 Acc 1 Speed 4 legality -15 inv PowerSource 1 name end BeamGun 9 Name Desig desc category factions type sdl_portrait Mass -5 Acc 1 Speed 3 Range 6 legality -10 inv PowerSource 3 name mass -2 end BeamGun 16 Name desig desc sdl_portrait category factions Range 7 Mass -8 Acc 1 Speed 4 Legality 10 inv PowerSource 4 name mass -2 end % *************************** % *** MISSILE LAUNCHERS *** % *************************** % ************************ % *** WEAPON ADD-ONS *** % ************************ MeleeAddOn 0 Name desc Category Factions Type Acc 1 Mass 1 sub Melee 8 mass -6 name Acc 1 end MeleeAddOn 2 Name desc Category Factions sub BeamGun 6 Name desc <> Integral Range 4 Acc -1 Type mass -5 end % ****************** % *** GRENADES *** % ****************** % *************** % *** ARMOR *** % *************** HeadArmor 2 name desc Category Factions SDL_PORTRAIT Mass -2 Legality -25 Sealed Hardened HeadArmor 4 name desc Category Factions SDL_PORTRAIT Mass -6 Legality -15 Sealed Hardened HeadArmor 5 name desc Category Factions SDL_PORTRAIT Mass -8 Legality -5 Sealed Hardened memo Set name desc Category Factions Legality -25 inv HeadArmor 2 name desc Category Factions SDL_PORTRAIT Mass -2 Legality -25 Sealed Hardened BodyArmor 2 name desc Category Factions Mass -3 Legality -25 Sealed Hardened sub SpaceFlight 3 end ArmArmor 2 name desc Category Factions Mass -3 Legality -25 Sealed Hardened LegArmor 2 name desc Category Factions Mass -3 Legality -25 Sealed Hardened ArmArmor 2 name desc Category Factions Mass -3 Legality -25 Sealed Hardened LegArmor 2 name desc Category Factions Mass -3 Legality -25 Sealed Hardened end Set name desc Category Factions legality -25 inv BodyArmor 1 Name desc SDL_PORTRAIT Category legality -25 ArmArmor 1 Name desc Category Mass -1 legality -25 ArmArmor 1 Name desc Category Mass -1 legality -25 LegArmor 1 Name desc Category Mass -1 legality -25 LegArmor 1 Name desc Category Mass -1 legality -25 end HeadArmor 1 name desc Category Factions SDL_PORTRAIT Mass -1 Hardened Set name desc Category Factions Legality -25 inv HeadArmor 2 name desc Category SDL_PORTRAIT Mass -2 Legality -25 Sealed Hardened Phone Charm 5 BodyArmor 2 name desc Category Mass -3 Legality -25 Sealed Hardened Charm 5 sub SpaceFlight 3 end ArmArmor 2 name desc Category Mass -3 Legality -25 Sealed Hardened Charm 5 LegArmor 2 name desc Category Mass -3 Legality -25 Sealed Hardened Charm 5 ArmArmor 2 name desc Category Mass -3 Legality -25 Sealed Hardened Charm 5 LegArmor 2 name desc Category Mass -3 Legality -25 Sealed Hardened Charm 5 end Set name desc Category Factions inv HeadArmor 4 name desc SDL_PORTRAIT <> Mass -6 Sealed Hardened Perception 5 BodyArmor 5 name desc Mass -7 Sealed Hardened Speed 2 Perception 5 sub Flight 3 end ArmArmor 4 name desc Mass -6 Speed 2 Hardened Sealed ArmArmor 4 name desc Mass -6 Speed 2 Hardened Sealed LegArmor 4 name desc Mass -6 Sealed Hardened Speed 2 sub Flight 2 end LegArmor 4 name desc Mass -6 Sealed Hardened Speed 2 sub Flight 2 end end Set name desc Category Factions inv HeadArmor 4 name desc SDL_PORTRAIT Mass -6 Legality 10 Sealed Hardened BodyArmor 4 name desc Mass -6 Legality 10 Sealed sub SpaceFlight 3 end ArmArmor 4 name desc Mass -6 Legality 10 Sealed LegArmor 4 name desc Mass -6 Legality 10 Sealed sub SpaceFlight 3 end ArmArmor 4 name desc Mass -6 Legality 10 Sealed LegArmor 4 name desc Mass -6 Legality 10 Sealed sub SpaceFlight 3 end end Set name desc Category Factions Legality 10 inv HeadArmor 6 name desc SDL_PORTRAIT <> Mass -8 Legality 10 Sealed Hardened Perception 5 Memo Phone Email News BodyArmor 6 name desc Mass -7 Legality 10 Sealed Hardened Body 10 Perception 5 sub HoverJet 3 end ArmArmor 6 name desc Mass -7 Legality 10 Sealed Hardened Body 5 Reflexes 5 ArmArmor 6 name desc Mass -7 Legality 10 Sealed Hardened Body 5 Reflexes 5 LegArmor 6 name desc Mass -7 Legality 10 Sealed Hardened Body 5 Speed 5 sub HoverJet 3 end LegArmor 6 name desc Mass -7 Legality 10 Sealed Hardened Body 5 Speed 5 sub HoverJet 3 end end Set name desc Category Factions Legality 10 inv HeadArmor 5 name desc SDL_PORTRAIT Mass -6 Legality 10 Sealed Hardened BodyArmor 5 name desc Mass -7 Legality 10 Sealed Hardened sub SpaceFlight 3 end ArmArmor 5 name desc Mass -7 Legality 10 Sealed Hardened LegArmor 5 name desc Mass -7 Legality 10 Sealed Hardened sub SpaceFlight 3 end ArmArmor 5 name desc Mass -7 Legality 10 Sealed Hardened LegArmor 5 name desc Mass -7 Legality 10 Sealed Hardened sub SpaceFlight 3 end end Set name desc Category Factions Legality -25 inv BodyArmor 3 name desc Category SDL_PORTRAIT Sealed sub SpaceFlight 3 end ArmArmor 3 name desc Category SDL_PORTRAIT Mass -2 Sealed ArmArmor 3 name desc Category SDL_PORTRAIT Mass -2 Sealed LegArmor 3 name desc Category SDL_PORTRAIT Mass -2 Sealed LegArmor 3 name desc Category SDL_PORTRAIT Mass -2 Sealed HeadArmor 3 name desc Category SDL_PORTRAIT Mass -3 Sealed end Set name desc Category Factions Legality -15 inv BodyArmor 3 name desc Category SDL_PORTRAIT Mass -4 Legality -15 Sealed sub SpaceFlight 3 end ArmArmor 3 name desc Category SDL_PORTRAIT Mass -4 Legality -15 Sealed ArmArmor 3 name desc Category SDL_PORTRAIT Mass -4 Legality -15 Sealed LegArmor 3 name desc Category SDL_PORTRAIT Mass -4 Legality -15 Sealed LegArmor 3 name desc Category SDL_PORTRAIT Mass -4 Legality -15 Sealed HeadArmor 2 name desc Category SDL_PORTRAIT Mass -2 Legality -25 Sealed Hardened end Set name desc Category Factions Legality -5 inv BodyArmor 4 name desc Category Mass -5 Sealed ArmArmor 4 name desc Category Mass -5 Sealed ArmArmor 4 name desc Category Mass -5 Sealed LegArmor 4 name desc Category Mass -5 Sealed sub SpaceFlight 2 end LegArmor 4 name desc Category Mass -5 Sealed sub SpaceFlight 2 end HeadArmor 4 name desc Category Mass -6 Sealed Phone end Set name desc Category Factions Legality -5 inv BodyArmor 4 name desc Category SDL_PORTRAIT Factions Mass -4 Legality -15 Sealed sub SpaceFlight 3 end ArmArmor 4 name desc Category SDL_PORTRAIT Factions Mass -4 Legality -15 Sealed LegArmor 4 name desc Category SDL_PORTRAIT Factions Mass -4 Legality -15 Sealed ArmArmor 4 name desc Category SDL_PORTRAIT Factions Mass -4 Legality -15 Sealed LegArmor 4 name desc Category SDL_PORTRAIT Factions Mass -4 Legality -15 Sealed HeadArmor 4 name desc Category Factions SDL_PORTRAIT Mass -6 Legality -15 Sealed Hardened end Set name desc Category Factions legality 0 inv BodyArmor 6 Name desc Category Factions Mass -10 legality -5 Hardened HeadArmor 4 Name desc Category Factions Mass -6 legality -5 Hardened ArmArmor 4 Name desc Category Factions Mass -7 legality -5 Hardened LegArmor 4 Name desc Category Factions Mass -7 legality -5 Hardened ArmArmor 4 Name desc Category Factions Mass -7 legality -5 Hardened LegArmor 4 Name desc Category Factions Mass -7 legality -5 Hardened end ArmArmor 7 Name desc Category Factions Mass -10 legality 0 Sealed sub Melee 18 Name desc Acc 1 Speed 4 Mass -12 end % ***************** % *** SHIELDS *** % ***************** Shield 6 Name desc Category Factions DefBonus 1 mass -5 legality -5 Hardened Shield 4 Name desc Category Factions DefBonus -2 mass -1 legality -5 Hardened % ********************* % *** ELECTRONICS *** % ********************* % ***************************** % *** MUSICAL INSTRUMENTS *** % ***************************** % ********************* % *** REPAIR FUEL *** % ********************* % ********************* % *** CONSUMABLES *** % ********************* Food 45 Mass -4 Name desc Category Factions SDL_PORTRAIT legality -50 Food 45 Mass -4 Name desc Category Factions SDL_PORTRAIT legality -50 Food 45 Mass -4 Name desc Category Factions SDL_PORTRAIT legality -50 % ******************* % *** CYBERWARE *** % ******************* gearhead-2-0.701/design/PC_Mecha Equipment.txt000066400000000000000000000321521321074026100207600ustar00rootroot00000000000000% ************************ % *** MELEE WEAPONS *** % ************************ Melee 13 Scale 2 name type Category factions Acc -1 mass -7 Legality -10 Melee 7 Name Category factions Scale 2 mass -2 Legality -10 Melee 8 Name Category factions Scale 2 Acc -1 Legality -10 Melee 14 Name Category factions Scale 2 Acc -1 mass -6 Legality -10 melee 12 name Scale 2 UsesSpeed Category factions acc 1 type Legality -10 Melee 20 Name Scale 2 UsesReflexes Category factions Speed 2 Acc 1 Type Mass -12 Legality -10 Melee 5 name Category factions type mass -3 speed 3 scale 2 Legality -10 % ******************************** % *** ENERGY MELEE WEAPONS *** % ******************************** EMelee 6 name Category factions Scale 2 Acc 1 Legality -10 EMelee 10 name Category factions Scale 2 Acc 1 Legality -10 % **************************** % *** BALLISTIC WEAPONS *** % **************************** STC MBAZ-17 desig <> Category factions Legality -10 STC MRIF-5 desig <> Category factions Legality -10 BeamGun 7 Name Category factions Scale 2 Range 4 Legality -20 STC SML-5 Category factions Legality -10 STC MAC-2 Category factions Legality -10 STC VC-5 Category factions Legality -10 STC MAC-4 Category factions Legality -10 STC RG-8 Category factions Legality -10 STC RG-16 Category factions Legality -10 STC GR-12 Category factions Legality -10 STC GR-24 Category factions Legality -10 STC LAS-3 Category factions Legality -10 STC LAS-5 Category factions Legality -10 STC LAS-5 name type Category factions mass -3 Legality -20 STC LAS-10 Category factions Legality -10 STC PHS-8 Category factions Legality -10 STC PHS-25 Category factions Legality -10 STC PAR-2 Category factions Legality -10 STC PAR-6 Category factions Legality -10 STC PAR-13 Category factions Legality -10 % *************************** % *** MISSILE LAUNCHERS *** % *************************** MLauncher 4 Scale 2 Category factions Legality -10 sub STC ICM-5 magazine 8 end MLauncher 20 scale 2 Category factions Legality -10 sub STC THM-12 magazine 16 end MLauncher 5 Scale 2 Category factions Legality -10 sub STC AAM-8 Magazine 6 end MLauncher 2 Scale 2 Category factions Legality -10 sub STC HWM-4 magazine 4 end MLauncher 8 Scale 2 Category factions Legality -10 sub STC GM-10 magazine 8 end MLauncher 10 Scale 2 Category factions Legality -10 sub STC HR-10 magazine 10 end MLauncher 9 scale 2 Category factions Legality -10 sub STC HFR-3 magazine 30 end MLauncher 2 scale 2 Category factions Legality -10 sub STC SCRM-1 magazine 20 end % ***************** % *** SHIELDS *** % ***************** Shield 2 Name DefBonus -2 Scale 2 Category factions Legality -20 Shield 3 Name DefBonus -1 Scale 2 Category factions Legality -20 Shield 4 Name Scale 2 Category factions Legality -20 Shield 5 Name DefBonus 1 Scale 2 Category factions Legality -20 EShield 3 Name DefBonus -1 Scale 2 Category factions Legality -20 EShield 5 Name Scale 2 Category factions Legality -20 EShield 9 Name DefBonus 1 Scale 2 Category factions Legality -20 % ***************** % *** SENSORS *** % ***************** Sensor 1 Category factions Scale 2 Legality -50 Sensor 2 Category factions Scale 2 Legality -50 Sensor 3 Category factions Scale 2 Legality -50 Sensor 4 Category factions Scale 2 Legality -50 Sensor 5 Category factions Scale 2 Legality -50 Sensor 6 Category factions Scale 2 Legality -50 Sensor 7 Category factions Scale 2 Legality -50 Sensor 8 Category factions Scale 2 Legality -50 Sensor 9 Category factions Scale 2 Legality -50 Sensor 10 Category factions Scale 2 Legality -50 ECM 1 Category factions Scale 2 Legality -50 ECM 2 Category factions Scale 2 Legality -50 ECM 3 Category factions Scale 2 Legality -50 ECM 4 Category factions Scale 2 Legality -50 ECM 5 Category factions Scale 2 Legality -50 ECM 6 Category factions Scale 2 Legality -50 % ************************** % *** MOVEMENT SYSTEMS *** % ************************** Wheels 1 desig Scale 2 Category factions Legality -50 Wheels 2 desig Scale 2 Category factions Legality -50 Wheels 3 desig Scale 2 Category factions Legality -50 Wheels 4 desig Scale 2 Category factions Legality -50 Wheels 5 desig Scale 2 Category factions Legality -50 Wheels 7 desig Scale 2 Category factions Legality -50 Wheels 9 desig Scale 2 Category factions Legality -50 Tracks 1 desig Scale 2 Category factions Legality -50 Tracks 2 desig Scale 2 Category factions Legality -50 Tracks 3 desig Scale 2 Category factions Legality -50 Tracks 4 desig Scale 2 Category factions Legality -50 Tracks 5 desig Scale 2 Category factions Legality -50 Tracks 7 desig Scale 2 Category factions Legality -50 Tracks 9 desig Scale 2 Category factions Legality -50 HoverJet 1 desig Scale 2 Category factions Legality -50 HoverJet 2 desig Scale 2 Category factions Legality -50 HoverJet 3 desig Scale 2 Category factions Legality -50 HoverJet 4 desig Scale 2 Category factions Legality -50 HoverJet 5 desig Scale 2 Category factions Legality -50 HoverJet 7 desig Scale 2 Category factions Legality -50 HoverJet 9 desig Scale 2 Category factions Legality -50 Flight 1 desig Scale 2 Category factions Legality -50 Flight 2 desig Scale 2 Category factions Legality -50 Flight 3 desig Scale 2 Category factions Legality -50 Flight 4 desig Scale 2 Category factions Legality -50 Flight 5 desig Scale 2 Category factions Legality -50 Flight 7 desig Scale 2 Category factions Legality -50 Flight 9 desig Scale 2 Category factions Legality -50 ArcJet 1 desig Scale 2 Category factions Legality -50 ArcJet 2 desig Scale 2 Category factions Legality -50 ArcJet 3 desig Scale 2 Category factions Legality -50 ArcJet 4 desig Scale 2 Category factions Legality -50 ArcJet 5 desig Scale 2 Category factions Legality -50 ArcJet 7 desig Scale 2 Category factions Legality -50 ArcJet 9 desig Scale 2 Category factions Legality -50 Overcharger 1 desig Scale 2 Category factions Legality -50 Overcharger 2 desig Scale 2 Category factions Legality -50 Overcharger 3 desig Scale 2 Category factions Legality -50 Overcharger 4 desig Scale 2 Category factions Legality -50 Overcharger 5 desig Scale 2 Category factions Legality -50 Overcharger 7 desig Scale 2 Category factions Legality -50 Overcharger 9 desig Scale 2 Category factions Legality -50 SpaceFlight 1 desig Scale 2 Category factions Legality -50 SpaceFlight 2 desig Scale 2 Category factions Legality -50 SpaceFlight 3 desig Scale 2 Category factions Legality -50 SpaceFlight 4 desig Scale 2 Category factions Legality -50 SpaceFlight 5 desig Scale 2 Category factions Legality -50 SpaceFlight 7 desig Scale 2 Category factions Legality -50 SpaceFlight 9 desig Scale 2 Category factions Legality -50 HeavyActuator 1 desig Scale 2 Category factions Legality -50 HeavyActuator 2 desig Scale 2 Category factions Legality -50 HeavyActuator 3 desig Scale 2 Category factions Legality -50 HeavyActuator 4 desig Scale 2 Category factions Legality -50 HeavyActuator 5 desig Scale 2 Category factions Legality -50 HeavyActuator 2 desig mass -1 Scale 2 Category factions Legality -50 HeavyActuator 3 desig mass -2 Scale 2 Category factions Legality -50 HeavyActuator 4 desig mass -2 Scale 2 Category factions Legality -50 HeavyActuator 5 desig mass -3 Scale 2 Category factions Legality -50 % ******************* % *** COMPUTERS *** % ******************* Computer 1 desig scale 2 Category factions Legality -50 Computer 2 desig scale 2 Category factions Legality -50 Computer 3 desig scale 2 Category factions Legality -50 Computer 4 desig scale 2 Category factions Legality -50 gearhead-2-0.701/design/PC_NPCOnly.txt000066400000000000000000000132451321074026100172770ustar00rootroot00000000000000%% %% Stuff that only NPCs can use. This includes sets of armor that are %% usually sold separately, makeshift weapons, and even the occasional %% powerful item not found in stores. %% %% Give everything here the NPCOnly category. %% % ************************ % *** MELEE WEAPONS *** % ************************ Melee 3 Name desc category factions SDL_PORTRAIT legality -15 Mass 2 Acc -2 Speed 3 % **************************** % *** BALLISTIC WEAPONS *** % **************************** % ****************** % *** GRENADES *** % ****************** % *************** % *** ARMOR *** % *************** HeadArmor 3 name sdl_portrait desc Set name Category Factions legality -10 inv HeadArmor 3 Name desc Category Factions SDL_PORTRAIT Mass -5 Memo Phone legality -5 BodyArmor 2 Name desc Category Factions Mass -3 legality -15 ArmArmor 2 Name desc sdl_Portrait Category Factions Mass -3 legality -20 LegArmor 2 Name desc Category Factions sdl_Portrait Mass -3 legality -20 ArmArmor 2 Name desc Category sdl_Portrait Factions Mass -3 legality -20 LegArmor 2 Name desc Category Factions sdl_Portrait Mass -3 legality -20 end Set name Category Factions legality -10 inv HeadArmor 3 Name desc Category Factions SDL_PORTRAIT Mass -5 Memo legality -10 BodyArmor 3 Name desc Category Factions legality -10 ArmArmor 3 Name desc Category Factions Mass -3 legality -5 LegArmor 3 Name desc Category Factions Mass -3 legality -5 ArmArmor 3 Name desc Category Factions Mass -3 legality -5 LegArmor 3 Name desc Category Factions Mass -3 legality -5 end Set name Category Factions legality 5 inv BodyArmor 2 Name desc Category Factions Mass -1 legality -5 ArmArmor 2 Name desc Category Factions Mass -1 legality 5 sub Melee 5 Mass -4 Speed 1 Name desc end LegArmor 2 Name desc Category Factions Mass -1 legality 5 sub Melee 5 Mass -4 Speed 1 Name desc end ArmArmor 2 Name desc Category Factions Mass -1 legality 5 sub Melee 5 Mass -4 Speed 1 Name desc end LegArmor 2 Name desc Category Factions Mass -1 legality 5 sub Melee 5 Mass -4 Speed 1 Name desc end end Set name Category Factions legality -10 inv BodyArmor 1 Name desc Category Factions Mass 3 legality -25 ArmArmor 1 Name desc Category sdl_Portrait Factions Mass -1 legality -25 LegArmor 1 Name desc Category Factions sdl_Portrait Mass -1 legality -25 ArmArmor 1 Name desc Category sdl_Portrait Factions Mass -1 legality -25 LegArmor 1 Name desc Category Factions sdl_Portrait Mass -1 legality -25 end gearhead-2-0.701/design/PC_Treasure.txt000066400000000000000000000241301321074026100176020ustar00rootroot00000000000000% ****************** % *** TREASURE *** % ****************** % % A collection of items with little or no practical use. Generally, these items % will not be sold in stores but must instead be discovered. % % TREASURE, JEWELRY, ARTWORK, ANTIQUE, GEMSTONE, MINERAL % ODDITIES % Treasure Fudge 250 name desc Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 500 name Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 720 name Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 1350 name Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 2000 name Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 3050 name Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 5000 name Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 7000 name desc Category Factions SDL_PORTRAIT Legality -50 Treasure Fudge 9500 name Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 6000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 12000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 48000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 32000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 60000 Category Factions SDL_PORTRAIT Legality -50 Treasure name mass 2 Fudge 96000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 15000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 20000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 25000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 35000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 45000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 50000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 55000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 65000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 75000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 85000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 115000 Category Factions SDL_PORTRAIT Legality -50 mass 1 Treasure name Fudge 205000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 350000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 550000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 850000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 105000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 1050000 Category Factions SDL_PORTRAIT <> Legality -50 Mass 3 Treasure name Fudge 1250000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 1750000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 3250000 Category Factions SDL_PORTRAIT Legality -50 Treasure name Fudge 6250000 Category Factions SDL_PORTRAIT <> Legality -50 Treasure name Fudge 4750000 Category Factions SDL_PORTRAIT <> Legality -50 mass 1 Treasure name Fudge 9750000 Category Factions SDL_PORTRAIT Legality -50 mass 1 Treasure name Fudge 18750000 Category Factions SDL_PORTRAIT <> Legality -50 mass 1 Treasure Fudge 25000000 name desc mass 1 Category Factions Legality -25 Treasure Fudge 500 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 800 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 1200 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 2200 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 4200 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 5000 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 9000 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 19000 name desc Category Factions SDL_PORTRAIT Legality -25 Treasure Fudge 70000 name desc Category Factions SDL_PORTRAIT special Treasure Fudge 300 name desc Category Factions SDL_PORTRAIT <> Legality -25 Treasure Fudge 4000 name desc Category Factions SDL_PORTRAIT <> Legality -25 Treasure Fudge 16000 name desc Category Factions SDL_PORTRAIT <> Legality -25 Treasure Fudge 36000 name desc Category Factions SDL_PORTRAIT <> Legality -25 Treasure Fudge 120000 name desc Category Factions special SDL_PORTRAIT <> Legality 0 Treasure Fudge 1500 name desc Category Factions SDL_PORTRAIT <> Legality -50 HeadArmor 2 name Fudge 1300000 desc Charm 10 Category Factions SDL_PORTRAIT Legality -50 HeadArmor 6 name Fudge 2200500 Hardened desc Charm 10 Ego 10 Category Factions SDL_PORTRAIT Legality -50 HeadArmor 2 name Fudge 4800000 desc Charm 10 Category Factions SDL_PORTRAIT Legality -50 %% %% ************************* %% *** UNOBTAINABLES *** %% ************************* %% %% The following items have no category and so may only be obtained %% through script events. %% PowerSource 6 name sdl_portrait mass -8 Biotech gearhead-2-0.701/design/Phoenix.txt000066400000000000000000000045371321074026100170510ustar00rootroot00000000000000Battroid 4 Name Desig Desc sdl_sprite type factions ROLE_MAQUI ROLE_SILKN ROLE_L5LAW ROLE_FCOMS ROLE_RISHI ROLE_REDMA ROLE_BOHEM sub Head Armor 4 Mass -3 sub CPit Armor 2 Mass -1 Sensor 6 end inv HeadArmor 4 Mass -3 sub STC SML-5 mass -1 end end Torso Armor 4 Mass -4 sub Gyro Armor 1 Mass -1 Engine Armor 1 Mass -1 Mount Name inv MLauncher 6 sub STC GM-10 Magazine 6 end end HeavyActuator 4 mass -2 end inv BodyArmor 5 Mass -5 end Storage Armor 4 Mass -2 Name sub Flight 9 Mount Name inv Gun 12 Range 8 Name Mass -5 caliber <130mm self-propelled> Magazine 20 sub Ammo 12 caliber <130mm self-propelled> Name Type end end end inv StorageArmor 4 Mass -3 end Arm Name Armor 4 Mass -4 sub Hand Name inv Gun 5 name caliber <40mm shell> Range 5 BV 2 Speed 3 Acc 1 mass -3 Magazine 60 sub Ammo 5 caliber <40mm shell> mass -3 end end Mount Name inv STC LAS-3 name type end HeavyActuator 2 mass -1 end inv ArmArmor 4 Mass -3 end Arm Name Armor 4 Mass -4 sub Hand Name Mount Name HeavyActuator 2 mass -1 end inv ArmArmor 4 Mass -3 end Leg Name Size 5 Armor 4 Mass -4 sub Flight 5 end inv LegArmor 5 Mass -5 end Leg Name Size 5 Armor 4 Mass -4 sub Flight 5 end inv LegArmor 5 Mass -5 end end gearhead-2-0.701/design/Picaro.txt000066400000000000000000000023331321074026100166440ustar00rootroot00000000000000Battroid 3 Name Desig type factions desc sub Head Armor 3 mass -3 sub Sensor 6 STC LAS-3 name type end torso Armor 4 mass -4 sub CPit ArcJet 6 ECM 4 end Arm Name Size 2 Armor 3 mass -2 sub Hand inv Melee 7 name desc type Speed 3 mass -5 end Mount Name inv STC SML-5 end end Arm Name Size 2 Armor 3 mass -2 sub Hand Mount Name inv BeamGun 5 name Range 6 Acc 1 end end Leg Name Size 4 Armor 3 mass -3 sub Mount name inv MLauncher 5 sub STC GM-10 Magazine 5 end end ArcJet 4 end Leg Name Size 4 Armor 3 mass -3 sub Mount name inv MLauncher 5 sub STC GM-10 Magazine 5 end end ArcJet 4 end end gearhead-2-0.701/design/Pixie.txt000066400000000000000000000057101321074026100165070ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ReflexSystem ROLE_MUGLE sub Head Size 5 Armor 4 Mass -5 Hardened sub Sensor 7 mass -2 ECM 6 mass -1 STC LAS-5 name type mass -4 Integral end inv HeadArmor 5 mass -8 sub ArcJet 4 Integral end end Torso Armor 4 Mass -5 Hardened sub CPit Armor 2 mass -2 Engine 4 Armor 2 mass -2 HighOutput Gyro Armor 2 mass -2 Computer 5 mass -5 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 1 name desc S_Information SInfo_MechaDex end Beamgun 8 name desc type Acc 1 Range 8 Recharge 1 mass -4 Integral Mount name inv Gun 8 Scale 2 name desig caliber <30mm ferrous> Range 6 BV 4 Magazine 100 mass -6 sub Ammo 8 type caliber <30mm ferrous> mass -6 end end Mount name inv MLauncher 18 mass -2 sub STC SPKM-3 Magazine 60 mass -2 end end end inv BodyArmor 6 mass -9 sub ArcJet 4 Integral end end Arm name Armor 4 Mass -4 Hardened sub Hand name inv Beamgun 6 name desc type Acc 1 Range 5 Recharge 3 mass -4 sub EMelee 8 name type Integral UsesSpeed end end STC PAR-2 Overcharger 4 Integral end inv ArmArmor 5 mass -8 sub ArcJet 3 Integral end end Arm name Armor 4 Mass -4 Hardened sub Hand name STC PAR-2 Overcharger 4 Integral end inv ArmArmor 5 mass -8 sub ArcJet 4 Integral end EShield 5 DefBonus 3 end Leg name Armor 4 Mass -4 Hardened sub ArcJet 5 end inv LegArmor 5 mass -8 sub ArcJet 4 Integral end end Leg name Armor 4 Mass -4 Hardened sub ArcJet 5 end inv LegArmor 5 mass -8 sub ArcJet 4 Integral end end Wing name size 5 Armor 4 mass -6 Hardened sub ArcJet 6 Integral end Wing name size 5 Armor 4 mass -6 Hardened sub ArcJet 6 Integral end end gearhead-2-0.701/design/Puma.txt000066400000000000000000000017061321074026100163340ustar00rootroot00000000000000Battroid 4 Name Desig type factions SDL_PORTRAIT desc sub Head Size 2 Armor 3 sub Sensor 6 mass -1 end torso Armor 4 sub CPit MLauncher 4 sub STC SWM-2 name Magazine 20 end ECM 2 end inv BodyArmor 3 name end Arm Name Armor 3 sub Hand Mount Name inv STC LAS-10 Recharge 2 end end Arm Name Armor 3 sub Hand Mount Name inv STC LAS-5 end end inv EShield 2 name DefBonus 1 end Leg Name Armor 3 sub Flight 4 end inv LegArmor 2 end Leg Name Armor 3 sub Flight 4 end inv LegArmor 2 end end gearhead-2-0.701/design/Radcliff.txt000066400000000000000000000036621321074026100171470ustar00rootroot00000000000000Battroid 6 Name Desig SDL_Sprite desc factions <> Sub Head Size 5 Armor 4 Mass -1 sub CPit Armor 2 Sensor 6 LongRangeScanner 4 Integral end Torso Armor 6 sub Computer 7 mass -9 sub Software 5 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 end STC LAS-5 mass -2 STC LAS-5 mass -2 ECM 7 end Arm Name Size 5 Armor 6 mass -1 sub STC PHS-8 mount name inv MLauncher 2 sub Rockets 20 Name Type Range 8 Acc 2 Magazine 1 end end HeavyActuator 1 end inv ArmArmor 3 end Arm Name Size 5 Armor 6 mass -1 sub STC PHS-8 mount name inv MLauncher 2 sub Rockets 20 Name Type Range 8 Acc 2 Magazine 1 end end HeavyActuator 1 end inv ArmArmor 3 end Leg Name Armor 6 mass -1 sub Mount Name inv MLauncher 12 sub STC SWM-2 Magazine 60 range 7 end end PowerSource 3 mass -4 HeavyActuator 3 Mass -1 end Leg Name Armor 6 mass -1 sub Mount Name inv MLauncher 12 sub STC SWM-2 Magazine 60 range 7 end end PowerSource 3 mass -4 HeavyActuator 3 Mass -1 end Storage Name Armor 5 Size 5 sub MLauncher 12 sub STC FRM-6 Magazine 20 range 8 mass -3 end end Storage Name Armor 5 Size 5 sub MLauncher 12 sub STC FRM-6 Magazine 20 range 8 mass -3 end end end gearhead-2-0.701/design/Ramstein.txt000066400000000000000000000026321321074026100172130ustar00rootroot00000000000000Battroid 4 Name SDL_Portrait <> Desig desc <> TYPE FACTIONS ROLE_SILKN sub Head size 3 Armor 3 Mass -1 sub Sensor 6 mass -1 STC LAS-5 name type end inv HeadArmor 2 Mass -2 end Torso Armor 4 Mass -2 sub CPit Armor 1 Flight 6 Mount name inv MLauncher 1 sub STC HWM-4 Magazine 2 end end Mount name inv MLauncher 1 sub STC HWM-4 Magazine 2 end end ECM 4 end inv BodyArmor 2 Mass -2 end Arm name Armor 3 Mass -2 sub Hand name inv Melee 8 name type Speed 1 mass -4 sub Gun 4 name Integral caliber <20mm ferrous> Range 6 Speed 3 BV 3 mass -4 Magazine 100 sub Ammo 4 caliber <20mm ferrous> end end end Overcharger 4 end Arm name Armor 3 Mass -2 sub Hand name Overcharger 4 end inv Shield 2 DefBonus 1 end Leg name Armor 4 Mass -2 sub Flight 5 end Leg name Armor 4 Mass -2 sub Flight 5 end end gearhead-2-0.701/design/Ramuh.txt000066400000000000000000000042001321074026100164760ustar00rootroot00000000000000Battroid 5 Name SDL_Sprite <> SDL_Portrait Desig desc TYPE FACTIONS ROLE_MAQUI ROLE_COMET ROLE_PRIVA sub Head size 4 Armor 5 Mass -4 sub Sensor 6 mass -1 ECM 5 STC LAS-3 name type mass -2 end inv HeadArmor 2 name mass -3 fits end Torso Armor 5 Mass -4 sub CPit Armor 1 Flight 6 Mount name inv MLauncher 2 sub STC HWM-4 Magazine 5 end end Mount name inv MLauncher 2 sub STC HWM-4 Magazine 5 end end Sensor 2 Computer 2 mass -3 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end end inv BodyArmor 3 name mass -3 fits sub Flight 2 Integral end end Arm name Armor 5 Mass -3 sub Hand name inv STC PAR-6 end Mount name inv EMelee 8 name end PowerSource 2 mass -2 end inv ArmArmor 2 mass -2 end Arm name Armor 5 Mass -3 sub Hand name Mount name PowerSource 2 mass -2 end inv Shield 3 name mass -2 sub STC MAC-4 Integral end ArmArmor 2 mass -2 end Leg name Armor 5 Mass -3 sub Mount name Flight 5 end inv LegArmor 2 mass -2 end Leg name Armor 5 Mass -3 sub Mount name Flight 5 end inv LegArmor 2 mass -2 end end gearhead-2-0.701/design/Roc.txt000066400000000000000000000044331321074026100161550ustar00rootroot00000000000000Gerwalk 6 Name Desig SDL_Sprite SDL_Portrait desc type factions ROLE_HOELL ROLE_ROCKE ROLE_FCOMS ROLE_RISHI sub head Size 3 Armor 5 mass -5 sub Sensor 6 mass -1 ECM 6 mass -1 end inv HeadArmor 4 mass -4 sub STC LAS-5 Name Type Acc 4 mass -3 end end torso Armor 7 Mass -10 sub Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end CPit Armor 2 mass -1 Gyro Armor 2 mass -1 Engine 6 Armor 2 mass -1 Sensor 4 BeamGun 12 Name Type Integral Acc 1 Speed 1 Range 7 Computer 5 mass -6 sub Software 2 name S_SpeedComp S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 2 name S_TRBoost S_BoostScale 2 end end inv BodyArmor 7 mass -7 sub OverCharger 4 end end Wing Name Armor 5 size 5 Mass -7 sub Mount Name inv STC RG-8 mass -6 end Flight 5 end Wing Name Armor 5 size 5 Mass -7 sub Mount Name inv STC SKBOR-4 mass -2 end Flight 5 end Leg Name size 7 Armor 7 Mass -9 sub Mount Name inv MLauncher 8 sub STC ICM-5 magazine 16 end end Flight 3 PowerSource 4 mass -4 end Leg Name size 7 Armor 7 Mass -9 sub Mount Name inv MLauncher 8 sub STC ICM-5 magazine 16 end end Flight 3 PowerSource 4 mass -4 end end gearhead-2-0.701/design/Savin.txt000066400000000000000000000357341321074026100165220ustar00rootroot00000000000000Battroid 7 Name Desig SDL_Sprite SDL_Portrait Desc TYPE FACTIONS ROLE_SILKN ROLE_COMET Sub Head Armor 5 Size 5 Mass -5 sub Sensor 6 mass -1 STC VC-5 Type Name PowerSource 2 mass -2 end inv HeadArmor 5 Mass -6 end Torso Armor 7 Mass -7 sub Sensor 1 CPit Armor 2 Mass -1 Computer 6 mass -6 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 1 name S_SpeedComp S_BoostScale 2 end Gyro Armor 2 Mass -1 Engine 7 Armor 2 Mass -1 STC LAS-5 mass -3 range 5 STC LAS-5 mass -3 range 5 ArcJet 3 ECM 5 end inv BodyArmor 8 Mass -9 sub MLauncher 4 sub STC HWM-4 Magazine 10 end end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name inv Gun 10 Name Range 7 caliber <60cm Nuclear> mass -6 Magazine 12 sub Ammo 10 Type caliber <60cm Nuclear> end end Mount Name inv EMelee 12 Name UsesReflexes Speed 3 Acc 1 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 6 Mass -7 sub STC MAC-2 Mass -3 range 6 end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name Mount Name ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 6 Mass -7 sub STC MAC-2 Mass -3 range 6 end Shield 5 DefBonus 1 Mass -5 end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 12 sub STC SWM-2 Magazine 60 end end ArcJet 7 end inv LegArmor 6 Mass -7 end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 12 sub STC SWM-2 Magazine 60 end end ArcJet 7 end inv LegArmor 6 Mass -7 end end Battroid 7 Name Desig SDL_Sprite SDL_Portrait Desc TYPE FACTIONS ROLE_SILKN ROLE_COMET Sub Head Armor 5 Size 5 Mass -5 sub Sensor 6 mass -1 STC VC-5 Type Name PowerSource 2 mass -2 end inv HeadArmor 4 Mass -5 end Torso Armor 7 Mass -7 sub Sensor 1 CPit Armor 2 Mass -1 Computer 6 mass -6 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 1 name S_SpeedComp S_BoostScale 2 end Gyro Armor 2 Mass -1 Engine 7 Armor 2 Mass -1 STC LAS-5 mass -3 range 5 STC LAS-5 mass -3 range 5 ArcJet 3 ECM 5 end inv BodyArmor 6 Mass -8 sub STC LAS-5 mass -3 range 5 STC LAS-5 mass -3 range 5 end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name Mount Name inv STC RG-16 mass -9 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 5 Mass -6 end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name Mount Name inv STC RG-16 mass -9 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 5 Mass -6 end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end ArcJet 7 end inv LegArmor 5 Mass -5 sub MLauncher 6 sub STC SWM-2 Magazine 30 end end end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end ArcJet 7 end inv LegArmor 5 Mass -5 sub MLauncher 6 sub STC SWM-2 Magazine 30 end end end end Battroid 7 Name Desig SDL_Sprite SDL_Portrait Desc TYPE FACTIONS ROLE_COMET ROLE_PRIVA Sub Head Armor 5 Size 5 Mass -5 sub Sensor 6 mass -1 STC VC-5 Type Name PowerSource 2 mass -2 end inv HeadArmor 5 Mass -6 end Torso Armor 7 Mass -7 sub Sensor 1 CPit Armor 2 Mass -1 Computer 6 mass -6 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 1 name S_SpeedComp S_BoostScale 2 end Gyro Armor 2 Mass -1 Engine 7 Armor 2 Mass -1 STC LAS-5 mass -3 range 5 STC LAS-5 mass -3 range 5 ArcJet 3 ECM 5 end inv BodyArmor 8 Mass -9 sub ArcJet 4 end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name inv EMelee 20 Name Speed 3 UsesReflexes Acc 2 Type Mass 6 end Mount Name inv STC MAC-4 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 6 Mass -7 sub STC MAC-2 Mass -3 range 6 end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name Mount Name ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 6 Mass -7 sub STC MAC-2 Mass -3 range 6 end Shield 3 DefBonus -1 sub EMelee 5 Name Type end end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 4 sub STC HWM-4 Magazine 10 end end ArcJet 7 end inv LegArmor 6 Mass -7 sub ArcJet 3 end end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 4 sub STC HWM-4 Magazine 10 end end ArcJet 7 end inv LegArmor 6 Mass -7 sub ArcJet 3 end end end Battroid 7 Name Desig SDL_Sprite SDL_Portrait Desc TYPE FACTIONS Sub Head Armor 5 Size 5 Mass -6 sub Sensor 6 mass -1 STC VC-5 Type Name PowerSource 2 mass -2 end inv HeadArmor 4 Mass -5 end Torso Armor 7 Mass -8 sub Sensor 1 CPit Armor 2 Mass -1 Computer 6 mass -6 sub Software 3 name S_TRBoost S_BoostScale 2 Software 3 name S_MVBoost S_BoostScale 2 end Gyro Armor 2 Mass -1 Engine 7 HighPerformance Armor 2 Mass -1 STC MAC-4 mass -3 HeavyActuator 2 mass -1 ArcJet 3 ECM 6 end inv BodyArmor 6 Mass -8 sub ArcJet 3 end end Arm Name Size 6 Armor 5 Mass -7 sub Hand Name inv Melee 20 name desc Acc 2 type Mass -14 end Mount Name inv STC LAS-10 name Range 5 type end HeavyActuator 3 mass -2 PowerSource 2 mass -2 end inv ArmArmor 5 Mass -6 sub MLauncher 3 sub STC ICM-5 magazine 6 end end end Arm Name Size 6 Armor 5 Mass -7 sub Hand Name Mount Name HeavyActuator 3 mass -2 PowerSource 2 mass -2 end inv ArmArmor 5 Mass -6 sub MLauncher 3 sub STC ICM-5 magazine 6 end end EShield 5 DefBonus 2 name end Leg Name Armor 7 Mass -8 sub Mount Name inv MLauncher 10 sub STC SWM-5 type Magazine 20 end end ArcJet 7 end inv LegArmor 5 Mass -5 sub ArcJet 3 end end Leg Name Armor 7 Mass -8 sub Mount Name inv MLauncher 10 sub STC SWM-5 type Magazine 20 end end ArcJet 7 end inv LegArmor 5 Mass -5 sub ArcJet 3 end end end Battroid 7 Name Desig SDL_Sprite SDL_Portrait Desc TYPE FACTIONS ROLE_COMET ROLE_L5LAW Sub Head Armor 5 Size 5 Mass -5 sub Sensor 6 mass -1 STC VC-5 Type Name PowerSource 2 mass -2 end inv HeadArmor 5 Mass -6 end Torso Armor 7 Mass -7 sub Sensor 1 CPit Armor 2 Mass -1 Computer 6 mass -6 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 1 name S_SpeedComp S_BoostScale 2 end Gyro Armor 2 Mass -1 Engine 7 Armor 2 Mass -1 STC LAS-5 mass -3 range 5 STC LAS-5 mass -3 range 5 ArcJet 3 ECM 5 end inv BodyArmor 8 Mass -9 sub MLauncher 4 sub STC GM-10 Magazine 4 end end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name inv Melee 10 Name Acc 1 speed 3 Type Mass -5 end Mount Name inv STC GR-24 desig mass -14 Recharge 2 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 5 Mass -6 sub STC PAR-2 Integral end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name Mount Name ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 5 Mass -6 sub STC PAR-2 Integral end Shield 4 name DefBonus 2 Mass -4 end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end ArcJet 7 end inv LegArmor 5 Mass -6 sub ArcJet 3 end end Leg Name Armor 7 Mass -7 sub Mount Name inv MLauncher 8 sub STC SWM-2 Magazine 40 end end ArcJet 7 end inv LegArmor 5 Mass -6 sub ArcJet 3 end end end Battroid 7 Name Desig SDL_Sprite SDL_Portrait Desc TYPE FACTIONS ROLE_COMET Sub Head Armor 5 Size 5 Mass -5 sub Sensor 6 mass -1 STC VC-5 Type Name PowerSource 2 mass -2 end inv HeadArmor 5 Mass -6 end Torso Armor 7 Mass -7 sub Sensor 1 CPit Armor 2 Mass -1 Computer 6 mass -6 sub Software 2 name S_TRBoost S_BoostScale 2 Software 2 name S_MVBoost S_BoostScale 2 Software 1 name S_SpeedComp S_BoostScale 2 end Gyro Armor 2 Mass -1 Engine 7 Armor 2 Mass -1 STC LAS-5 mass -3 range 5 STC LAS-5 mass -3 range 5 ArcJet 3 ECM 5 end inv BodyArmor 8 Mass -9 sub ArcJet 4 end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name inv EMelee 12 Name Type UsesReflexes Mass 4 end Mount Name inv Melee 4 Name Mass -3 type Speed 1 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 6 Mass -7 sub STC MAC-2 Mass -3 range 6 end end Arm Name Size 6 Armor 5 Mass -6 sub Hand Name inv EMelee 15 Name UsesReflexes Type Acc 1 end Mount Name inv Melee 4 Name Mass -3 type Speed 1 end ArcJet 3 PowerSource 2 mass -2 end inv ArmArmor 6 Mass -7 sub STC MAC-2 Mass -3 range 6 end end Leg Name Armor 7 Mass -7 sub Mount Name inv Gun 13 name caliber desig range 5 Speed 1 type mass -6 Magazine 15 sub Ammo 13 caliber type end end ArcJet 7 end inv LegArmor 6 Mass -7 sub ArcJet 3 end end Leg Name Armor 7 Mass -7 sub Mount Name inv Gun 13 name caliber desig range 5 Speed 1 type mass -6 Magazine 15 sub Ammo 13 caliber type end end ArcJet 7 end inv LegArmor 6 Mass -7 sub ArcJet 3 end end end gearhead-2-0.701/design/Secutor.txt000066400000000000000000000031441321074026100170540ustar00rootroot00000000000000Battroid 5 Name Desig SDL_Sprite <> SDL_PORTRAIT desc type factions sub Head Armor 4 mass -1 sub Sensor 5 STC LAS-3 name type CPit Armor 2 mass -1 end inv HeadArmor 3 Mass -2 end torso Armor 4 mass -2 sub Sensor 1 Mount Name inv MLauncher 8 sub STC LM-20 magazine 4 end end Engine 5 Armor 2 HighPerformance mass -1 Gyro Armor 2 mass -1 HeavyActuator 2 mass -1 HoverJet 4 end inv BodyArmor 3 Mass -2 end Arm Name Armor 3 mass -1 sub Hand name inv Melee 9 name type end Mount Name inv Gun 6 name caliber <55mm shell> Range 6 BV 2 mass -5 Magazine 60 sub Ammo 6 caliber <55mm shell> end end HeavyActuator 4 mass -2 end inv ArmArmor 4 Mass -2 end Arm Name Armor 3 mass -1 sub Hand name Mount Name HeavyActuator 4 mass -2 end inv Shield 4 DefBonus 1 Mass -1 end Leg Name Armor 4 mass -1 sub HoverJet 6 end inv LegArmor 2 Mass -1 end Leg Name Armor 4 mass -1 sub HoverJet 6 end inv LegArmor 2 Mass -1 end end gearhead-2-0.701/design/Shard.txt000066400000000000000000000020571321074026100164730ustar00rootroot00000000000000AeroFighter 4 Name Desig SDL_Sprite SDL_PORTRAIT type FACTIONS desc ROLE_HOELL sub torso Armor 4 mass -4 sub CPit Armor 1 Sensor 6 Mount Name inv STC MAC-4 end Mount Name inv end BeamGun 5 name type Integral Range 6 end Wing Name Armor 3 size 3 mass -2 sub Mount Name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end Flight 2 end Wing Name Armor 3 size 3 mass -2 sub Mount Name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end Flight 2 end Storage name Armor 4 mass -2 sub Flight 6 Overcharger 4 end end gearhead-2-0.701/design/Skull.txt000066400000000000000000000020111321074026100165120ustar00rootroot00000000000000HoverFighter 4 name desig type desc factions ROLE_CRIHN ROLE_REDMA SDL_PORTRAIT sub Torso Armor 3 sub CPit Sensor 5 Flight 7 end inv BodyArmor 1 fits name end Turret Size 2 Armor 3 sub Mount name inv STC LAS-10 end MLauncher 1 sub STC LR-1 Magazine 10 end end Wing name Size 5 Armor 2 sub Flight 2 Overcharger 3 Mount name inv STC MAC-2 end end Wing name Size 5 Armor 2 sub Flight 2 Overcharger 3 Mount name inv STC MAC-2 end end end gearhead-2-0.701/design/Tauner.txt000066400000000000000000000013371321074026100166700ustar00rootroot00000000000000Groundcar 5 name SDL_Sprite <> SDL_Portrait <> Desig desc TYPE FACTIONS ROLE_MUGLE ROLE_L5LAW sub Torso Armor 5 Mass -2 Sub CPit Sensor 5 ECM 3 Mount name inv MLauncher 4 sub STC SWM-2 magazine 20 end end Wheels 8 Mass -4 end Turret Armor 4 mass -1 Sub BeamGun 4 name Speed 3 BV 1 Range 6 STC LAS-3 name type Mount name End End gearhead-2-0.701/design/Thorshammer.txt000066400000000000000000000025641321074026100177260ustar00rootroot00000000000000Battroid 7 Name Desig SDL_Sprite desc sdl_portrait type factions ROLE_BOHEM sub Torso Armor 7 Mass -4 sub CPit Sensor 5 Computer 4 mass -4 sub Software 2 name S_MVBoost S_BoostScale 2 Software 2 name S_TRBoost S_BoostScale 2 end Mount Name inv MLauncher 24 sub STC SWM-2 Magazine 120 end end BeamGun 10 name Range 6 Recharge 3 end Arm Name Size 5 Armor 7 Mass -1 sub Hand Mount Name inv Gun 16 name caliber range 6 Speed 3 Magazine 25 sub Ammo 16 caliber type end end end Arm Name Size 5 Armor 7 Mass -1 sub Hand Mount Name inv STC SML-5 end end Leg Name Size 6 Armor 7 Mass -1 sub HeavyActuator 4 mass -1 end Leg Name Size 6 Armor 7 Mass -1 sub HeavyActuator 4 mass -1 end end gearhead-2-0.701/design/Trailblazer.txt000066400000000000000000000027421321074026100177060ustar00rootroot00000000000000Battroid 2 Name SDL_Sprite SDL_Portrait Desig desc TYPE FACTIONS ROLE_SILKN ROLE_COMET sub Head size 3 Armor 2 sub Sensor 7 mass -1 end inv HeadArmor 1 mass -1 end Torso Armor 3 mass -1 sub CPit Armor 1 LongRangeScanner 2 mass -1 ECM 3 Computer 1 mass -1 sub Software name S_Information SInfo_MechaDex end end inv BodyArmor 1 mass -1 sub Flight 1 end end Arm Name Size 2 Armor 2 mass -1 sub Mount name inv STC PAR-2 end Hand end inv ArmArmor 1 mass -1 end Arm Name Size 2 Armor 2 mass -1 sub mount name inv MLauncher 1 sub STC HWM-4 Magazine 2 end end Hand inv EMelee 5 Name type end end inv ArmArmor 1 mass -1 end Leg Name Armor 2 mass -1 sub Flight 3 end inv LegArmor 1 mass -1 sub Flight 1 end end Leg Name Armor 2 mass -1 sub Flight 3 end inv LegArmor 1 mass -1 sub Flight 1 end end end gearhead-2-0.701/design/Vadel.txt000066400000000000000000000107021321074026100164610ustar00rootroot00000000000000Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT desc ROLE_MAQUI ROLE_SILKN ROLE_AEGIS ROLE_CRIHN ROLE_L5LAW ROLE_FCOMS ROLE_RISHI ROLE_BOHEM ROLE_ROCKE ROLE_REDMA ROLE_AEGSF sub Head Size 3 Armor 4 mass -4 sub Sensor 5 end torso Armor 4 mass -6 sub CPit Armor 2 Mass -1 Sensor 1 Computer 2 mass -3 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end Overcharger 6 STC VC-5 mass -11 STC VC-5 mass -11 end inv BodyArmor 4 Mass -4 sub HoverJet 2 end end Arm Name Size 4 Armor 4 Mass -5 sub Hand inv BeamGun 7 name type Range 5 Acc 1 end HoverJet 3 end inv ArmArmor 3 Mass -3 sub MLauncher 4 Name Integral sub STC AAM-8 Magazine 5 end end end Arm Name Size 4 Armor 4 Mass -5 sub Hand inv Melee 7 Name mass -3 end HoverJet 3 end inv ArmArmor 3 Mass -3 sub MLauncher 4 Name Integral sub STC AAM-8 Magazine 5 end end Shield 3 DefBonus -1 Mass -1 end Leg Name Armor 4 Mass -5 sub Mount Name inv MLauncher 8 sub STC SWM-5 Magazine 16 end end HoverJet 5 end inv LegArmor 3 Mass -3 sub Overcharger 2 end end Leg Name Armor 4 Mass -5 sub Mount Name inv MLauncher 8 sub STC SWM-5 Magazine 16 end end HoverJet 5 end inv LegArmor 3 Mass -3 sub Overcharger 2 end end end Battroid 5 Name Desig type factions SDL_Sprite SDL_PORTRAIT desc sub Head Size 3 Armor 4 mass -4 sub Sensor 5 ECM 4 end torso Armor 4 mass -6 sub CPit Armor 2 Mass -1 Sensor 1 Engine 5 HighPerformance Computer 2 mass -3 sub Software 1 name S_TRBoost S_BoostScale 2 Software 1 name S_MVBoost S_BoostScale 2 end Overcharger 6 STC VC-5 mass -11 STC VC-5 mass -11 end inv BodyArmor 4 Mass -4 sub HoverJet 2 end end Arm Name Size 4 Armor 4 Mass -5 sub Hand inv STC RG-8 end HoverJet 3 end inv ArmArmor 3 Mass -3 sub MLauncher 4 Name Integral sub STC AAM-8 Magazine 5 end end end Arm Name Size 4 Armor 4 Mass -5 sub Hand inv Melee 8 Name type mass -4 end HoverJet 3 end inv ArmArmor 3 Mass -3 sub MLauncher 4 Name Integral sub STC AAM-8 Magazine 5 end end Shield 3 DefBonus -1 Mass -1 end Leg Name Armor 4 Mass -5 sub Mount Name inv MLauncher 8 sub STC SWM-5 Magazine 16 end end HoverJet 5 end inv LegArmor 3 Mass -3 sub Overcharger 2 end end Leg Name Armor 4 Mass -5 sub Mount Name inv MLauncher 8 sub STC SWM-5 Magazine 16 end end HoverJet 5 end inv LegArmor 3 Mass -3 sub Overcharger 2 end end end gearhead-2-0.701/design/War Cry.txt000066400000000000000000000023351321074026100167000ustar00rootroot00000000000000Battroid 4 Name SDL_Sprite <> SDL_Portrait Desig desc TYPE FACTIONS ROLE_HOELL ROLE_PRIVA ROLE_FCOMS sub Head size 3 Armor 3 Mass -1 sub Sensor 5 ECM 2 end Torso Armor 3 Mass -1 sub CPit Armor 1 HoverJet 4 Mount name inv MLauncher 4 sub STC HR-10 Magazine 4 end end Sensor 2 end inv BodyArmor 2 Mass -2 end Arm name Armor 3 Mass -1 sub Hand name inv STC RG-8 end Mount name inv EMelee 3 name end end Arm name Armor 3 Mass -1 sub Hand name inv end Mount name inv end end inv Shield 2 DefBonus 1 end Leg name Armor 4 Mass -1 sub HoverJet 4 end Leg name Armor 4 Mass -1 sub HoverJet 4 end end gearhead-2-0.701/design/Wolfram.txt000066400000000000000000000060751321074026100170450ustar00rootroot00000000000000Battroid 5 Name Desig factions type SDL_Sprite Desc SDL_PORTRAIT Legality -20 sub Head Size 3 Armor 3 mass 2 sub Sensor 3 end inv HeadArmor 3 Name end Torso Armor 5 sub CPit Mount Name Mount Name inv BeamGun 7 Name Range 4 Acc -1 Speed 1 end HeavyActuator 1 end Arm Name Armor 4 sub Hand inv Melee 14 Name Acc 1 end HeavyActuator 3 end Arm Name Armor 4 sub Hand HeavyActuator 3 end Leg Name Armor 4 sub HeavyActuator 4 end Leg Name Armor 4 sub HeavyActuator 4 end end Battroid 5 Name Desig <> factions type SDL_Sprite Desc SDL_PORTRAIT Legality -20 sub Head Size 3 Armor 3 mass 2 sub Sensor 6 mass -1 Computer 2 mass -1 sub Software name S_Information SInfo_MechaDex Software 2 name S_SpeedComp S_BoostScale 2 end end inv HeadArmor 3 name Hardened mass -3 sub Flight 2 end end Torso Armor 5 sub CPit Mount Name inv MLauncher 8 sub STC GM-10 Magazine 8 Mass -1 end end Mount Name inv MLauncher 8 sub STC SWM-5 Magazine 16 Mass -1 end end HeavyActuator 2 Flight 1 Computer 6 mass -4 sub Software 3 name S_TRBoost S_BoostScale 2 Software 3 name S_MVBoost S_BoostScale 2 end end inv BodyArmor 4 name Hardened mass -3 sub Flight 2 end end Arm Name Armor 4 sub Hand inv EMelee 14 Name Acc 1 Mass 1 type end HeavyActuator 4 Flight 1 end inv ArmArmor 3 name Hardened mass -3 sub Flight 2 end end Arm Name Armor 4 sub Hand inv STC RG-8 Mass -4 end HeavyActuator 4 Flight 1 end inv ArmArmor 3 name Hardened mass -3 sub Flight 2 end end Leg Name Armor 4 sub HeavyActuator 5 Flight 1 end inv LegArmor 3 name Hardened mass -3 sub Flight 2 end end Leg Name Armor 4 sub HeavyActuator 5 Flight 1 end inv LegArmor 3 name Hardened mass -3 sub Flight 2 end end end gearhead-2-0.701/design/Wraith.txt000066400000000000000000000022301321074026100166610ustar00rootroot00000000000000AeroFighter 4 Name Desig SDL_Sprite SDL_Portrait SDL_MESH SDL_SKIN type FACTIONS desc ROLE_MAQUI ROLE_FCOMS ROLE_RISHI ROLE_REDMA ROLE_L5LAW sub torso Armor 4 sub CPit Sensor 5 Mount Name inv STC MAC-4 mass -4 end Mount Name inv MLauncher 5 sub STC HR-10 Magazine 5 end end STC VC-5 Flight 2 end Wing Name Size 3 Armor 4 sub Mount Name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end Flight 3 end Wing Name Size 3 Armor 4 sub Mount Name inv MLauncher 4 sub STC SWM-2 Magazine 20 end end Flight 3 end end gearhead-2-0.701/design/Zerosaiko.txt000066400000000000000000000043101321074026100173720ustar00rootroot00000000000000Battroid 5 Name Desig SDL_Sprite SDL_Portrait desc sdl_portrait factions type ReflexSystem sub Head Size 4 Armor 5 mass -4 sub Sensor 5 Computer 4 mass -4 sub Software 3 name S_TRBoost S_BoostScale 2 Software 1 name desc Category Factions S_Information SInfo_MechaDex end end inv HeadArmor 3 Mass -2 Fits end Torso Armor 5 mass -5 sub CPit Armor 2 mass -1 Gyro Armor 1 MLauncher 3 sub STC LR-1 Magazine 24 end Computer 3 mass -3 sub Software 3 name S_MVBoost S_BoostScale 2 end ArcJet 3 Mount Name Mount Name end inv BodyArmor 5 Mass -5 Fits sub ArcJet 3 end end Arm Name Armor 5 mass -4 sub Hand inv STC RG-8 name end Mount Name HeavyActuator 4 Mass -2 end inv ArmArmor 3 mass -2 sub STC LAS-5 Name Type mass -3 end Shield 2 DefBonus -4 end Arm Name Armor 5 mass -4 sub Hand Mount Name inv EMelee 13 Speed 3 Name UsesReflexes end HeavyActuator 4 Mass -2 end inv ArmArmor 3 mass -2 sub STC LAS-5 Name Type mass -3 end Shield 2 DefBonus -4 end Leg Name Armor 5 mass -4 sub ArcJet 5 HeavyActuator 1 end inv LegArmor 3 Fits Mass -2 sub ArcJet 2 end end Leg Name Armor 5 mass -4 sub ArcJet 5 HeavyActuator 1 end inv LegArmor 3 Fits Mass -2 sub ArcJet 2 end end end gearhead-2-0.701/design/Zwar.txt000066400000000000000000000036221321074026100163540ustar00rootroot00000000000000Battroid 8 Name Desig type factions sdl_sprite SDL_PORTRAIT Desc ROLE_AEGIS sub Head Size 3 Armor 6 Mass -4 sub Sensor 6 STC LAS-5 name Speed 3 type end Torso Armor 8 Mass -9 sub ECM 4 Engine 7 Armor 2 Mass -1 HighOutput CPit Armor 2 mass -1 Computer 7 sub Software 2 name S_MVBoost S_BoostScale 2 Software 5 name S_TRBoost S_BoostScale 2 end PowerSource 1 mass -1 BeamGun 16 Name Type Integral mass -4 Acc 1 Speed 2 BV 3 Range 8 Mount Name inv mlauncher 16 sub STC SWM-5 magazine 32 end end Mount Name inv mlauncher 16 sub STC GM-10 magazine 16 end end Sensor 5 end Arm Name Size 9 Armor 8 Mass -10 sub Hand Mount name inv STC MAC-4 end Melee 13 name type Integral BeamGun 10 name Range 5 type Integral end Leg Name size 8 Armor 9 Mass -9 sub PowerSource 2 mass -3 HoverJet 7 end Leg Name size 8 Armor 9 Mass -9 sub PowerSource 2 mass -3 HoverJet 7 end Leg Name size 8 Armor 9 Mass -9 sub PowerSource 2 mass -3 HoverJet 7 end Leg Name size 8 Armor 9 Mass -9 sub PowerSource 2 mass -3 HoverJet 7 end end gearhead-2-0.701/doc/000077500000000000000000000000001321074026100141615ustar00rootroot00000000000000gearhead-2-0.701/doc/ASLref.txt000066400000000000000000000371031321074026100160420ustar00rootroot000000000000001. TRIGGERS * If adding a new trigger which may be linked to a plot, don't forget to add it to gamedata/standard_triggers.txt 5MIN Generated every five minutes ALARM Called when something sets off alarm ATTACK Called when PC shares same tile as hostile encounter APPLAUSE Using performance skill, favorable result CLEANUP Story is ending, do plot cleanup CLUE_SURVIVAL CLUE_REPAIR CLUE_MEDICINE CLUE_SCIENCE CLUE_CODEBREAKING CLUE_MYSTICISM CLUE_INSIGHT DEPLOY Placed in team gears; called during map deployment ENCGOAL[scene ID] An encounter has reached its target square END Trigger placed when scene ends EWM[scene number] Objection Check: Enter via world map FAINT[cid] Model with Character ID has been destroyed FIRE! Trigger set when a fire starts on map GET[ID Number] Get item which has Narrative ID GREETING First label called when interact started HALFHOUR Generated every half hour of game time HOUR Generated every hour of game time KEY[ID Number] Objection Check; Item used/activated LOCAL[ID Number] Triggers a local event MOVE[cid] Model with Character ID has moved NPCOPENDOOR Request sent when NPC wants to open a door NU[team] Number of masters on team has changed PCATTACK PC has fired a weapon PUMPNEWS Applied to quests and moods once every 6 hours QUARTER Generated every 6 hours (1/4 of a day) RESCUE The PC has resuscitated a disaster victim RESULT[number] Called when interact menu item selected REVEAL Called when hidden metaterrain is discovered START First trigger placed when scene entered SURRENDER[cid] Model with Character ID has surrendered TD[uid] Model on gameboard removed from play THIEF! Trigger set whenever PC botches a PickPockets roll TMOVE[team] Member of given team has moved UPDATE Sent to all objects on gameboard at start and when requested USE First label called when prop activated *** DEBUG TRIGGERS ONLY *** PLAYERVILLAIN Player has gained villainous reputation from DeclarationOfHostilities procedure. I'm hoping to use this to finally track down all the bugs. 2. BASIC COMMANDS &[label] Calls a local macro, stored either in the current gear, the plot, or the story. Local macros are defined exactly as regular macros are. Think of them as procedures and functions for ASL. ACCEPT Return TRUE from ConditionAccepted ACTIVATEMEME [NarrativeID] [SceneID] Activate a meme ADDCHAT [number] Add prompt[num] to interact menu ADDDEBRIEFING [npc] [msg] Add a message from NPC for debriefing [ARENA MODE] ADDREACT [value] Alter NPC reaction score AIRRAIDSIREN All NPCs will flee the gameboard ALERT [msg number] Display a text message the PC won't ignore ALTERCONTEXT [changes label] Will alter the story context by the string provided ALTERCOREMISSIONNTEXT [changes label] Will alter the core mission context [ARENA MODE] ANNOUNCE [tag] [num] Display an announcement after this conversation or event ARENAREP [change] Changes the arena team reputation by a set amount [ARENA MODE ONLY] ATTACK [Team1] [Team2] Team1 will attack Team2 BLOCK Erase trigger, prevent further events BOMB Blow up current scene CASHPRIZE [value] Give player money CHECKCOMPONENTS Load new component if needed COMPOSE [trigger label] [parameter] [cmd line identifier] Create new script event DELETENPC [Character ID] Eliminate NPC DELETEFACTION [Faction ID] Deactivate faction DRAWTERR [x] [y] [terrain] Alter the gameboard DYNAMIC [Scale] [Renown] [Strength] [NU1 label] [NU2 label] Start a dynamic scene DYNANPC [cid] [team] Insert NPC into dynamic scene E= Set Plot Element EMAIL [idnum] Store message for PC in source gear ENDCHAT Delete all items from interact menu ENDPLOT Delete the current plot EndPlotsByConID [Controller ID] Delete all plots associated with a given Controller ID ENDSTORY Delete source story, pass CLEANUP to plots EXIT [code] Leave current scene with exit code EXPRESSDELIVERY Have shopkeeper ship mecha from another town FORCECHAT [CID] Force conversation with NPC FORCEEXIT [scene] Exit the scene, overriding previous EXIT commands FREEZENPC [CID] Remove NPC from play; store as global G= [idnum] [value] Set global variable G+ [idnum] [value] Add global variable GOTO [label] Jump to another line in the program HISTORY [idnum] Add message to adventure history IF= [value1] [value2] IF# [value1] [value2] If V1 <> V2... IFFACTION [FID] If faction is active... IFFACTIONENEMY [FID] If faction is arch-enemy of PC... IFG [value1] [value2] If V1 > V2... IFKEYITEM [NID] If PC has key item... IFM [uid] If root level gear is active... IFMECHACANENTERSCENE [scene id] If PC's mecha can enter listed scene... IFMERITBADGE [badge id] If the PC has this merit badge (or ability)... IFNOOBJECTIONS [trigger label] [parameter] If the produced trigger is not blocked... IFNPCARCHENEMY [UID] If NPC is arch-enemy of PC... IFPERSONA [CID] If NPC is alive... IFSAFEAREA If the current scene is safe... IFSCENE [label] If current scene matches description... IFSKILLTEST [Skill] [Stat] [Target] If the PC passes a skill test... IFSTORYLESS If SOURCE has no linked story... IFUSKILLTEST [Skill] [Stat] [Target] If the PC passes a skill test... (unlimited tries) IFYESNO [desc msg] ["yes" msg] ["no" msg] L= [idnum] [value] Set local variable L+ [idnum] [value] Add local variable LOADD [script line label] Load a dynamic scene LOSERENOWN PC will lose 5 points or 25% of renown LTRIGGER [tag] Set a local trigger, calling some other script in same gear MAGICMAP Makes all tiles on current map visible MECHAPRIZE [faction list label] [renown] [theme] [modpoints] Give PC a mecha appropriate to factions, renown, theme, and modification points. MEMO [idnum] Store reminder for PC in source gear MONOLOGUE [cid] [msg id] The requested NPC will speak a single line outside of conversation MORETEXT [script line label] Display text file from disk MOREMEMO [tag] View memos of type [tag] - EMAIL,MEMO,NEWS MOVENPC [CID] [Scene ID] Move a character to the requested scene N= Set Story Element NEWCHAT Reset interact menu NEWD [scale] Create a new dynamic scene NEWS [idnum] Store global news message in source gear NEXTCOMP End current core story plot, load next component P= [idnum] [value] Set plot variable P+ [idnum] [value] Add plot variable PCENEMY [cid] NPC will become PC's enemy PMEMO [plotid] [Message Number] Store a memo relating to this subplot PRINT [idnum] Display message in console area PURGESTORY Pass CLEANUP to and delete story plots PUMPNEWS Applies the PUMPNEWS to city subcoms in this world QMEMO [qid] [Message Number] Store a memo relating to this quest RANDOMMECHA [faction list label] [renown] Give PC a mecha appropriate to factions, renown REPUTATION [rep num] [value] Alter PC's reputation RETREAT [team ID] Remove team from the gameboard RETURN Exit a dynamic scene REVERTPERSONA Switch from plot-based person to permanent persona RUNAWAY [CID] Remove NPC from the gameboard S= [idnum] [value] Set story variable S+ [idnum] [value] Add story variable SAVEPOS Remember PC's current location SAY [idnum] Display message in interact area SAYANYTHING Say meaningless random message SAYPLOTMSG [plot message id] Say a message taken from the PLOT rather than PERSONA SCHOOL [skill list identifier] PC can train skills SEEKGATE [scene ID] Player will enter next map at entry of specified scene ***IMPORTANT*** Only works if called after Exit, Return, etc SEEKTERR [terrain type] Set where PC will enter next scene SETENCOUNTER [SID] [Value] Sets encounters active/inactive by SceneID SETMOOD [Mood NID] [City] Activates a prefab mood SETSCENEFACTION [scene ID] [faction ID] SHOP [wares identifier] SHUTTLE Start the intercity shuttle service SMEMO [Message Number] Store a memo in the story STARTPLOT [script line label] [renown] Load a plot STARTSTORY [script line label] [renown] Load a story The plot request may include paramaters if called from within a plot TIME [delay] Advance game clock TORD [team] [order ] Set orders for team TRAINNPC [CID] [script line label] Will teach the NPC one of the listed skills/talents. Skills are +, Talents are -. TRANSFORM [frame] Switch a prop's appearance to alternate state TREPUTATION [team number] [rep num] [value] Set rep for team TRIGGER [base] [value] Add an event trigger to the queue TRIGGER0 [base] Add an event trigger w/o parameter to the queue UPDATEPLOTS Check the plots for this city; load more if needed UPDATEPROPS Sends an "UPDATE" trigger to all gears on gameboard V= [idnum] [value] Set source variable V+ [idnum] [value] Add source variable VMSG [ident] [value] Print standard value message WMECHA [team] [renown] [strength] Stock current scene with mecha WMONSTER [team] [renown] [strength] Stock current scene with monsters XPV [experience award] Give PC experience 3. BASIC FUNCTIONS *Remember when adding a new function to arenascript.pp to update the macro initializer. &[label] Calls a local macro, stored either in the current gear, the plot, or the story. Local macros are defined exactly as regular macros are. Think of them as procedures and functions for ASL. ?Mecha [team number] Random mecha UID ?Pilot [team number] Random pilot UID @[gear ID] UID of root-level gear * [A] [B] Returns A * B CHATNPCID CID of interact NPC COMTIME CONCERT [size] [sktar] Play concert minigame; score returned in range 0-150. D[Die Size] E[idnum] Plot element value EScene [idnum] Element's scene ID FacBuddies [idnum] Number of friends/lovers/allies PC has in faction FacMem [idnum] Number of members FacScene [idnum] Number of controlled scenes FXPNeeded [level] Number of faction XP for next level G[idnum] Global variable value HARDSKILLTAR [Reputation] Scales a target number for skill rolls HOSTILEFACTIONS Number of active, military factions with active, military enemies L[idnum] Local variable value MAPTILE [X] [Y] Terrain value of tile X,Y N[idnum] Story (Narrative) element value NEXTDAY Returns the start of the next game day NPCREP [CID] [idnum] NPC's reputation score P[idnum] Plot variable value PCFAC PC's faction ID PCMEKS Number of meks PC owns PCREP [idnum] PC's reputation score PCSCALE Scale of PC's root level gear PCUID PC's unique ID PCX PC X position PCY PC Y position PCSKILLVAL [skill] [stat] Highest skill value from player lance PRICE [renown] [percent] Good asking price for plot event RANGE [uid1] [uid2] Range between two gears by UID REACT Reaction score with interact NPC REWARD [renown] [percent] Good salary for combat mission S[idnum] Story variable SCENEFACTION [scene ID] Scene's faction ID SCENEID ID of current scene SELFUID Source's unique ID SKILLTAR [Reputation] Scales a target number for skill rolls SKROLL [Skill Number] [Stat Number] PC makes a skill roll SOCSKILLTAR [Reputation] Gives a target number for social skill rolls T[team number] Number of active masters on team THREAT [Reputation] [Percent] Good difficulcy value for mission V[idnum] Source variable value WMTHREAT [Reputation] Good WMon difficlulcy value for level WORLDID Returns the ID of the current world 4. MESSAGE FORMATTING STRINGS \CHATNPC The NPC currently being spoken with \CHATNPCMECHA The name of the mecha of the NPC being spoken with \DATE [time] Converts [time] to game display format \ELEMENT [n] The name of plot element N \EXACT_SCENE [ID] The exact_name of a scene \FACRANK [FID] [Rank] Faction rank name \FACTION [FID] The name of the faction \FACTION_DESIG [FID] The designation of the faction \HINT [LayerID] Returns a subplot hint \HINT_MEMO [LayerID] Returns a subplot hint and records a plot memo \ITEM [NID] The name of an item \ITEM_DESC [NID] The description of an item \ITEM_HISTORY [NID] The history of an item \ITEM_USAGE [NID] The probable use for an item \MEK [UID] The name of a mecha, grabbed by unique ID \NARRATIVE [n] The name of story element N \OFFSPRING [CID] Child noun (Son/Daughter) for NPC (0=PC) \OPR [NID] Object pronoun (him,her) for NPC (0=PC) \PC The PC's name \PCJOB The PC's job \PERSONA [CID] The name of a NPC \PILOT [UID] The name of a character, grabbed by unique ID \PPR [NID] Posessive determiner (his,her) for NPC (0=PC) \RANK PC's rank name \SCENE [ID] The name of a scene \SECRET [NID] A plot secret \SIBLING [CID] Sibling noun (Sister/Brother) for NPC (0=PC) \SOURCE The name of the script source gear \SPR [NID] Subject pronoun (he,she) for NPC (0=PC) \VAL [x] The provided value 5. GEAR GRABBERS GrabDesig [desig] Grabs an item by its designation GRABCONTROLLER [ID] Grabs a plot controller: either a scene or a mood GRABENTRANCE [scene ID] Grabs a scene entrance GRABLOCAL [uid] Grabs a model from gameboard GRABPARENT Grabs the parent of the currently grabbed gear GrabRoot Grabs the root of the currently grabbed gear GrabRootScene Grabs the root scene of the currently grabbed scene GRABSUBSCENE [n] Grabs a subscene of the current scene GRABTEAMNAME [name] Grabs a team with the provided name 6. GRABBED GEAR COMMANDS DELETEGG Deletes the grabbed (physical) gear DEPLOYGG [team] Places the grabbed gear in the current scene GADDNATT [G] [S] [V] GALTERCONTEXT [label] Alters the story context string by the changes provided GIVEGG Gives the grabbed (physical) gear to PC GMENTAL The grabbed gear will waitaminute and 5MP GMONOLOGUE [msg id] The grabbed gear will speak a single line outside of conversation GMORALEDMG [Morale] Adds morale damage to the grabbed gear GNEWPART [label] Adds new item, monster, NPC to game board GOPENINV Allows PC to trade items with grabbed gear GQUITLANCE The grabbed gear quits the lance GRUNAWAY If easily found on map, GG runs away GSETNATT [G] [S] [V] GSETSATT [key] [info label] GSETSTAT [Slot] [Value] GSKILLLEVEL [Reputation] Scales skill points to set level GSKILLXP [Skill] [XP] Gives skill-specific experience to GG GSTAMINA The grabbed gear will waitaminute and 5SP IFGARCHALLY True if GG an arch-ally of PC IFGARCHENEMY True if GG an arch-enemy of PC IFGCANJOINLANCE True if GG can join the lance, assuming GG is an ally IFGHASITEM [NID] True if GG has item in possession IFGHASSKILL [Skill] True if GG has the listed skill IFGINPLAY True if GG on map and operational IFGDEAD True if GG is Nil or is destroyed IFGOK True if GG exists and not destroyed IFGSEALED True if NPC GG is enviro-sealed IFGSEXY True if NPC GG exists and is sexy to PC IFTEAMCANSEEGG [team] If the grabbed gear can be seen by team... MOVEANDPACIFYGG [Scene ID] Move GG to scene, setting teamdata to something peaceful MOVEGG [Scene ID] Moves the grabbed (physical) gear 7. GRABBED GEAR FUNCTIONS *Remember when adding a new function to arenascript.pp to update the macro initializer. GNatt [G] [S] GS Grabbed gear S descriptor GSCENE Scene ID of the grabbed gear GStat [stat] GV Grabbed gear V descriptor 8. METACOMMANDS !Talk [CID] Forces conversation with NPC gearhead-2-0.701/doc/Credits.txt000066400000000000000000000004371321074026100163230ustar00rootroot00000000000000I am a bad bad developer- I haven't been keeping a credits file for GH2. Well, no time like the present to start... I need to merge this file with the GH1 credits file. Roc Sprite DS French Portraits Ladene Kosaka Tung Nguyen Item Images Francisco Munoz Phil Munoz gearhead-2-0.701/doc/SA Glossary.txt000066400000000000000000000163171321074026100170210ustar00rootroot00000000000000String Attribute Glossary A catch-all doc for the strange things that may be stored in string attributes. CATEGORY Determines what shopkeepers will stock. WEAPON, MELEE, MISSILE, THROWN, EXOTIC TOOL, SCIENCE, INSTRUMENT, RELIGION GRENADE ACCESSORY ARMOR, CLOTHING, SHIELD MEDICINE, FOOD ENTERTAINMENT CYBERWARE ELECTRONICS, COMPUTER, SOFTWARE, COMMUNICATIONS SPACE CONTRABAND SPORTS ODDITIES, NINJA, PIRATE SOUVENIR TREASURE, JEWELRY, ARTWORK, ANTIQUE, GEMSTONE, MINERAL MEXTRA, MEK_WEP, MEK_ARM, MEK_ENG NPCONLY - This item will only be generated for NPCs; useful for armor sets ESSENTIAL - These items will be generated in the CavClub store CONTEXT Used for selecting random content connected to this thing. Tags are 5 chars long. MILIT, POLIT, POLIC, CRIME, CORPO: Faction types SPACE DESC Description. Contains a readable description of the gear. DESIG Designation A unique identifier for this gear which is independant of its name. Designations are used to select gears from the STC file. Designations may be applied to map features to direct the placement of scene elements. The map feature designated as "EXIT" will recieve gateways to all sub-scenes. For map features: EntranceGrid, ExitGrid DESTINATION Used in the ATLAS file. Holds the name of the destination for a metaterrain gear. EFFECT Contains an effect string that gets triggered when food is eaten. EXACT_NAME Contains the exact name of a scene, in cases where ambiguity would result from using the common name. FACTIONS Included in scenes in the Atlas file. Tells which factions are active in this location. Factions are identified by their DESIG. Also included in the Design files. Tells what factions will use what equipment. Items available to all factions should have the GENERAL tag. HABITAT Included in monsters and dungeons to limit the monster selection. If the habitat attribute is undefined or empty, this model can show up in any habitat. The habitat description of a scene will likely be composed of two tags separated by a period. The first tag describes the world, and the second tag describes the specific region of that world. Available keywords are: SPACE, EARTH URBAN, ASTER, MNTNS HINT_%plotid% Contains a hint associated with a subplot. When the PC is given a mission they may also be given a hint. If a given hint begins with a colon, it will redirect the hint search to a different subplot. For instance: HINT_%plotid% <:%plotid1%> When the hint for %plotid% is called, actually it will be the hint for %plotid1% which will be shown. KEYWORDS Lists keywords by which the PC can telephone somebody. The keywords include all the categories sold by the NPC (see above), plus: RESTAURANT, SHUTTLE NAME The name of the gear. To link a persona to a unique NPC in the atlas file, name the persona "[NPC Name] Persona". Teams may be requested by name by random scene content. See that documentation for a list of the standard team names. NEVERFAIL[n] Holds instructions for the creation of a neverfail plot element. This attribute is placed in the story or plot alongside the relevant element request. For characters, this string holds the NPC's job. PARAM Included in a scene. Contains a custom map generation sequence. PERSONATYPE Included in a root scene. This is added to all persona requests taking place in this location. City,Town,Village,asteroid,mine,spinner,capitol,mine PLACE[n] Determines the placement of a prefab element in a plot or story. Contains the value of the element where this prefab element should be inserted. It may also contain a !Near clause to place the prefab element close to another element in the destination scene. Placed in the plot or story itself. If the first character is ~, will place element in the same scene as the other element. QUEST Contains a Static Adventure Content request. See those docs for more information. SDL_PORTRAIT Contains the filename of the SDL portrait for a NPC. The portraits are filtered based on age, charm, and mecha. Age: [Y]oung (<25), [O]ld (>35), [A]dult (>24), [J]unior (<36) Charm: [C]harming (>14), [U]gly (<10), [P]lain (<15), [A]verage(>9) Mecha: [Y]es, [N]o SPCONTEXT Used internally to pass context information to subplots. SPECIAL Holds instructions for the handling of this gear. Scene tags: NoExit, Solo, NoRescue, NoPillage, Arena (ejection rolls automatically succeed) Unsafe (never counts as a safe place to rest or repair) Unregulated (local tolerance value overrides city-wide tolerance rating) Unchartable (map will be generated anew each time the PC visits) Uniform (encounters will inherit same map generator as parent) RandMaps tags: StartHere, Cell: doors surrounding the feature initialized, AddExit (Mall, MonkeyMap, ClubMap only), SubZone: Feature inherits the subzone type of its parent, SharedPalette: Feature inherits the palette definitions of its parent MapFeature tags: NoGo (randomly placed gears won't be placed here) Persona Special tags: NOESCAPE (can't quit conversation with ESC) UNLISTED (can't contact NPC by phone) NOPLOTS (NPC won't be selected for random plots) Job Special tags: NeedsFaction Encounter Special tags: NoMSID (Encounter won't be given MetaScene ID when added as prefab) Quest Fragment tags: Reusable TERRAIN Used in a scene or metascene to specify what mecha can be deployed there. GROUND is the default. SPACE indicates a battle in space. Duh. Must correspond to the TYPE string attribute of a mecha. TYPE Generally used when randomly selecting an example. For a scene: Building Dungeon Capitol City: Included in root scene, indicates this is a largish city Corporate: This scene belongs to a corporation Cuisine: This city is famous for its food Culture: This city is famous for its art, music, or performances Dangerous Disaster: This city was seriously damaged by some accident Dystopia: This city is thoroughly, systematically evil Environs: Outside the city limits where encounters can be placed Financial: This city is a financial hub Ground, Space: Terrain types Hometown: Included in root scene; may be selected as home of new PCs Industrial Laboratory Legit, Sleazy Meeting: This is a place used for meetings and dates Military: This city has a big military base Mine: This is a mine. Not yours, a mine. Open: A dungeon which is automatically accessable Outdoors: An outside area where encounters can be placed Poor Preserve: A nature preserve; used for an outdoors scene, instead of park Public, Private Residence: An apartment, house, or other residential building Resort Rich Research: This city does a lot of scientific research Safe Target: A city that might get invaded Town: Included in root scene, indicates that people live here TradeHub University Urban: A city area where encounters can be placed For a mecha: Ground, Space For a monster: Habitat designations For a weapon: Holds the attack attributes ZONE_SPECIAL Contained in a random scene content gear. This string attribute is copied to the map zone associated with the content. gearhead-2-0.701/doc/Style Guide.txt000066400000000000000000000072261321074026100170470ustar00rootroot00000000000000 In general, the traits Sociable, Cheerful, and Easygoing are associated with regular, down to earth people. The traits Shy, Melancholy, and Passionate are associated with badasses who have trouble integrating with polite society. ************************** *** SOCIABLE ACTS *** ************************** Sociable characters are conscious of society and their place in it. They share their feelings and thoughts easily with other characters. They are loyal to their friends and feel responsibility to their social groups. Esteem and public image are important to them. They sometimes have difficulty keeping secrets. Examples: Relena Peacecraft, Yumiko Star - Asking about or expressing interest in other characters - Volunteering information about self - Accepting soceital obligations ********************* *** SHY ACTS *** ********************* Shy characters don't care much for society. They may feel that they don't have any need of other people, but in any case they exist on the fringes of the social network. They tend to be more concerned with the material rewards of their job than with praise or the esteem of their community; because of this, shy characters can often appear selfish. They don't share very much information about themselves. Examples: Cloud Strife, Sylia Stingray - Expressing disinterest in the lives of others - "As long as I'm paid well" - Being evasive, especially when there's no good reason to do so ************************** *** CHEERFUL ACTS *** ************************** Cheerful characters are optimists. They don't worry too much about setbacks, instead rebounding and getting on with making things better. When speaking they tend to avoid mentioning unpleasant things. They view life as a game and try to have as much fun as possible. Examples: Shiro Armada, Ed - Saying cheerful or lighthearted things - Remaining optimistic even in the face of defeat **************************** *** MELANCHOLY ACTS *** **************************** Melancholy characters are pessimists. They tend to talk about death, either their own (the Emo variant) or other people's (the Charles Bronson variant). They may be described as grumpy, irritable, or angsty. Most melancholy characters would agree that a problem can't be entirely solved until someone dies. Examples: Captain Harlock, Wolverine - Making death threats, especially to noncombatants - Sounding depressed, angsty, or bitter *************************** *** EASYGOING ACTS *** *************************** Easygoing characters don't take anything too seriously. Given the choice between doing something the easy way or the hard way, they'd take the easy way. They are quite willing to compromise in order to get what they want. They value comfort and generally try to avoid conflict. Examples: Justy Ueki Tylor, Han Solo - Expressing reluctance to do something hard or dangerous - Begging, whining, or making excuses - Compromising, changing one's mind, or admitting a mistake **************************** *** PASSIONATE ACTS *** **************************** Passionate characters believe in a black and white universe with inflexible rules. Given the choice between doing something the easy way or the hard way, they'd take the hard way just to prove that they can. They are unwilling to compromise; they are also unwilling to change their minds, even in the face of overwhelming evidence. These characters admire strength and seek out conflict. Examples: Priss, Tatewaki Kuno - Challenging someone to a duel - Refusing to compromise or admit a mistake - "My honor is besmirched!" gearhead-2-0.701/doc/adventure_structure.txt000066400000000000000000000001421321074026100210340ustar00rootroot00000000000000ARENA CAMPAIGN Adventure Sub PC units End Inv Factions Mission Scene End gearhead-2-0.701/doc/arena_missions.txt000066400000000000000000000057461321074026100177500ustar00rootroot00000000000000Arena Mode missions are stored in series\ARENAMISSION_*.txt They are written as scenes, but may also request elements like plots. A mission must have a "requires" SATt which describes what factions can take it. This string can include: - Faction type, designation - Difficulcy rating: !Ex, !Hi, !Md, !Lo, !Ne - Unspent coupons for reward missions: SKILL_TRAIN_MISSION, MECHA_SOURCE_MISSION - Mecha sources which haven't been earned [f(faction id number)] - Skill trainers which haven't been earned [s(skill id)] As with plots, the first invcoms of the mission will be taken as the prefab elements. To assign a faction to a prefab NPC, use a negative value to assign one of the elements (E1..E10 = -1..-10). The plot string substitutions can now be used in arena missions. **************************** *** COMBAT MISSIONS *** **************************** This is the default mission type. These missions must include a *MISSION tag in their requires attribute. **************************** *** REWARD MISSIONS *** **************************** These are difficult missions which offer special rewards. They don't appear as often as combat missions do. Reward missions must include a *REWARD tag in their requires attribute, and all the good rewards need a coupon as well. Remember to spend the coupon when applying the reward at the end of the mission. ************************** *** CORE CAMPAIGN *** ************************** The context for the core campaign is stored in a SAtt in the adventure named CORE_CONTEXT. Oooh, such nice alliteration. The context information includes: - The story context, stored as CORE_CONTEXT - The context of the player faction, marked as "P:" - The context of the enemy faction, marked as "F:" - The difficulcy rating of the mission (!Ne..!Ex) If KEY is requested as an element, it selects the core campaign enemy faction. As of right now the story context contains only one thing, the propp state. The propp states for the arena campaign are as follows: +P-- Initial State +Pin We Want Information +Pha Home under attack +Pls Looking for something +Par Arms Race +Ptf Take the fight to them +Ppt Peace Talks +Pfm Fleet mobilized +Pew Enemy superweapon - Initial State The PC's faction will fight the enemy faction. Probably. - We Want Information The PC must discover what the enemy faction is up to. - Home Under Attack The enemy faction plans to attack the PC's home turf. - Looking for Something The enemy faction is searching for something, and the PC faction should find it first. - Arms Race Both sides rush to produce new weapons. - Take the Fight to Them The PC's team will take the fight to the enemy's home turf. - Peace Talks The enemy faction has agreed to negotiate. - Fleet Mobilized The enemy has launched its fleet; prepare for total war. - Enemy Weapons Program The enemy is working on a new superweapon. gearhead-2-0.701/doc/city moods.txt000066400000000000000000000027631321074026100170040ustar00rootroot00000000000000Moods are wonderous things. Basically they modify the plots which load in their attached cities. How do they do this? Several ways: - Moods alter the city's TYPE, thereby causing differnt plots to be loaded - Moods may load plots of types other than *GENERAL - Moods may have their own set of elements, like stories { CITYMOOD DEFINITION } { G = GG_CityMood } { S = Undefined } { V = Number of attached plots } { A mood may also have TYPE and a PLOT_TYPE string attributes. The first modifies the } { type of the city to which the mood is attached. The second determines what sort of plot } { will be loaded by this mood; the default value is *GENERAL. } { The ControllerID for a mood is assigned automatically. } Elements 1 through 9 add their context to the plot request. If a mood is defined as a prefab element, it may grab elements from the plot or story which generated it. Positive indicies indicate the plot, while negative indicies will grab directly from the story. When a mood is inserted, its time limit is set (ComTime + Value, if nonzero) and its UPDATE script is called. String Substitutions: %city% The name of the city %me_name1% ... %me_name20% Mood element names %me_1% ... %me_20% Mood Element IDs Moods also function as memes for the cities in which they are installed. To do this, include a MSG <> block in the mood. Make sure to include a good variety of phrases since unlike regular memes, moods don't have a maximum view limit. gearhead-2-0.701/doc/city_types.txt000066400000000000000000000014631321074026100171220ustar00rootroot00000000000000***************************************************** *** SAFE vs DANGEROUS, LAWFUL vs LAWLESS *** ***************************************************** These types determine the defense and other patrols that will be encountered in this city. SAFE: No hostile patrols, no Rk2 combat missions. Normal: Defense patrols, nemesis hostile patrols, all missions OK DANGEROUS: As normal + extra hostile patrols LAWFUL: Unforgiving police patrols, no Rk2 police missions Normal: Regular police patrols, all police missions OK LAWLESS: No police patrols or missions at all A hostile patrol is defined as someone wandering around looking for fights- it may be the PC's nemesis, a faction enemy, or a group of bandits. ****************************** *** LOCAL SPECIALTIES *** ****************************** gearhead-2-0.701/doc/element search.txt000066400000000000000000000055301321074026100176040ustar00rootroot00000000000000.: CURRENT SCENE The current scene will be inserted as this element. In the case of a quest, this selects the root scene. A: ARTIFACT Select one of the artifacts from the adventure list. C: CHARACTER F: FACTION G: Grab The element will be grabbed from the story into which this plot or scene content is being inserted. Using G will result in an error if the plot/content isn't being inserted into a story. KEY: Key Element From an arena mission it selects the core campaign enemy faction. M: METASCENE N: NEW NPC A new, completely random NPC will be generated. The format for a NPC request is as follows: NPC [Faction Element] [Hometown Element] [CharDesc commands] Note that [Faction Element] and [Hometown Element] may be 0. The faction element may either point to the faction itself, or to any gear that is a member of that faction. P: PREFAB Q: QUEST SCENE A brand new permanent scene to add to the adventure. This scene must have a metascene described in the subcoms of the quest fragment. Requesting this type of element from anything other than a quest could have disasterous results. S: SCENE ******************** *** CRITERIA *** ******************** ArchAlly, ArchEnemy LOVER, FAMILY, FRIEND, LANCEMATE, NEMESIS INUSE, NOTUSED YOUNG, OLD HASMECHA GENDER:M, GENDER:F, GENDER:N, GENDER:U PCFAC, NOFAC RECHARGED @A.???, @M.??? Attitude, Motivation for NPCs @P.??? Plan for Factions MISSION This NPC is a mission-giver ************************************** *** RELATIVE SEARCH CRITERIA *** ************************************** !L Lancemate !G Global Gear !N [E1] Near. E1 and E2 must be in same root scene. !F [E1] Far. E1 and E2 must have different root scenes. !M [FacID] Member. E2 must be member of listed faction. !C [E1] Comrade. E1 and E2 must belong to exactly the same faction. !X [E1] Excluding. E1 and E2 must not be allies. !O [E1] Okay. E1 and E2 must not be enemies. !E [E1] Enemies. E1 and E2 must be enemies. !A [E1] Allies. E1 and E2 must be allies. Positive element indicies refer to elements of the plot/content. Negative element indicies refer to the elements of the story into which the plot/content is being inserted. ************************** *** PLACE STRINGS *** ************************** The elements of plots, subplots, quests, and random scene content can be positioned in the adventure by using place strings. PLACE(n) <[~](Destination Slot) [(team name)] [team data]> If the destination slot is preceded by a ~. the element will be placed in the same scene as the destination rather than in the destination itself. A subplot can assign a place string to an element it inherits. If (Destination Slot) is "/", the element will be frozen in a special holding zone and will need to be moved by the script. gearhead-2-0.701/doc/glossary.txt000066400000000000000000000045341321074026100165730ustar00rootroot00000000000000 Age of Superpowers A period of time during which the Earth was divided among several Battroid A typical humanoid mecha. Birthworld The Earth, the birthplace of humanity. Only offworlders refer to the Earth this way. Cavalier A mecha-piloting adventurer. Colony In modern usage, the word Colony refers to an enclosed space habitat of some kind. Both the Lunar domes and the L5 spinners are commonly referred to as colonies. Dead Zone An area so scarred by the Night of Fire that it will support only minimal life. Exodus The period following the Night of Fire when many survivors fled to space. Green Zone An area on Earth in which life flourishes. It is thought that certain green zones are the result of genetic contamination from PreZero biotech research. Homecoming The period following the Exodus when many people returned to recolonize the Earth. Imperator Zeta One of the superpowers from the PreZero age. Imperator Zeta controlled most of Asia. They were responsible for the Triumvirate Program. Lostech Any technology or scientific principle known during the Age of Superpowers that cannot or has not yet been replicated in modern times. Also refers to the belief that near-magical technologies existed in PreZero times. Luna The moon, the second most heavily populated body in the solar system. Night of Fire The nuclear attack which marks the end of the Age of Superpowers and the beginning of the modern period. Over two thirds of all human beings on Earth were killed in this attack. Ornithoid A bird-form mecha. Pax Europa One of the superpowers from the PreZero age. Pax Europa controlled much of Europe and Northern Asia. They created the Argoseyer series of mecha. They were also the original settlers of what would later become Aegis Overlord. PreZero The time before Year Zero of the NewType Calender. It is also used colloqually to mean a long long time ago. Spinner A space colony, usually built as an O'Neil cylinder. Artificial gravity is provided by the rotation of the colony. The word "Spinner" may also refer to a resident of these colonies. Triumvirate Program A biotech weapon program by superpower Imperator Zeta. Unification War The war on Luna in which Aegis Overlord came to dominate that world. Wangtta A loser, outcast, or pariah. Zoanoid An animal-form mecha. gearhead-2-0.701/doc/known bugs.txt000066400000000000000000000004451321074026100170020ustar00rootroot00000000000000Quest: Selecting Team For Use in Blank Scene Bug Characters are placed into blank scenes before the metascene stuff is, so the usual team selection mechanism will give bad results. Solution? Explicitly give team numbers to prefab gears that you're planning to place in blank scenes. gearhead-2-0.701/doc/man_chara.txt000066400000000000000000000152731321074026100166430ustar00rootroot00000000000000% MANAGING YOUR CHARACTER Doc Jan 30 2004 *********************************** *** MANAGING YOUR CHARACTER *** *********************************** The most important command for taking care of your PC is the View Character interface, accessed by pressing "C". From here you can take care of most of your character's needs. BACKPACK This option allows you to view your character's worldly goods. The backpack is divided into two area. The top area is your equipment; these are the items which your PC is currently using. The bottom area is your inventory; these are the items which your PC is carrying but isn't currently using. You can switch back and forth between inventory and equipment by pressing the '/' key. The backpack menu can also be accessed during exploration mode. Press 'i' to view your inventory, or 'e' to view your equipment. VIEW INJURIES Your PC's health point total is always shown in the info window, but for a more detailed overview of your character's health you can always use the View Injuries option. If your character is moving slowly, or doesn't seem to be using one of his arms, come here to see if his limbs have been damaged. TRAINING Spending experience points is what is good in life. From the training sub-menu it is possible to view all your skills, to improve them, to learn new skills, and once you are sufficiently advanced to even improve your PC's statistics. Your character will pay more for training if too many skills are known. The total number of skills your character can have without penalty is determined by the Knowledge stat. FIELD HQ The Field HQ allows you to handle all of your PC's wargear. All items which your PC owns but which are too heavy to be carried personally may be viewed and edited here. See the MANAGING YOUR MECHA doc for more information. TALENTS Talents are special things your character can do that most people can't. You only get a limited number of talents, so choose them wisely. XP TALENTS 0 1 10,000 2 30,000 3 60,000 4 100,000 5 Purchasing a talent costs 1000XP. ANATOMIST Because of your anatomical knowledge, you are able to target an opponent's vital points. (+1 Penetration versus living targets) PreRequisite: Medicine +5 ANIMAL TRAINER You are good at teaching your pets how to do tricks. (new pets gain XP bonus) PreRequisite: Dominate Animal +5 BADASS The angrier you get, the harder you fight. (Bonus to combat skills while bad morale) PreRequisite: Must be melancholy BISHOUNEN You are androgynously beautiful. (may use Flirtation skill with all NPCs, regardless of gender) PreRequisite: Charm 15 BODY BUILDER You are totally pumped up. (+2 Body) PreRequisite: Weight Lifting +5 BORN TO FLY You are an expert with flying mecha. (+3 Mecha Piloting while flying) PreRequisite: Mecha Piloting +5 BUSINESS SENSE You are very good at negotiating favorable deals. (+25% to most mission cash rewards) PreRequisite: Shopping +5 CAMARADERIE Your friends all want to take part in your adventure. (May recruit friends as lancemates) PreRequisite: Leadership +5 COMBAT MEDIC You are capable of performing emergency medicine in dangerous places. (First Aid and Medicine take 1/3 normal time) PreRequisite: First Aid +5 CYBERPSYCHO Your body is adjusted to cyberware, it's just your mind that suffers. (May avoid trauma by spending MP) PreRequisite: Body 15 DIPLOMATIC In conversation, you can avoid controversial topics. (may avoid reaction loss due to personality clash) PreRequisite: Ego 15 EXTROPIAN You are philosophically prepared to deal with the loss of your humanity. (may have one implant per 3 points Cybertech with no chance of disfunction) PreRequisite: Cybertech 5 GATE CRASHER You're very good at destroying inanimate objects. (+2 penetration against inanimate objects) PreRequisite: Must be passionate HAP-KI-DO You are an expert at self defense. (May block attacks using Martial Arts) PreRequisite: Martial Arts +5 HARD AS NAILS You don't feel pain like normal people. (All attacks against PC are at -2 Penetration) PreRequisite: Resistance +5 HULL DOWN As long as you aren't flying, you can position your mecha so as to prevent critical hits. (All attacks against walking or rolling mecha are at -3 Penetration) PreRequisite: Electronic Warfare +5 IDEALIST BLOOD Your ancestry includes some genetic engineering. (+1 to three random stats) INNOVATION You are a master of mecha engineering. (may install more equipment into mecha than normally possible) PreRequisite: Mecha Engineering +10 JACK OF ALL TRADES You can do a little bit of everything. (May use unknown skills without -2 penalty) PreRequisite: Craft 15 KUNG-FU Your hands are lethal weapons. (+3 Penetration for Martial Arts attacks) PreRequisite: Martial Arts +5 NINJITSU Your surprise attacks are deadly. (damage bonus when attacking unseen) PreRequisite: Stealth +5 PRESENCE You have mastered a commanding stage presence. (+2 Charm) PreRequisite: Performance +5 ROAD HOG You are an expert with cars and other wheeled vehicles. (+2 Mecha Piloting while rolling) PreRequisite: Mecha Piloting +5 SAVANT You have more skills than is normal for a person of your intelligence. (can learn 5 more skills without penalty) SCIENTIFIC METHOD Your scientific training has made you very good at discovering new things. (+2 Knowledge) PreRequisite: Science +5 SNIPER One shot is all you ever need. (bonus to single fire damage rolls) PreRequisite: Spot Weakness +5 STRENGTH OF FAITH You have the power of faith on your side. (+2 Ego) PreRequisite: Mysticism +5 STUNT DRIVING As long as you're going fast, you can find a way to avoid most attacks. (may reroll dodge if traveling at full speed) PreRequisite: Speed 15 SURE FOOTED You are an expert with walkers and zoanoids. (+2 Mecha Piloting while walking) PreRequisite: Mecha Piloting +5 TECH VULTURE When awarded salvage, you'll grab everything worth taking. (can salvage individual modules from defeated mecha) PreRequisite: Mecha Repair +5 gearhead-2-0.701/doc/man_mecha.txt000066400000000000000000000060721321074026100166370ustar00rootroot00000000000000% MANAGING YOUR MECHA Doc Jan 6 2003 ******************************* *** ******************************* *** MANAGING YOUR MECHA *** ******************************* When you start the game you will most likely have little more than the clothes you wear and a few credits for some basic equipment. Later on you will acquire new weapons, armor, and eventually the most prized item in this game world- a mecha. In GearHead, Wargear is defined as any item which your PC owns but which is too heavy to be carried personally. Mecha fall into this category, as do mecha-scale weapons. To view, assign, and edit your wargear you'll need to use the Field HQ interface. This can be accessed through the View Character menu, or by pressing the 'H' key in exploration mode. You will be presented with a list of your character's wargear. Selecting an item will bring you to a sub-menu, from which you can perform various actions depending upon the wargear's type and condition. INVENTORY MANAGEMENT Every mecha has its own inventory and equipment lists, just like your PC has. To access a mecha's carried items you can select the View Inventory command It is possible to trade items between mecha. Select the item you wish to trade, then choose the Transfer Item option. You will then be able to move the item to a different mecha. ALLOCATING ITEMS When you buy a mecha-sized weapon at the store it does not go to your PC's inventory. To use the item you'll need to select it in the Field HQ, then transfer it to the mecha you want to equip it on. REPAIRING ITEMS If a part is damaged you may want to try repairing it yourself instead of bringing it to the shop. Repair options will only be shown in the Field HQ if there is some damage that needs to be repaired, and if the PC knows the required skill. CUSTOMIZING YOUR MECHA Selecting the Examine Parts option will bring up a complete list of all the components making up this mecha. To remove a part from your mecha, select it from the list and choose Remove Part. Not all parts can be removed: some parts are so integral to a design that they cannot be replaced. If the removed part survived the extraction process, it will be placed in the mecha's inventory. It is also possible to install components into a mecha. In order to be installed, the item must be located in the mecha's inventory. Select the item you want, and then choose the Install Part option. You will be presented with a list of legal installation slots, or an error message if the selected item cannot be installed. While installing and removing components keep an eye on the stat display located above the parts list. This display will keep track of how your modifications affect the mecha's speed, maneuverability, and other statistics. You may want to try removing parts from one mecha, then transferring them to be installed on another. In order to remove and install parts most effectively, your character should know the Mecha Engineering skill. gearhead-2-0.701/doc/man_umek.txt000066400000000000000000000150171321074026100165220ustar00rootroot00000000000000% UNDERSTANDING YOUR MECHA Doc Jan 6 2003 ************************************ *** UNDERSTANDING YOUR MECHA *** ************************************ Maneuverability, abbreviated as MV on the display, indicates how agile your mecha is. A high MV score will allow you to dodge attacks in combat more easily. Targeting, abbreviated as TR on the display, indicates how good your onboard targeting systems are. A high TR score will allow you to hit enemies more often in combat. Sensors, abbreviated as SE on the display, indicates how much information about the outside world your mecha passes on to you. A high score means that your mecha has a wonderful sensor package, with resolution enhancers and target highlighters. A low score could mean that you're forced to stare out a tiny window at the front of your cockpit while listening for pings from your obsolete radar unit. Mass is listed in either kg or tons, depending upon the scale of the mecha being described. It doesn't have much direct effect upon the game, but it does determine the base values for maneuverability, targeting, and speed. ************************** *** TYPES OF MECHA *** ************************** Mecha can be built according to one of several design types. There are a few things that all mecha have in common. They are all constructed from a series of modules. They all require a body module, a cockpit, an engine, a gyroscope, and sensors. Battroids are traditional giant robots. They usually have two arms, two legs, and a head but can vary significantly from this plan. They have good maneuverability and targeting scores. Battroids don't have any special bonuses other than their versatility, but then again they don't have any special weaknesses either. Groundhuggers are low-lying wheeled or hover vehicles. Because of their sloped design and compact propulsion, groundhuggers have a better targeting score and better armor protection than battroids. However, their maneuverability is worse. Possibly the greatest advantage that groundhuggers have is their ability to use the turret module; weapons mounted in a turret may be fired in a 360-degree arc. Zoanoids are mecha built in the form of animals. Often these mecha are designed to resemble either big cats or dinosaurs. Because their frame construction exactly mirrors that of a living creature, these mecha have incredible strength and maneuverability. Unfortunately this design also prevents zoanoid mecha from optimizing their weapon arrays, so their targeting score suffers. Arachnoids are walking tanks. They usually resemble spiders, though they may have as few as two legs. Their legs provide a great range of motion and they therefore have better maneuverability than groundhuggers, while the stability of their form gives them a better targeting score than battroids. Arachnoid mecha can make use of turret modules. *********************** *** MECHA COMBAT *** *********************** The best way to survive an attack is to not get hit. If you're a mecha pilot, this means that you're going to have to practice the Mecha Piloting skill. It is this skill which determines whether or not any attack will hit your mecha. A good MV score, some cover, and a long distance between yourself and the opponent will also help you to avoid attacks. Of course, not even the best pilot is going to dodge every shot. Fortunately mecha have very thick armor. The first couple of shots in any battle are likely to bounce off this protection without causing any apparent damage. However, with each hit the armor gets a little bit weaker. Eventually the armor will give way and the mecha will collapse like a house of cards. Weapons like machine guns and swarm missiles are good for weakening armor, since they can hit the target several times with each attack. High damage non-repeating weapons like gauss rifles and beam swords are more likely to penetrate armor on the first hit, but don't tend to weaken the armor as well as a low damage weapon with a high burst value. Most mecha have at least one weapon of each type, allowing them to choose an attack suited to their opponent's skill level and armor rating. The ultimate weapon would have both a high burst value and high damage, but it would likely be both prohibitively expensive to manufacture and too heavy to mount on anything short of a Monstrous. **************************** *** ATTACK ATTRIBUTES *** **************************** ANTI-AIR Flying targets are harder to hit. Anti-Air weapons ignore this penalty. ARMORPIERCING Armor-Piercing weapons are good at penetrating armor. Your target gets only half armor protection from attacks of this type. BLAST Blast weapons affect everything in a radius from their detonation point. BRUTAL Brutal weapons cause a lot of messy, obvious damage. They eat through armor at twice the normal rate. DRONE Drone weapons release a number of small robotic airplanes which will then attack your enemies. EXTEND Extended melee weapons may be used to attack at a target up to two squares distant, as opposed to regular melee weapons which are limited to a range of one. FLAIL Flails are flexible weapons. They may not be easily blocked or parried. GAS Gas weapons do no damage, but release a toxic gas which can quickly kill living beings. HYPER Hyper weapons are particularly devastating. They deal damage to every component piece of a target simultaneously. INTERCEPT A weapon with the Intercept attack attribute may be used to shoot down incoming missiles. LINE A line attack affects every target in a straight line. OVERLOAD These weapons cause electrical interference to an enemy mecha's powerplant. SCATTER Instead of dealing a single powerful hit, scatter weapons divide their damage into multiple smaller attacks, thereby spreading out over more of the target. SMOKE Smoke weapons cause no damage, but may be used to provide cover. SWARM Swarm weapons spread out to attack multiple targets simultaneously. THROWN Throwable melee weapons may be thrown. The range depends on your character's BODY stat. THROWN, RETURNING Some throwing weapons will return to the user after they've been thrown. Things like boomerangs and rocket-powered tridents fall in this category. gearhead-2-0.701/doc/map generator.txt000066400000000000000000000005521321074026100174500ustar00rootroot00000000000000***************************** *** COMMAND REFERENCE *** ***************************** Lattice [Field] [Marble] [Corridor (Special)] Generates a lattice of 5x5 cells separated by corridors of width 3. The NW, SW, NE, and SE corner cells can be selected by map features bearing those designations. The corridor is drawn using the Special pen. gearhead-2-0.701/doc/megaplot reference.txt000066400000000000000000000154011321074026100204520ustar00rootroot00000000000000A megaplot is a plot with extras. Actually, it's a plot constructed from multiple components. **************************** *** PLOT COMPONENTS *** **************************** Plots are constructed from components. The root component will be a plot file; from here, additional components can be recursively requested to build a tree structure of narrative nodes. Each node should represent a state that the plot can be in. Each node is given its own LayerID. This ID is used for the node's message and variable namespace. The standard triggers of a subplot (START, UPDATE, etc) will only be called when the subplot is active. This doesn't apply to the base plot, only the subplots. ********************** *** METASCENES *** ********************** A MetaScene is a temporary scene used in a plot. It exists for as long as it is needed, and is deleted when the plot ends. You can define a faction for a MetaScene using SetFaction [ElementID]; to do this, the faction you want must be an element of the controlling subplot. If you use an ElementID which does not correspond to a faction unpredictable behavior may result. ****************************** *** NARRATIVE THREADS *** ****************************** A narrative thread is a series of events constructed from plot components. Only one node in a thread should be active at any given time. Each narrative thread has its own PlotID. NPCs are attached to the thread which first defines a persona for them; personal rumors can only be keyed to LayerIDs belonging to this thread. ****************** *** QUESTS *** ****************** A Quest is a special case of a megaplot. Quests are created at the start of an RPG campaign and permanently incorporated into the adventure. Quests may not use regular metascenes, but may define Quest Scenes which get added as new permanent scenes. Quests should not perform a Character search, but should instead create any NPCs they need using either PREFAB or NEWNPC. Combatants created by a quest are scaled to the difficulty level of the quest. If "." is requested as an element, it will be the city where this story takes place. Artifacts requested by a quest will be of the same challenge rating as the quest itself. This may be overridden by including a difficulty tag in the element search. ************************* *** QUEST SCENES *** ************************* If a quest scene's HABITAT isn't defined, it will be inherited from the city. If a dungeon is added as a quest scene, its ElementID will point to the goal level rather than the entry level. Quest dungeons may override the normal quest difficulty rating by defining a nonzero DifficultyLevel in their definition. An entrance for a quest scene may be added by attaching the designation "ENTRANCE %n%", where N is the index of the scene. ******************************** *** REQUESTING SUBPLOTS *** ******************************** A subplot may be requested by adding a string describing the subplot. SUBPLOT[n] <[*SubPlot Type] ([#threat]) Param1 Param2 ...> Basically it's the same as adding subquests. Up to 8 subplots may be defined, and they all must be numbered. If this plot is based on a story, the story context will be used. The element context for all parameters will be used. If the second character of the SubPlot Type is a colon, this subplot will be a side plot with its own PlotID. In this way it's possible for a single plot to have multiple narrative threads running at once. Threat is an optional value which, if added, will determine the threat rating of the subplot being generated. Normally the threat rating is automatically determined. ******************************** *** REPLACEMENT STRINGS *** ******************************** %plotid% The Plot ID %id% The individual layer ID of this component. %threat% The difficulcy rating of this plot, measured as Renown %1%..%10% The element IDs %name1%..%name10% The element names %id1%..%id8% Layer IDs of subplots %plotid1%..%plotid8% Plot IDs of subplots %e1%..%e10% Element Index of subplot element *************************** *** SUBPLOT CHANGES *** *************************** Each subplot may have a CHANGES attribute defined. The megaplot structure may not include two components which have any change tags in common. Change tags include: XXRan Plot Descriptors: +P, +C, +H, +B, +G Element Existence: [Element Code]:++, i.e. E:++, R:++ NPC Motivation/Attitude: [Element Code]:M or [Element Code]:A, i.e. E:A, R:M The +T and +F plot descriptors are set by the component which ends the plot, so these don't need to be included in the CHANGES list. Likewise, the Target NPC is associated with the +T task, so T:++ doesn't need to be specified either. Use CHANGES tags to prevent two parts of the same plot from changing the same story element. For instance, you wouldn't want two parts of the same plot to assign two completely different Enemy NPCs, or to change the Propp state in one direction somewhere and in a completely unrelated direction somewhere else. ****************************** *** ELEMENT PLACEMENT *** ****************************** You can move elements automatically by defining a "PLACE[n]" string for them. Place[n] <[~](Destination Index) ((Team Name)) (team data)> The destination index is the element number where where the gear should be moved. If a team name is indicated in parenthesis (), this will be used. If the first character in the place attribute is a ~, the gear will be placed in the scene of the requested element rather than the element itself. If being moved to a metascene, if there's a map feature with the designation "HOME [index]", where [index] is the Element Index of this element, it will be placed there as mini map component 1. Note that the Index here refers to the master plot index, not the subplot index; use %e1%..%e10% to set it. Also if being moved to a metascene, at the conclusion of the plot the gear will be moved back to its original home. Note that elements moved in this way are moved when the plot is initialized, not when the appropriate node is triggered. If you want to move a NPC at a specific point during the plot you'll need to do this manually. Design Flow - Add extension - Assign layer ID - Select candidate - Copy Elements, Match to adventure - Add sub-extensions - Repeat until everything initialized OK - Delink candidate from adventure and return - leave stub in place to defend NPC selections - Remove the stubs - Assemble the plot Assembly Flow - Do all replacement strings %1%..%10% etc - Merge element lists - Merge personas - Merge plot scripts - Merge Metascenes - Move gears as required gearhead-2-0.701/doc/persona fragments.txt000066400000000000000000000046051321074026100203450ustar00rootroot00000000000000Persona Fragments These are little slices of conversation that can be inserted into persona gears semi-randomly. They are stored in the series directory in the files "PFRAG_*.txt". CONTEXT - NPC character description - NPC mecha theme (if applicable) written as "[MTn]", where n is the theme number - Plot Context (if applicable) - Story Context (if plot is subcom of a story) - Scene Context (if plot is random scene content) - Local scene context, type, desig - Root scene desig, personatype, faction desig - World desig - Fragment type label - Designation + Type of NPC's faction, designation preceded with C: Fragments are selected if their "REQUIRES" string attribute matches the character description of the target NPC, the total context of the governing story (if appropriate), a type label provided when the fragment is requested, plus the designation and context of the NPC's faction. If generated as part of random scene content, the context includes the context list used by the content generator. The NPC's own faction designation is in quotes; in fact, this applies to all traits of the NPC. All persona fragments need a type label. All personas of a given type label should share the same interface. When inserted into a persona, the fragment is assigned an ID number. A fragment may use messages and variables in the range %id%00 to %id%99. To prevent conflict script lines used by persona fragments should be of the form ".%id%_[label]". A fragment may require up to eight parameters. These are strings which are inserted into the fragment in place of the symbols %1% to %8%. The parameters required by a given fragment vary by its type label. Requesting a Persona Fragment *[script label] <[fragment type] [param1] [param2] ...> Any string attribute in a persona that starts with "*" will be interpreted as a fragment request. The "START" string attribute will be inserted into the persona in place of the fragment request, minus the *. All other lines will be copied directly. The first symbol in a fragment request will be the type label of the fragment type required. These type labels must always begin with "*". The next symbols will be parameters 1 through 8 to be inserted into the fragment. Parameter 1 is generally the script label to call when the fragment has been completed successfully. gearhead-2-0.701/doc/quests.txt000066400000000000000000000120771321074026100162550ustar00rootroot00000000000000*************************** *** IMPORTANT NOTE *** *************************** Quests are now covered under MegaPlots. In general, a quest will function in the same was as a megaplot. I'm leaving this document here for a bit until the merger gets ironed out. ... For convenience an instance of static adventure content will be referred to as a Quest, even though most will not follow the standard quest format seen in most games (do foo, receive bar). A Quest is a collection of adventure components combined with each other and then embedded into the adventure. QUEST FRAGMENT NAMES A quest fragment type name must begin with an asterix, as in "*SUCCESS". A quest fragment that begins a new subquest must begin with an asterix and a colon, as in "*:PROVESELF". A quest fragment name may include optional descriptors separated from the main name by periods, as in "*:FINDMONEY.CHARITY" or "*:FINDMONEY.INVESTMENT". ELEMENT HANDLING Do not insert a persona fragment in the greeting of a persona! This will be megalisted in, and mess up loads of stuff. Rumors attached to a persona have the format "RUMOR%id% <>". When the quest status equals ID this rumor will be used. If "." is requested as an element, it will be the city where this story takes place. QUEST REQUEST [Quest Type] [Quest Threat Rating] A character can request one quest. A scene can request as many as wanted. Contained in a "QUEST" string attribute of a scene. If the Quest Threat Rating is omitted or 0, a default difficulcy rating will be calculated based on the location of the quest relative to the PC's starting point. Must contain a content type preceded by a *. The context for the content request includes the following: - SceneContext for the city, in quotes - Context string, desig, type, terrain, static/dynamic, personatype, faction, world - XXRan context for the key scene, prefixed by "S:" - Existence, faction, context SAtt - XXRan context for the key character, prefixed by "C:" - Existence, relation to PC, faction, job context - XXRan context for params, prefixed as "1:" through "9:" QUEST REQUEST FROM A QUEST [ Content Type ] [#threat] [E1] [E2] ... Starts with the content type. Next is an optional threat value override. If a number preceded by a hash is included, this overrides the current difficulcy rating of this quest thread. Next, a list of element IDs to be passed from the current fragment to the next. Note that only 9 parameters can be passed, despite the fact that a plot may have (as of now, anyway) up to 10 elements. REPLACEMENT STRINGS %id% The layer ID of this component. %qid% The quest ID associated with this component. %prev_id% The layer ID of the parent fragment %prev_qid% The quest ID of the parent fragment %scenesq_id% The layer ID of this scene's subquest (NewScene only) %scenesq_qid% The quest ID of this scene's subquest (NewScene only) %scenechar% The CID of the scene's key character (NewScene only) %scenefac% The ID of the scene's faction (NewScene only) %threat% The difficulcy level of the fragment, expressed as renown. %sktar% An appropriate skill roll target, based on the difficulcy. %sktar_hard% A hard skill roll based on the difficulcy. %1%..%10% The element IDs %name1%..%name10% The element names %s1%..%s10% The element scenes %id1%..%id8% Layer IDs of subquests %qid1%..%qid8% Quest IDs of subquests VALUES ASSOCIATED WITH A QUEST Quest ID: Every quest has a globally unique ID number. This number is used to determine the "plot state" of a given quest. Layer ID: Every layer in a quest has an ID number which is unique within the scope of the quest. A component can set the Quest Status to either its own ID (which it should do when initializing) or to a negative number (which means that the quest is over). If a subquest relies on information from the parent quest, it should have a check to make sure the parent quest is at the right state before doing anything. REUSABLE FRAGMENTS Most fragments are unique- once added to the adventure, they are removed from the list and will not be used again. If a fragment has a "REUSABLE" tag in its SPECIAL string attribute, it will be reusable over and over again. Reusable fragments should not include any new characters. QUEST MEMOS AND QUEST SUBMEMOS Quest memos are stored in the adventure itself. Each quest can have one memo. The key for a quest's memo is "MEMO_" + QuestID. After a quest is completed the memo no longer appears. Quests may have any number of submemos. These are located in gears associated with the quest. The key for a submemo is "MEMO_" + QuestStatus. The QuestID is known from the value stored in the gear. This memo only appears when QuestStatus holds the specified value. These should be used when a subquest needs to add a memo but shouldn't overwrite the main memo; most of these are used in entrance quests. QUEST FRAGMENT Sub PERSONAS METASCENES contain scene scripts Inv Prefab Elements gearhead-2-0.701/doc/random scene content.txt000066400000000000000000000067741321074026100207310ustar00rootroot00000000000000RANDOM METASCENE CONTENT Requested during the map drawing process. Pregenerated maps cannot have random content. Map content gears are actually plots. During the map drawing process, their elements are selected and copied to the map along with personas and any scripts stored in the gear itself. Stored in the files "Series\RANCON_*.txt". CONTEXT Local scene context, type, desig, terrain, faction desig Root scene desig, faction desig, personatype World desig DIFFERENCES FROM PLOTS The elements can have two string attributes to describe the teams they should be placed in. "TEAM[n]" gives the name of the team this element should be assigned to. This only works if the existing teams are named, and a suitable team can be found. "TEAMDATA[n]" is a standard team description string to be used if the named team cannot be found. A door prototype may be included as a subcom, just as if the random scene content were a map feature. STANDARD TEAM NAMES Citizens, Guards REPLACEMENT STRINGS The SAtts located in the content and its subcoms will have the following strings replaced: %id% Replaced with content ID number %param% Replaced with the provided parameter %1%..%8% Replaced with the element ID numbers IMPORTANT: Don't try to access E1..E2, P-variables, \ELEMENT, or EScene in scene content! It's not a real plot and these things won't work! Use the %1%..%8% replacement strings for the same effect. %name1%..%name8% The element names %pop% Replaced with the label of the line displaced by this one in the megalist REQUESTING CONTENT FROM MAPS AND MAP FEATURES [Fill|Some (n) (%)|Variety (min) (max)] [Here|Sub] [CONTENT TYPE] [CONTENT PARAMETER] FILL: Enough content will be added to use up all remaining cells. SOME: Up to n content frags will be added, % chance each. VARIETY: Between min and max frags will be added with no repetition. HERE: The content is added directly to the source map feature. SUB: The content is added to a new sub-zone of the map feature. REQUESTING CONTENT FROM CONTENT Often, a given piece of content will need more content to link to. The content request is stored in the content gear in string attributes "CONTENT[x]". Required content should be placed first in the list, and there should only be one required branch for any given content. [OPTIONAL|REQUIRED] [CONTENT TYPE] [CONTENT PARAMETER] [LOCAL|DISTANT] CONTENT LABELS Combat/Social/Investigation - Describe the basic gist of the scene Dynamic/Static - Describe whether this is a metascene or a perminant scene SUPERPROPS Superprops are not the same as random scene content, but act similarly so here they are. A superprop is a collection of props or other physical gears which are deployed on the map in a specific pattern. For example: SuperProp requires <*EnemyBase> Will load an "Enemy Base" superprop and assemble it in the parent map feature. The same context used for random scene content will be used. A superprop must be a subcom of a map feature. It will not function if simply placed as an invcom of the scene. May set Team1 to Team4. Depending on the superprop type, there may be several sub-groups into which the prop is divided. For example, for a spaceship there may be engines, weapons, and command sub-groups. If a specific team is not assigned, the team of the superprop request is used. Any scripts stored in the SuperProp request will be megalisted into all the component parts as it is assembled. gearhead-2-0.701/doc/rules_v2.txt000066400000000000000000000055111321074026100164650ustar00rootroot00000000000000THE RULES OF GEARHEAD CONSTRUCTION RULES v2.0 Mecha System INVENTORY RULES *Illegal inventory for master gears must be explicitly excluded *Legal inventory for other types must be explicitly defined Variable form modules use invcoms of their primary form Master gears may not equip: MetaTerrain MetaTerrain may not equip: MetaTerrain Mounting Points may equip: weapons, tools Only one item may be equipped at a time Hands may equip: weapons, tools Only one item may be equipped at a time Arms and Tails may equip: armor, shields, harnesses Other Modules may equip: armor, harnesses Weapons may equip: Power Source, WeaponAddOn (only if they themselves are invcom) A gear may only equip one item of any specific type SUBCOMPONENT RULES *Legal subcomponent types must be explicitly defined No two siblings may share the same CyberSlot SAtt At most one movement system of specific type per location At most one support system of specific type per location At most one Module/Body per location (i.e. a mek can only have one body) At most one holder of specific type per location, except... Module/Body may have up to two holders installed At most one ammunition gear per location At most one power source per location Variable form modules use subcoms of their primary form Mecha may have as subcoms: Modules, Modifiers (mecha type only) Modules must use the same material as the mecha itself Body Modules may have: Cockpits, Weapons, MoveSys, Holders (Mount), Sensors, Support, Computers, Power Source, Usables Arm Modules may have: Cockpits, Weapons, MoveSys, Holders (Mount + Hand), Sensors, Computers, Power Source, Usables Other Modules may have as subcoms: Cockpits, Weapons, MoveSys, Holders (Mount), Sensors, Computers, Power Source, Usables Characters may have as subcoms: Modules, Modifiers (chara type only) Cockpits may have as subcoms: Characters Physical Shields may have as subcoms: Weapons External Armor may have as subcoms: Weapons, Movement Systems Weapon/Projectile may have as subcoms: Ammo/Projectile (with matching CALIBER string attribute), Integral Weapons Weapon Add-Ons may have as subcoms: Weapons Weapon/Missile may have as subcoms: Ammo/Missile, Integral Weapons Weapon/Melee,EMelee may have as subcoms: Integral Weapons Computers may have as subcoms: Software Harnesses may have as subcoms: Weapons, Power Sources, Computers, Movement Systems Tools may have as subcoms: Weapons, Power Sources, Computers Props may have any gears as subcoms ADVENTURE STRUCTURE Adventure sub World sub Scene (City) sub Scene (Building) Meme inv Local Plots inv Local Plots Scene (independant) inv Global Plots Dynamic Scene Factions gearhead-2-0.701/doc/standard context types.txt000066400000000000000000000005361321074026100213200ustar00rootroot00000000000000CONTEXT For a faction... gov Government faction (Aegis, FedCon, L5 Council) mil Military faction (Aegis, Solar Navy, Maquise Defense Force) cor Corporate (Kettel, Biocorp, Comet) For a scene... pln Planetside spc Space Colony !Ne, !Lo, !Md, !Hi, !Ex Difficulcy levels for dungeon Negligible, Low, Medium, High, Extreme gearhead-2-0.701/doc/standard scripts.txt000066400000000000000000000017061321074026100201760ustar00rootroot00000000000000******************** *** OVERVIEW *** ******************** Certain scripts get used over and over again in many places. Rather than copying them all over the place, the standard scripts mechanism allows them to be defined once and then used as needed. ******************************************* *** REQUESTING A STANDARD SCRIPT *** ******************************************* Only physical gears (those with G > 0) may request a standard script. The request is a string attribute which begins with *. For instance: *CLUE_SURVIVAL <*Survival_GetMeat 25> This will add a script to the gear's CLUE_SURVIVAL trigger. The script to be added is *SURVIVAL_GETMEAT, and it takes one parameter (25). Standard scripts are identified by their name. Names must be unique. ********************************* *** SUBSTITUTION STRINGS *** ********************************* %id% Script ID/Namespace %1%..%8% Parameters gearhead-2-0.701/doc/tech_chardesc.txt000066400000000000000000000007261321074026100175060ustar00rootroot00000000000000Character description strings may be used to define a NPC's traits. ****************************** *** CHARDESC COMMANDS *** ****************************** Heroic, Villainous, Lawful, Criminal, Sociable, Shy, Easygoing, Passionate, Cheerful, Melancholy, Renowned, Wangtta, Pragmatic, Spiritual Male,Female Sets the indicated gender Old Character's age set to above 40 Young Character's age set to below 20 PCFriend, PCFamily, PCLover, PCEnemy gearhead-2-0.701/doc/the_egg.txt000066400000000000000000000040311321074026100163220ustar00rootroot00000000000000**************************************************** *** CHARACTER CREATION : TECHNICAL DETAILS *** **************************************************** ***************************** *** BIOGRAPHY EVENTS *** ***************************** A character's biography will be made up of two events, termed the FAMILY and the BIO. FAMILY sets the home and mentor NPCs BIO sets the conflict Since the BIO comes second, its options override those of the FAMILY. DEFINING ATTRIBUTES ------------------- A biography event may define several string attributes which affect the adventure generation. Events which come later override events which come earlier. CONTEXT: This context is applied to the core story. CONFLICT: This tag is applied to the end of egg-related quest requests (default: ".General") DEFINING NPCS ------------- A biography event may describe an NPC. If said NPC is a mentor, they will pass on skill training to the PC. A biography event may only describe two mentors, but arbitrarily many other NPCs. All attributes of the NPC request will be copied to the eventual NPC. The NPC may request a quest. Generally, the primary mentor will load a "*EGG_Primary" quest and the secondary will load a "*EGG_Secondary" quest. If no quest is explicitly requested, the NPC will be initialized with a "*EGG_Default" quest. The Age attribute of the NPC request is relative to the age of the PC, plus or minus three years. DEFINING RESIDENCE ------------------ A residence type can be defined using the "RESIDENCE <>" attribute. If no residence is defined, a scene of type *EGG_RESIDENCE_Apartment will be used. Following installation, the residence's designation will be changed to PCHOME. DESCRIPTION ----------- Each biography event should have a DESC attribute which contributes to the PC's biography attribute. Several substitution strings may be included in this description: %job% The PC's job %job1%...%job[x]% Jobs of the defined NPCs %name1%... Names of the defined NPCs %sd% Son/Daughter, as appropriate gearhead-2-0.701/doc/typelist.txt000066400000000000000000000061031321074026100165770ustar00rootroot00000000000000TYPES LIST GEAR GENERAL DESCRIPTORS -21: Secret -20: City Mood -19: Theme -18: Meme -17: SuperProp -16: Plot Thing Set GG_ContentSet = -15; { The collection of unique scene content in an adventure. } GG_ArtifactSet = -14; { The collection of unique artifacts in an adventure. } GG_Set = -13; { A collection of things at a store, purchased as one item. } GG_World = -12; { A planet, moon, or region of space. A place to group scenes. } GG_MetaScene = -11; -10: Story -9 : Plot -8 : Map Feature -7 : Adventure -6 : Faction -5 : Persona -4 : Team -3 : Scene -2 : -1 : Delete this gear 0 : Mecha 1 : Module 2 : Character 3 : Cockpit / Submecha Storage 4 : Weapon System 5 : Ammunition 6 : Movement System 7 : Holder 8 : Sensor System 9 : Support Equipment 10 : Shield 11 : External Armor 12 : Swag 13 : Prop 14 : Electronics (ghsensor.pp) 15 : MetaTerrain (ghprop.pp) 16 : Tools (ghswag.pp) 17 : Repair Fuel (ghswag.pp) 18 : Consumable (ghswag.pp) 19 : Modifier (ghmodule.pp) 20 : Weapon Add-On (ghweapon.pp) 21 : Power Source (ghweapon.pp) 22 : Computer 23 : Software 24 : Harness (ghguard.pp) 25 : Usable System (ghsupport.pp) NUMERICAL ATTRIBUTE GENERAL DESCRIPTORS -25: Completed XXRan Goals (narration.pp) -24: Team Ally/Enemy (locale.pp) -23: Mood Data (narration.pp) -22: Plot Status (narration.pp) -21: SubPlot Plot IDs (narration.pp) -20: Master Plot Element Index (narration.pp) -19: SubPlot Layer IDs (narration.pp) -18: Pro Duelist League Arena Data (arenascript.pp) -17: Meme Data (narration.pp) -16: Skill Roll Counters (arenascript.pp) -15: EntryDirections (locale.pp) -14: Quest Status (narration.pp) -13: Quest Element Scene (narration.pp) -12: Quest Info (narration.pp) -11: SubQuest Layer IDs (narration.pp) -10: SubQuest Quest IDs (narration.pp) -9 : Plot/Story Element IDs (narration.pp) -8 : Content Desc (randmaps.pp) -7 : XXRAN data (narration.pp) -6 : ParaLocation -5 : Visibility Marker {locale.pp} -4 : Episode Data -3 : Weapon Modifiers -2 : Action Data -1 : Location Data / Game Map Coordinates 0 : Script Variable (ArenaScript.pp) 1 : Skill (GHChar.pp) 2 : Gear Options (gear.pp) 3 : Character Description (GHChar.pp) 4 : Experience Record (GHChar.pp) 5 : Personal Variables (interact.pp) 6 : Relationship Score (interact.pp) 7 : Narrative Variables (gears.pp) 8 : Faction Score (interact.pp) 9 : Condition (ghchars.pp) 10 : Relationship Type (interact.pp) 11 : Statistic Improvement Level (ghchar.pp) 12 : Damage (damage.pp) 13 : SDL Frame Number (gears.pp) 14 : Status Effect Counter (ghweapon.pp) 15 : Prefrences (gears.pp) 16 : Talents (ghchars.pp) 17 : Faction Rewards (ghchars.pp) 18 : Intrinsic (ghintrinsic.pp) 19 : MetaTerrain Appearance (ghprop.pp) 20 : Arena Mission Info (gh2arena.pp) 21 : Scene Data (locale.pp) 22 : Environment Data (movement.pp) 23 : Mission Report (locale.pp) 24 : Mission Coupons (gh2arena.pp) 25 : ArenaHQ Skill Trainers (gh2arena.pp) 26 : ArenaHQ Mecha Factions (gh2arena.pp) 27 : ArenaHQ Data (gh2arena.pp) 28 : Merit Badge (ghchars.pp) gearhead-2-0.701/doc/xxranplotgen.txt000066400000000000000000000301501321074026100174520ustar00rootroot00000000000000************************************************** *** EXTRA EXTRA RANDOM PLOT GENERATION *** ************************************************** First there was random plot generation. Then there was extra-random plot generation. So, what do I name the improved plot generator for GH2? Extra-extra-random plot generation, of course. The plot components are selected based on a context string provided by the governing story. This string contains the xxran descriptors; a label telling the difficulcy level; descriptions for the xxran palette; and the PC's job. A label preceded by a ~ is optional. It will increase the chance of this component being selected, but doesn't have to be present. A label preceded by a - must not be present in the story context. If it is, this component cannot be selected. A group of labels surrounded by parenthesis and separated by |s is an or-block. One of the individual labels must be present and it will increase the chance of this component being selected. If the label "COMMON" is included in the list, this component is more likely to be selected. ************************** *** STORY CONTEXT *** ************************** The story context contains: - The xxran descriptors - Tags for the completed dramatic choices, preceded by ":" - The current difficulty level - The context for the elements - Certain merit badges, preceded by C: DIFFICULCY LEVEL: EXTREME, HIGH, MEDIUM, LOW, NEGLIGIBLE !Ex, !Hi, !Md, !Lo, !Ne ************************* *** PLOT REQUEST *** ************************* The plot label will be *CORE_[a], where [a] is the identifier for the PC's dramatic choice. At the beginning of the game the choice will be INTRO. This identifier should always be five characters long. ****************************** *** EPISODE STRUCTURE *** ****************************** An episode consists of a base plot, several attached subplots, and the stubs for the dramatic choices. The basic structure for the plots is: CORE Exposition; sets up the next part. MEET Complication; PC is offered a mission, or somesuch. MAIN The main course, usually a mecha battle. GOAL The episode is won, the dramatic choice pays off. FAIL The episode is lost, the PC must try something else. CORE is the base plot. It must request a MEET-type subplot. Likewise, the MEET subplot must request a MAIN-type subplot and usually also a GOAL and a FAIL. **************************** *** DRAMATIC CHOICE *** **************************** At the end of an episode, the PC gets to make a dramatic choice. These choices are built into the episode like subplots. They may request or grab elements as needed. ***************************** *** PERSONAL HISTORY *** ***************************** Family type: Standard, Orphanage, Circle, etc Community members: Parents, siblings, dependants (may be siblings or children), mentor, rival, twin/clone - Each family member should get attitude set. Siblings are Jr/Sr, while parents can be affectionate, protective, or distant. Family atmosphere: Warm, Cool, Despairing, etc Affluence: Rich, Middle Class, Poor, Destitute Personal Issues: Seek lost parent, Seek revenge/justice A problematic family history is essentially a wager; a penalty to starting PCs, but a benefit if it can be overcome. On the other hand a positive family may cause problems later on; for instance, if your family starts out rich, at some point in the future there will likely be a crisis. Resolving the personal issues will result in a merit badge, and this may open up new options in the core story. ************************************* *** NON-XXRAN PLOT CONTEXTS *** ************************************* The core story will have the descriptors below. A non-core story can have its own label defined in a context SAtt. This may be used to limit certain persona fragments and random scene content to a particular story or plot. This label must start with a "=", may be as long as you want (as long as it's unique), and may have several sections divided by underscores. For instance, the label "=MIL_DEFAULT" can be used by the MILitary story DEFAULT or any of its plots. ************************** *** XXRAN PALETTE *** ************************** Every story that uses the xxran commands should have a standard set of elements. This is so components can share elements between them, and also so that components can be selected quickly without doing full element searches. The standard palette entries are: 1. Enemy Character (E:) 2. Enemy Faction (F:) 3. Target Faction (P:) 7. Root location of current episode (L:) 8. Target Item (I:) These labels are followed by "++" if the element exists, or "--" if the element doesn't. In addition, the desig and context strings for each element are included. For instance, the tag "P:cor" would indicate that the PC's faction (P:) is corporate. The tag F:AEGIS would indicate that the enemy faction is Aegis Overlord. In addition, the designation of the element's faction is included if appropriate. If the element is an enemy of the PC, an "ENEMY" tag is included. If the element is an ally of the PC, an "ALLY" tag is included. If the element has the same faction as the PC, "PCFAC" is included. If the element has no faction, "NOFAC" is included. If the element is a NPC, the relationship is also included. This may be one of "FAMILY", "LOVER", or "FRIEND". If the element is a lancemate and currently in the party, "LANCE" will be included. Also if the element is a NPC, the heroic/villainous trait will be included as "GOOD_" or "EVIL_". If the element is a scene, its terrain SAtt will also be included: "SPACE", "WATER", "GROUND" The job group of the PC is included as C:. The available job designations are: %% ACADE: Academic %% ADVEN: Adventurer %% CORPO: Merchant, Corporate, White Collar %% CRAFT: Craftsman %% FAITH: Monk, Priest, etc %% LABOR: Skilled or unskilled labor %% MEDIA: Media, Performance, etc %% MEDIC: Medical %% MILIT: Military %% POLIT: Politician %% POLIC: Police %% THIEF: Criminal See series\CG_JOBS_* for a complete and up-to-date list. A lancemate will have a :TRAIN tag if they are currently eligible for the TrainNPC command. **************************** *** PLOT DESCRIPTORS *** **************************** The first two characters of a plot descriptor are its type. Only one label of a given type may be used in a single description. If a second label of the same type is added, the original label is deleted. In plot description, optional traits ("the flavor elements") are preceded by a ~. These are not nessecary but will increase the likelihood of this component being selected if they're present. The story is made up of episodes. Each episode takes place in a single city. Each episode is made up of several components. BACKGROUND DESCRIPTORS These describe the PC's background and motivation +B?? Background State +G?? PC's Goal +B?? PC Background/Motivation Describes the situation of the PC's life so far. +B-- Nothing special. +Bor PC was an orphan/raised communaly +Bpd PC's parents died +Bad PC responsible for ally/partner/friend's death +Bld PC's love interest died +Bam PC has amnesia +G?? PC Goal The stated goal of the PC. +G-- Nothing special. +Gre Revenge +Gmo Money +Gfa Fame +Gpe Peace +Gpo Power +Gkn Knowledge +Glo Love Faction Arc Descriptors [f]:P.??? Faction Plot Arc [f]:P.--- Default state [f]:P.sub Subterfuge. Political maneuvering against enemies. [f]:P.cat Covert attacks. Attack enemies secretly. [f]:P.pil Public Image, Luh. Increase profile, improve reputation. [f]:P.pat Public attacks. Attack enemies openly. [f]:P.art Seek Artifact. Searching for Target Item. [f]:P.gat Gathering. Foraging for resources. [f]:P.wep Weapon Program. Developing new weapons and mecha. [f]:P.war War. Like the other attack states, but embiggened. [f]:P.ina Inactive. This faction has withdrawn from play. [f]:R.??? Faction's Reaction to PC Character Arc Descriptors [c]:M.??? NPC Character Arc/Motivation [c]:M.--- Unknown quantity [c]:M.mer Mercenary. NPC is in it for the money. [c]:M.pro Professional. NPC seeks his personal best. [c]:M.com Competitor. NPC seeks to be better than others. [c]:M.ggd Greater Good. NPC believers self to be working for greater good. [c]:M.see Seeker. The NPC still hasn't found what they're looking for. [c]:M.rev Revenge. [c]:M.cha Change. The NPC decided to change their current situation. [c]:M.nih Nihilism. The NPC seeks destruction. [c]:A.??? Attitude towards PC [c]:A.--- Unknown quantity [c]:A.nme NPC hasn't met the PC yet [c]:A.jr_ NPC is PC's junior/subordinate/student [c]:A.sr_ NPC is PC's senior/superior/mentor [c]:A.ant NPC feels antagonistic towards the PC [c]:A.tha NPC feels thankful to the PC [c]:A.sec NPC is keeping a secret from the PC [c]:A.equ NPC respects the PC as an equal [c]:A.dis NPC has lost respect for the PC [c]:A.env NPC envies the PC [c]:A.adm NPC admires the PC [c]:A.pch PC has a reason to hate the NPC [c]:A.hat NPC has a reason to hate the PC [c]:A.mut The PC and NPC have reasons to hate one another mutually [c]:A.obs NPC obsessed with the PC ************************************ *** DEPRECIATED DESCRIPTORS *** ************************************ +P?? Propp State Describes the overall course of the story. The Advancement descriptor describes which stage of this propp state the player is currently at. +P-- Peaceful life *** THE SETUP *** +Pme PC meets an enemy pilot +Pun Under Attack +Psh PC is shanghaied into working for faction +P A stalemate between the PC and the enemy +Pla PC learns of the existence of an artifact +P EF attempting to subvert PF +P PC obtains the target item +P Enemy obtains the target item +Pew Enemy working on new weapon program Can enter at !Lo *** THE REACTION *** +Ppw Player faction working on new weapon program +P PF begins defensive program +P PF begins offensive program +P PC switches sides +P Attempting Diplomacy *** THE COUNTER-REACTION *** +P Under open attack by EF +P Bring the fight to EF +P EF reveals E is renegade; will help PC oppose him +P Peace negotiations with enemy +P PC is branded as a traitor +P There are traitors within PF +P You've been a pawn, manipulated so far +P EF unveils new superweapons +C?? Character State Describes the current state of the PC's life +C-- Just going along with the flow +Cro PC is a rookie member of faction +Clk Love interest has been kidnapped +Cre PC attempts to reform enemy +Cfa The PC has to live down a failure +Csu PC has achieved minor personal success +C Searching for lost friend/lover/family +C Investigating mystery of past (+B) +C PC is falsely accused of something (+B) +C Love interest works for the enemy +C Love triangle with enemy +C Collaborating with enemy to end conflict +C Actively seeking revenge (+B) +T?? Task Describes the PC's current short-term goal. +T-- No task. +T01 Introduction. The first task given. +Tgt Goto target for conversation +Tgs PC will go to a certain scene - often to gather information or something +Tga PC will go to the Amore NPC for conversation/date/whatever +F?? Task Flavor Describes the PC's task in greater detail. +F-- No flavor. +Fmi To get mission. +Fin To find information +Ffi Fight! probably... +Frt PC has Rescued Target +Fch PC will challenge NPC to a duel +Fpa It's going to be a party (+Tgs) +Fcc It's a company conference (+Tgs) +Far To arrest the target (+Tht) +Fpr To protect the target +Fap To apologize to the target (+Tgt,+Tga) +H?? Hanging Thread Describes an event which the PC has hanging over his head +H-- How's it hanging? It isn't. +Hed Enemy has defected (H:++) +Hrt Have rescued someone (H:++) +Hmi Someone will offer you a mission (H:++) +Hre Someone will try to get revenge (H:++) +Hra Have rescued Amore NPC +Has Assassination attempt will be made against the PC. gearhead-2-0.701/effects.pp000066400000000000000000003331021321074026100153760ustar00rootroot00000000000000unit effects; { previosly attacker.pp } { This unit handles attack, defense, and spells in GearHead. } { Spells? What is this, a FRPG? Well, that's just how I usually } { describe "special effects" such as healing, status changes, etc. } { This unit does not concern itself with UI, so requesting } { attacks and informing the user of their outcome } { has to be done elsewhere. The EFFECTS_History variable } { points to a list of SATTs describing the last processed } { effect in full. It's up to the calling procedure to pass } { this info on the user. } { TARGET LISTS: A list of targets will be stored as a list of gears. The } { actual gear being tageted will be stored as the parent. The number of } { shots against this target will be stored as the V descriptor. } { TARGET DESC } { T^.Parent = Actual targeted gear } { T^.V = Number of shots against this target } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} interface uses gears,locale; const MOSMeasure = 5; { One Measure Of Success is gained for beating } { the opponent's defense roll by this many points. } PenaltyPerDepth = 5; { Penalty for making called shots, per distance from root level. } PenaltyPerScale = 4; ClicksPerPenalty = 20; { For every 20dpr of a target's speed, there's a -1 modifier. } StopPenalty = 3; { Stopped mecha are easier to hit. } ImmobilePenalty = 15; { Broken down mecha are even easier. } StopBonus = 3; { It's easier to aim if you're standing still. } RunPenalty = 3; { It's harder to aim if you're traveling at full speed. } Parry_Bonus = 3; { Bonus to parrying an attack. } UnderwaterAttackPenalty = 3; { It's harder to aim if you're underwater. } Non_Weapon_MOS_Penalty = 2; { Non-weapons are less effective against armor. } { This mostly applies to modules. } DodgeMeleePenalty = 5; { It's hard to dodge melee attacks; parry or block instead. } { Note that this doesn't usually apply to thrown weapons. } FlyingPenalty = 3; { Penalty for firing at an airborne unit without AntiAir weapon. } HighGroundBonus = 1; { Bonus for firing at a target lower than self. } CritHitMinTar = 5; { Minimum target number for Spot Weakness rolls. } LongRangePenalty = 3; ShortRangeBonus = 2; Has_Minimum_Range = 6; { Weapons with a range greater than this have a minimum useful range. } { Attacks against targets within this minimum range happen at a penalty. } { Animation directions are stored in the history list. } { An animation direction may require some additional information, } { as detailed below. } SAtt_Anim_Direction = 'ANIM_'; GS_Shot = 1; { X1 Y1 Z1 X2 Y2 Z2 } GS_DamagingHit = 2; { X Y Z } GS_ArmorDefHit = 3; { X Y Z } GS_Parry = 4; { X Y Z } GS_Dodge = 5; { X Y Z } GS_Backlash = 6; { X Y Z } GS_AreaAttack = 7; { X Y Z } GS_ECMDef = 8; { X Y Z } GS_Block = 9; { X Y Z } GS_Intercept = 10; { X Y Z } GS_Resist = 11; { X Y Z } { Maximum length of line when adding list of destroyed parts. } Damage_List_Text_Length = 170; FX_CauseDamage = 'DAMAGE'; { Param 1 = Attack Skill } { Param 2 = Attack Stat } { Param 3 = Critical Hit Skill } { Param 4 = Critical Hit Stat } FX_CauseStatusEffect = 'STATUS'; { Param 1 = Status Number } FX_RemoveStatusEffect = 'CURE'; { Param 1 = Status Number } FX_OVERLOAD = 'OVERLOAD'; FX_HEALING = 'HEALING'; { PARAM 1 = Healing Type } FX_CreateSTC = 'CREATESTC'; { PARAM 1 = STC item designation } FX_CanDodge = 'CANDODGE'; FX_CanParry = 'CANPARRY'; FX_CanBlock = 'CANBLOCK'; FX_CanResist = 'CANRESIST'; FX_CanECM = 'CANECM'; FX_CanIntercept = 'CANINTERCEPT'; AtOp_NonLethal = -1; var ATTACK_History: SAttPtr; CTM_Modifiers: String; { Records all the modifiers, for the roll history. } Function ReadyToFire( GB: GameBoardPtr; User,Weapon: GearPtr ): Boolean; Function FindQuickFireWeapon( GB: GameBoardPtr; Master: GearPtr ): GearPtr; Function BasicDefenseValue( Target: GearPtr ): Integer; Function WeaponArcCheck( GB: GameBoardPtr; Master , Weapon: GearPtr; X,Y: Integer ): Boolean; Function WeaponArcCheck( GB: GameBoardPtr; Master , Weapon , Target: GearPtr ): Boolean; Function RangeArcCheck( GB: GameBoardPtr; Master , Weapon: GearPtr; X,Y,Z: Integer ): Boolean; Function RangeArcCheck( GB: GameBoardPtr; Master , Weapon , Target: GearPtr ): Boolean; Function BlastRadius( GB: GameBoardPtr; Attacker: GearPtr; AList: String ): Integer; Function AttackSkillNeeded( Attacker: GearPtr ): Integer; Function AttackStatNeeded( Attacker: GearPtr ): Integer; Function Firing_Weight( Weapon: GearPtr; AtOp: Integer ): Integer; Function Firing_Weight_Limit( User: GearPtr ): Integer; Function CalcTotalModifiers( gb: GameBoardPtr; Attacker,Target: GearPtr; AtOp: Integer; AtAt: String ): Integer; Procedure DestroyTerrain( GB: GameBoardPtr; X,Y: Integer ); Procedure Explosion( GB: GameBoardPtr; X0,Y0,DC,R: Integer ); Procedure DoAttack( GB: GameBoardPtr; Attacker,Target: GearPtr; X,Y,Z,AtOp: Integer); Procedure DoCharge( GB: GameBoardPtr; Attacker,Target: GearPtr ); Procedure DoReactorExplosion( GB: GameBoardPtr; Victim: GearPtr ); Procedure HandleEffectString( GB: GameBoardPtr; Target: GearPtr; FX_String,FX_Desc: String ); Procedure MassEffectString( GB: GameBoardPtr; FX_String,FX_Desc: String ); implementation uses ability,action,gearutil,ghchars,ghmodule,ghguard,gearparser,ui4gh, ghprop,ghsensor,ghsupport,ghweapon,movement,rpgdice,skilluse,texutil, ghholder,ghmecha,ghmovers; Type EffectRequest = Record AttackName,AttackMessage: String; { The description string for this attack's } { CauseDamage effect. } Originator,Weapon: GearPtr; FXList: SAttPtr; FXDice,FXMod: Integer; end; DefenseReport = Record HiRoll: Integer; { The highest defense roll rolled. } HiRollType: Integer; { The type of roll. GS_Parry, GS_Dodge... } end; MapStencil = Array [1..MaxMapWidth,1..MaxMapWidth] of Boolean; var EFFECTS_Event_Order: Integer; FX_Current_Message: String; Procedure StartNewAnnouncement; { Store the current line, then start a new one. } begin AddSAtt( ATTACK_History , 'ANNOUNCE_' + BStr( EFFECTS_Event_Order ) + '_' , FX_Current_Message ); FX_Current_Message := ''; end; Procedure RecordAnnouncement( msg: String ); { Record an announcememnt in the history list. } begin if Length( FX_Current_Message ) + Length( msg ) < 250 then begin FX_Current_Message := FX_Current_Message + ' ' + msg; end else begin StartNewAnnouncement; FX_Current_Message := msg; end; end; Procedure FlushAnnouncements; { Make sure there are no announcements left in the queue. } begin StartNewAnnouncement; end; Procedure Add_Shot_Precisely( GB: GameBoardPtr; X0,Y0,Z0,X1,Y1,Z1: Integer ); { Add a shot animation to the history list. } var msg: String; begin msg := BStr( GS_Shot ) + ' ' + BStr( X0 ) + ' ' + BStr( Y0 ) + ' ' + BStr( Z0 ); msg := msg + ' ' + BStr( X1 ) + ' ' + BStr( Y1 ) + ' ' + BStr( Z1 ); AddSAtt( ATTACK_History , SAtt_Anim_Direction + BStr( EFFECTS_Event_Order ) + '_' , msg ); end; Procedure Add_Shot_Animation( GB: GameBoardPtr; Attacker , Target: GearPtr ); { Add a shot animation to the history list. } var P0,P1: Point; begin Attacker := FindRoot( Attacker ); Target := FindRoot( Target ); P0 := GearCurrentLocation( Attacker ); P0.Z := MekAltitude( GB , FindRoot( Attacker ) ); P1 := GearCurrentLocation( Target ); P1.Z := MekAltitude( GB , FindRoot( Target ) ); Add_Shot_Precisely( GB , P0.X , P0.Y , P0.Z , P1.X , P1.Y , P1.Z ); end; Procedure Add_Point_Animation( X,Y,Z: Integer; CMD: Integer ); { Add a shot animation to the history list. } var msg: String; begin msg := BStr( cmd ) + ' ' + BStr( X ) + ' ' + BStr( Y ) + ' ' + BStr( Z ); AddSAtt( ATTACK_History , SAtt_Anim_Direction + BStr( EFFECTS_Event_Order ) + '_' , msg ); end; Procedure Add_Mek_Animation( GB: GameBoardPtr; Target: GearPtr; CMD: Integer ); { Add a shot animation to the history list. } var P: Point; begin { Only add animations for visible mecha. } if not MekVisible( GB , Target ) then Exit; { Find the location of the target, and just pass that on to } { the point animation procedure above. } Target := FindRoot( Target ); P := GearCurrentLocation( Target ); Add_Point_Animation( P.X , P.Y , MekAltitude( GB , Target ) , CMD ); end; Procedure ClearAttackHistory; { Get rid of any history variables leftover from previous attacks. } begin DisposeSAtt( ATTACK_History ); EFFECTS_Event_Order := 0; FX_Current_Message := ''; end; Procedure ClearStencil( var Stencil: MapStencil ); { Set all the tiles in the map stencil to FALSE. } var X,Y: Integer; begin for X := 1 to MaxMapWidth do for Y := 1 to MaxMapWidth do Stencil[ X , Y ] := False; end; Function InGeneralInventory( Part: GearPtr ): Boolean; { If in the general inventory, it may be thrown. } begin InGeneralInventory := IsInvCom( Part ) and ( Part^.Parent = FindMaster( Part ) ); end; Function MustBeThrown( GB: GameBoardPtr; Master, Weapon: GearPtr; TX,TY: Integer ): Boolean; { Return TRUE if WEAPON must be thrown in order to hit TARGET. } { Return FALSE if WEAPON could be used normally, i.e. not thrown. } { A weapon will only be thrown if it could not be used against } { the target otherwise- this is because throwing a weapon is a } { pain in the arse. You've got to go pick it up afterwards. } begin if Weapon^.G = GG_Ammo then begin { If you're attacking with ammo, that better be a grenade. } MustBeThrown := True; end else if InGeneralInventory( Weapon ) then begin { If this weapon is in the general inventory, it must } { have been thrown. } MustBeThrown := True; end else begin { If the attack range is greater than the regular weapon } { range, then the weapon must have been thrown. } MustBeThrown := ( ThrowingRange( GB , Master , Weapon ) > 0 ) and OnTheMap( GB , TX , TY ) and ( Range( Master , TX , TY ) > WeaponRange( GB , Weapon , RANGE_Long ) ); end; end; Function ReadyToFire( GB: GameBoardPtr; User,Weapon: GearPtr ): Boolean; { Return TRUE if the gear in question is ready to perform an attack, } { or FALSE if it is currently unable to do so. } { Check to make sure that... } { 1) ATTACKER is a functional part. } { 2) ATTACKER is a part that can be used in attack. } { 3) ATTACKER has sufficient ammunition to attack. } { 4) COMTIME is greater or equal to the RECHARGE time. } { 5) ATTACKER is mounted in a usable limb. } { 6) ATTACKER's MASTER is the same as ATTACKER's ROOT. } var AttackOK: Boolean; begin { In order to be used, a weapon must be active and in a good module and } { not have its safety switch on. } { This first check will return false if Weapon is nil, so I won't check here. } { Throwable weapons don't have to be in a good module- they can } { be in the general inventory. } if ( Weapon <> Nil ) and ( User <> Nil ) and ( ThrowingRange( GB, USer, Weapon ) > 0 ) then begin AttackOK := PartActive( Weapon ) and ( NAttValue( Weapon^.NA , NAG_WeaponModifier , NAS_SafetySwitch ) = 0 ); end else begin AttackOK := PartActive( Weapon ) and InGoodModule( Weapon ) and ( NAttValue( Weapon^.NA , NAG_WeaponModifier , NAS_SafetySwitch ) = 0 ); end; if AttackOK then begin { Applicability Check } if ( Weapon^.G = GG_Weapon ) then begin { Normally, all weapons may be used to attack. Duh. } { However, ballistic and missile weapons can't attack } { if they have no ammo left. } if ( Weapon^.S = GS_Ballistic ) or ( Weapon^.S = GS_Missile ) then begin if LocateGoodAmmo( Weapon ) = Nil then AttackOK := False; { Likewise, energy and beam weapons can't be used without power. } { Mecha can fire energy weapons using reserve power, with the } { problem that they'll become overloaded and maybe shut down. } end else if ( ( Weapon^.S = GS_BeamGun ) or ( Weapon^.S = GS_EMelee ) ) and ( User <> Nil ) then begin if ( EnergyPoints( User ) < EnergyCost( Weapon ) ) and ( User^.G <> GG_Mecha ) then AttackOK := False; end; end else if Weapon^.G = GG_Module then begin { Only Arms, Legs, and Tails may be used. } if ( Weapon^.S <> GS_Arm ) and ( Weapon^.S <> GS_Leg ) and ( Weapon^.S <> GS_Tail ) then begin AttackOK := False; end; end else if Weapon^.G = GG_Ammo then begin if Weapon^.S <> GS_Grenade then AttackOK := False; if not InGeneralInventory( Weapon ) then AttackOK := False; end else begin { No other parts may be used to attack. So, } { set AttackOK to False. } AttackOK := False; end; { ComTime Check } if NAttValue( Weapon^.NA , NAG_WeaponModifier , NAS_Recharge ) > GB^.ComTime then AttackOK := False; { If yer piloting a mecha can't punch the other guy yourself Check } if FindMaster( Weapon ) <> FindRoot( Weapon ) then AttackOK := False; end; ReadyToFire := AttackOK; end; Function FindQuickFireWeapon( GB: GameBoardPtr; Master: GearPtr ): GearPtr; { QUICKFIRE: Helper function } { Finds the first weapon on Master whose QUICKFIRE NAtt is 1. } { Master: The master gear to seek the QuickFire weapon of. } { Returns a reference to the QuickFire weapon on success, or Nil otherwise. } var FoundWpn: GearPtr; MaxQF: Integer; { PROCEDURES BLOCK } Procedure CheckAlongPath( Part: GearPtr ); { Check along the path specified for a QUICKFIRE weapon. } begin while ( Part <> Nil ) do begin { To avoid affecting QuickFire settings of characters in mecha } if ( Part^.G <> GG_Cockpit ) then begin if ( Part^.G = GG_Weapon ) and ( NAttValue( Part^.NA, NAG_WeaponModifier, NAS_QuickFire ) > MaxQF ) and ReadyToFire( GB , Master , Part ) then begin { Found one! } FoundWpn := Part; MaxQF := NAttValue( Part^.NA, NAG_WeaponModifier, NAS_QuickFire ); end else begin CheckAlongPath( Part^.SubCom ); CheckAlongPath( Part^.InvCom ); end; end; Part := Part^.Next; end; end; begin FoundWpn := Nil; MaxQF := -1; CheckAlongPath( Master^.SubCom ); CheckAlongPath( Master^.InvCom ); FindQuickFireWeapon := FoundWpn; end; Function WeaponArcCheck( GB: GameBoardPtr; Master , Weapon: GearPtr; X,Y: Integer ): Boolean; { Return TRUE if the target is in an appropriate fire arc for } { WEAPON, or FALSE otherwise. } var X0 , Y0 , D: Integer; { Position of the firer. } A: Integer; { Range and Arc of the attack. } begin if ( Master = Nil ) or ( Weapon = Nil ) then Exit( False ); if Master^.Parent <> Nil then Master := FindRoot( Master ); X0 := NAttValue( Master^.NA , NAG_Location , NAS_X ); Y0 := NAttValue( Master^.NA , NAG_Location , NAS_Y ); D := NAttValue( Master^.NA , NAG_Location , NAS_D ); A := WeaponArc( Weapon ); { Now check Range and Arc. } WeaponArcCheck := ArcCheck( X0 , Y0 , D , X , Y , A ); end; Function WeaponArcCheck( GB: GameBoardPtr; Master , Weapon , Target: GearPtr ): Boolean; { Return TRUE if the target is in an appropriate fire arc for } { WEAPON, or FALSE otherwise. } var X0 , Y0 , D , X , Y: Integer; { Position of the firer. } A: Integer; { Range and Arc of the attack. } begin if ( Target = Nil ) or ( Master = Nil ) or ( Weapon = Nil ) then Exit( False ); if Target^.Parent <> Nil then Target := FindRoot( Target ); if Master^.Parent <> Nil then Master := FindRoot( Master ); X := NAttValue( Target^.NA , NAG_Location , NAS_X ); Y := NAttValue( Target^.NA , NAG_Location , NAS_Y ); X0 := NAttValue( Master^.NA , NAG_Location , NAS_X ); Y0 := NAttValue( Master^.NA , NAG_Location , NAS_Y ); D := NAttValue( Master^.NA , NAG_Location , NAS_D ); A := WeaponArc( Weapon ); { Now check Range and Arc. } WeaponArcCheck := ArcCheck( X0 , Y0 , D , X , Y , A ); end; Function RangeArcCheck( GB: GameBoardPtr; Master , Weapon: GearPtr; X,Y,Z: Integer ): Boolean; { Check the range, arc, and cover between the listed gear and the listed tile. } { Returns true if the shot can take place, false otherwise. } var X0,Y0,D,A: Integer; rng: Integer; { Range and Arc of the attack. } OK: Boolean; begin { Calculate Range and Arc. } rng := WeaponRange( GB , Weapon , RANGE_Long ); X0 := NAttValue( Master^.NA , NAG_Location , NAS_X ); Y0 := NAttValue( Master^.NA , NAG_Location , NAS_Y ); D := NAttValue( Master^.NA , NAG_Location , NAS_D ); A := WeaponArc( Weapon ); OK := ArcCheck( X0 , Y0 , D , X , Y , A ); { If out of range, no shot is possible. } if OK and ( Range( Master , X , Y ) > rng ) then begin { OK is false, unless the target is within throwing range. } OK := ThrowingRange( GB , Master , Weapon ) >= Range( Master , X , Y ); end; { If Line of Sight is blocked, no shot is possible. } if OK and ( CalcObscurement( X0 , Y0 , MekALtitude( GB , Master ) , X , Y , Z , gb ) = -1 ) then OK := False; RangeArcCheck := OK; end; Function RangeArcCheck( GB: GameBoardPtr; Master , Weapon , Target: GearPtr ): Boolean; { Check the range, arc, and cover between the listed gear and the listed tile. } { Returns true if the shot can take place, false otherwise. } var X , Y: Integer; { Position of the firer. } begin { Determine initial values for all the stuff. } if Target = Nil then Exit( False ); if Target^.Parent <> Nil then Target := FindRoot( Target ); X := NAttValue( Target^.NA , NAG_Location , NAS_X ); Y := NAttValue( Target^.NA , NAG_Location , NAS_Y ); RangeArcCheck := RangeArcCheck( GB , Master , Weapon , X , Y , MekALtitude( GB , Target ) ); end; Function BlastRadius( GB: GameBoardPtr; Attacker: GearPtr; AList: String ): Integer; { Return the blast radius of this weapon. } var AA: String; R,T: Integer; begin if not HasAttackAttribute( AList , AA_BlastAttack ) then Exit( 0 ); { Initialize radius to 0. } R := 0; { Move through the string looking for the BLAST attribute. } { The radius should be right after it. } while ( AList <> '' ) and ( R = 0 ) do begin AA := UpCase( ExtractWord( AList ) ); if AA = AA_Name[ AA_BlastAttack ] then R := ExtractValue( AList ); end; if Attacker^.Scale > GB^.Scale then begin for t := 1 to ( Attacker^.Scale - GB^.Scale ) do r := r * 2; end else begin { The weapon scale must be smaller then the } { game board scale. } for t := 1 to ( GB^.Scale - Attacker^.Scale ) do r := r div 2; end; { Error check on the blast radius's range. } if R < 1 then R := 1 else if R > Max_Blast_Rating then R := Max_Blast_Rating; { Return the result. } BlastRadius := R; end; Function RechargeTime( Attacker: GearPtr; AtOp: Integer ): Integer; { Return the modified recharge time for this weapon. } var WAO: GearPtr; it: Integer; begin if Attacker^.G = GG_Weapon then begin it := Attacker^.Stat[STAT_Recharge]; end else begin it := 2; end; { Modify for weapon token. } WAO := Attacker^.InvCom; while WAO <> Nil do begin if ( WAO^.G = GG_WeaponAddOn ) and NotDestroyed( WAO ) then begin it := it + WAO^.Stat[ STAT_Recharge ]; end; WAO := WAO^.Next; end; if ( Attacker^.G = GG_Weapon ) and ( Attacker^.S = GS_BeamGun ) and ( AtOp > 0 ) then begin { Beamguns which use rapid fire take MUCH longer to recharge than normal. } RechargeTime := 2 * ClicksPerRound div it; end else begin RechargeTime := ClicksPerRound div it; end; end; Function ClearAttack( GB: GameBoardPtr; Attacker: GearPtr ; var AtOp: Integer ): Boolean; { This function sets up the weapon for performing an attack. } { It reduces ammo count by an appropriate amount. } { It sets the RECHARGE attribute. } { Return TRUE if everything is okay, FALSE if there's some reason } { why this attack can't take place. } { Note that this function does not do a range check. } var AttackOK: Boolean; Ammo: GearPtr; begin { First, make sure that this attack can even take place. } { Check to make sure that ATTACKER is active. } AttackOK := ReadyToFire( GB , FindRoot( Attacker ) , Attacker ); { If AtOp is less that 0 (thereby requesting a special attack), set it to zero } { so as to not confuse anyone. } if AtOp < 0 then AtOp := 0; if AttackOK and ( Attacker^.G = GG_Weapon ) then begin { Do an ammunition check for projectile weapons and missiles. } if ( Attacker^.S = GS_Missile ) or ( Attacker^.S = GS_Ballistic ) then begin { Locate the ammo to be used. } Ammo := LocateGoodAmmo( Attacker ); if Ammo <> Nil then begin { Reduce the ammo count by an appropriate amount. } { AtOp is the number of missiles being fired. } { If this goes over the number of missiles present, correct that problem. } if ( AtOp > 0 ) then begin if ( Ammo^.Stat[STAT_AmmoPresent] - NAttValue( Ammo^.NA , NAG_WeaponModifier , NAS_AmmoSpent ) ) < (AtOp + 1) then begin AtOp := ( Ammo^.Stat[STAT_AmmoPresent] - NAttValue( Ammo^.NA , NAG_WeaponModifier , NAS_AmmoSpent ) ) - 1; end; end; { Do the actual ammo count thing here. } AddNAtt( Ammo^.NA , NAG_WeaponModifier , NAS_AmmoSpent , AtOp + 1 ); end else begin { This weapon has no ammo. The attack cannot proceed. } AttackOK := False; end; end; end else if Attacker^.G = GG_Ammo then begin { Grenades don't get a choice what AtOp they use. } AtOp := Attacker^.Stat[ STAT_BurstValue ]; end; { Set the recharge time now. } if AttackOK then begin SetNAtt( Attacker^.NA , NAG_WeaponModifier , NAS_Recharge , GB^.ComTime + RechargeTime( Attacker , AtOp ) ); end; ClearAttack := AttackOK; end; Function AttackSkillNeeded( Attacker: GearPtr ): Integer; { Return the index number of the skill used to attack with this } { particular weapon. } var ASkill: Integer; AMaster: GearPtr; begin { The skills for human-scale and mecha-scale are set up in } { the same order, with the mecha skills being 1 to 3 and the } { personal skills being 4 to 6. So, just find the skill number } { based on ATTACKER's type, then add +3 if the master is a } { character instead of a mecha. } if Attacker^.G = GG_Weapon then begin if ( Attacker^.S = GS_Melee ) or ( Attacker^.S = GS_EMelee ) then begin { Use armed combat/weapons skill. } ASkill := NAS_MechaFighting; end else begin ASkill := NAS_MechaGunnery; end; end else if Attacker^.G = GG_Ammo then begin ASkill := NAS_MechaGunnery; end else begin { Not a weapon- use Fighting/Martial Arts. } ASkill := NAS_MechaFighting; end; { If the master isn't a mecha, add +5 to the skill index. } AMaster := FindMaster( Attacker ); if ( AMaster <> Nil ) and ( AMaster^.G <> GG_Mecha ) then begin ASkill := ASkill + 3; end; { Return the value we found. } AttackSkillNeeded := ASkill; end; Function AttackStatNeeded( Attacker: GearPtr ): Integer; { Return the stat needed for this attack. } var AtStat: Integer; begin if ( Attacker^.G = GG_Weapon ) or ( Attacker^.G = GG_Ammo ) then begin { Weapons have their attack stat stored. } AtStat := Attacker^.Stat[ STAT_AttackStat ]; end else begin { Not a weapon- use Body. } AtStat := STAT_Body; end; { Return the value we found. } AttackStatNeeded := AtStat; end; Function AttemptShieldBlock(GB: GameBoardPtr; TMaster , Attacker: GearPtr; SkRoll: Integer ): Integer; { Attempt to block an attack using a shield. Return the defense } { roll result, or 0 if no shield could be found. } var DefGear: GearPtr; DefSkill,DefRoll: Integer; Procedure SeekShield( Part: GearPtr ); { Seek a shield which is capable of parrying an attack. } begin while ( Part <> Nil ) do begin if NotDestroyed( Part ) then begin if ( Part^.G = GG_Shield ) and InGoodModule( Part ) then begin if ( NAttValue( Part^.NA , NAG_WeaponModifier , NAS_Recharge ) <= GB^.ComTime ) and ( ( Attacker = Nil ) or ( Part^.Scale >= Attacker^.Scale ) ) then begin if DefGear = Nil then DefGear := Part; end; end; if ( Part^.SubCom <> Nil ) then SeekShield( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekShield( Part^.InvCom ); end; Part := Part^.Next; end; end; begin { Try to find a shield. } DefGear := Nil; DefRoll := 0; SeekShield( TMaster^.SubCom ); { If a shield is found, proceed with the defense roll... } if DefGear <> Nil then begin { Find the appropriate skill value. } if TMaster^.G = GG_Mecha then begin { For mecha, this will be Mecha Fighting } DefSkill := NAS_MechaFighting; end else begin { For characters, this will be Armed Combat } DefSkill := NAS_CloseCombat; end; { Set the recharge time for the shield. } SetNAtt( DefGear^.NA , NAG_WeaponModifier , NAS_Recharge , GB^.ComTime + ( ClicksPerRound div 3 ) ); { Give some skill-specific experience points. } DoleSkillExperience( TMaster , DefSkill , XPA_SK_Basic ); { Make the skill roll + Shield Bonus } SkillComment( GearName( TMaster ) + ' to block with ' + GearName( DefGear ) + ' [' + SgnStr( DefGear^.Stat[ STAT_ShieldBonus ] ) + ']' ); DefRoll := SkillRoll( GB , TMaster , DefSkill , STAT_Speed , SkRoll , DefGear^.Stat[ STAT_ShieldBonus ] , False , True ); { If the parry was successful, there will be some after-effects. } if DefRoll >= SkRoll then begin { The shield is going to take damage from the hit, whether it was an } { energy shield or a beam shield- but beam shields only take damage } { from energy weapons. } if ATtacker <> Nil then begin if DefGear^.S = GS_EnergyShield then begin if CanDamageBeamShield( Attacker ) then begin DamageGear( GB , DefGear , Attacker , Attacker^.V , 0 , 1 , '' ); end; end else begin { Physical shields take damage from everything. } DamageGear( GB , DefGear , Attacker , Attacker^.V , 0 , 1 , '' ); end; { An energy shield will do damage back to any CC weapon that hits it. } if ( DefGear^.S = GS_EnergyShield ) then begin if ( Attacker^.G = GG_Module ) or ( Attacker^.S = GS_Melee ) then begin { Indicate the attacker damage here. } Add_Mek_Animation( GB , FindRoot( Attacker ) , GS_Backlash ); DamageGear( GB , Attacker , DefGear , DefGear^.V , 0 , 1 , '' ); end; end; end; end; end; AttemptShieldBlock := DefRoll; end; Function AttemptParry(GB: GameBoardPtr; TMaster , Attacker: GearPtr; SkRoll: Integer ): Integer; { Try to parry this attack, if it is in fact parryable. } var DefGear: GearPtr; DefSkill,DefRoll: Integer; Procedure SeekParryWeapon( Part: GearPtr ); { Seek a weapon which is capable of parrying an attack. } begin while ( Part <> Nil ) do begin if ( Part^.G = GG_Weapon ) and (( Part^.S = GS_Melee ) or ( Part^.S = GS_EMelee )) then begin if ReadyToFire( GB , TMaster , Part ) and InGoodModule( Part ) and ( ( Attacker = Nil ) or ( Part^.Scale >= Attacker^.Scale ) ) then begin if DefGear = Nil then DefGear := Part else if Part^.Stat[STAT_Accuracy] > DefGear^.Stat[STAT_Accuracy] then DefGear := Part; end; end; if ( Part^.SubCom <> Nil ) then SeekParryWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekParryWeapon( Part^.InvCom ); Part := Part^.Next; end; end; begin DefRoll := 0; { Search for a usable CC weapon. } DefGear := Nil; SeekParryWeapon( TMaster^.SubCom ); { If one was found, do the parry attempt. } if DefGear <> Nil then begin { Make an attack roll to parry. } DefSkill := AttackSkillNeeded( DefGear ); SkillComment( GearName( TMaster ) + ' to parry with ' + GearName( DefGear ) + ' [' + SgnStr( DefGear^.Stat[ STAT_Accuracy ] ) + ']' ); DefRoll := SkillRoll( GB , TMaster , DefSkill , STAT_Speed , SkRoll , Parry_Bonus + DefGear^.Stat[ STAT_Accuracy ] , False , True ); { Give some skill-specific experience points. } DoleSkillExperience( TMaster , DefSkill , XPA_SK_Basic ); { If the parry was successful, there will be some after-effects. } if DefRoll >= SkRoll then begin { After a succeful parry, weapon is "tapped". } DefSkill := 0; ClearAttack( GB , DefGear , DefSkill ); { If the parrying weapon is not an energy weapon, } { it will take damage from the parrying attempt. } if Attacker <> Nil then begin if ( DefGear^.S <> GS_EMelee ) then begin if ( Attacker^.G = GG_Weapon ) and ( Attacker^.S = GS_EMelee ) then begin DamageGear( GB , DefGear , Attacker , Attacker^.V , 0 , 1 , '' ); end else begin DamageGear( GB , DefGear , Attacker , 1 , 0 , 1 , '' ); end; { If the parrying weapon is an energy weapon, then } { the attacker's weapon is going to take damage unless } { it too is an energy weapon. } end else if ( Attacker^.G <> GG_Weapon ) or ( Attacker^.S <> GS_Emelee ) then begin Add_Mek_Animation( GB , FindRoot( Attacker ) , GS_Backlash ); DamageGear( GB , Attacker , DefGear , DefGear^.V , 0 , 1 , '' ); end; end; end; end; { Return the resultant defense roll. } AttemptParry := DefRoll; end; Function AttemptIntercept(GB: GameBoardPtr; TMaster , Attacker: GearPtr; SkRoll: Integer ): Integer; { Try to intercept this attack. } var DefGear: GearPtr; DefSkill,DefRoll: Integer; Procedure SeekInterceptWeapon( Part: GearPtr ); { Seek a weapon which is capable of intercepting an attack. } begin while ( Part <> Nil ) do begin if ( Part^.G = GG_Weapon ) and HasAttackAttribute( WeaponAttackAttributes( Part ) , AA_Intercept ) then begin if ReadyToFire( GB , TMaster , Part ) and InGoodModule( Part ) and ( ( Attacker = Nil ) or ( Part^.Scale >= Attacker^.Scale ) ) then begin if DefGear = Nil then DefGear := Part else if Part^.Stat[STAT_Accuracy] > DefGear^.Stat[STAT_Accuracy] then DefGear := Part; end; end; if ( Part^.SubCom <> Nil ) then SeekInterceptWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekInterceptWeapon( Part^.InvCom ); Part := Part^.Next; end; end; begin DefRoll := 0; { Search for a usable CC weapon. } DefGear := Nil; SeekInterceptWeapon( TMaster^.SubCom ); { If one was found, do the parry attempt. } if DefGear <> Nil then begin { Make an attack roll to parry. } DefSkill := AttackSkillNeeded( DefGear ); SkillComment( GearName( TMaster ) + ' to intercept with ' + GearName( DefGear ) + ' [' + SgnStr( DefGear^.Stat[ STAT_Accuracy ] + DefGear^.Stat[ STAT_BurstValue ] ) + ']' ); DefRoll := SkillRoll( GB , TMaster , DefSkill, STAT_Speed , SkRoll , DefGear^.V + DefGear^.Stat[ STAT_Accuracy ] + DefGear^.Stat[ STAT_BurstValue ] , False , True ); { Give some skill-specific experience points. } DoleSkillExperience( TMaster , DefSkill , XPA_SK_Basic ); { If the parry was successful, there will be some after-effects. } if DefRoll >= SkRoll then begin { After a succeful parry, weapon is "tapped". } ClearAttack( GB , DefGear , DefSkill ); end; end; { Return the resultant defense roll. } AttemptIntercept := DefRoll; end; Function AttemptEWBlock(GB: GameBoardPtr; TMaster , Attacker: GearPtr; SkRoll: Integer ): Integer; { Try to stop this attack using Electronic Counter-Measures. } var DefGear: GearPtr; DefRoll: Integer; begin DefRoll := 0; { Search for a usable CC weapon. } DefGear := SeekActiveIntrinsic( TMaster , GG_Sensor , GS_ECM ); { If one was found, do the parry attempt. } if DefGear <> Nil then begin { Make an attack roll to block. } SkillComment( GearName( TMaster ) + ' to ECM with ' + GearName( DefGear ) + ' [' + SgnStr( DefGear^.V ) + ']' ); DefRoll := SkillRoll( GB , TMaster , NAS_ElectronicWarfare , STAT_Craft , SkRoll , DefGear^.V - 5 , False , True ); { Give some skill-specific experience points. } DoleSkillExperience( TMaster , 17 , XPA_SK_Basic ); end; { Return the resultant defense roll. } AttemptEWBlock := DefRoll; end; Function AttemptResist( GB: GameBoardPtr; TMaster: GearPtr; SkRoll: Integer ): Integer; { Attempt to resist damage using either the RESISTANCE or } { ELECTRONIC WARFARE skills, depending upon whether the target } { is a character or a mecha. } var RSkill: Integer; begin if TMaster^.G = GG_MEcha then begin { Mecha use ELECTRONIC WARFARE. } RSkill := NAS_ElectronicWarfare; end else begin { Characters use RESISTANCE. } RSkill := NAS_Toughness; end; { Return the resultant defense roll. } DoleSkillExperience( TMaster , RSkill , XPA_SK_Basic ); AttemptResist := SkillRoll( GB , TMaster , RSkill , STAT_Ego , SkRoll , 0 , False , True ); end; Function AttemptDodge( GB: GameBoardPtr; TMaster,Attacker: GearPtr; SkRoll: Integer; const FX: String ): Integer; { TMaster will attempt to dodge. } var DodgeSkill,DodgeStat,SkMod: Integer; begin SkMod := 0; if TMaster^.G = GG_MEcha then begin { Mecha use Mecha Piloting. } DodgeSkill := NAS_MechaPiloting; DodgeStat := STAT_Reflexes; { Adjust the dodge skill value for talents. } if ( NAttValue( TMaster^.NA , NAG_Action , NAS_MoveMode ) = MM_Walk ) and HasTalent( TMaster , NAS_SureFooted ) then begin SkMod := SkMod + 2; end else if ( NAttValue( TMaster^.NA , NAG_Action , NAS_MoveMode ) = MM_Fly ) and HasTalent( TMaster , NAS_BornToFly ) then begin SkMod := SkMod + 3; end else if ( NAttValue( TMaster^.NA , NAG_Action , NAS_MoveMode ) = MM_Roll ) and HasTalent( TMaster , NAS_RoadHog ) then begin SkMod := SkMod + 2; end; end else begin { Characters use Dodge. } DodgeSkill := NAS_Dodge; DodgeStat := STAT_Speed; end; { Adjust the modifier for melee attacks. These are hard to dodge, but should } { be blocked or parried instead. } if ( Attacker <> Nil ) and ( ( Attacker^.G = GG_Module ) or (( Attacker^.G = GG_Weapon ) and (( Attacker^.S = GS_Melee ) or ( Attacker^.S = GS_EMelee )) ) ) and AStringHasBString( FX , 'WasThrown' ) then begin SkMod := SkMod - DodgeMeleePenalty; end; DoleSkillExperience( TMaster , DodgeSkill , XPA_SK_Basic ); AttemptDodge := SkillRoll( GB , TMaster , DodgeSkill , DodgeStat , SkRoll , SkMod , False , True ); end; Function AttemptAcrobatics( GB: GameBoardPtr; TMaster: GearPtr; SkRoll: Integer ): Integer; { Try to evade this attack using Acrobatics. } var Part,Armor: GearPtr; DefRoll,SkMod: Integer; CanUseAcrobatics: Boolean; begin { You need the talent to even attempt acrobatics. } if not HasTalent( TMaster , NAS_Acrobatics ) then Exit( 0 ); CanUseAcrobatics := False; SkMod := 0; if TMaster^.G = GG_Character then begin { First, check to see whether or not the character can even use Acrobatics. } { In order to do so he must not be wearing any armor higher than DC2. } { Assume TRUE until shown false. } CanUseAcrobatics := True; Part := TMaster^.SubCom; while Part <> Nil do begin if Part^.G = GG_Module then begin Armor := SeekCurrentLevelGear( Part^.InvCom , GG_EXArmor , Part^.S ); if ( Armor <> Nil ) and ( Armor^.V > 2 ) then CanUseAcrobatics := False; end; Part := Part^.Next; end; end else if TMaster^.G = GG_Mecha then begin CanUseAcrobatics := HasMechaTrait( TMaster , MT_ReflexSystem ); SkMod := -5; end; DefRoll := 0; { If it's possible, do the acrobatics attempt. } if CanUseAcrobatics then begin { Make an attack roll to block. } DefRoll := SkillRoll( GB , TMaster , NAS_Dodge , STAT_Speed , SkRoll , SkMod + ToolBonus( TMaster , -NAS_Acrobatics ) , False , True ); end; { Return the resultant defense roll. } AttemptAcrobatics := DefRoll; end; Function AttemptDefenses( GB: GameBoardPtr; TMaster,Attacker: GearPtr; SkRoll: Integer; const FX: String ): DefenseReport; { The target has just been attacked. Roll any appropriate } { defenses. Return the highest defense roll. } var DefRoll: Integer; DR: DefenseReport; begin { First, check to see if this attack will be ineffective. } { If this is a NOMETAL attack or a GASATTACK, it won't affect metal targets. } if ( HasAttackAttribute( FX , AA_NoMetal ) or HasAttackAttribute( FX , AA_GasAttack ) ) and ( NAttValue( TMaster^.NA , NAG_GearOps , NAS_Material ) = NAV_Metal ) then begin DR.HiRoll := 255; DR.HiRollType := GS_Resist; Exit( DR ); end; { If this is a GASATTACK, and the target is enviro-sealed, it won't work. } if HasAttackAttribute( FX , AA_GasAttack ) and IsEnviroSealed( TMaster ) then begin DR.HiRoll := 255; DR.HiRollType := GS_Resist; Exit( DR ); end; DR.HiRoll := 0; DR.HiRollType := 0; { All attacks get a dodge attempt. } { Make the dodge roll, then dole appropriate experience. } DR.HiRoll := 0; if AStringHasBString( FX , FX_CanDodge ) then begin DR.HiRoll := AttemptDodge( GB , TMaster , Attacker , SkRoll , FX ); DR.HiRollType := GS_Dodge; end; { If dodgeable, try acrobatics next. } if AStringHasBString( FX , FX_CanDodge ) and ( DR.HiRoll < SkRoll ) then begin DefRoll := AttemptAcrobatics( GB , TMaster , SkRoll ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Dodge; end; end; { Attempt ECM defense. } if AStringHasBString( FX , FX_CanECM ) and ( DR.HiRoll < SkRoll ) then begin DefRoll := AttemptEWBlock( GB , TMaster , ATtacker , SkRoll ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_ECMDef; end; end; { Attempt physical shield parry, if charged. } if AStringHasBString( FX , FX_CanBlock ) and ( DR.HiRoll < SkRoll ) then begin DefRoll := AttemptShieldBlock( GB , TMaster , ATtacker , SkRoll ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Block; end; end; { Attempt anti-missile intercept. } if AStringHasBString( FX , FX_CanIntercept ) and ( DR.HiRoll < SkRoll ) then begin DefRoll := AttemptIntercept( GB , TMaster , ATtacker , SkRoll ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Intercept; end; end; { If a close combat attack, attempt a parry with any active } { CC weapon. } if AStringHasBString( FX , FX_CanParry ) and ( DR.HiRoll < SkRoll ) then begin DefRoll := AttemptParry( GB , TMaster , ATtacker , SkRoll ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Parry; end; end; { If resistable, try to resist. } if AStringHasBString( FX , FX_CanResist ) and ( DR.HiRoll < SkRoll ) then begin DefRoll := AttemptResist( GB , TMaster , SkRoll ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Resist; end; end; { Attempt HapKiDo block. } { Can only do this if it's a character being attacked, the } { talent is know, the character isn't tired... } if AStringHasBString( FX , FX_CanBlock ) and ( DR.HiRoll < SkRoll ) and ( TMaster^.G = GG_CHaracter ) and HasTalent( TMaster , NAS_HapKiDo ) and ( CurrentStamina( TMaster ) > 0 ) then begin DefRoll := SkillRoll( GB , TMaster , NAS_CloseCombat , STAT_Speed , SkRoll , 0 , False , True ); AddStaminaDown( TMaster , 1 ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Block; end; end; { Attempt Stunt Driving dodge. } { Can only do this if it's a mecha being attacked, the } { talent is know, and they're moving at full speed, and the } { pilot has stamina points left... } if AStringHasBString( FX , FX_CanDodge ) and ( DR.HiRoll < SkRoll ) and ( TMaster^.G = GG_Mecha ) and HasTalent( TMaster , NAS_StuntDriving ) and ( NAttValue( TMaster^.NA , NAG_Action , NAS_MoveAction ) = NAV_FullSpeed ) and ( CurrentStamina( TMaster ) > 0 ) then begin DefRoll := SkillRoll( GB , TMaster , NAS_MechaPiloting , STAT_Speed , SkRoll , 0 , False , True ); AddStaminaDown( TMaster , 1 ); if DefRoll > DR.HiRoll then begin DR.HiRoll := DefRoll; DR.HiRollType := GS_Dodge; end; end; { If defense was successful, may drain a point of stamina. } { If the defense wasn't successful, no point adding insult } { to injury. } if ( DR.HiRoll > SkRoll ) and ( Random( 3 ) = 1 ) then begin AddStaminaDown( TMaster , 1 ); end; { Return the defense report. } AttemptDefenses := DR; end; Function Firing_Weight( Weapon: GearPtr; AtOp: Integer ): Integer; { Return the firing weight of this weapon operating at the given AtOp. } var bfw: Integer; begin bfw := GearMass( Weapon ); { Melee weapons count as larger than they actually are. } if ( Weapon^.G = GG_Weapon ) and (( Weapon^.S = GS_Melee ) or ( Weapon^.S = GS_EMelee )) then begin bfw := bfw * 2; { Rapid fire also increases the firing weight. } { Missile launchers don't get a penalty for burst firing; probably recoilless. } end else if ( AtOp > 0 ) and not (( Weapon^.G = GG_Weapon ) and ( Weapon^.S = GS_Missile )) then begin bfw := bfw + ( AtOp * 3 ) div 2; end; Firing_Weight := bfw; end; Function Firing_Weight_Limit( User: GearPtr ): Integer; { Return the maximum firing weight this user can handle. } begin if User^.G = GG_Mecha then begin Firing_Weight_Limit := User^.V * 2 + 2; end else if User^.G = GG_Character then begin Firing_Weight_Limit := CStat( User , STAT_Body ); end else begin Firing_Weight_Limit := 100; end; end; Function CalcTotalModifiers( gb: GameBoardPtr; Attacker,Target: GearPtr; AtOp: Integer; AtAt: String ): Integer; { Calculate the total modifiers to this attack roll. } var SkRoll,Spd,ZA,ZT,SWB,ShortRange: Integer; AMaster,TMaster,AModule,AShield,Ammo,SW: GearPtr; Procedure AddModifier( ModLabel: String; ModValue: Integer ); { Add a modifier to the total. } begin if ModValue <> 0 then begin if CTM_Modifiers <> '' then CTM_Modifiers := CTM_Modifiers + '; '; CTM_Modifiers := CTM_Modifiers + ModLabel + SgnStr( ModValue ); SkRoll := SkRoll + ModValue; end; end; Function NotIntegralWeapon( Part: GearPtr ): Boolean; { Return TRUE if part is an invcom or the descendant of an invcom. } begin NotIntegralWeapon := IsExternalPart( AMaster , Part ); end; Function WeaponWeightModifier: Integer; { Return the targeting modifier caused by the weight of this weapon. } Function HasFreeHand( LList: GearPtr ): Boolean; { Return TRUE if you can find a hand of equal scale to AMaster } { along this linked list, or FALSE otherwise. } var HandFound: Boolean; begin HandFound := False; while ( LList <> Nil ) and ( not HandFound ) do begin if ( LList^.G = GG_Holder ) and ( LList^.S = GS_Hand ) and ( LList^.Scale >= AMaster^.Scale ) and ( LList^.InvCom = Nil ) then begin HandFound := True; end else begin HandFound := HasFreeHand( LList^.SubCom ); end; LList := LList^.Next; end; HasFreeHand := HandFound; end; var W,L: Integer; Weapon_Module: GearPtr; begin W := Firing_Weight( Attacker , AtOp ) * ( Attacker^.Scale + 1 ); L := Firing_Weight_Limit( AMaster ) * ( AMaster^.Scale + 1 ); Weapon_Module := FindModule( Attacker ); if ( Weapon_Module <> Nil ) and ( Weapon_Module^.S = GS_Body ) then L := L * 2; if HasFreeHand( AMaster^.SubCom ) then L := L * 3; if W > L then begin WeaponWeightModifier := -5 - ( ( W - L ) div ( AMaster^.Scale + 1 ) ) div 2; end else begin WeaponWeightModifier := 0; end; end; begin SkRoll := 0; AMaster := FindRoot( Attacker ); TMaster := FindRoot( Target ); CTM_Modifiers := ''; { Add the weapon accuracy, and possibly Attack Options. } if Attacker^.G = GG_Weapon then begin if Attacker^.S = GS_Missile then begin Ammo := LocateGoodAmmo( Attacker ); if Ammo <> Nil then AddModifier( 'ammo' , Ammo^.Stat[STAT_Accuracy] ); end else begin AddModifier( 'acc' , Attacker^.Stat[STAT_Accuracy] ); end; { Add a modifier for any weapon add-ons that might be attached. } { I'll use the AShield var for this instead of declaring a new variable... } AShield := Attacker^.InvCom; while AShield <> Nil do begin if ( AShield^.G = GG_WeaponAddOn ) and NotDestroyed( AShield ) then begin AddModifier( 'addon' , AShield^.Stat[STAT_Accuracy] ); end; AShield := AShield^.Next; end; { Missiles use sensor rating instead of targeting rating. } if ( Attacker^.S = GS_Missile ) and ( AMaster^.G = GG_Mecha ) then begin AddModifier( 'sensor' , ( MechaSensorRating( AMaster ) - MechaTargeting( AMaster ) ) ); end; if ( Attacker^.S = GS_Ballistic ) or ( Attacker^.S = GS_BeamGun ) or ( Attacker^.S = GS_Missile ) then begin if AtOp > 0 then begin if AtOp < 10 then AddModifier( 'BV' , ( AtOp div 2 ) ) else AddModifier( 'BV' , 5 ); end; end; end else begin { Modules and other non-weapon attacking parts suffer } { a -2 to their hot rolls. } AddModifier( 'acc' , -2 ); end; { Modify the attack roll for overheavy weapons. } if NotIntegralWeapon( Attacker ) then begin AddModifier( 'weight', WeaponWeightModifier ); end; { Modify the attack roll for wielded shields. } AModule := FindModule( Attacker ); if AModule <> Nil then begin AShield := SeekGearByG( AModule^.InvCom , GG_Shield ); if ( AShield <> Nil ) and ( AShield <> Attacker^.Parent ) then begin AddModifier( 'shield' , -5 - AShield^.Stat[ STAT_ShieldBonus ] ); end; end; { Modify the attack score for scale and target depth. } { Depth refers to the subcomponent level that TARGET is at... } if not HasAttackAttribute( AtAt , AA_BlastAttack ) then begin if Attacker^.Scale <> Target^.Scale then begin AddModifier( 'scale' , -( Attacker^.Scale - Target^.Scale ) * PenaltyPerScale ); end; AddModifier( 'depth' , - GearDepth( Target ) * PenaltyPerDepth ); end; { Modify the attack roll for target and attacker movement. } { The modifier from the attacker is based on MoveAction, } { while the modifier for the defender is based upon actual speed. } Spd := NAttValue( AMaster^.NA , NAG_Action , NAS_MoveAction ); if Spd = NAV_Stop then AddModifier( 'at-stop' , StopBonus ) else if Spd = NAV_FullSpeed then AddModifier( 'at-run' , -RunPenalty ); { Modify for target speed. } Spd := CalcRelativeSpeed( TMaster , GB ); if Spd > 0 then begin { Calc the penalty. } Spd := Spd div ClicksPerPenalty; AddModifier( 'tr-speed' , -Spd ); { Check to see if the attacker has speed-compensation software. } SW := SeekSoftware( AMaster , S_SpeedComp , TMaster^.Scale , False ); if SW <> Nil then begin SWB := SW^.V; if SWB > Spd then SWB := Spd; AddModifier( 'software-speedcomp' , SWB ); end else begin end; end else begin if CurrentMoveRate( GB^.Scene , TMaster ) > 0 then AddModifier( 'tr-stop' , StopPenalty ) else if ( TMaster^.G <> GG_Prop ) or ( NAttValue( TMaster^.NA , NAG_Skill , NAS_Dodge ) = 0 ) then AddModifier( 'tr-immobile' , ImmobilePenalty ); end; { Modify for attack attributes. } if HasAttackAttribute( AtAt , AA_STRAIN ) and ( AMaster <> Nil ) and ( CurrentStamina( AMaster ) < 1 ) then AddModifier( 'strain' , -10 ); if HasAttackAttribute( AtAt , AA_COMPLEX ) and ( AMaster <> Nil ) and ( CurrentMental( AMaster ) < 1 ) then AddModifier( 'complex' , -10 ); { Do the modifiers that only count if both meks are on the game board. } if OnTheMap( GB , AMaster ) and OnTheMap( GB , TMaster ) then begin { Add the surprise attack bonuses. } if not MekCanSeeTarget( GB , TMaster , AMaster ) then begin if HasTalent( AMaster , NAS_Ninjitsu ) then begin AddModifier( 'stealth' , MOSMeasure * 2 ); end else begin AddModifier( 'stealth' , MOSMeasure ); end; end; if not HasAttackAttribute( AtAt , AA_BlastAttack ) then begin { Adjust the attack roll for obscurement between attacker & target. } { Yeah, I'm reusing the SPD variable for cover. Big deal. } Spd := CalcObscurement( AMaster , TMaster , GB ); if Spd > 0 then begin AddModifier( 'cover' , -Spd ); end; end; { If the firer is underwater, this will be a more difficult shot. } if MekAltitude( gb , AMaster ) < 0 then AddModifier( 'at-water' , - UnderwaterAttackPenalty ); { Add range modifier. } if IsMissileWeapon( Attacker ) then begin { Still using the SPD variable for all these other uses... } SPD := Range( gb , AMaster , TMaster ); ShortRange := WeaponRange( GB , Attacker , RANGE_Short ); { Apply penalty for within minumum range } if ( ShortRange > HAS_MINIMUM_RANGE ) and ( Spd < ( ShortRange - 2 ) ) then begin { For every square inside minimum range, there's a -1 attack penalty. Sound familiar? } AddModifier( 'minrange' , -( ShortRange - 2 ) + Spd ); end else if SPD <= ShortRange then AddModifier( 'range' , ShortRangeBonus ) else if SPD > WeaponRange( GB , Attacker , RANGE_Medium ) then AddModifier( 'range' , - LongRangePenalty ); end; { Apply the blindness penalty. } if HasStatus( AMaster , NAS_Blinded ) and ( SPD > 0 ) then begin AddModifier( 'blind' , - SPD ); end; { Add altitude modifier. Attacking an airborne mecha is more difficult, } { unless the ANTIAIR attribute is had. If the attacker is higher than the } { defender there's a slight bonus there as well. } ZA := MekAltitude( GB , AMaster ); ZT := MekAltitude( GB , TMaster ); if ( ZT = 5 ) and ( ZA <> 5 ) and not HasAttackAttribute( AtAt , AA_AntiAir ) then AddModifier( 'tr-fly' , -FlyingPenalty ); if ( ZA > ZT ) and ( ZT >= 0 ) then AddModifier( 'elevation' , HighGroundBonus ); end; CalcTotalModifiers := SkRoll; end; Function BasicDefenseValue( Target: GearPtr ): Integer; { Return the value of this target's basic defense. If the target is a } { mecha, this will be its Mecha Piloting skill value. If the target is } { a character, this will be its Dodge. Otherwise return 5. } begin if Target = Nil then begin { Error! } BasicDefenseValue := 0; end else if Target^.G = GG_Mecha then begin BasicDefenseValue := SkillValue( Target , NAS_MechaPiloting , STAT_Reflexes ); end else if Target^.G = GG_Character then begin BasicDefenseValue := SkillValue( Target , NAS_Dodge , STAT_Speed ); end else begin BasicDefenseValue := 5; end; end; { PROCESS EFFECTS VS GEAR TARGETS } Function PAG_CauseDamage( GB: GameBoardPtr; AtDesc: String; ER: EffectRequest; Target: GearPtr; AtOp: Integer ): Boolean; { Return TRUE if the attack hit and further effects should continue, } { or FALSE if the attack missed. } var AtSkill,AtStat,CritSkill,CritStat,AtRoll,ModMOSMeasure,MOS,NumberOfHits,CritTar,CritHit,T: Integer; TPilot,TMaster: GearPtr; DefRep: DefenseReport; DR: DamageRec; msg: String; DP: SAttPtr; { Destroyed Part } Function MeleeNumberOfHits: Integer; { Melee weapons and modules can cause multiple hits. Make an Initiative } { roll to find out how many. ModMOSMeasure and DefRep.HiDefRoll must be } { initialized already. } const Base_Number_Of_Attacks_Denominator = 3; { I gave this const a long name to make my CS prof proud. } var InitRoll,BonusNumH,NumH: Integer; begin { Start by determining the maximum number of bonus attacks that this character can } { have, based on skill rank. } BonusNumH := 1 + ( SkillRank( ER.Originator , AtSkill ) div Base_Number_Of_Attacks_Denominator ); { Like BV weapons, this number of hits is limited by the attack roll. } if BonusNumH > ( AtRoll - DefRep.HiRoll + 1 ) then BonusNumH := ( AtRoll - DefRep.HiRoll + 1 ); if BonusNumH < 1 then BonusNumH := 1; { Make an Initiative roll to maybe increase the number of hits. } InitRoll := SkillRoll( GB , ER.Originator , NAS_Initiative , STAT_Speed , DefRep.HiRoll , 0 , False , GearOperational( TMaster ) and IsMasterGear( TMaster ) ); if InitRoll > DefRep.HiRoll then begin NumH := 2 + (( InitRoll - DefRep.HiRoll ) div ( ModMOSMeasure * 2 )); end else begin NumH := 1; end; NumH := NumH + Random( BonusNumH ); if AStringHasBString( AtDesc , 'WasThrown' ) and ( NumH > 2 ) then NumH := 2; MeleeNumberOfHits := NumH; end; begin { Error check- if the damage is to be applied to a metaterrain gear with } { a damage score of 0, just exit. There's nothing to be done here. } if ( Target^.G = GG_MetaTerrain ) and ( GearMaxDamage( Target ) = 0 ) then begin Exit( False ); end; { The four parameters for this command are the attack skill, attack stat, crit hit skill, } { and crit hit stat. If the CritSkill is 0, then this attack will not use critical hits. } AtSkill := ExtractValue( AtDesc ); AtStat := ExtractValue( AtDesc ); CritSkill := ExtractValue( AtDesc ); CritStat := ExtractValue( AtDesc ); { Locate the pilot and the master of the target, just in case they aren't } { the pilot himself. } TMaster := FindRoot( Target ); TPilot := LocatePilot( TMaster ); { Add the surprise attack bonuses. } if ( ER.Originator <> Nil ) and not MekCanSeeTarget( GB , FindRoot( TMaster ) , ER.Originator ) then begin if HasTalent( ER.Originator , NAS_Ninjitsu ) then begin ER.FXDice := ER.FXDice * 2; end else begin ER.FXDice := ER.FXDice * 4 div 3; end; end; { Make the skill roll. } if ER.Originator <> Nil then begin { Don't award any XP yet- we don't know how well the attack went. } AtRoll := SkillRoll( GB , ER.Originator , AtSkill , AtStat , BasicDefenseValue( TMaster ) + 2 , CalcTotalModifiers( gb , ER.Weapon , Target , AtOp , AtDesc ) + ER.FXMod , False , False ); SkillComment( CTM_Modifiers ); end else begin if AtSkill < 5 then AtSkill := 5; AtRoll := RollStep( AtSkill ); end; { Roll the defense dice. } DefRep := AttemptDefenses( GB , TMaster , ER.Weapon , AtRoll , AtDesc ); { If this is a blast attack, dodging becomes harder. } if HasAreaEffect( AtDesc ) and ( AtRoll <= DefRep.HiRoll ) then begin { For every time the defense roll beat the attack roll, } { the damage of the attack is reduced by half. } if AtRoll < 1 then AtRoll := 1; T := DefRep.HiRoll; while T >= AtRoll do begin T := T - MOSMeasure; ER.FXDice := ER.FXDice div 2; end; if ER.FXDice < 1 then DefRep.HiRoll := AtRoll + 1 else DefRep.HiRoll := AtRoll - 1; end; if ( AtRoll > DefRep.HiRoll ) then begin { The attack hit. } { Dole the experience award for the roll now, since we didn't do it earlier. } if GearOperational( TMaster ) and IsMasterGear( TMaster ) and ( ER.Originator <> Nil ) then GiveSkillRollXPAward( ER.Originator , AtSkill , AtRoll , DefRep.HiRoll ); { Determine base margin of success. This will be modified later. } { First, determine the modified MOSMeasure. } ModMOSMeasure := BasicDefenseValue( TMaster ) div 3; if ModMOSMeasure < MOSMeasure then ModMOSMeasure := MOSMeasure; { Next, based on the hit roll, determine base MOS. } if IsMasterGear( TMaster ) and ( TMaster^.G <> GG_Prop ) then begin MOS := ( AtRoll - DefRep.HiRoll ) div ModMOSMeasure; end else if HasTalent( ER.Originator , NAS_GateCrasher ) then begin MOS := 3; end else begin MOS := 0; end; { Set the base number of hits. This will be modified later. } NumberOfHits := 1 + AtOp; { Perform modifications which only count if } { we have a pointer to the weapon. } if ( ER.Weapon <> Nil ) then begin { Modify number of hits by weapon type and AtAt. } if ER.Weapon^.G = GG_Weapon then begin if ( ER.Weapon^.S = GS_Ballistic ) or ( ER.Weapon^.S = GS_BeamGun ) or ( ER.Weapon^.S = GS_Missile ) then begin if AtOp > 0 then begin NumberOfHits := AtRoll - DefRep.HiRoll; if AtOp > 9 then begin if NumberOfHits > 10 then NumberOfHits := 10; NumberOfHits := ( ( AtOp + 1 ) * NumberOfHits ) div 10; end else begin if NumberOfHits > (AtOp + 1) then NumberOfHits := AtOp + 1; end; end; end else if ( ER.Weapon^.S = GS_Melee ) or ( ER.Weapon^.S = GS_EMelee ) then begin { Close combat weapons can trade a high MOS for multiple hits. } NumberOfHits := MeleeNumberOfHits; end; end else if ER.Weapon^.G = GG_Module then begin { Fighting attacks have a higher chance of scoring } NumberOfHits := MeleeNumberOfHits; MOS := MOS - Non_Weapon_MOS_Penalty; { Modify the MOS for KungFu. } { This will be modified again later for being a nonweapon... } if HasTalent( ER.Originator , NAS_KungFu ) then MOS := MOS + Non_Weapon_MOS_Penalty + 1; end; end; if HasAttackAttribute( AtDesc , AA_ArmorIgnore ) then MOS := MOS + 12; { If called shots are illegal right now, reduce MOS } { by 2 to represent the general lack of precision. } if CritSkill = 0 then begin MOS := MOS - 2; end else if ER.Originator <> Nil then begin { Modify MOS for Critical Hit skill. } { Use variable SPD to represent the critical hit target # } CritTar := DefRep.HiRoll; { If the high defense roll was lower than the Critical } { Hit Minimum Target number, raise it. } if CritTar < CritHitMinTar then CritTar := CritHitMinTar; CritHit := SkillRoll( GB , ER.Originator , CritSkill , CritStat , CritTar , 0 , False , GearOperational( TMaster ) and IsMasterGear( TMaster ) ); if CritHit > CritTar then begin MOS := MOS + ( ( CritHit - CritTar ) div ModMOSMeasure ) + 1; end; { If the originator has Spot Weakness skill, modify damage for that. } if HasSkill( ER.Originator , NAS_SpotWeakness ) then begin if HasTalent( ER.Originator , NAS_Sniper ) and ( ER.Weapon <> Nil ) and ( ER.Weapon^.G = GG_Weapon ) and (( ER.Weapon^.S = GS_Ballistic ) or ( ER.Weapon^.S = GS_BeamGun )) then begin ER.FXDice := ER.FXDice + SkillRank( ER.Originator , NAS_SpotWeakness ); end else if ( ER.Weapon <> Nil ) and (( ER.Weapon^.G <> GG_Weapon ) or ( ER.Weapon^.S = GS_EMelee ) or ( ER.Weapon^.S = GS_Melee )) then begin ER.FXDice := ER.FXDice + ( SkillRank( ER.Originator , NAS_SpotWeakness ) div 2 ); end else begin ER.FXDice := ER.FXDice + ( SkillRank( ER.Originator , NAS_SpotWeakness ) div 5 ); end; end; { Modify MOS for miscellaneous other talents. } { ANATOMIST talent - +1 MOS vs Meat targets } if HasTalent( ER.Originator , NAS_Anatomist ) and ( NAttValue( Target^.NA , NAG_GearOps , NAS_Material ) = NAV_Meat ) then begin MOS := MOS + 1; end; end; { If the weapon has the ARMORPIERCING attribute, its MOS will always be at least 2. } if HasAttackAttribute( AtDesc , AA_ArmorPiercing ) then begin if MOS < 2 then MOS := 2 else MOS := MOS + 1; end; { If dealing with an energy weapon, MOS has a minimum value. } if ( ER.Weapon <> Nil ) and ( ER.Weapon^.G = GG_Weapon ) then begin if ER.Weapon^.S = GS_EMelee then begin if MOS < 2 then MOS := 2 else MOS := MOS + 1; end else if ( ER.Weapon^.S = GS_BeamGun ) and ( ER.Weapon^.Scale > 0 ) then begin if MOS < 1 then MOS := 1 else MOS := MOS + 1; end; end; { Modify MOS for the "HARD AS NAILS", "HULL DOWN" talents. } if ( TMaster^.G = GG_Character ) and HasTalent( TMaster , NAS_HardAsNails ) then MOS := MOS - 2; if (TMaster^.G = GG_Mecha) and HasTalent(TMaster,NAS_HullDown) and ((NAttValue(TMaster^.NA,NAG_Action,NAS_MoveMode)= MM_WALK) or (NAttValue(TMaster^.NA,NAG_Action,NAS_MoveMode)=MM_ROLL)) then MOS := MOS - 3; DR := DamageGear( GB , Target , ER.Weapon , ER.FXDice , MOS , NumberOfHits , AtDesc ); { Record the animation for this attack. } if DR.DamageDone < 1 then begin Add_Mek_Animation( GB , TMaster , GS_ArmorDefHit ); end else begin Add_Mek_Animation( GB , TMaster , GS_DamagingHit ); end; { Record the announcement about this attack. } if ER.AttackMessage <> '' then begin msg := ReplaceHash( ER.AttackMessage , PilotName( TMaster ) ); end else if NumberOfHits > 1 then begin msg := ReplaceHash( MsgString( '#ishit#timesfor#damage' ) , PilotName( TMaster ) ); msg := ReplaceHash( msg , BStr( NumberOfHits ) ); end else begin msg := ReplaceHash( MsgString( '#ishitfor#damage' ) , PilotName( TMaster ) ); end; msg := ReplaceHash( msg , BStr( DR.DamageDone ) ); DP := Destroyed_Parts_List; while ( DP <> Nil ) and ( Length( msg ) < Damage_List_Text_Length ) do begin msg := msg + ' ' + ReplaceHash( MsgString( '#destroyed' ) , DP^.Info ); DP := DP^.Next; end; if DR.MechaDestroyed then begin if Destroyed( TMaster ) then begin msg := msg + ' ' + ReplaceHash( MsgString( '#destroyed!' ) , GearName( TMaster ) ); end else begin msg := msg + ' ' + ReplaceHash( MsgString( '#disabled!' ) , GearName( TMaster ) ); end; end; if DR.PilotDied then msg := msg + ' ' + ReplaceHash( MsgString( '#died' ) , GearName( TPilot ) ) else if DR.EjectOK then msg := msg + ' ' + ReplaceHash( MsgString( '#ejected' ) , GearName( TPilot ) ); RecordAnnouncement( msg ); { If, at the beginning of this attack, the target was } { functioning, check to see if the attacker gets extra } { experience for taking the target out. } if ( ER.Originator <> Nil ) then begin if TMaster^.G = GG_Mecha then begin if DR.MechaDestroyed then DoleExperience( ER.Originator , TMaster , XPA_DestroyMaster ); end else if DR.PilotDied then begin DoleExperience( ER.Originator , TMaster , XPA_DestroyMaster ); end else begin { Destroying a non-master gear only gives 1 XP. } if Destroyed( Target ) then DoleExperience( ER.Originator , XPA_DestroyThing ); end; end; PAG_CauseDamage := True; end else begin { The attack missed. } { Only report on the attack missing if this isn't a specially-named attack... } if ER.AttackMessage = '' then begin Add_Mek_Animation( GB , TMaster , DefRep.HiRollType ); msg := ReplaceHash( MsgString( 'AttackMissed_' + BStr( DefRep.HiRollType ) ) , PilotName( TMaster ) ); if msg = '' then msg := 'ERROR: Unknown Defense ' + BStr( DefRep.HiRollType ); RecordAnnouncement( msg ); end; PAG_CauseDamage := False; end; { Receiving an attack causes the target to take a morale check, whether the attack hit or not. } SetNAtt( TMaster^.NA , NAG_Action , NAS_MightGiveUp , 1 ); end; Function PAG_CauseStatusEffect( GB: GameBoardPtr; AtDesc: String; ER: EffectRequest; Target: GearPtr ): Boolean; { Cause a status effect against this target. } { PARAM 1 : Status Number } var SFX,AtRoll: Integer; DefRep: DefenseReport; begin { Extract the parameters. } SFX := ExtractValue( AtDesc ); { get the root of the target. } Target := FindRoot( Target ); { If the target is dead, no status effect changes are possible. } if not GearActive( Target ) then Exit( False ); if SX_Vunerability[ SFX , NAttValue( Target^.NA , NAG_GearOps , NAS_Material ) ] then begin { Make the skill roll. } AtRoll := RollStep( ( ER.FXDice ) div 2 + 5 ); { Make the defense roll. } DefRep := AttemptDefenses( GB , Target , ER.Weapon , AtRoll , AtDesc ); if ( DefRep.HiRoll < AtRoll ) then begin AddNAtt( Target^.NA , NAG_StatusEffect , SFX , 3 + Random( 4 ) ); RecordAnnouncement( ReplaceHash( MsgString( 'Status_Announce' + BStr( SFX ) ) , GearName( Target ) ) ); end; end; { No matter what happened here, keep processing effects. } PAG_CauseStatusEffect := True; end; Function PAG_RemoveStatusEffect( GB: GameBoardPtr; AtDesc: String; ER: EffectRequest; Target: GearPtr ): Boolean; { Remove a status effect from this target. } { PARAM 1 : Status Number } var SFX: Integer; Procedure AttemptStatusChange( Part: GearPtr ); { Attempt a status change for this part. If successful, record a message. } begin { It's only possible to get a status change from an existing, } { active gear. } if ( Part = Nil ) or not GearActive( Part ) then Exit; if NAttValue( Part^.NA , NAG_StatusEffect , SFX ) > 0 then begin SetNAtt( Part^.NA , NAG_StatusEffect , SFX , 0 ); RecordAnnouncement( ReplaceHash( MsgString( 'Status_Remove' ) , GearName( Target ) ) ); end; end; begin { Extract the parameters. } SFX := ExtractValue( AtDesc ); { get the root of the target. } Target := FindRoot( Target ); { Try to change its status. } AttemptStatusChange( Target ); { If the target is a mecha, try to change its pilot's status too. } if ( Target <> Nil ) and ( Target^.G = GG_Mecha ) then begin Target := LocatePilot( Target ); AttemptStatusChange( Target ); end; { No matter what happened here, keep processing effects. } PAG_RemoveStatusEffect := True; end; Function PAG_Overload( GB: GameBoardPtr; AtDesc: String; ER: EffectRequest; Target: GearPtr ): Boolean; { Apply overload to the target, unless it resists. } { PARAM 1 = Skill to use } var AtRoll: Integer; DefRep: DefenseReport; begin { Can only process this effect if we have a target and a valid } { status effect to process. } Target := FindRoot( Target ); if GearActive( Target ) and ( Target^.G = GG_Mecha ) then begin { Make the skill roll. } AtRoll := RollStep( ( ER.FXDice ) div 2 + 5 ); { Make the defense roll. } DefRep := AttemptDefenses( GB , Target , ER.Weapon , AtRoll , AtDesc ); if AtRoll > DefRep.HiRoll then begin AddNAtt( Target^.NA , NAG_Condition , NAS_PowerSpent , 10 + Random( 10 ) + ( ( AtRoll - DefRep.HiRoll ) div 2 ) ); RecordAnnouncement( ReplaceHash( MsgString( 'Status_Overload' ) , GearName( Target ) ) ); end else begin AddNAtt( Target^.NA , NAG_Condition , NAS_PowerSpent , Random( ER.FXDice ) + 2 ); end; end; PAG_Overload := True; end; Function PAG_Healing( GB: GameBoardPtr; AtDesc: String; ER: EffectRequest; Target: GearPtr ): Boolean; { Do healing on target. } { PARAM 1 = Healing Type } var RepSkill: Integer; RepairRoll,D0,D1: LongInt; msg: String; begin { Determine what repair skill to use. } RepSkill := ExtractValue( AtDesc ); { Can only process this effect if we have a target. } Target := FindRoot( Target ); if ( Target <> Nil ) then begin { Record how much repairable damage we started with... } D0 := TotalRepairableDamage( Target , RepSkill ); { Do some repairs. } RepairRoll := RollStep( ER.FXDice ); ApplyRepairPoints( Target , RepSkill , RepairRoll , False ); { Find out how much repairable damage we have now... } D1 := TotalRepairableDamage( Target , RepSkill ); { Record the announcement, if any healing done. } if ( D0 - D1 ) > 0 then begin msg := ReplaceHash( MsgString( 'Healing_Announce' ) , GearName( Target ) ); msg := ReplaceHash( msg , BStr( D0 - D1 ) ); RecordAnnouncement( msg ); end; end; PAG_Healing := True; end; Procedure DoEffectAgainstGear( GB: GameboardPtr; ER: EffectRequest; Target: GearPtr; AtOp: Integer ); { Perform all the bits of the provided effect request against Target. Store information } { on the process as needed. } var TheLine,Cmd: String; SA: SAttPtr; Continue: Boolean; begin StartNewAnnouncement; { The FX components of this effect are stored in the FXList. } { Go through the list and do whatever needs doing. } SA := ER.FXList; Continue := True; while ( SA <> Nil ) and Continue do begin TheLine := SA^.Info; cmd := UpCase( ExtractWord( TheLine ) ); if cmd = FX_CauseDamage then Continue := PAG_CauseDamage( GB , TheLine , ER , Target , AtOp ) else if cmd = FX_CauseStatusEffect then Continue := PAG_CauseStatusEffect( GB , TheLine , ER , Target ) else if cmd = FX_RemoveStatusEffect then Continue := PAG_RemoveStatusEffect( GB , TheLine , ER , Target ) else if cmd = FX_Overload then Continue := PAG_Overload( GB , TheLine , ER , Target ) else if cmd = FX_Healing then Continue := PAG_Healing( GB , TheLine , ER , Target ); SA := SA^.Next; end; end; { PROCESS EFFECTS VS TILE TARGETS } Procedure DestroyTerrain( GB: GameBoardPtr; X,Y: Integer ); { Destroy the terrain in this spot. Pretty simple actually. } var Smoke: GearPtr; begin { Start with an error check... } if not OnTheMap( GB , X , Y ) then Exit; { If this terrain has a DESTROYED type set, change the tile. } if TerrMan[ TileTerrain( GB,X,Y ) ].Destroyed <> 0 then SetTerrain( GB,X,Y, TerrMan[ TileTerrain( GB,X,Y ) ].Destroyed ); { If terrain is destroyed, it will probably cause smoke and maybe fire. } if Random( 2 ) = 1 then begin Smoke := LoadNewSTC( 'SMOKE-1' ); if Smoke <> Nil then begin Smoke^.Scale := GB^.Scale; AppendGear( GB^.Meks , Smoke ); Smoke^.Stat[ STAT_CloudDuration ] := RollStep( 5 ); SetNAtt( Smoke^.NA , NAG_Location , NAS_X , X ); SetNAtt( Smoke^.NA , NAG_Location , NAS_Y , Y ); SetNAtt( Smoke^.NA , NAG_EpisodeData , NAS_Temporary , 1 ); end; end; if ( Random( 3 ) = 1 ) and TerrMan[ TileTerrain( GB,X,Y ) ].Flammable and (( GB^.Scene = Nil ) or ( NAttValue( GB^.Scene^.NA , NAG_EnvironmentData , NAS_Atmosphere ) <> NAV_Vacuum )) then begin Smoke := LoadNewSTC( 'FIRE-1' ); if Smoke <> Nil then begin Smoke^.Scale := GB^.Scale; AppendGear( GB^.Meks , Smoke ); SetNAtt( Smoke^.NA , NAG_Location , NAS_X , X ); SetNAtt( Smoke^.NA , NAG_Location , NAS_Y , Y ); SetNAtt( Smoke^.NA , NAG_EpisodeData , NAS_Temporary , 1 ); end; SetTrigger( GB , 'FIRE!' ); end; end; Procedure SceneryChewing( GB: GameBoardPtr; ER: EffectRequest; X,Y: Integer; Accident: Boolean; AtAt: String ); { Tile X,Y has been hit. Try and damage it. } { Set ACCIDENT to TRUE if the tile is not the primary target of } { the attack, FALSE if it is. } var Terr: Integer; DC,Roll,R2: Integer; begin { Start with an error check... } if not OnTheMap( GB , X , Y ) then Exit; { Add an animation. } if not Accident then Add_Point_Animation( X , Y , TerrMan[ TileTerrain( GB , X , Y ) ].Altitude , GS_AreaAttack ); { See if the weapon is big enough to damage terrain. } DC := ER.FXDice; if ( ER.Weapon <> Nil ) then begin { The weapon must be at least the same scale as the map. } if ER.Weapon^.Scale < GB^.Scale then Exit; end; if HasAttackAttribute( AtAt , AA_Brutal ) then begin DC := DC * 2; end; if HasAttackAttribute( AtAt , AA_BlastAttack ) then begin DC := DC + Random( DC + 1 ); end; { Modify for accidental damage. If not intentionally trying } { to cause terrain damage, it becomes far less likely to happen. } if ( DC > 0 ) and Accident then DC := Random( DC ); if DC > 0 then begin Roll := Random( DC ); { Modify for GateCrasher talent. } if ( ER.Originator <> Nil ) and HasTalent( ER.Originator , NAS_GateCrasher ) then begin R2 := Random( DC + 1 ); if R2 > Roll then Roll := R2; end; Terr := TileTerrain( GB,X,Y ); if ( Roll >= TerrMan[ Terr ].DMG ) and ( TerrMan[ Terr ].DMG > 0 ) then begin { Demolish the scenery... unless there's an error in the definitions. } if ( TerrMan[ terr ].Destroyed > 0 ) and ( TerrMan[ terr ].Destroyed <= NumTerr ) then begin DestroyTerrain( GB , X , Y ); end; end; end; end; Procedure ProcessCreateSTC( GB: GameBoardPtr; EF: EffectRequest; X , Y: Integer; TheLine: String ); { Create an item from the STC file in position X,Y. } var desig: String; item: GearPtr; Team: Integer; begin { Determine the designation of the item to load. } desig := ExtractWord( TheLine ); item := LoadNewSTC( desig ); { If ITEM is a master, better do some more work on it. } if IsMasterGear( item ) and ( EF.Originator <> Nil ) and ( EF.Weapon <> Nil ) then begin { Step one- decide on the team for our drones! } Team := NAttValue( FindRoot( EF.Originator )^.NA , NAG_Location , NAS_Team ); if ( Team = NAV_DefPlayerTeam ) or ( Team = NAV_LancemateTeam ) then begin Team := -1; end; Rescale( Item , EF.Weapon^.Scale ); SetNAtt( Item^.NA , NAG_Skill , 6 , EF.FXDice div 2 ); SetNAtt( Item^.NA , NAG_Skill , 10 , ( EF.FXDice + 1 ) div 2 ); SetNAtt( Item^.NA , NAG_Location , NAS_Team , Team ); GearUp( Item ); end else if ( EF.Weapon <> Nil ) and ( Item^.G = GG_MetaTerrain ) and ( Item^.S = GS_MetaCloud ) then begin Item^.Stat[ STAT_CloudDuration ] := EF.FXDice * 5; Item^.Scale := EF.Weapon^.Scale; end; SetNAtt( item^.NA , NAG_Location , NAS_X , X ); SetNAtt( item^.NA , NAG_Location , NAS_Y , Y ); SetNAtt( item^.NA , NAG_Location , NAS_D , Random( 8 ) ); SetNAtt( Item^.NA , NAG_EpisodeData , NAS_Temporary , 1 ); AppendGear( GB^.Meks , item ); SetNAtt( item^.NA , NAG_EpisodeData, NAS_UID, MaxIdTag( GB^.Meks , NAG_EpisodeData, NAS_UID ) + 1 ); end; Procedure DoEffectAgainstTile( GB: GameBoardPtr; ER: EffectRequest; X , Y: Integer; Accident: Boolean ); { Perform the required effect against this tile. } var TheLine,Cmd: String; SA: SAttPtr; begin { The FX components of this effect are stored in the FXList. } { Go through the list and do whatever needs doing. } SA := ER.FXList; while ( SA <> Nil ) do begin TheLine := SA^.Info; cmd := UpCase( ExtractWord( TheLine ) ); if cmd = FX_CauseDamage then SceneryChewing( GB , ER , X , Y , Accident , TheLine ) else if cmd = FX_CreateSTC then ProcessCreateSTC( GB , ER , X , Y , TheLine ); SA := SA^.Next; end; end; Procedure ProcessEffect( GB: GameBoardPtr; ER: EffectRequest; TargetList: GearPtr ); { An effect is taking place against a list of gears. } var T: GearPtr; Area: MapStencil; P: Point; X,Y: Integer; begin { Keep track of where targets have been affected. } ClearStencil( Area ); { Process the targets. } T := TargetList; while T <> Nil do begin DoEffectAgainstGear( GB , ER , T^.Parent , T^.V ); P := GearCurrentLocation( FindRoot( T^.Parent ) ); if OnTheMap( GB , P.X , P.Y ) then Area[ P.X , P.Y ] := True; T := T^.next; end; { Process the map tiles. } for X := 1 to GB^.Map_Width do begin for Y := 1 to GB^.Map_Height do begin if Area[ X , Y ] then begin DoEffectAgainstTile( GB , ER , X , Y , True ); end; end; end; end; Procedure ProcessEffect( GB: GameBoardPtr; ER: EffectRequest; Area: MapStencil; AtOp: Integer ); { An effect is taking place over an area. Do the effect against every model in } { the area, then against all the tiles as well. } var T: GearPtr; P: Point; X,Y: Integer; begin T := GB^.Meks; while T <> Nil do begin P := GearCurrentLocation( T ); if OnTheMap( GB , P.X , P.Y ) and Area[ P.X , P.Y ] then begin DoEffectAgainstGear( GB , ER , T , AtOp ); end; T := T^.Next; end; for X := 1 to GB^.Map_Width do begin for Y := 1 to GB^.Map_Height do begin if Area[ X , Y ] then begin DoEffectAgainstTile( GB , ER , X , Y , False ); end; end; end; end; Procedure InitEffectRequest( var ER: EffectRequest ); { Given a supposedly new effect request, initialize its fields to default values. } begin ER.FXList := Nil; ER.Originator := Nil; ER.Weapon := Nil; ER.AttackName := ''; ER.AttackMessage := ''; ER.FXDice := 0; ER.FXMod := 0; end; Procedure FinishEffectRequest( var ER: EffectRequest ); { Given an effect request, dispose of the attached lists. } begin DisposeSAtt( ER.FXList ); end; Procedure DrawBlastEffect( GB: GameBoardPtr; X0,Y0,Z0,RNG: Integer; var Stencil: MapStencil ); { Calculate all the squares targeted by a blast effect centered } { upon X0,Y0 with radius RNG. Store the results of the calculation } { in the STENCIL array. } { The stencil array must have been initialized by clearing previously; it's not } { done here in case multiple blast circles are to be drawn on the map. } const DBA_True = 1; DBA_False = -1; DBA_Maybe = 0; var temp: Array [ -Max_Blast_Rating..Max_Blast_Rating , -Max_Blast_Rating..Max_Blast_Rating ] of integer; x,y: Integer; Procedure CheckLine(XT,YT: Integer); var t: Integer; {A counter, and a terrain type.} Wall: Boolean; {Have we hit a wall yet?} p: Point; begin {Check every point on the line from the origin to XT,YT,} {recording the results in the Temp array.} { The variable WALL represents a boundary that cannot be } { blasted through. } Wall := false; for t := 1 to rng do begin {Locate the next point on the line.} p := SolveLine(0,0,XT,YT,t); {Determine the terrain of this tile.} if OnTheMap( GB , p.X + X0 , p.Y + Y0 ) then begin {If we have already encountered a wall, mark this square as UPV_False} if Wall then temp[p.x,p.y] := DBA_False; Case temp[p.x,p.y] of DBA_False: Break; {This LoS is blocked. No use searching any further.} DBA_Maybe: begin {We will mark this one as true, but check for a wall later.} temp[p.x,p.y] := DBA_True; end; {If we got a DBA_True, we just skip merrily along without doing anything.} end; {If this current square is a wall,} {or if we have too much obscurement to see,} {set Wall to true.} if TileBlocksLOS( GB , p.X + X0 , p.Y + Y0 , Z0 ) then Wall := True; end; end; end; begin { Start by updating the shadow map. This is needed for the } { TILEBLOCKSLOS function. } UpdateShadowMap( GB ); { Error check. } if not OnTheMap( GB , X0 , Y0 ) then exit; {Set every square in the temp array to Maybe.} for x := -Max_Blast_Rating to Max_Blast_Rating do begin for y := -Max_Blast_Rating to Max_Blast_Rating do begin temp[x,y] := DBA_Maybe; end; end; {Set the origin to True.} temp[0,0] := DBA_True; { If the origin blocks the blast, it will be the only tile } { affected. } if not TileBlocksLOS( GB , X0 , Y0 , Z0 ) then begin {Check the 4 cardinal directions} CheckLine( 0, rng ); CheckLine( 0, -rng ); CheckLine( rng, 0 ); CheckLine( -rng, 0 ); {Check the 4 diagonal directions} CheckLine(rng,rng); CheckLine(rng,-rng); CheckLine(-rng,rng); CheckLine(-rng,-rng); For X := -rng + 1 to -1 do begin Checkline(X,-rng); CheckLine(X,rng); end; For X := rng -1 downto 1 do begin Checkline(X,-rng); CheckLine(X,rng); end; For Y := -rng + 1 to -1 do begin Checkline(rng,Y); CheckLine(-rng,Y); end; For Y := rng - 1 downto 1 do begin CheckLine(rng,Y); CheckLine(-rng,Y); end; end; { Copy over the TEMP array into the STENCIL array. } for x := -Max_Blast_Rating to Max_Blast_Rating do begin for y := -Max_Blast_Rating to Max_Blast_Rating do begin if OnTheMap( GB , X0 + X , Y0 + Y ) and ( temp[ X , Y ] = DBA_True ) and ( Range( 0 , 0 , X , Y ) <= rng ) then begin Stencil[ X0 + X , Y0 + Y ] := True; end; end; end; end; Procedure Explosion( GB: GameBoardPtr; X0,Y0,DC,R: Integer ); { An explosion has been requested. Do it. } var ER: EffectRequest; Area: MapStencil; begin ClearAttackHistory; InitEffectRequest( ER ); ClearStencil( Area ); DrawBlastEffect( GB , X0 , Y0 , TerrMan[ TileTerrain( GB , X0 , Y0 ) ].Altitude , R , Area ); ER.FXDice := DC; StoreSAtt( ER.FXList , 'DAMAGE 10 0 0 0 BLAST 1' ); ProcessEffect( GB , ER , Area , 0); { Finalize any pending announcements. } FlushAnnouncements; { Get rid of any dynamic resources allocated. } FinishEffectRequest( ER ); end; Procedure ExperimentifyAttack( var ER: EffectRequest; var AtAt: String; var ATOp: Integer ); { This attack is going to get weird. An EXPERIMENTAL weapon is kind of like } { a wand of wonder- you never really know what's going to happen. One special } { effect will be applied to the attack- it may gain a blast radius, hit bonuses } { or penalties, do nothing but launch smoke... there's no way of telling. } var roll: Integer; begin roll := Random( 25 ) + 1; case Roll of 11: begin { Accuracy Bonus } ER.FXMod := ER.FXMod + 5; end; 12: begin { Accuracy Penalty } ER.FXMod := ER.FXMod - 10; end; 13: begin { Damage Boost } ER.FXDice := ER.FXDice * 2; end; 14: begin { Damage Thwack } ER.FXDice := 1; end; 15: begin { Blast Radius } AtAt := AtAt + ' BLAST ' + BStr( Random( 3 ) + 1 ); end; 16: begin { Weak Blast } ER.FXDice := ER.FXDice div 3 + 1; AtAt := AtAt + ' BLAST ' + BStr( Random( 6 ) + 1 ); end; 17: begin { Smoke } AtAt := ATAt + ' SMOKE BLAST ' + BStr( Random( 4 ) + 1 ); end; 18: begin { A Little Smoke } AtAt := ATAt + ' SMOKE'; end; 19: begin { Hyper } AtAt := ATAt + ' HYPER'; ER.FXDice := ER.FXDice div 2 + 1; ER.FXMod := ER.FXMod - 5; end; 20: begin { Haywire } AtAt := ATAt + ' HAYWIRE'; end; 21,22: begin { Burn } AtAt := ATAt + ' BURN'; end; 23: begin { Nonlethal } AtAt := ATAt + ' NONLETHAL'; end; 24: begin { Weak Disintegrate } ER.FXDice := 1; ER.FXMod := ER.FXMod - 5; AtAt := AtAt + ' DISINTEGRATE'; end; 25: begin { Stun } AtAt := ATAt + ' STUN'; end; end; end; Procedure FunkyMartialArts( var ER: EffectRequest; var AtAt: String; var ATOp: Integer ); { This attack may well get some special bonuses. } const NumFMABase = 10; Num_Funky_Things = 14; FT_Cost: Array [1..Num_Funky_Things] of Byte = ( 3, 3, 4, 8, 4, 4, 4, 3, 1, 3, 1, 2, 1, 5 ); FT_AA: Array [1..Num_Funky_Things] of String[15] = ( '','','SCATTER','HYPER','ARMORPIERCING', 'BRUTAL','STONE','HAYWIRE','STUN','FLAIL', '', '', '','BURN' ); FT_Heroic = 1; FT_Zen = 2; FT_1000Blows = 3; FT_Hyper = 4; FT_ArmorPiercing = 5; FT_Brutal = 6; FT_Stoning = 7; FT_Haywire = 8; FT_Stunning = 9; FT_Snake = 10; FT_Tragic = 11; FT_Passion = 12; FT_Accurate = 13; FT_Burn = 14; var SkRk,TP: Integer; Adjective,Noun: SAttPtr; msg,C: String; FTTaken: Array [1..Num_Funky_Things] of Boolean; Function CanGetFunkyThing( N: Integer ): Boolean; { Return TRUE if the attacker can do this funky thing, based on } { Technique Points and whatever else, or FALSE if he can't. } begin if FTTaken[N] then begin CanGetFunkyThing := False; end else if FT_Cost[ N ] <= TP then begin case N of FT_Heroic: CanGetFunkyThing := NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Heroic ) > 10; FT_Zen: CanGetFunkyThing := NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Pragmatic ) < -10; FT_Tragic: CanGetFunkyThing := NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Cheerful ) < -10; FT_Passion: CanGetFunkyThing := NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Easygoing ) < -10; else CanGetFunkyThing := True; end; end else begin { If not enough points, can't do this thing. } CanGetFunkyThing := False; end; end; Procedure ApplyFunkyThing( N: Integer ); { Apply the funky thing to the attack request; reduce the total number } { of technique points; store a noun and an adjective to describe this } { attack. } var trait: Integer; begin TP := TP - FT_Cost[ N ]; AtAt := AtAt + ' ' + FT_AA[ N ]; if Random( 2 ) = 1 then StoreSAtt( Adjective , MsgString( 'FMAFT_A' + BStr( N ) ) ) else StoreSAtt( Noun , MsgString( 'FMAFT_N' + BStr( N ) ) ); FTTaken[ N ] := True; { Add bonuses for special things here. } if N = FT_Heroic then begin { A heroic attack increases damage done based on the character's heroism. } ER.FXDice := ER.FXDice + ( NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Heroic ) div 5 ); end else if N = FT_Zen then begin { A zen attack increases damage based on the character's spirituality. } ER.FXMod := ER.FXMod + ( Abs( NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Pragmatic ) ) div 10 ); end else if N = FT_Tragic then begin { A tragic attack increases damage+accuracy based on the character's melancholy. } trait := Abs( NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Cheerful ) ); ER.FXDice := ER.FXDice + trait div 20; ER.FXMod := ER.FXMod + ( trait + 10 ) div 15; end else if N = FT_Passion then begin { A passionate attack increases damage, decreases accuracy. } ER.FXDice := ER.FXDice + ( Abs( NAttValue( ER.Originator^.NA , NAG_CharDescription , NAS_Easygoing ) ) div 5 ) + 3; ER.FXMod := ER.FXMod - 5; end else if N = FT_Accurate then begin ER.FXMod := ER.FXMod + 1 end; end; begin { First, make sure we have an originator, and that it knows kung fu. } if ( ER.Originator = Nil ) or ( ER.Originator^.G <> GG_Character ) or ( not HasTalent( ER.Originator , NAS_KungFu ) ) then begin AtOp := 0; exit; end; { The attacker must have a martial arts skill of at least 5 to benefit. } SkRk := NAttValue( ER.Originator^.NA , NAG_Skill , NAS_CloseCombat ) - 4; if SkRk < 1 then begin if NAttValue( ER.Originator^.NA , NAG_GearOps , NAS_Material ) = NAV_Meat then AtOp := -1; Exit; end; { Initialize the technique array. } for TP := 1 to Num_Funky_Things do FTTaken[TP] := False; { TP is Technique Points. } TP := SkRk + Random( 3 ); { If any technique points were gained, put them to good use here. } { Technique points can buy attack improvements: attack attributes, various bonuses, } { status effects... } if TP > 0 then begin { Initialize the variables needed for our attack name generator. } Adjective := Nil; Noun := Nil; if ( ER.Weapon^.S = GS_Arm ) and ( Random( 5 ) <> 1 ) then begin Msg := MsgString( 'FMA_Name_Punch_' + BStr( Random( NumFMABase ) + 1 ) ); end else if ( ER.Weapon^.S = GS_Leg ) and ( Random( 5 ) <> 1 ) then begin Msg := MsgString( 'FMA_Name_Kick_' + BStr( Random( NumFMABase ) + 1 ) ); end else begin Msg := MsgString( 'FMA_Name_Misc_' + BStr( Random( NumFMABase ) + 1 ) ); end; while TP > 0 do begin SkRk := Random( Num_Funky_Things ) + 1; if CanGetFunkyThing( SkRk ) then begin ApplyFunkyThing( SkRk ); end else if Random( 3 ) <> 1 then begin { If the thing chosen can't be gotten, just give a bonus } { to damage. } Inc( ER.FXDice ); Dec( TP ); end; end; ER.AttackName := ''; while msg <> '' do begin C := ExtractWord( msg ); if C = '%A' then begin if Adjective <> Nil then begin ER.AttackName := ER.AttackName + ' ' + SelectRandomSAtt( Adjective )^.Info; end else begin ER.AttackName := ER.AttackName + ' ' + MsgString( 'FMAFT_MISCA' + BStr( Random( 5 ) + 1 ) ); end; end else if C = '%N' then begin if Noun <> Nil then begin ER.AttackName := ER.AttackName + ' ' + SelectRandomSAtt( Noun )^.Info; end else begin ER.AttackName := ER.AttackName + ' ' + MsgString( 'FMAFT_MISCN' + BStr( Random( 5 ) + 1 ) ); end; end else begin ER.AttackName := ER.AttackName + ' ' + C; end; end; DisposeSAtt( Adjective ); DisposeSAtt( Noun ); end; end; Procedure AddNonDamagingEffects( var AtAt: String; var ER: EffectRequest ); { Add status effects and other non-damaging effects to this effect request. } { ResistSkill is the skill number recorded in status effect checks; for attacks, this should } { be the Electronic Warfare skill, but for non-attack effects it should be a straight } { resistance target. } var msg: String; T: Integer; begin if AStringHasBString( AtAt , AA_Name[ AA_Overload ] ) then begin msg := FX_Overload + ' ' + FX_CANRESIST; StoreSAtt( ER.FXList , msg ); end; if AStringHasBString( AtAt , AA_Name[ AA_Smoke ] ) then begin msg := FX_CreateSTC + ' SMOKE-1'; StoreSAtt( ER.FXList , msg ); end; if AStringHasBString( AtAt , AA_Name[ AA_Gas ] ) then begin msg := FX_CreateSTC + ' GAS-1'; StoreSAtt( ER.FXList , msg ); end; if AStringHasBString( AtAt , AA_Name[ AA_Drone ] ) then begin msg := FX_CreateSTC + ' DRONE-1'; StoreSAtt( ER.FXList , msg ); end; { Add status effects here. } for t := 1 to Num_Status_FX do begin if AStringHasBString( AtAt , SX_Name[ T ] ) then begin { All status effects are done using EW skill now. } msg := FX_CauseStatusEffect + ' ' + BStr( T ) + ' ' + FX_CANRESIST; StoreSAtt( ER.FXList , msg ); end; end; end; Function BuildAttackRequest( GB: GameBoardPtr; Attacker: GearPtr; AtOp,X,Y: Integer; var AtAt: String ): EffectRequest; { Create the effect request for this particular attack. } var ER: EffectRequest; msg: String; begin InitEffectRequest( ER ); ER.FXDice := WeaponDC( Attacker ); ER.Weapon := Attacker; ER.Originator := FindRoot( Attacker ); { Modify for power loss. } if ( EnergyPoints( ER.Originator ) < EnergyCost( Attacker ) ) and ( EnergyCost( Attacker ) > 0 ) then begin ER.FXMod := -5; ER.FXDice := ER.FXDice div 2; if ER.FXDice < 1 then ER.FXDice := 1; ER.AttackName := ReplaceHash( MsgString( 'LOW_POWER_ATTACK' ) , GearName( Attacker ) ); end; { Modify the AtOp for close combat attacks if NONLETHAL is turned on. } if ( Attacker^.G = GG_Module ) and ( NAttValue( ER.Originator^.NA , NAG_Prefrences , NAS_UseNonLethalAttacks ) <> 0 ) then AtOp := AtOp_NonLethal; if ( Attacker^.G = GG_Module ) and ( ER.Originator^.G = GG_Character ) and ( AtOp = 0 ) then FunkyMartialArts( ER , AtAt , AtOp ); if HasAttackAttribute( AtAt , AA_Experimental ) then ExperimentifyAttack( ER , AtAt , AtOp ); { If the weapon must be thrown, make a note of that here. } if MustBeThrown( GB , FindRoot( Attacker ) , Attacker , X , Y ) then begin AtAt := AtAt + ' WasThrown'; end; if not NonDamagingAttack( AtAt ) then begin { This is not a NonDamaging effect. So, the first command in the } { effect queue should be a damage effect. } { PARAM 1 = Attack Skill } msg := FX_CauseDamage + ' ' + BStr( AttackSkillNeeded( Attacker ) ) + ' ' + BStr( AttackStatNeeded( Attacker ) ); { PARAM 3 = Critical Hit Skill, 0 for none } if NoCalledShots( AtAt , AtOp ) then msg := msg + ' 0 0' else msg := msg + ' ' + BStr( NAS_SpotWeakness ) + ' ' + BStr( STAT_Craft ); { All attacks can be dodged. } msg := msg + ' ' + FX_CanDodge; { Close combat attacks can be parried. Ranged attacks can be ECM'd. } if ( Attacker^.G = GG_Module ) or ( Attacker^.S = GS_Melee ) or ( Attacker^.S = GS_EMelee ) then begin { The one exception is flailing weapons, which can't be parried. } if not HasAttackAttribute( AtAt , AA_Flail ) then msg := msg + ' ' + FX_CanParry; end else begin msg := msg + ' ' + FX_CanECM; end; { Missiles can be intercepted. } if ( Attacker^.G = GG_Weapon ) and ( Attacker^.S = GS_Missile ) then msg := msg + ' ' + FX_CanIntercept; { If no blast radius, the attack can be blocked. } if not HasAreaEffect( AtAt ) then msg := msg + ' ' + FX_CanBlock; { The attack attributes are added to the end of the string. } msg := msg + ' ' + AtAt; { If this is a nonlethal attack, add the NONLETHAL attack attribute. } if atop = AtOp_NonLethal then msg := msg + ' ' + AA_Name[ AA_NonLethal ]; StoreSAtt( ER.FXList , msg ); end; { Add the extra effects here. } AddNonDamagingEffects( AtAt , ER ); BuildAttackRequest := ER; end; Procedure PostAttackCleanup( GB: GameBoardPtr; Attacker: GearPtr; TX,TY,TZ: Integer ); { Deal with whatever needs to be dealt with. } var Master: GearPtr; P: Point; begin Master := FindRoot( Attacker ); P.X := TX; P.Y := TY; { Spend power points here. } if ( Attacker^.G = GG_Weapon ) and ( ( Attacker^.S = GS_EMelee ) or ( Attacker^.S = GS_BeamGun ) ) then begin SpendEnergy( Master , EnergyCost( Attacker ) ); end; if HasAttackAttribute( WeaponAttackAttributes( Attacker ) , AA_STRAIN ) and ( Master <> Nil ) then AddStaminaDown( Master , 10 ); if HasAttackAttribute( WeaponAttackAttributes( Attacker ) , AA_COMPLEX ) and ( Master <> Nil ) then AddMentalDown( Master , 10 ); { If the weapon was thrown, deal with that here. } if MustBeThrown( GB , Master , Attacker , P.X , P.Y ) then begin if Attacker^.G = GG_Ammo then begin { Lower the ammo count for grenades, deleting if nessecary. } AddNAtt( Attacker^.NA , NAG_WeaponModifier , NAS_AmmoSpent , 1 ); if ( Attacker^.Stat[STAT_AmmoPresent] - NAttValue( Attacker^.NA , NAG_WeaponModifier , NAS_AmmoSpent ) ) < 1 then begin if IsInvCom( Attacker ) then begin RemoveGear( Attacker^.Parent^.InvCom , Attacker ); end else if IsSubCom( Attacker ) then begin RemoveGear( Attacker^.Parent^.SubCom , Attacker ); end; end; end else if not HasAttackAttribute( WeaponAttackAttributes( Attacker ) , AA_Returning ) then begin if IsInvCom( Attacker ) then begin DelinkGear( Attacker^.Parent^.InvCom , Attacker ); AppendGear( GB^.Meks , Attacker ); SetNAtt( Attacker^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( Attacker^.NA , NAG_Location , NAS_Y , P.Y ); SetNAtt( Attacker^.NA , NAG_Location , NAS_Team , NAttValue( Master^.NA , NAG_Location , NAS_Team ) ); end else if IsSubCom( Attacker ) then begin DelinkGear( Attacker^.Parent^.SubCom , Attacker ); AppendGear( GB^.Meks , Attacker ); SetNAtt( Attacker^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( Attacker^.NA , NAG_Location , NAS_Y , P.Y ); SetNAtt( Attacker^.NA , NAG_Location , NAS_Team , NAttValue( Master^.NA , NAG_Location , NAS_Team ) ); end; end else begin Inc( EFFECTS_Event_Order ); Add_Shot_Precisely( GB , TX , TY , TZ , NAttValue( Master^.NA , NAG_Location , NAS_X ) , NAttValue( Master^.NA , NAG_Location , NAS_Y ) , MekALtitude( GB , Master ) ); end; end; end; Function SwarmRadius( GB: GameBoardPtr; Attacker: GearPtr ): Integer; { Return the radius at which this weapon swarms. } begin if Attacker^.Scale < GB^.Scale then begin SwarmRadius := 2; end else if Attacker^.Scale > GB^.Scale then begin SwarmRadius := 10; end else begin SwarmRadius := 5; end; end; Function MekIsTargetInRadius( GB: GameBoardPtr; Mek,Attacker,Weapon,Spotter: GearPtr; X,Y,R: Integer ): Boolean; { Used by the NumTargetsInRadius and FindTargetInRadius functions. } { Returns TRUE is Mek is an enemy of ATTACKER, is visible by } { SPOTTER, and is within the prescribed screen area. } begin Spotter := FindRoot( SPotter ); MekIsTargetInRadius := AreEnemies( GB , Attacker , Mek ) and MekCanSeeTarget( GB , Spotter , Mek ) and RangeArcCheck( GB , Attacker , Weapon , Mek ) and ( Range( Mek , X , Y ) <= R ) and GearOperational( Mek ); end; Function NumTargetsInRadius( GB: GameBoardPtr; Attacker,Weapon,Spotter: GearPtr; X,Y,R: Integer ): Integer; { Determine the number of targets within the radius which can be } { seen by SPOTTER and are enemies of ATTACKER. } var N: Integer; M: GearPtr; begin N := 0; M := GB^.Meks; while M <> Nil do begin if MekIsTargetInRadius( GB, M, Attacker, Weapon, Spotter, X, Y, R ) then Inc( N ); M := M^.Next; end; NumTargetsInRadius := N; end; Function GenerateTargetList( GB: GameBoardPtr; Attacker,Target: GearPtr; X,Y,Z,AtOp: Integer; const AtAt: String; AddAnims: Boolean ): GearPtr; { Generate a target list for this attack. } { This procedure will also make the shot animations. } var TarList: GearPtr; Procedure AddTargetToList( T: GearPtr; NumShots: Integer ); var it: GearPtr; begin it := AddGear( TarList , Nil ); it^.Parent := T; it^.V := NumShots; if AddAnims then Add_Shot_Animation( GB , FindRoot( Attacker ) , FindRoot( T ) ); end; Procedure CreateSwarmTargetList; { Create a list of targets to be affected by this swarm attack. } var Mek: GearPtr; r,n,T,AtOp2: Integer; begin R := SwarmRadius( GB , Attacker ); N := NumTargetsInRadius( GB , FindRoot( Attacker ) , Attacker , FindRoot( Attacker ) , X , Y , R ); if N > 0 then begin Mek := GB^.Meks; T := 1; while Mek <> Nil do begin AtOp2 := ( AtOp + 1 ) div N - 1; if T <= ( ( AtOp + 1 ) mod N ) then Inc( AtOp2 ); if MekIsTargetInRadius( GB, Mek, FindRoot( Attacker ) , Attacker, FindRoot( Attacker ), X, Y, R ) and ( AtOp2 >= 0 ) then begin AddTargetToList( Mek , AtOp2 ); Inc( T ); end; mek := Mek^.Next; end; end; end; begin if NoCalledShots( AtAt , AtOp ) then Target := FindRoot( Target ); TarList := Nil; if ( AtOp > 0 ) and HasAttackAttribute( AtAt , AA_SwarmAttack ) then begin CreateSwarmTargetList; end else if ( Target <> Nil ) then begin AddTargetToList( Target , AtOp ); end; Inc( Effects_Event_Order ); GenerateTargetList := TarList; end; Function GenerateAttackTemplate( GB: GameBoardPtr; Attacker: GearPtr; var X , Y , Z , AtOp: Integer; const AtAt: String ): MapStencil; { ATTACKER is a weapon supposedly with a blast radius. } { See where it goes. } { This procedure will also make the shot animations. } var Radius,X0,Y0,Z0: Integer; Stencil: MapStencil; Procedure PlaceBlastSpot( BX,BY,BZ: Integer ); { A blast is gonna take place here. } { Draw the shot animation and roll for deviation. } var Rng: Integer; begin Rng := Range( X0, Y0, BX , BY ); { Make a skill roll to see if the blast will deviate slightly. } if ( Radius > 0 ) and ( Rng > RollStep( SkillValue( FindRoot( Attacker ) , AttackSkillNeeded( Attacker ) , AttackStatNeeded( Attacker ) ) ) ) then begin BX := BX + Random( Radius div 3 + 2 ) - Random( Radius div 3 + 2 ); BX := BX + Random( Radius div 3 + 2 ) - Random( Radius div 3 + 2 ); end; Add_Shot_Precisely( GB , X0 , Y0 , Z0 , BX , BY , BZ ); if Radius > 0 then begin DrawBlastEffect( GB , BX , BY , BZ , Radius , Stencil ); end else if OnTheMap( GB , BX , BY ) then begin Stencil[ BX , BY ] := True; end; end; Procedure PlaceMultipleBlasts( Radius: Integer ); { While AtOp > 0, place multiple blasts around the board. } var BX,BY: Integer; begin BX := X + Random( 2 ) - Random( 2 ); BY := Y + Random( 2 ) - Random( 2 ); while AtOp > 0 do begin PlaceBlastSpot( BX , BY , Z ); BX := X + Random( Radius + 1 ) - Random( Radius + 1 ); BY := Y + Random( Radius + 1 ) - Random( Radius + 1 ); Dec( AtOp ); end; end; Procedure DrawOneLine( X1,Y1,Z1,rng: Integer ); { Draw a line from X0,Y0,Z0 to X1,Y1,Z1. } var T: Integer; P: Point; begin T := 0; while ( T < rng ) do begin Inc( T ); P := SolveLine( X0 , Y0 , Z0 , X1 , Y1 , Z1 , T ); if OnTheMap( GB , P.X , P.Y ) then begin Stencil[ P.X , P.Y ] := True; if TileBlocksLOS( GB , P.X , P.Y , P.Z ) then T := rng; end; end; end; Procedure PlaceLineAttack; { Do the messy work involved in drawing the line attack. } var rng,t_rad: Integer; { Range of line attack, termination radius } t_stencil: MapStencil; TX,TY: Integer; begin rng := Range( X , Y , X0 , Y0 ); t_rad := rng div 3; if t_rad > 0 then begin ClearStencil( t_stencil ); DrawBlastEffect( GB , X , Y , Z , T_Rad , T_Stencil ); for tx := 1 to MaxMapWidth do begin for ty := 1 to MaxMapWidth do begin if OnTheMap( GB , TX , TY ) and T_Stencil[ TX , TY ] then DrawOneLine( TX , TY , Z , rng ); end; end; end else begin DrawOneLine( X,Y,Z,rng ); end; end; var TarList,Mek: GearPtr; P: Point; begin ClearStencil( Stencil ); Radius := BlastRadius( GB , ATtacker , AtAt ); X0 := NAttValue( FindRoot( Attacker )^.NA , NAG_Location , NAS_X ); Y0 := NAttValue( FindRoot( Attacker )^.NA , NAG_Location , NAS_Y ); Z0 := MekAltitude( GB , FindRoot( Attacker ) ); if HasAttackAttribute( AtAt , AA_LineAttack ) then begin { Draw a line from the originator to the target. } PlaceLineAttack; end else if HasAttackAttribute( AtAt , AA_SwarmAttack ) and ( AtOp > 0 ) then begin TarList := GenerateTargetList( GB , Attacker , Nil , X , Y , Z , AtOp , AtAt , False ); if TarList <> Nil then begin Mek := TarList; P := GearCurrentLocation( Mek^.Parent ); PlaceBlastSpot( P.X , P.Y , MekAltitude( GB , Mek^.Parent ) ); RemoveGear( TarList , Mek ); Dec( AtOp ); end else begin PlaceBlastSpot( X , Y , Z ); end; if AtOp > 0 then PlaceMultipleBlasts( Radius ); end else if ( Radius > 0 ) and ( AtOp > 0 ) then begin PlaceMultipleBlasts( Radius ); end else begin PlaceBlastSpot( X , Y , Z ); end; Inc( Effects_Event_Order ); GenerateAttackTemplate := Stencil; end; Procedure GiveAwayPosition( GB: GameBoardPtr; Master: GearPtr ); { Firing weapons automatically gives away the firer's position. } var EMek: GearPtr; begin EMek := GB^.Meks; while EMek <> Nil do begin if AreEnemies( GB , EMek , Master ) and not MekCanSeeTarget( GB , EMek , Master ) then begin RevealMek( GB , Master , EMek ); end; EMek := Emek^.Next; end; end; Procedure DoAttack( GB: GameBoardPtr; Attacker,Target: GearPtr; X,Y,Z,AtOp: Integer); { ATTACKER is a weapon. TARGET is a target. X,Y,Z are map coordinates in case } { the target=Nil. } var ER: EffectRequest; AtAt,msg: String; Stencil: MapStencil; TarList,Master: GearPtr; begin { Clear the attack history and build the effect request. } ClearAttackHistory; AtAt := WeaponAttackAttributes( Attacker ); ER := BuildAttackRequest( GB , Attacker , AtOp , X , Y , AtAt ); { Add a divider to the skill roll history. } SkillCommentDivider; { Now that we have the effect request, see if the Originator is on the PC's team. } { If so, better throw a PCATTACK trigger. } if ( ER.Originator <> Nil ) and ( NAttValue( ER.Originator^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then SetTrigger( GB , TRIGGER_PCAttack ); { Either generate a template for the attack or generate the list of targets. } { Also, verify that the targets are legal. If NOCALLEDSHOTS applies, take the } { root level parent of the target. } if ClearAttack( GB , Attacker , AtOp ) then begin if ( Attacker^.G = GG_Module ) or ( Attacker^.S = GS_Melee ) or ( Attacker^.S = GS_EMelee ) then begin msg := MsgString( 'XattackswithY' ); end else begin msg := MsgString( 'XfiresY' ); end; msg := ReplaceHash( msg , PilotName( FindRoot( Attacker ) ) ); if ER.AttackName <> '' then begin msg := ReplaceHash( msg , ER.AttackName ); end else begin msg := ReplaceHash( msg , GearName( Attacker ) ); end; RecordAnnouncement( msg ); StartNewAnnouncement; { Error check- make sure X,Y,Z refer to the correct spots. } if Target <> Nil then begin X := NAttValue( FindRoot( Target )^.NA , NAG_LOcation , NAS_X ); Y := NAttValue( FindRoot( Target )^.NA , NAG_LOcation , NAS_Y ); Z := MekAltitude( GB , FindRoot( Target ) ); end; { If this weapon has an area effect, generate the stencil here. } { Otherwise, generate the list of targets. } if HasAreaEffect( AtAt ) then begin Stencil := GenerateAttackTemplate( GB , Attacker , X , Y , Z , AtOp , AtAt ); ProcessEffect( GB , ER , Stencil , AtOp ); end else begin TarList := GenerateTargetList( GB , Attacker , Target , X , Y , Z , AtOp , AtAt , True ); if TarList = Nil then begin Stencil := GenerateAttackTemplate( GB , Attacker , X , Y , Z , AtOp , AtAt ); ProcessEffect( GB , ER , Stencil , AtOp ); end else begin ProcessEffect( GB , ER , TarList ); DisposeGear( TarList ); end; end; { Do the side effects of the attack: set calltime, reveal the } { attacker's position, and update the reactions of other teams. } Master := FindRoot( Attacker ); if Master <> Nil then begin { Set the calltime for the next attack. } SetNAtt( Master^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ReactionTime( Master ) ); GiveAwayPosition( GB , Master ); { Update the alleigances of everyone involved. } if Target <> Nil then DeclarationOfHostilities( GB , NAttValue( Master^.NA , NAG_Location , NAS_Team ) , NAttValue( FindRoot( Target )^.NA , NAG_Location , NAS_Team ) ); end; { Perform cleanup duties. } PostAttackCleanup( GB , Attacker , X , Y , Z ); end; { Finalize any pending announcements. } FlushAnnouncements; { Get rid of any dynamic resources allocated. } FinishEffectRequest( ER ); end; Procedure DoCharge( GB: GameBoardPtr; Attacker,Target: GearPtr ); { ATTACKER is charging TARGET. Do the math. } { Both ATTACKER and TARGET are root level gears of SF:1 or larger. } Function ChargeDCBonus( Master: GearPtr ): Integer; { Certain mecha get a bonus to charge attack damage. Calculate } { that here. } var CDCB: Integer; HeavyActuator: Integer; begin CDCB := 0; if Master^.G = GG_Mecha then begin { May also get a bonus from heavy Actuator. } HeavyActuator := CountActivePoints( Master , GG_MoveSys , GS_HeavyActuator ); if HeavyActuator > 0 then CDCB := CDCB + ( HeavyActuator div Master^.V ); { Zoanoids get a CC damage bonus. Apply that here. } if Master^.S = GS_Zoanoid then begin CDCB := CDCB + ZoaDmgBonus; end; end; ChargeDCBonus := CDCB; end; Function ChargeDC( Master: GearPtr ): Integer; { Return the DC for this charge. } begin ChargeDC := ( GearMass( Master ) div 8 ) + ChargeDCBonus( Master ) + ( NAttValue( Master^.NA , NAG_Action , NAS_ChargeSpeed ) div 30 ); end; var ER: EffectRequest; FXScript,Msg: String; begin ClearAttackHistory; InitEffectRequest( ER ); ER.FXDice := ChargeDC( Attacker ); ER.Originator := Attacker; ER.Weapon := Attacker; ER.FXMod := 2; FXScript := '2 3 0 0 SCATTER ' + FX_CanDodge; { Add a divider to the skill roll history. } SkillCommentDivider; { If the Originator is on the PC's team, better throw a PCATTACK trigger. } if NAttValue( Attacker^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam then SetTrigger( GB , TRIGGER_PCAttack ); { Record the charge announcement. } msg := MsgString( 'XchargesY' ); msg := ReplaceHash( msg , PilotName( Attacker ) ); msg := ReplaceHash( msg , GearName( Target ) ); RecordAnnouncement( msg ); StartNewAnnouncement; { If this attack hits, do a countercharge. } if PAG_CauseDamage( GB , FXScript , ER , Target , 0 ) then begin PrepAction( GB , Target , NAV_Stop ); FlushAnnouncements; ER.FXDice := ChargeDC( Target ) div 3 + 1; ER.Originator := Target; ER.Weapon := Target; ER.FXMod := 0; FXScript := '2 3 0 0 SCATTER ' + FX_CanDodge + ' ' + FX_CanBlock; PAG_CauseDamage( GB , FXScript , ER , Attacker , 0 ) end; { Declare hostilities. } DeclarationOfHostilities( GB , NAttValue( Attacker^.NA , NAG_Location , NAS_Team ) , NAttValue( Target^.NA , NAG_Location , NAS_Team ) ); { Give away the charger's position. } GiveAwayPosition( GB , Attacker ); { Finalize any pending announcements. } FlushAnnouncements; { Get rid of any dynamic resources allocated. } FinishEffectRequest( ER ); end; Procedure DoReactorExplosion( GB: GameBoardPtr; Victim: GearPtr ); { Yay! This mecha is going to blow up. It might not actually be a mecha- maybe } { it's a radioactive rat or a barrel of explosive chemicals or something else. } const Standard_Reactor_Explosion = 'DAMAGE 15 0 0 0 BRUTAL BLAST 1 ' + FX_CanDodge; { Cause DAMAGE, Dodge 15 to defend, Brutal } var FX_String: String; ER: EffectRequest; Area: MapStencil; P: Point; R: Integer; msg: String; begin InitEffectRequest( ER ); { Determine the FX_String. This will tell us everything we need to know. } FX_String := SAttValue( Victim^.SA , SA_Explosion ); if FX_String = '' then begin { No custom string. This must be a regular reaction explosion. } FX_String := Standard_Reactor_Explosion; ER.FXDice := 5 + 3 * MasterSize( Victim ); end else begin { Custom string. Groovy. The first value should be the intensity of the effect. } ER.FXDice := ExtractValue( FX_String ); end; { Record the explosion announcement. } msg := SAttValue( Victim^.SA , 'EXPLOSION_DESC' ); if msg = '' then msg := MsgString( 'EXPLOSION_DESC' ); msg := ReplaceHash( msg , GearName( Victim ) ); RecordAnnouncement( msg ); StartNewAnnouncement; { Store the primary effect. } StoreSAtt( ER.FXList , FX_String ); { Determine the non-damaging effects; status FX and the like. } AddNonDamagingEffects( FX_String , ER ); { Determine the blast radius. This will always be at least 1. } R := BlastRadius( GB , Victim , FX_String ); if R < 1 then R := 1; { Draw the blast radius. } ClearStencil( Area ); P := GearCurrentLocation( Victim ); DrawBlastEffect( GB , P.X , P.Y , MekAltitude( GB , Victim ) , R , Area ); ProcessEffect( GB , ER , Area , 0); { Finalize any pending announcements. } FlushAnnouncements; { Get rid of any dynamic resources allocated. } FinishEffectRequest( ER ); end; Procedure HandleEffectString( GB: GameBoardPtr; Target: GearPtr; FX_String,FX_Desc: String ); { An effect string has been triggered. Better do whatever it says. } var ER: EffectRequest; begin { Clear the effect history and generate the effect request. } ClearAttackHistory; InitEffectRequest( ER ); ER.AttackMessage := FX_Desc; { Add a divider to the skill roll history. } SkillCommentDivider; ER.FXDice := ExtractValue( FX_String ); StoreSAtt( ER.FXList , FX_String ); { Add status effects here. } AddNonDamagingEffects( FX_String , ER ); DoEffectAgainstGear( GB , ER , Target , 0 ); { Finalize any pending announcements. } FlushAnnouncements; { Get rid of any dynamic resources allocated. } FinishEffectRequest( ER ); end; Procedure MassEffectString( GB: GameBoardPtr; FX_String,FX_Desc: String ); { Do an effect against every last model on the board. Wow. } var ER: EffectRequest; M: GearPtr; begin { Initialize the effect request. } ClearAttackHistory; InitEffectRequest( ER ); ER.AttackMessage := FX_Desc; { Add a divider to the skill roll history. } SkillCommentDivider; ER.FXDice := ExtractValue( FX_String ); StoreSAtt( ER.FXList , FX_String ); { Add status effects here. } AddNonDamagingEffects( FX_String , ER ); { Loop through all the models on the board, and apply the effect against them. } M := GB^.Meks; while M <> Nil do begin if GearActive( M ) and OnTheMap( GB , M ) then DoEffectAgainstGear( GB , ER , M , 0 ); M := M^.Next; end; { Finalize any pending announcements. } FlushAnnouncements; { Get rid of any dynamic resources allocated. } FinishEffectRequest( ER ); end; initialization { Set the history list to 0, for now. } ATTACK_History := Nil; EFFECTS_Event_Order := 0; finalization DisposeSAtt( ATTACK_History ); end. gearhead-2-0.701/fcolor.pas000066400000000000000000000036151321074026100154120ustar00rootroot00000000000000Program fcolor; { Show mecha in the colors for all factions. } { GearHead2, a roguelike mecha CRPG Copyright (C) 2005 Joseph Hewitt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The full text of the LGPL can be found in license.txt. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA } {$LONGSTRINGS ON} {$APPTYPE GUI} uses gears,gearparser,sdl,glgfx; const ZONE_All: TSDL_Rect = (x: 5 ; y:5; w:ScreenWidth - 10; h:ScreenHeight - 10 ); X_Size = 150; Y_Size = 80; Text_Offset = 64; var MyDest,TextDest: TSDL_Rect; F: GearPtr; S: SensibleSpritePtr; begin ClrScreen; InfoBox( ZONE_All ); MyDest.X := ZONE_All.X; MyDest.Y := ZONE_All.Y; TextDest.X := ZONE_All.X; TextDest.Y := ZONE_All.Y + Text_Offset; F := Factions_List; while F <> Nil do begin S := LocateSprite( 'btr_buruburu.png' , SAttValue( F^.SA , 'mecha_colors' ) , 64 , 64 ); DrawSprite( S , MyDest , 1 ); QuickText( SAttValue( F^.SA , 'name' ) , TextDest , StdWhite , Small_Font ); MyDest.X := MyDest.X + X_Size; if ( MyDest.X + X_Size ) > ( ZONE_All.X + ZONE_All.W ) then begin MyDest.X := ZONE_All.X; MyDest.Y := MyDest.Y + Y_Size; end; TextDest.Y := MyDest.Y + Text_Offset; TextDest.X := MyDest.X; F := F^.Next; end; DoFlip; MoreKey; end. gearhead-2-0.701/gamedata/000077500000000000000000000000001321074026100151575ustar00rootroot00000000000000gearhead-2-0.701/gamedata/CALIBER_default.txt000066400000000000000000000076461321074026100205020ustar00rootroot00000000000000%% %% CALIBER INFO %% %% This file contains descriptions for custom ammunition types. It's divided %% into sets based on caliber type. Each set will contain custom ammo stubs %% with different TYPE strings. %% Set name <5mm rifle> % Used all over for a wide variety of weapons sub Ammo name type Ammo name type Ammo name type Legality 5 Ammo name type Legality 10 Ammo name type Legality 20 Ammo name type Legality -15 end Set name <5mm auto> % Common in the L5 Region sub Ammo name type Ammo name type Ammo name type Legality 5 Ammo name type Legality -15 end Set name <12mm auto> % Used by miniguns sub Ammo name type Legality 25 Ammo name type Legality 20 Ammo name type end Set name <18mm shell> % Shotgun shells sub Ammo name Legality -15 Ammo name type Legality 15 Ammo name type Legality 10 Ammo name type Legality 20 Ammo name type Legality 35 end Set name <.32 revolver> % Primitive missile weapon sub Ammo name type Ammo name type Legality 10 Ammo name type Legality -15 end Set name % Used by the Glitter Pistol and Glitter Cannon in the L5 Region sub Ammo name type Legality 20 end Set name <2mm ferrous> % Used by man-pac railguns, like the Pin Rifle sub Ammo name type Legality 5 end Set name <6mm pistol> % Commonly used by handguns sub Ammo name type Ammo name type Legality -15 Ammo name type Ammo name type Legality 5 Ammo name type Legality 20 end Set name <22mm cone> % Cone Rifle sub Ammo name type Legality 10 Ammo name type Legality 50 end Set name <8mm self-propelled> %% L5Pat rocket rifles sub Ammo name type Legality 10 Ammo name type Legality -10 Ammo name type Legality 5 Ammo name type Legality 5 Ammo name type Legality 15 end Set name <20mm cannister> sub Ammo name type Legality -10 Ammo name type Legality 5 Ammo name type Legality 50 Ammo name type Legality 25 end Set name <60mm rocket> % Bazooka sub Ammo name type Ammo name type Legality 25 end Set name <20cm bolt> % Used in the Magna Crossbow and some other weapons sub Ammo name type Ammo name type Ammo name type Legality 5 Ammo name type Legality 10 Ammo name type Legality 15 Ammo name type Legality 20 Ammo name type Legality 25 end gearhead-2-0.701/gamedata/TC_1_1.txt000066400000000000000000000003701321074026100166660ustar00rootroot00000000000000% Heroic Chatter Items heroism helping people being nice volunteer work peace charity virtue honor compassion hope love songs ethical behavior recycling donating blood good movies superheroes altruism quests anonymous donations gearhead-2-0.701/gamedata/TC_1_2.txt000066400000000000000000000006321321074026100166700ustar00rootroot00000000000000% Villainous Trait Chatter Items villainy burning things kicking animals stealing candy from babies murder arson war death metal horror movies veal reality TV email spam vice evil massive destruction exploitation social darwinism trolling knives sadism cries of pain bloodlust maniacal laughter complex masterplans having henchmen gloating explaining my evil plans insider trading gearhead-2-0.701/gamedata/TC_2_1.txt000066400000000000000000000003661321074026100166740ustar00rootroot00000000000000% Lawful Chatter Items law rules fair play chess detective stories math honesty justice responsibility dependability tradition home cooking classical music safety higher education loyalty codes of honor regulations accounting gearhead-2-0.701/gamedata/TC_2_2.txt000066400000000000000000000005021321074026100166650ustar00rootroot00000000000000% Chaotic Chatter Items chaos surrealism punk rock fusion cuisine hackers freedom strangeness new things neoclassical music spontaneity free thought free software stealing tax evasion smuggling contraband anarchy lottery tickets dissent gambling dice games cats monkeys intrigue changing my mind gearhead-2-0.701/gamedata/TC_3_1.txt000066400000000000000000000004721321074026100166730ustar00rootroot00000000000000% Sociable Chatter Items meeting new people talking arguing socializing buffet restaurants joining clubs internet chatting flirting shaking hands writing letters writing email mobile phones the city parties dining out team sports relationships making friends long conversations gossip crowds gearhead-2-0.701/gamedata/TC_3_2.txt000066400000000000000000000003201321074026100166640ustar00rootroot00000000000000% Shy Chatter Items staying at home comic books computer games video games reading quiet keeping a diary being alone the countryside solitude privacy empty places peace and quiet walking alone gearhead-2-0.701/gamedata/TC_4_1.txt000066400000000000000000000004221321074026100166670ustar00rootroot00000000000000% Easygoing Chatter Items relaxing sleeping in comfortable shoes fast food pizza cola pop songs free time doing nothing pina colodas slow songs donuts bagel sandwiches ferns oatmeal beige pan pipes elevator music rocking chairs flower arranging bonsai gearhead-2-0.701/gamedata/TC_4_2.txt000066400000000000000000000006071321074026100166750ustar00rootroot00000000000000% Passionate Chatter Items excitement emotion spicy food passion bliss roller coasters challenge action movies tequila expresso dramatic movies romance exercise dancing waterfalls thunderstorms running wild animals bungee jumping extreme sports talking about politics talking about religion bright colors trumpets fireworks madness sex skydiving primal screams gearhead-2-0.701/gamedata/TC_5_1.txt000066400000000000000000000003241321074026100166710ustar00rootroot00000000000000% Cheerful Chatter Items happiness dance music funny movies ice cream kittens puppies stupid jokes jokes sunshine birthday cards fun smiling laughing giggling just being alive skipping optimism gearhead-2-0.701/gamedata/TC_5_2.txt000066400000000000000000000005441321074026100166760ustar00rootroot00000000000000% Melancholy Chatter Items sadness vampire movies gothic rock the color black graveyards abandoned buildings absinthe night time sarcasm rain long walks on the moors lost innocence irrevocable loss tragedy bittersweet memories forbidden love dark secrets anything Gothic frowning crying brooding rust and decay sad songs pessimism gearhead-2-0.701/gamedata/TC_6_1.txt000066400000000000000000000004061321074026100166730ustar00rootroot00000000000000% Renowned Chatter Items fame renown celebrity status respect expensive clothes glamour television radio internet news other people's opinions movies movie stars pop stars famous people beauty popularity contests seeing my name in the credits gearhead-2-0.701/gamedata/TC_6_2.txt000066400000000000000000000003331321074026100166730ustar00rootroot00000000000000% Wangtta Chatter Items obscurity being left along mediocrity being average animated movies other people's scorn underdogs apathy ignoring society unattractiveness stupidity nerds geeks outcasts mutants gearhead-2-0.701/gamedata/TC_7_1.txt000066400000000000000000000005241321074026100166750ustar00rootroot00000000000000% Pragmatic Chatter Items logic science evidence scifi movies healthy food skepticism determinism reductionism rational thought planning ahead astronomy cold hard facts big words nonfiction books biology physics chemistry sensible shoes vitamins studying repairing things myself documentaries bug fixes pockets gearhead-2-0.701/gamedata/TC_7_2.txt000066400000000000000000000005101321074026100166710ustar00rootroot00000000000000% Spiritual Chatter Items religion crystals incense meditation prayer faith superstition ritual chanting hypnotism fantasy movies organic food beads feng shui magic precognition fate astrology spirituality sandals aromatherapy contemplation so-called coincidences ignoring physics comparative religion gearhead-2-0.701/gamedata/adjectives.txt000066400000000000000000000016311321074026100200420ustar00rootroot00000000000000%%% adjectives for random conversation generator %%% frigid useless useful artificial adorable uncomfortable comfortable good bad open modern shiny bright honorable stupid smart healthy sinful interesting surprising bland sexy loud quiet new important wonderful great fun beautiful pretty ugly cool strange fast slow lucky big huge long small tiny exciting gigantic cosmic natural unwanted delicate stormy fragile strong flexible rigid cold hot irradiated poor living dead creamy delicious cool excellent boring happy sad confusing valuable old young loud hidden bouncy magnetic smelly hard easy serious kind gentle greedy lovely cute dangerous silly smart fresh obsolete perfect ideal professional current fat rich poor wise absurd foolish blind deaf creepy nice adequate expensive cheap fluffy rusted hormonal gearhead-2-0.701/gamedata/aslmacro.txt000066400000000000000000000521561321074026100175320ustar00rootroot00000000000000% ArenaScript Command Macros V= V+ L= L+ G= G+ P= P+ E= S= S+ N= rcs PCSick PCVerySick SetTeamFaction SetSelfFaction SetSelfTeam RandomMecha CustomMecha EraseRumor EraseSpecial ItemVar= ItemStat= ItemVar+ FacVar= AddFortune NPCVar= NPCVar+ SceneVar= SceneVar+ EncounterVar= SetEncounterActive SetEncounterInactive SetEntranceActive SetEntranceInactive SetSelfEncInactive IfSelfEncActive ifEntranceActive SetItemStolen SetItemFudge SetItemDesc SetEncounterName SetStat NPCSkill+ SelfSkill= PCSkill+ PCSTAT+ PCSTAT= NPCSTAT= NPCSTAT+ PCENEMY PCFENEMY PCALLY PCFAMILY PCFRIEND PCLOVER PCNORELATION REPUTATION FacXP+ FacXP= FacVar+ PCFacXP= PCFacLevel+ PCRewardLevel+ CASHPRIZE CASH+ ADDNPCREACT SAVEPOS SETPCFAC CLEARFACXP SETNPCFACTION SETSCENEFACTION SetNPCTeam SetItemTeam SetChatNPCTeam DELETENPC KILLNPC KILLPC DELETEITEM KILLMOOD KILLPLOTMOOD ANNIHILATEPLOTMOOD SETMOODTIMELIMIT DeleteSelf MOVENPC MOVEANDPACIFYNPC DEPLOYNPC DynaNPC NPCLevel SelfLevel ItemLevel % Can be used for monsters. ConGen % set NPC opposite gender of PC IsoGen % set NPC same gender as PC NPCConGen % set first NPC opposite gender of second NPC IsoAge DynaItem DynaItemTeam MOVEITEM FREEZENPC FREEZEITEM GIVEITEM IFSELFOK IFSELFDEAD IFNPCOK IFNPCDEAD IFNPCMALE IFNPCFEMALE IFPCOK IFPCSealed ifNPCinPlay ifChatNPCinPlay ifChatNPCisCombatant ifChatNPCnotCombatant IFM IFITEMOK IFItemDESTROYED IFSCENEEXISTS IFFACTIONEXISTS IFNPCSEXY IFNPCKNOWN IFNPCUNKNOWN IFCHATNPCSEXY IFCHATNPCKNOWN IFCHATNPCUNKNOWN IFNPCALLY IFCHATNPCALLY IFCHATNPCCANJOINLANCE IFFACTIONENEMY IFFACTIONALLY IFNPCARCHENEMY IFCHATNPCARCHENEMY IFCHATNPCNOTFAMILY ifVisible DYNAMIC DynaFaction DynaVar= ComposeD SetMapType SetMapString SOLOATTACK GSD GTargetPC GQuickShot MonsterUP MonsterSet MonsterOFF NPCgoPC UIDgoPC UIDSeekEdge NPCSeekEdge PCMoraleDmg SkillXP GiveSTC GiveSTCTreasure PCEat RunAway LocalRunAway SelfRunAway TeamAttack TeamAttackTeam % Note: Does not force reciprocity ifTeamHostile ifTeamNotHostile TeamAlly TeamNeutral MENTAL STAMINA OneVictimRecovered SetNPCRep NPCQuitLance ifPCMale ifPCFemale ifChatNPCMale ifChatNPCFemale ifChatNPCFriend ifChatNPCLover ifChatNPCFamily ifChatNPCNotNemesis ifNPCLancemate ifChatNPCLancemate ifNPCHeroic ifNPCVillainous ifNPCLawful ifNPCCriminal ifNPCSociable ifNPCShy ifNPCEasygoing ifNPCPassionate ifNPCCheerful ifNPCMelancholy ifNPCRenowned ifNPCWangtta ifNPCPragmatic ifNPCSpiritual ifPCHeroic ifPCVillainous ifPCLawful ifPCCriminal ifPCSociable ifPCShy ifPCEasygoing ifPCPassionate ifPCCheerful ifPCMelancholy ifPCRenowned ifPCWangtta ifPCPragmatic ifPCSpiritual AddHeroic AddLawful AddSociable AddEasygoing AddCheerful AddRenown AddPragmatic FacAlliance SetLMTactics GSetOptMax GSetOptMin Gut Flay StoryDL= StoryDL+ WinDramaticGoal CancelDramaticGoal WinEpisode PyrrhicWinEpisode % Win the episode, but without achieving goal LoseEpisode < GrabStory GAddNAtt -7 6 1 NextComp> IfDCCompleted IfDCNotCompleted IfFacReward IfAccessArmory IfNotAccessArmory GiveFacReward Alarm IfTeamCanSeePC IfNPCCanSeePC ifPCCanSeeLocal StoryNote IfMechaPiloting IfIntimidation IfUIntimidation IfConversation IfUConversation IfCodeBreaking IfCodeBreakingKn IfUCodeBreaking IfStealth IfStealthCH % Used for lying and disguises IfUStealth IfAwareness IfUAwareness IfInsight IfInsightKN % Used for obscure knowledge and trivia IfUInsight IfScience IfUScience IfMysticism IfUMysticism IfSurvival IfSurvivalKN % Used for theoretical rather than practical knowledge IfUSurvival IfRepair IfURepair IfTaunt IfUTaunt IfMedicine IfUMedicine IfShopping IfShoppingKN % Used for economic theory and business administration IfUShopping IfMartialArts IfToughness IfUToughness IfDancing SeekEntrance IfNPCSurrendered IfChatNPCSurrendered UnSurrender ReAttack ShowEncounter ShowEntrance ifEncounterVisible AddDatePoint SetQuestStatus SetPlotStatus CancelSubPlot WinSubPlot LoseSubPlot IfSubPlotWon IfSubPlotLost IfSubPlotCanceled IfSubPlotOver IfSubPlotNotWon AddTempLancemate ChatNPCJoinLance RemoveTempLancemate AddLancemate RemoveLancemate AddPartyPoint OpenInv IfSelfHasItem GiveArenaPay WinArenaMission LoseArenaMission WinCoreMission LoseCoreMission SetCoreMissionEnemy % For the arena mode core campaign AlterCoreMissionContext CommanderSays % The commander will give orders in Arena mode. HeadDebriefing % CommanderDebriefing % Note that these debriefings only MechanicDebriefing % work in arena mode, and only for the standard MedicDebriefing % faction NPCs. SupplyDebriefing % IntelDebriefing % SetDebriefing % A debriefing for core story plots Debrief % To deliver the above debriefing, in the GOAL component AddMechaSource % Adds a mecha faction for arena unit AddMajorSkill % Adds a skill trainer for arena unit AddMinorSkill % Adds a skill trainer for arena unit AlterContext IfPCHasSkill IfPCHasPerformance SetSelfArenaState AddSelfArenaWin AddSelfArenaThreat SetArenaThreat SetArenaState ClearSelfChallengerID SetSelfArenaRecharge ResetArena PrepArena SetChallengerID SetChallengerHome Jump SetSelfX SetSelfY StoreOldFaction RestoreOldFaction ClearOriginalHome ChatLMFollow SetDestination SetXXRMotivation SetXXRAttitude SetXXRPlot ifNPCAttitude Monologue TeamMonologue PCMonologue ClearEncounterRecharge GiveMeritBadge SilentMeritBadge SetPayRate SetNPCBio LoseTrainingChance gearhead-2-0.701/gamedata/asvmacro.txt000066400000000000000000000272161321074026100175430ustar00rootroot00000000000000% ArenaScript Value Macros V L G P E S N M MoodE ControllerID RandomTheme % returns a random mecha theme PartyScore ScenePartyScore CitizensTeam EnemiesTeam EnemyNPCTeam NumDatePoints na <0> %% "Not Applicable", passed as PFrags and RanCon optional param PCHomeTown ERootScene RootSceneID ParentSceneID SCENEFAC PCHomeTownFAC ROOTSCENEFAC CURRENTSCENEFAC GUARDTEAM NPCVar NPCScene ChatNPCScene ItemScene ItemVar SceneVar LOCALFACTION Destination Visibility PCFac OldFac PCDamage NPCFac ChatNPCFac ChatNPCTeam ChatNPCTheme SelfTeam PCRep PCHeroism ChatNPCHeroism NPCHeroism NPCLaw PCLaw PCEasygoingness PCCheerfulness PCRenown NPCRenown ChatNPCRenown PCX PCY PCHunger PC$ PCUID TruePCSkillVal NPCSkill PCGENDER NPCGENDER PCSTATVAL PCREFLEXES PCBODY PCSPEED PCPERCEPTION PCCRAFT PCEGO PCKNOWLEDGE PCCHARM NPCEGO CHATNPCEGO NPCCHARM PCDAGE CHATNPCDAGE NPCDAGE CHATNPCPERCEPTION ChatNPCRelationship NPCRelationship FacRelationship FacFortune NPCUID NPCX NPCY LocalX LocalY EncounterX EncounterY ArenaPay ArenaRenown SELFX SELFY SELFUID SELFNID SelfS SelfFac RReact NPCRep NPCLaw ChatNPCLaw ChatNPCRep ChatNPCRenown ChatNPCID DesigNPCID PayRate QuestStatus PlotStatus NAG_ElementID <-9> StatVal MetaPass STAT_Reflexes <1> STAT_Body <2> STAT_Speed <3> STAT_Perception <4> STAT_Craft <5> STAT_Ego <6> STAT_Knowledge <7> STAT_Charm <8> NAS_MechaGunnery <1> NAS_MechaFighting <2> NAS_MechaPiloting <3> NAS_RangedCombat <4> NAS_CloseCombat <5> NAS_Dodge <6> NAS_Vitality <7> NAS_Athletics <8> NAS_Concentration <9> NAS_Awareness <10> NAS_Initiative <11> NAS_Survival <12> NAS_Repair <13> NAS_Medicine <14> NAS_ElectronicWarfare <15> NAS_SpotWeakness <16> NAS_Conversation <17> NAS_Shopping <18> NAS_Stealth <19> NAS_Intimidation <20> NAS_Science <21> NAS_MechaEngineering <22> NAS_CodeBreaking <23> NAS_Mysticism <24> NAS_Performance <25> NAS_Toughness <26> NAS_Insight <27> NAS_Taunt <28> NAS_KungFu <1> NAS_HapKiDo <2> NAS_Anatomist <3> NAS_HardAsNails <4> NAS_StuntDriving <5> NAS_Polymath <6> NAS_Bishounen <7> NAS_BornToFly <8> NAS_SureFooted <9> NAS_RoadHog <10> NAS_TechVulture <11> NAS_Idealist <12> NAS_BusinessSense <13> NAS_JackOfAll <14> NAS_Rage <15> NAS_Ninjitsu <16> NAS_HullDown <17> NAS_GateCrasher <18> NAS_Sniper <19> NAS_Innovation <20> NAS_Camaraderie <21> NAS_Entourage <22> NAS_Robotics <23> NAS_Acrobatics <24> NAS_DominateAnimal <25> NAS_PickPockets <26> NAS_Extropian <27> CodeBreaking Conversation Intimidation Awareness Survival Stealth Taunt Shopping FacVar PCFacXP PCFacLevel PCRewardLevel FacXPNeeded DynaRenown DynaStrength WildMap <1> CityMap <-6> CaveMap <-2> ArenaMap <-15> AsteroidMap <-30> RoughMap <11> #VictimsRecovered NPCTEAM NAG_XXRan <-7> NAS_EpisodeNumber <6> NAS_DramaticChoice <8> NAS_XXChar_Motivation <101> NAS_XXChar_Attitude <102> NAS_XXFac_Plan <103> CurrentDramaticGoal NAS_DebriefingMsg <11> XXRDebriefingMsg NAG_Completed_DC <-25> DEF_4 <4> DEF_5 <5> OFF_4 <9> OFF_5 <10> XXR_M_Mercenary <1> XXR_M_Professional <2> XXR_M_Competitor <7> XXR_M_GreaterGood <3> XXR_M_Seeker <4> XXR_M_Revenge <5> XXR_M_Change <6> XXR_M_Nihilism <8> XXR_A_NeverMet <11> XXR_A_IsJunior <1> XXR_A_IsSenior <2> XXR_A_Antagonistic <12> XXR_A_Thankful <10> XXR_A_HasSecret <3> XXR_A_IsEqual <4> XXR_A_EnviesPC <5> XXR_A_Disrespect <14> XXR_A_Admire <13> XXR_A_PCHates <6> XXR_A_HatesPC <7> XXR_A_MutualHate <8> XXR_A_Obsession <9> XXR_P_Subterfuge <1> XXR_P_CovertAttack <2> XXR_P_PublicImage <3> XXR_P_PublicAttack <4> XXR_P_SeekArtifact <5> XXR_P_Gathering <6> XXR_P_WeaponProgram <7> XXR_P_War <8> XXR_P_Inactive <9> StoryDL EpisodeNumber STAT_Pass <3> STAT_Lock <6> STAT_MetaVisibility <5> STAT_MapGenerator <1> STAT_WMonster <2> NAG_ParaLocation <-6> NAS_OriginalHome <255> NAG_Location <-1> NAG_ScriptVar <0> NAG_CharDescription <3> NAS_DAge <1> NAS_CharType <2> NAV_TempLancemate <2> NAS_IsCombatant <3> NAG_Experience <4> NAG_Personal <5> NAS_OldFaction <14> NAS_NumConversation <8> NAS_MechaTheme <12> NAS_OptMax <6> NAS_OptMin <7> NAG_ReactionScore <6> NAG_Relationship <10> NAS_Credits <2> NAS_TotalXP <0> NAS_FacXP <3> NAS_FacLevel <4> NAS_RewardLevel <5> NAS_CID <0> NAS_FactionID <2> NAS_X <0> NAS_Y <1> NAS_Team <4> NAV_Friend <1> NAV_ArchAlly <2> NAV_Family <3> NAV_Lover <4> NAV_ArchEnemy <-1> NAV_DefPlayerTeam <1> NAV_DefEnemyTeam <2> NAV_LancemateTeam <-3> NAG_EpisodeData <-4> NAS_SurrenderStatus <14> NAV_NowSurrendered <1> NAS_EncVis <16> NAS_UID <0> NAS_Target <1> NAS_Orders <2> NAG_Action <-2> NAS_CallTime <4> NAG_Narrative <7> NAS_HomeTownID <11> NAS_ControllerID <29> NAS_EncounterActive <34> NAS_Fortune <36> NAS_LancemateTraining_Spent <39> NAG_PlotStatus <-22> NAS_NID <0> NAS_VictimsRecovered <201> NAS_ReturnTo <18> NAS_DifficultyLevel <26> NAG_Condition <9> NAS_StaminaDown <0> NAS_Hunger <2> NAG_SideReaction <-24> NAG_Skill <1> NAG_StatusEffect <14> NAS_Poison <1> NAS_Stoned <4> NAS_Sickness <20> NAG_FacReward <17> NAS_DatePoints <14> NAS_PartyPoints <17> TERRAIN_THRESHOLD <15> ArenaState ArenaRecharge SelfArenaState SelfArenaThreat SelfArenaForces SelfChallengerID SelfChallengerHome ArenaWins ArenaThreat NAG_ArenaData <-18> NAS_ArenaState <1> NAV_AS_Vacant <0> NAV_AS_Ready <1> NAV_AS_Battle <2> NAV_AS_Win <3> NAV_AS_Loss <4> NAS_ArenaWins <2> NAS_ArenaThreat <4> NAS_ArenaForces <5> NAS_ChallengerID <6> NAS_ChallengerHome <7> NAS_ArenaRecharge <8> NAG_Damage <12> NAS_StrucDamage <0> NAG_MeritBadge <28> NAS_MB_PopStar <1> NAS_MB_ArenaChampion <2> NAS_MB_Lancemate2 <3> NAS_MB_Lancemate3 <4> NAS_MB_MoralHighGround <5> NAS_MB_UncompromisingDef <6> MAQUI <1> COMET <2> SILKN <3> AEGIS <4> PDASS <5> PRIVA <6> HOELL <7> CRIHN <8> L5LAW <9> FCOMS <10> RISHI <11> BOHEM <12> MUGLE <13> ROCKE <14> REDMA <15> AEGSF <16> gearhead-2-0.701/gamedata/blankpersona.txt000066400000000000000000000001711321074026100203760ustar00rootroot00000000000000Persona 0 name % A default persona used when no other persona exists. greeting gearhead-2-0.701/gamedata/chat_msg.txt000066400000000000000000000014421321074026100175060ustar00rootroot00000000000000% Standard messages for the INTERACT.PP unit. FACTION_IS_INACTIVE < has been disbanded.> RUMOR_Membership1 RUMOR_Membership2 <.> TRAITCHAT_Like1 TRAITCHAT_Like2 TRAITCHAT_Like3 TRAITCHAT_Hate1 TRAITCHAT_Hate2 TRAITCHAT_Hate3 TRAITCHAT_Ehhh1 TRAITCHAT_Ehhh2 TRAITCHAT_Ehhh3 TRAITCHAT_Lead1 TRAITCHAT_Lead2 TRAITCHAT_Lead3 TRAITCHAT_Lead4 TRAITCHAT_Lead5 TRAITCHAT_Lead6 TRAITCHAT_Lead7 gearhead-2-0.701/gamedata/ghpmacro.txt000066400000000000000000000340631321074026100175260ustar00rootroot00000000000000% GHParser quickparts list - use spaces not tabs please torso Head Arm Leg Wing Tail Turret Storage Gear Stat NAtt Battroid Zoanoid GroundHugger Arachnoid AeroFighter Ornithoid Gerwalk HoverFighter GroundCar Mecha Chara Sensor ECM CPit Melee EMelee Gun BeamGun MLauncher Ammo Rockets Grenade Range Acc Recharge BV MissileCap Magazine Hand Mount Wheels Tracks HoverJet Flight ArcJet Overcharger SpaceFlight HeavyActuator Armor HighTier MidTier LowTier Shield EShield StatCyberware SkillCyberware StatMechaSys SkillMechaSys DefBonus BodyArmor These cmds create external armor gears HeadArmor ArmArmor LegArmor WingArmor TailArmor TurretArmor StorageArmor Gyro Gyros no longer have rating Engine Engine automatically added by LoadGear HighOutput - High-output engine HighPerformance - High-performance engine Treasure RepairFuel MedicineFuel BiotechFuel Mass DomTarget EvolveAt Skill Talent MechaGunnery MechaFighting MechaPiloting RangedCombat CloseCombat Dodge Vitality Athletics Concentration Awareness Initiative Survival Repair Medicine ElectronicWarfare SpotWeakness Conversation Shopping Stealth Intimidation Science MechaEngineering CodeBreaking Mysticism Performance Toughness Insight Taunt SetTeam SetPersona SetFaction SetKeyItem Metal Meat Biotech Cash Scene World MetaScene ArenaMission Story XRANStory Plot Content Choice Persona Faction Adventure ArenaUnit Team Passive PDir MidX MidY XPos YPos Dir PARAX PARAY MallMap ClubMap CaveMap WildMap CityMap MegaCityMap CityBlockMap BoxMap ComplexMap ForestMap HvForestMap ArenaMap MonkeyMap MountainMap AsteroidMap SpaceMap WarzoneMap SpaceScroll MapWidth MapHeight XWrap XYWrap YWrap Microgravity Vacuum Ceiling RockyTiles PalaceParkTiles IndustrialTiles OrganicTiles SpaceBackdrop MapFeature CityZone MegaCityZone Rect Room Void BoxRoom CastleZone Forest Hills Lake Swamp BigHill GroundZero MFX MFY Width Height FloorType MarbleType BorderType WallType FeatureType LockedDoorChance SecretDoorChance Prop CombatProp Mesh SuperProp Team1 Team2 Team3 Team4 Door MetaTerrain Altitude Pass Frame AltFrame StairsUp StairsDown Elevator TrapDoor Rubble Sign Cloud Flame Building Encounter EncounterMove Encounter_Hostile ENCOUNTER_Defense ENCOUNTER_NonCombat Instrument Lockpick HolySymbol ScienceTool AcrobatTool RepairTool SurvivalTool InsightTool Destination Hidden Hide Lock Damage Food FoodMorale FoodFX:Healing FoodFX:Regeneration FoodFX:CureStatus FoodFX:CauseStatus FoodMod FoodMod:Poison FoodMod:Sickness FoodMod:Stone FoodXP:MechaGunnery FoodXP:MechaFighting FoodXP:MechaPiloting FoodXP:RangedCombat FoodXP:CloseCombat FoodXP:Dodge FoodXP:Vitality FoodXP:Athletics FoodXP:Concentration FoodXP:Awareness FoodXP:Initiative FoodXP:Survival FoodXP:Repair FoodXP:Medicine FoodXP:ElectronicWarfare FoodXP:SpotWeakness FoodXP:Conversation FoodXP:Shopping FoodXP:Stealth FoodXP:Intimidation FoodXP:Science FoodXP:MechaEngineering FoodXP:CodeBreaking FoodXP:Mysticism FoodXP:Performance FoodXP:Toughness FoodXP:Insight FoodXP:Taunt FoodXPAmount Reflexes Body Speed Perception Craft Ego Knowledge Charm Regen Age Combatant NonCombatant Fudge GAddOn GunAddOn HeavyAddOn MeleeAddOn UsesReflexes AttackStat for weapons and grenades UsesBody UsesSpeed UsesPerception UsesCraft UsesEgo UsesKnowledge UsesCharm SkillModSkill SkillModAmount SkillModMechaGunnery SkillModMechaFighting SkillModMechaPiloting SkillModRangedCombat SkillModCloseCombat SkillModDodge SkillModAwareness SkillModInitiative SkillModSurvival SkillModRepair SkillModMedicine SkillModElectronicWarfare SkillModSpotWeakness SkillModConversation SkillModShopping SkillModStealth SkillModIntimidation SkillModScience SkillModMechaEngineering SkillModCodeBreaking SkillModMysticism SkillModPerformance SkillModToughness SkillModInsight SkillModTaunt MiniMapComponent StolenGoods PowerSource Job Family Biography Heroism Lawfulness Sociability Easygoingness Cheerfulness Renown Pragmatism Memo Email News Phone Sealed Integral Personadex Hardened AntiBeam Legality Tolerance Controller FacTheme PCFriend PCFamily PCMentor NonMissionGiver PCLover Set NeededCells LMFollow LMPassive Theme BM_SpacePort BM_Hospital BM_Garage BM_DepartmentStore BM_CavClub BM_SilverFortress BM_Arena BM_Park BM_Government Computer Software S_MVBoost S_TRBoost S_BoostScale S_SkillBoost S_SpeedComp S_Information SInfo_CreatureDex SInfo_RobotDex SInfo_SynthDex SInfo_MechaDex PayRate BodyHarness These cmds create harness gears HeadHarness ArmHarness LegHarness WingHarness TailHarness TurretHarness StorageHarness MonsterTV Meme Secret MaxMemeViews MemeTimeLimit SetTheme SetSpecSkill RandomLoot ReflexSystem Mood MinorMood MoodTimeLimit Transform:Battroid Transform:Zoanoid Transform:GroundHugger Transform:Arachnoid Transform:Aerofighter Transform:Ornithoid Transform:Gerwalk Transform:Hoverfighter Transform:Groundcar LongRangeScanner VariableHead VariableArm VariableLeg VariableWing VariableTail VariableTurret VariableStorage DifficultyLevel % To manually set the difficulty level of a quest scene ForGoalLevel % Marks an item to be placed in the goal level of a quest dungeon ForEntryLevel % Marks an item to be placed in the entry level of a quest dungeon NoCorpse MOTIVATION:Mercenary MOTIVATION:Professional MOTIVATION:GreaterGood MOTIVATION:Seeker MOTIVATION:Revenge MOTIVATION:Change MOTIVATION:Competition MOTIVATION:Nihilism gearhead-2-0.701/gamedata/lmtactics.txt000066400000000000000000000064161321074026100177120ustar00rootroot00000000000000Persona 0 name % This persona exists so that the PC can set tactics for the lancemates. % There should be messages for a wide variety of personality types and configurations, % since the same persona is being used for every last lancemate and we don't want % to lose all traces of individuality. greeting result1 result2 result3 Msg1 Msg1_1 <> CMsg1_1 Msg1_2 CMsg1_2 Msg1_3 CMsg1_3 Msg1_4 <> CMsg1_4 Msg1_5 CMsg1_5 Msg1_6 CMsg1_6 Msg1_7 <> CMsg1_7 Msg1_8 <> CMsg1_8 Msg1_9 <> CMsg1_9 Msg1_10 CMsg1_10 Msg1_11 CMsg1_11 Msg1_12 <> CMsg1_12 Msg2 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 Msg2_3 CMsg2_3 Msg2_4 CMsg2_4 Msg2_104 CMsg2_104 Msg2_5 CMsg2_5 Msg2_6 CMsg2_6 Msg2_7 CMsg2_7 Msg2_8 CMsg2_8 Msg2_108 CMsg2_108 Msg2_208 CMsg2_208 Msg2_9 CMsg2_9 Msg2_10 CMsg2_10 Msg2_110 CMsg2_110 Msg2_11 CMsg2_11 Msg2_12 CMsg2_12 Msg3 Msg3_1 <> CMsg3_1 Msg3_2 <> CMsg3_2 Msg3_3 <> CMsg3_3 Msg3_4 <> CMsg3_4 Msg3_5 <> CMsg3_5 Msg3_6 <> CMsg3_6 Msg3_7 <> CMsg3_7 Msg3_8 <> CMsg3_8 Msg3_9 <> CMsg3_9 Msg3_10 <> CMsg3_10 Msg3_11 <> CMsg3_11 Msg3_12 <> CMsg3_12 Prompt1 Prompt2 Prompt3 gearhead-2-0.701/gamedata/mesh1.obj000066400000000000000000000025711321074026100166750ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jan 17 20:56:09 GMT+09:00 2007 mtllib mesh1.mtl g mesh01 usemtl Untitled_1 v -0.5 0.4 -0.5 v 0.5 0.4 -0.5 v 0.5 0 -0.5 v -0.5 0 -0.5 v 0.5 0 0.5 v 0.5 0.4 0.5 v -0.5 0.4 0.5 v -0.5 0 0.5 v -0.19965 0.5988 0.20011 v -0.19965 0.5988 -0.19919 v 0.19965 0.5988 -0.19919 v 0.19965 0.5988 0.20011 vn -0.63169 0.44928 -0.63175 vn 0.63169 0.44928 -0.63175 vn 0.57735 -0.57735 -0.57735 vn -0.57735 -0.57735 -0.57735 vn 0.57735 -0.57735 0.57735 vn 0.63182 0.44909 0.63176 vn -0.63182 0.44909 0.63176 vn -0.57735 -0.57735 0.57735 vn -0.22191 0.94939 0.22228 vn -0.222 0.94952 -0.22164 vn 0.222 0.94952 -0.22164 vn 0.22191 0.94939 0.22228 vt -0.62568 0.56707 vt 0.12568 0.56707 vt 0.12568 0.16707 vt -0.62568 0.16707 vt -0.12432 0.16707 vt -0.12432 0.56707 vt -0.37568 0.56707 vt -0.37568 0.16707 vt -0.37423 0.76587 vt -0.62423 0.76587 vt 0.12423 0.76587 vt -0.12577 0.76587 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 5/5/5 6/6/6 7/7/7 f 7/7/7 8/8/8 5/5/5 f 8/8/8 7/7/7 1/1/1 f 1/1/1 4/4/4 8/8/8 f 2/2/2 6/6/6 5/5/5 f 5/5/5 3/3/3 2/2/2 f 3/3/3 5/5/5 8/8/8 f 8/8/8 4/4/4 3/3/3 f 9/9/9 10/10/10 1/1/1 f 1/1/1 7/7/7 9/9/9 f 10/10/10 11/11/11 2/2/2 f 2/2/2 1/1/1 10/10/10 f 11/11/11 12/12/12 6/6/6 f 6/6/6 2/2/2 11/11/11 f 12/12/12 9/9/9 7/7/7 f 7/7/7 6/6/6 12/12/12 f 9/9/9 12/12/12 11/11/11 f 11/11/11 10/10/10 9/9/9 gearhead-2-0.701/gamedata/mesh10.obj000066400000000000000000000012021321074026100167430ustar00rootroot00000000000000# # Homemade .obj file - Shop shelf # v -0.35 0.0 -0.2 v -0.35 0.0 0.2 v 0.35 0.0 0.2 v 0.35 0.0 -0.2 v -0.35 0.6 -0.2 v -0.35 0.4 0.2 v 0.35 0.4 0.2 v 0.35 0.6 -0.2 # # Texture Coords # vt 1.0 1.0 vt 0.5 1.0 vt 0.0 1.0 vt 0.5 0.5 vt 0.0 0.5 vt 1.0 0.0 vt 0.5 0.0 vt 0.0 0.0 vt 0.0 0.8 vt 1.0 0.5 # # Normal Coords # vn 0.0 0.0 -1.0 vn 1.0 0.0 0.0 vn 0.0 0.0 1.0 vn -1.0 0.0 0.0 vn 0.0 1.0 0.4 # # Start making faces... # f 1/4/2 5/2/2 6/9/2 2/5/2 f 2/6/3 6/10/3 7/4/3 3/7/3 f 3/5/4 7/9/4 8/2/4 4/4/4 f 4/7/1 8/4/1 5/5/1 1/8/1 f 6/10/5 5/1/5 8/2/5 7/4/5 gearhead-2-0.701/gamedata/mesh11.obj000066400000000000000000000011571321074026100167550ustar00rootroot00000000000000# # Homemade .obj file - Crate # v -0.3 0.0 -0.15 v -0.3 0.0 0.15 v 0.3 0.0 0.15 v 0.3 0.0 -0.15 v -0.3 0.3 -0.15 v -0.3 0.3 0.15 v 0.3 0.3 0.15 v 0.3 0.3 -0.15 # # Texture Coords # vt 1.0 1.0 vt 0.5 1.0 vt 0.0 1.0 vt 1.0 0.5 vt 0.5 0.5 vt 0.0 0.5 vt 1.0 0.0 vt 0.5 0.0 vt 0.0 0.0 # # Normal Coords # vn 0.0 0.0 -1.0 vn 1.0 0.0 0.0 vn 0.0 0.0 1.0 vn -1.0 0.0 0.0 vn 0.0 1.0 0.0 # # Start making faces... # f 1/5/2 5/2/2 6/3/2 2/6/2 f 2/7/3 6/4/3 7/5/3 3/8/3 f 3/6/4 7/3/4 8/2/4 4/5/4 f 4/8/1 8/5/1 5/6/1 1/9/1 f 5/1/5 8/2/5 7/5/5 6/4/5 gearhead-2-0.701/gamedata/mesh12.obj000066400000000000000000000011631321074026100167530ustar00rootroot00000000000000# # Homemade .obj file - End Table # v -0.2 0.0 -0.45 v -0.2 0.0 -0.05 v 0.2 0.0 -0.05 v 0.2 0.0 -0.45 v -0.2 0.4 -0.45 v -0.2 0.4 -0.05 v 0.2 0.4 -0.05 v 0.2 0.4 -0.45 # # Texture Coords # vt 1.0 1.0 vt 0.5 1.0 vt 0.0 1.0 vt 1.0 0.5 vt 0.5 0.5 vt 0.0 0.5 vt 1.0 0.0 vt 0.5 0.0 vt 0.0 0.0 # # Normal Coords # vn 0.0 0.0 -1.0 vn 1.0 0.0 0.0 vn 0.0 0.0 1.0 vn -1.0 0.0 0.0 vn 0.0 1.0 0.0 # # Start making faces... # f 1/5/2 5/2/2 6/3/2 2/6/2 f 2/7/3 6/4/3 7/5/3 3/8/3 f 3/6/4 7/3/4 8/2/4 4/5/4 f 4/8/1 8/5/1 5/6/1 1/9/1 f 5/1/5 8/2/5 7/5/5 6/4/5 gearhead-2-0.701/gamedata/mesh13.obj000066400000000000000000000027571321074026100167660ustar00rootroot00000000000000# # Homemade .obj file - Bed # v -0.32 0.0 -0.40 v -0.32 0.0 0.37 v 0.32 0.0 0.37 v 0.32 0.0 -0.40 v -0.30 0.3 -0.40 v -0.30 0.3 0.35 v 0.30 0.3 0.35 v 0.30 0.3 -0.40 # Headboard v -0.32 0.0 -0.45 v -0.32 0.0 -0.40 v 0.32 0.0 -0.40 v 0.32 0.0 -0.45 v -0.32 0.5 -0.45 v -0.32 0.5 -0.40 v 0.32 0.5 -0.40 v 0.32 0.5 -0.45 # Pillow v -0.2 0.30 -0.39 v -0.2 0.30 -0.19 v 0.2 0.30 -0.19 v 0.2 0.30 -0.39 v -0.18 0.37 -0.37 v -0.18 0.35 -0.21 v 0.18 0.35 -0.21 v 0.18 0.37 -0.37 # # Texture Coords # vt 1.0 1.0 vt 0.875 1.0 vt 0.625 1.0 vt 0.5 1.0 vt 0.0 1.0 vt 0.44 0.94 vt 0.06 0.94 vt 0.44 0.81 vt 0.06 0.81 vt 0.5 0.75 vt 0.0 0.75 vt 0.5 0.625 vt 0.375 0.625 vt 0.0 0.625 vt 0.875 0.125 vt 0.625 0.125 vt 1.0 0.0 vt 0.5 0.0 vt 0.375 0.0 vt 0.0 0.0 # # Normal Coords # vn 0.0 0.0 -1.0 vn 1.0 0.0 0.0 vn 0.0 0.0 1.0 vn -1.0 0.0 0.0 vn 0.0 1.0 0.0 # # Start making faces... # f 1/1/2 5/2/2 6/15/2 2/17/2 f 2/17/3 6/15/3 7/16/3 3/18/3 f 3/18/4 7/16/4 8/3/4 4/4/4 f 5/2/5 8/3/5 7/16/5 6/15/5 f 9/18/2 13/12/2 14/13/2 10/19/2 f 10/19/3 14/13/3 15/14/3 11/20/3 f 11/19/4 15/13/4 16/12/4 12/18/4 f 12/20/1 16/14/1 13/13/1 9/19/1 f 13/10/5 16/11/5 15/14/5 14/12/5 f 17/4/5 21/6/5 22/8/5 18/10/5 f 18/10/5 22/8/5 23/9/5 19/11/5 f 19/11/5 23/9/5 24/7/5 20/5/5 f 20/5/5 24/7/5 21/6/5 17/4/5 f 21/6/5 24/7/5 23/9/5 22/8/5 gearhead-2-0.701/gamedata/mesh2.obj000066400000000000000000000061761321074026100167030ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jan 17 18:19:40 GMT+09:00 2007 mtllib mesh2.mtl g mesh01 usemtl Game_Tex v 0.25 0.5 -0.43301 v 0.5 0.5 0 v 0.5 0 0 v 0.25 0 -0.43301 v 0.12125 1 -0.21001 v 0.2425 1 0 v -0.12125 1 -0.21001 v -0.25 0.5 -0.43301 v -0.5 0.5 0 v -0.25 0 -0.43301 v -0.5 0 0 v -0.2425 1 0 v -0.25 0.5 0.43301 v -0.25 0 0.43301 v -0.12125 1 0.21001 v 0.25 0.5 0.43301 v 0.25 0 0.43301 v 0.12125 1 0.21001 v -0.16536 0.36051 -0.45464 v -0.16557 0 -0.45464 v 0.16557 0 -0.45464 v 0.16578 0.36051 -0.45464 v -0.16536 0.36053 -0.49877 v -0.16557 0.00002 -0.49877 v 0.16557 0.00002 -0.49877 v 0.16578 0.36053 -0.49877 vn 0.50578 0.24703 -0.82654 vn 0.9755 0.21999 0 vn 0.79241 -0.60999 0 vn 0.46968 -0.62936 -0.61912 vn 0.31237 0.78083 -0.54104 vn 0.62474 0.78083 0 vn -0.31237 0.78083 -0.54104 vn -0.50576 0.24698 -0.82656 vn -0.9755 0.21999 -0 vn -0.46966 -0.62933 -0.61916 vn -0.79241 -0.60999 0 vn -0.62474 0.78083 -0 vn -0.48775 0.21998 0.84481 vn -0.3962 -0.60999 0.68624 vn -0.31237 0.78083 0.54104 vn 0.48775 0.21998 0.84481 vn 0.3962 -0.60999 0.68624 vn 0.31237 0.78083 0.54104 vn -0.4069 0.34886 -0.84423 vn -0.38402 -0.8738 -0.29834 vn 0.38389 -0.87396 -0.29803 vn 0.40762 0.34833 -0.84411 vn -0.57714 0.57765 -0.57726 vn -0.57756 -0.57705 -0.57744 vn 0.57719 -0.57736 -0.5775 vn 0.57751 0.57734 -0.5772 vt 0.13549 0.58198 vt -0.04559 0.58198 vt -0.04559 0.16532 vt 0.13549 0.16532 vt 0.07725 0.99865 vt -0.08733 0.99865 vt -0.57721 0.99865 vt -0.63547 0.58198 vt -0.45441 0.58198 vt -0.63547 0.16532 vt -0.45441 0.16532 vt -0.41269 0.99865 vt -0.31525 0.58198 vt -0.31525 0.16532 vt -0.30242 0.99865 vt -0.18477 0.58198 vt -0.18477 0.16532 vt -0.19761 0.99865 vt -0.67091 0.46574 vt -0.67083 0.16532 vt 0.17085 0.16532 vt 0.17077 0.46574 vt -0.67959 0.46576 vt -0.67951 0.16533 vt 0.17953 0.16533 vt 0.17945 0.46576 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 5/5/5 6/6/6 2/2/2 f 2/2/2 1/1/1 5/5/5 f 7/7/7 5/5/5 1/1/1 f 1/1/1 8/8/8 7/7/7 f 9/9/9 8/8/8 10/10/10 f 10/10/10 11/11/11 9/9/9 f 12/12/12 7/7/7 8/8/8 f 8/8/8 9/9/9 12/12/12 f 13/13/13 9/9/9 11/11/11 f 11/11/11 14/14/14 13/13/13 f 15/15/15 12/12/12 9/9/9 f 9/9/9 13/13/13 15/15/15 f 16/16/16 13/13/13 14/14/14 f 14/14/14 17/17/17 16/16/16 f 18/18/18 15/15/15 13/13/13 f 13/13/13 16/16/16 18/18/18 f 2/2/2 16/16/16 17/17/17 f 17/17/17 3/3/3 2/2/2 f 6/6/6 18/18/18 16/16/16 f 16/16/16 2/2/2 6/6/6 f 6/6/6 5/5/5 7/7/7 f 7/7/7 12/12/12 15/15/15 f 15/15/15 18/18/18 6/6/6 f 6/6/6 7/7/7 15/15/15 f 17/17/17 14/14/14 11/11/11 f 11/11/11 10/10/10 4/4/4 f 4/4/4 3/3/3 17/17/17 f 17/17/17 11/11/11 4/4/4 f 19/19/19 20/20/20 10/10/10 f 10/10/10 8/8/8 19/19/19 f 20/20/20 21/21/21 4/4/4 f 4/4/4 10/10/10 20/20/20 f 21/21/21 22/22/22 1/1/1 f 1/1/1 4/4/4 21/21/21 f 22/22/22 19/19/19 8/8/8 f 8/8/8 1/1/1 22/22/22 f 23/23/23 24/24/24 20/20/20 f 20/20/20 19/19/19 23/23/23 f 24/24/24 25/25/25 21/21/21 f 21/21/21 20/20/20 24/24/24 f 25/25/25 26/26/26 22/22/22 f 22/22/22 21/21/21 25/25/25 f 26/26/26 23/23/23 19/19/19 f 19/19/19 22/22/22 26/26/26 f 23/23/23 26/26/26 25/25/25 f 25/25/25 24/24/24 23/23/23 gearhead-2-0.701/gamedata/mesh3.obj000066400000000000000000000025541321074026100167000ustar00rootroot00000000000000# # Homemade .obj file. This is a standup video game cabinet. Here's hoping it works. # v -0.2 0.0 -0.2 v 0.2 0.0 -0.2 v -0.2 0.0 0.2 v 0.2 0.0 0.2 v -0.2 0.4 0.2 v 0.2 0.4 0.2 v -0.2 0.5 0.1 v 0.2 0.5 0.1 v -0.2 0.62 0.2 v 0.2 0.62 0.2 v -0.2 0.65 -0.2 v -0.2 0.68 0.2 v 0.2 0.68 0.2 v 0.2 0.65 -0.2 # Texture coordinates # The textures on the side of the cabinet look strange, but as near as I can # figure that's because of OpenGL and not because there's anything wrong here. # To test it yourself draw a big colorful "X" on the mesh's skin and see how # the lines are distorted. It's probably because the cabinet side is such a # big polygon. It'd probably render better if I broke it down to several quads/triangles. vt 1.0 1.0 vt 0.5 1.0 vt 0.0 1.0 vt 1.0 0.875 vt 0.5 0.875 vt 1.0 0.75 vt 0.5 0.75 vt 0.25 0.75 vt 0.0 0.75 vt 1.0 0.5 vt 0.5 0.5 vt 1.0 0.0 vt 0.5 0.0 vt 0.25 0.0 vt 0.0 0.0 vt 0.5 0.850 vt 0.5 0.825 vt 0.5 0.785 # Normal coordinates vn 0.0 0.0 1.0 vn 1.0 0.0 0.0 vn 0.0 0.0 -1.0 vn -1.0 0.0 0.0 vn 0.0 1.0 0.0 # Start making faces f 1/14/1 2/15/1 14/9/1 11/8/1 f 1/14/4 11/8/4 12/7/4 9/16/4 7/17/4 5/18/4 3/13/4 f 2/14/2 4/13/2 6/18/2 8/17/2 10/16/2 13/7/2 14/8/2 f 3/12/3 5/10/3 6/11/3 4/13/3 f 5/10/3 7/6/3 8/7/3 6/11/3 f 7/6/3 9/4/3 10/5/3 8/7/3 f 9/4/3 12/1/3 13/2/3 10/5/3 f 11/2/5 14/3/5 13/9/5 12/7/5 gearhead-2-0.701/gamedata/mesh4.obj000066400000000000000000000012571321074026100167000ustar00rootroot00000000000000# # Homemade .obj file. This is a vending machine; actually a big box. # I guess you could use it for all kinds of stuff. # v -0.25 0.0 -0.2 v 0.25 0.0 -0.2 v 0.25 0.0 0.2 v -0.25 0.0 0.2 v -0.25 0.8 -0.2 v 0.25 0.8 -0.2 v 0.25 0.8 0.2 v -0.25 0.8 0.2 # # Texture Coords # vt 1.0 1.0 vt 0.5 1.0 vt 0.0 1.0 vt 0.5 0.75 vt 0.25 0.75 vt 0.0 0.75 vt 1.0 0.0 vt 0.5 0.0 vt 0.25 0.0 vt 0.0 0.0 # # Normal Coords # vn 0.0 0.0 -1.0 vn 1.0 0.0 0.0 vn 0.0 0.0 1.0 vn -1.0 0.0 0.0 vn 0.0 1.0 0.0 # # Start making faces... # f 4/7/3 8/1/3 7/2/3 3/8/3 f 4/8/4 1/9/4 5/5/4 8/4/4 f 1/10/1 2/9/1 6/5/1 5/6/1 f 2/9/2 3/8/2 7/4/2 6/5/2 f 5/2/5 6/3/5 7/6/5 8/4/5 gearhead-2-0.701/gamedata/mesh5.obj000066400000000000000000000027701321074026100167020ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jun 27 13:59:28 KST 2007 mtllib mesh5.mtl g mesh01 usemtl Untitled_1 v -0.5 0.5 -0.5 v 0.5 0.5 -0.5 v 0.5 0 -0.5 v -0.5 0 -0.5 v 0.5 0 0.5 v 0.5 0.5 0.5 v -0.5 0.5 0.5 v -0.5 0 0.5 vn -0.57735 0.57735 -0.57735 vn 0.57735 0.57735 -0.57735 vn 0.57735 -0.57735 -0.57735 vn -0.57735 -0.57735 -0.57735 vn 0.57735 -0.57735 0.57735 vn 0.57735 0.57735 0.57735 vn -0.57735 0.57735 0.57735 vn -0.57735 -0.57735 0.57735 vt -0.62566 -0.39183 vt 0.12566 -0.39183 vt 0.12566 -0.60817 vt 0.12566 -0.60817 vt -0.62566 -0.60817 vt -0.62566 -0.39183 vt -0.12566 -0.60817 vt -0.12566 -0.39183 vt -0.37434 -0.39183 vt -0.37434 -0.39183 vt -0.37434 -0.60817 vt -0.12566 -0.60817 vt -0.37434 -0.60817 vt -0.37434 -0.39183 vt -0.62566 -0.39183 vt -0.62566 -0.39183 vt -0.62566 -0.60817 vt -0.37434 -0.60817 vt 0.12566 -0.39183 vt -0.12566 -0.39183 vt -0.12566 -0.60817 vt -0.12566 -0.60817 vt 0.12566 -0.60817 vt 0.12566 -0.39183 vt -0.37434 -0.39183 vt -0.12566 -0.39183 vt 0.12566 -0.39183 vt 0.12566 -0.39183 vt -0.62566 -0.39183 vt -0.37434 -0.39183 vt 0.12566 -0.60817 vt -0.12566 -0.60817 vt -0.37434 -0.60817 vt -0.37434 -0.60817 vt -0.62566 -0.60817 vt 0.12566 -0.60817 f 1/1/1 2/2/2 3/3/3 f 3/4/3 4/5/4 1/6/1 f 5/7/5 6/8/6 7/9/7 f 7/10/7 8/11/8 5/12/5 f 8/13/8 7/14/7 1/15/1 f 1/16/1 4/17/4 8/18/8 f 2/19/2 6/20/6 5/21/5 f 5/22/5 3/23/3 2/24/2 f 7/25/7 6/26/6 2/27/2 f 2/28/2 1/29/1 7/30/7 f 3/31/3 5/32/5 8/33/8 f 8/34/8 4/35/4 3/36/3 gearhead-2-0.701/gamedata/mesh6.obj000066400000000000000000000016251321074026100167010ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jun 27 14:06:40 KST 2007 mtllib mesh6.mtl g mesh01 usemtl Untitled_1 v 0.49478 0.30003 0.5 v -0.49478 0.30003 0.5 v -0.49478 0.19997 0.5 v 0.49478 0.19997 0.5 v -0.5 0 -0.5 v -0.5 0.5 -0.5 v 0.5 0.5 -0.5 v 0.5 0 -0.5 vn 0.58669 0.51275 0.6268 vn -0.58669 0.51275 0.6268 vn -0.58669 -0.51275 0.6268 vn 0.58669 -0.51275 0.6268 vn -0.56918 -0.63626 -0.52078 vn -0.56918 0.63626 -0.52078 vn 0.56918 0.63626 -0.52078 vn 0.56918 -0.63626 -0.52078 vt -0.1264 -0.4774 vt -0.3736 -0.4774 vt -0.3736 -0.5226 vt -0.1264 -0.5226 vt -0.62566 -0.60817 vt -0.62566 -0.39183 vt 0.12566 -0.39183 vt 0.12566 -0.60817 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 5/5/5 6/6/6 7/7/7 f 7/7/7 8/8/8 5/5/5 f 8/8/8 7/7/7 1/1/1 f 1/1/1 4/4/4 8/8/8 f 2/2/2 6/6/6 5/5/5 f 5/5/5 3/3/3 2/2/2 f 7/7/7 6/6/6 2/2/2 f 2/2/2 1/1/1 7/7/7 f 3/3/3 5/5/5 8/8/8 f 8/8/8 4/4/4 3/3/3 gearhead-2-0.701/gamedata/mesh7.obj000066400000000000000000000016731321074026100167050ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jun 27 14:14:48 KST 2007 mtllib mesh7.mtl g mesh01 usemtl Untitled_1 v 0.49953 0.30001 0.5 v -0.29985 0.298 0.30032 v -0.29985 0.19793 0.30032 v 0.49953 0.19994 0.5 v -0.4999 0.20002 -0.49428 v -0.4999 0.29998 -0.49428 v 0.5 0.5 -0.5 v 0.5 0 -0.5 vn 0.51281 0.50681 0.69294 vn -0.58827 0.55374 0.58933 vn -0.58721 -0.55601 0.58825 vn 0.51378 -0.50546 0.69321 vn -0.69504 -0.50549 -0.51126 vn -0.69477 0.50688 -0.51027 vn 0.52365 0.66989 -0.52635 vn 0.52421 -0.66899 -0.52693 vt -0.1402 -0.47736 vt -0.37586 -0.46884 vt -0.37586 -0.5331 vt -0.1402 -0.5222 vt -0.60898 -0.52221 vt -0.60898 -0.47733 vt 0.12589 -0.38082 vt 0.12589 -0.61874 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 5/5/5 6/6/6 7/7/7 f 7/7/7 8/8/8 5/5/5 f 8/8/8 7/7/7 1/1/1 f 1/1/1 4/4/4 8/8/8 f 2/2/2 6/6/6 5/5/5 f 5/5/5 3/3/3 2/2/2 f 7/7/7 6/6/6 2/2/2 f 2/2/2 1/1/1 7/7/7 f 3/3/3 5/5/5 8/8/8 f 8/8/8 4/4/4 3/3/3 gearhead-2-0.701/gamedata/mesh8.obj000066400000000000000000000103151321074026100166770ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jun 27 14:31:43 KST 2007 mtllib mesh8.mtl g mesh01 usemtl Untitled_1 v 0.5 0.5 0.5 v -0.5 0.5 0.5 v -0.5 0 0.5 v 0.5 0 0.5 v -0.5 0 -0.5 v -0.5 0.5 -0.5 v 0.5 0.5 -0.5 v 0.5 0 -0.5 v 0.17215 1.05668 -0.05952 v 0.17215 1.05668 0.39633 v -0.17153 1.05668 0.39633 v -0.17153 1.05668 -0.05952 v 0.09511 1.54253 0.11155 v 0.09496 1.64601 0.36226 v -0.09406 1.64601 0.36226 v -0.09392 1.54253 0.11155 v 0.09496 1.64932 0.39642 v 0.17215 1.05999 0.43048 v -0.17153 1.05999 0.43048 v -0.09406 1.64932 0.39642 v 0.0705 1.57311 0.40082 v 0.12773 1.1362 0.42608 v -0.12707 1.1362 0.42608 v -0.06964 1.57311 0.40082 v 0.0705 1.55849 0.24997 v 0.12773 1.12158 0.27522 v -0.12707 1.12158 0.27522 v -0.06964 1.55849 0.24997 v 0.05473 1.56462 0.16506 v 0.05464 1.62392 0.30874 v -0.05368 1.62392 0.30874 v -0.0536 1.56462 0.16506 v 0.00052 2.02985 0.05713 vn 0.71145 0.23174 0.66343 vn -0.71144 0.23201 0.66334 vn -0.57735 -0.57735 0.57735 vn 0.57735 -0.57735 0.57735 vn -0.57735 -0.57735 -0.57735 vn -0.66026 0.32898 -0.67515 vn 0.66025 0.32885 -0.67523 vn 0.57735 -0.57735 -0.57735 vn 0.59366 0.57828 -0.5596 vn 0.90087 0.02381 0.43343 vn -0.89912 0.019 0.43729 vn -0.59353 0.57485 -0.56327 vn 0.56927 0.58681 -0.57583 vn 0.62983 0.75406 -0.18629 vn -0.62622 0.7584 -0.18076 vn -0.54844 0.59699 -0.58551 vn 0.53387 0.6534 0.5367 vn 0.61975 -0.49421 0.60964 vn -0.61989 -0.49408 0.60961 vn -0.53371 0.65351 0.53672 vn -0.29208 -0.28775 0.91208 vn -0.30511 0.31272 0.89951 vn 0.30511 0.31256 0.89957 vn 0.2921 -0.28789 0.91203 vn -0.53666 -0.5627 0.62878 vn -0.6145 0.58595 0.52825 vn 0.61464 0.58583 0.52823 vn 0.53651 -0.56281 0.62881 vn 0.25485 0.76055 -0.59717 vn 0.25486 0.96038 -0.11275 vn -0.25483 0.96035 -0.11311 vn -0.25491 0.76032 -0.59744 vn -0.00001 0.92437 -0.3815 vt -0.08401 -0.76336 vt -0.41608 -0.76321 vt -0.41608 -0.84933 vt -0.08401 -0.84946 vt -0.65171 -0.79083 vt -0.65171 -0.69892 vt 0.1518 -0.69898 vt 0.1518 -0.79089 vt 0.15934 -0.56913 vt -0.13081 -0.58714 vt -0.36919 -0.58714 vt -0.65933 -0.56913 vt 0.12952 -0.10208 vt -0.15995 -0.1052 vt -0.33985 -0.10513 vt -0.62995 -0.10182 vt -0.17406 -0.12054 vt -0.14343 -0.57579 vt -0.35658 -0.57578 vt -0.32575 -0.12047 vt -0.19315 -0.13535 vt -0.16394 -0.48893 vt -0.33604 -0.48893 vt -0.30668 -0.1353 vt -0.08023 -0.0584 vt -0.07425 -0.51327 vt -0.42574 -0.51327 vt -0.41955 -0.05826 vt 0.11304 -0.0517 vt -0.16814 -0.07019 vt -0.33145 -0.07009 vt -0.61372 -0.05143 vt 0.24979 -0.05398 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 5/5/5 6/6/6 7/7/7 f 7/7/7 8/8/8 5/5/5 f 8/8/8 7/7/7 1/1/1 f 1/1/1 4/4/4 8/8/8 f 2/2/2 6/6/6 5/5/5 f 5/5/5 3/3/3 2/2/2 f 3/3/3 5/5/5 8/8/8 f 8/8/8 4/4/4 3/3/3 f 9/9/9 10/10/10 1/1/1 f 1/1/1 7/7/7 9/9/9 f 10/10/10 11/11/11 2/2/2 f 2/2/2 1/1/1 10/10/10 f 11/11/11 12/12/12 6/6/6 f 6/6/6 2/2/2 11/11/11 f 12/12/12 9/9/9 7/7/7 f 7/7/7 6/6/6 12/12/12 f 13/13/13 14/14/14 10/10/10 f 10/10/10 9/9/9 13/13/13 f 15/15/15 16/16/16 12/12/12 f 12/12/12 11/11/11 15/15/15 f 16/16/16 13/13/13 9/9/9 f 9/9/9 12/12/12 16/16/16 f 17/17/17 18/18/18 10/10/10 f 10/10/10 14/14/14 17/17/17 f 18/18/18 19/19/19 11/11/11 f 11/11/11 10/10/10 18/18/18 f 19/19/19 20/20/20 15/15/15 f 15/15/15 11/11/11 19/19/19 f 20/20/20 17/17/17 14/14/14 f 14/14/14 15/15/15 20/20/20 f 21/21/21 22/22/22 18/18/18 f 18/18/18 17/17/17 21/21/21 f 22/22/22 23/23/23 19/19/19 f 19/19/19 18/18/18 22/22/22 f 23/23/23 24/24/24 20/20/20 f 20/20/20 19/19/19 23/23/23 f 24/24/24 21/21/21 17/17/17 f 17/17/17 20/20/20 24/24/24 f 25/25/25 26/26/26 22/22/22 f 22/22/22 21/21/21 25/25/25 f 26/26/26 27/27/27 23/23/23 f 23/23/23 22/22/22 26/26/26 f 27/27/27 28/28/28 24/24/24 f 24/24/24 23/23/23 27/27/27 f 28/28/28 25/25/25 21/21/21 f 21/21/21 24/24/24 28/28/28 f 25/25/25 28/28/28 27/27/27 f 27/27/27 26/26/26 25/25/25 f 29/29/29 30/30/30 14/14/14 f 14/14/14 13/13/13 29/29/29 f 30/30/30 31/31/31 15/15/15 f 15/15/15 14/14/14 30/30/30 f 31/31/31 32/32/32 16/16/16 f 16/16/16 15/15/15 31/31/31 f 32/32/32 29/29/29 13/13/13 f 13/13/13 16/16/16 32/32/32 f 29/29/29 33/33/33 30/30/30 f 30/30/30 33/33/33 31/31/31 f 31/31/31 33/33/33 32/32/32 f 32/32/32 33/33/33 29/29/29 gearhead-2-0.701/gamedata/mesh9.obj000066400000000000000000000717751321074026100167210ustar00rootroot00000000000000#Produced by Art of Illusion 2.4, Wed Jun 27 15:09:39 KST 2007 mtllib mesh9.mtl g mesh03 usemtl Untitled_1 v -0.05759 0.36392 -0.37451 v -0.02744 0.25142 -0.37451 v -0.02744 0.25142 -0.49951 v -0.05759 0.36392 -0.49951 v -0.05759 0.36392 -0.24951 v -0.02744 0.25142 -0.24951 v -0.05759 0.36392 -0.12451 v -0.02744 0.25142 -0.12451 v -0.05759 0.36392 0.00049 v -0.02744 0.25142 0.00049 v -0.05759 0.36392 0.12549 v -0.02744 0.25142 0.12549 v -0.05759 0.36392 0.25049 v -0.02744 0.25142 0.25049 v -0.05759 0.36392 0.37549 v -0.02744 0.25142 0.37549 v -0.05759 0.36392 0.50049 v -0.02744 0.25142 0.50049 v -0.13994 0.44627 -0.37451 v -0.13994 0.44627 -0.49951 v -0.13994 0.44627 -0.24951 v -0.13994 0.44627 -0.12451 v -0.13994 0.44627 0.00049 v -0.13994 0.44627 0.12549 v -0.13994 0.44627 0.25049 v -0.13994 0.44627 0.37549 v -0.13994 0.44627 0.50049 v -0.25244 0.47642 -0.37451 v -0.25244 0.47642 -0.49951 v -0.25244 0.47642 -0.24951 v -0.25244 0.47642 -0.12451 v -0.25244 0.47642 0.00049 v -0.25244 0.47642 0.12549 v -0.25244 0.47642 0.25049 v -0.25244 0.47642 0.37549 v -0.25244 0.47642 0.50049 v -0.36494 0.44627 -0.37451 v -0.36494 0.44627 -0.49951 v -0.36494 0.44627 -0.24951 v -0.36494 0.44627 -0.12451 v -0.36494 0.44627 0.00049 v -0.36494 0.44627 0.12549 v -0.36494 0.44627 0.25049 v -0.36494 0.44627 0.37549 v -0.36494 0.44627 0.50049 v -0.4473 0.36392 -0.37451 v -0.4473 0.36392 -0.49951 v -0.4473 0.36392 -0.24951 v -0.4473 0.36392 -0.12451 v -0.4473 0.36392 0.00049 v -0.4473 0.36392 0.12549 v -0.4473 0.36392 0.25049 v -0.4473 0.36392 0.37549 v -0.4473 0.36392 0.50049 v -0.47744 0.25142 -0.37451 v -0.47744 0.25142 -0.49951 v -0.47744 0.25142 -0.24951 v -0.47744 0.25142 -0.12451 v -0.47744 0.25142 0.00049 v -0.47744 0.25142 0.12549 v -0.47744 0.25142 0.25049 v -0.47744 0.25142 0.37549 v -0.47744 0.25142 0.50049 v -0.4473 0.13892 -0.37451 v -0.4473 0.13892 -0.49951 v -0.4473 0.13892 -0.24951 v -0.4473 0.13892 -0.12451 v -0.4473 0.13892 0.00049 v -0.4473 0.13892 0.12549 v -0.4473 0.13892 0.25049 v -0.4473 0.13892 0.37549 v -0.4473 0.13892 0.50049 v -0.36494 0.05656 -0.37451 v -0.36494 0.05656 -0.49951 v -0.36494 0.05656 -0.24951 v -0.36494 0.05656 -0.12451 v -0.36494 0.05656 0.00049 v -0.36494 0.05656 0.12549 v -0.36494 0.05656 0.25049 v -0.36494 0.05656 0.37549 v -0.36494 0.05656 0.50049 v -0.25244 0.02642 -0.37451 v -0.25244 0.02642 -0.49951 v -0.25244 0.02642 -0.24951 v -0.25244 0.02642 -0.12451 v -0.25244 0.02642 0.00049 v -0.25244 0.02642 0.12549 v -0.25244 0.02642 0.25049 v -0.25244 0.02642 0.37549 v -0.25244 0.02642 0.50049 v -0.13994 0.05656 -0.37451 v -0.13994 0.05656 -0.49951 v -0.13994 0.05656 -0.24951 v -0.13994 0.05656 -0.12451 v -0.13994 0.05656 0.00049 v -0.13994 0.05656 0.12549 v -0.13994 0.05656 0.25049 v -0.13994 0.05656 0.37549 v -0.13994 0.05656 0.50049 v -0.05759 0.13892 -0.37451 v -0.05759 0.13892 -0.49951 v -0.05759 0.13892 -0.24951 v -0.05759 0.13892 -0.12451 v -0.05759 0.13892 0.00049 v -0.05759 0.13892 0.12549 v -0.05759 0.13892 0.25049 v -0.05759 0.13892 0.37549 v -0.05759 0.13892 0.50049 v 0.4473 0.36685 -0.37457 v 0.47745 0.25435 -0.37457 v 0.47745 0.25435 -0.49957 v 0.4473 0.36685 -0.49957 v 0.4473 0.36685 -0.24957 v 0.47745 0.25435 -0.24957 v 0.4473 0.36685 -0.12457 v 0.47745 0.25435 -0.12457 v 0.4473 0.36685 0.00043 v 0.47745 0.25435 0.00043 v 0.4473 0.36685 0.12543 v 0.47745 0.25435 0.12543 v 0.4473 0.36685 0.25043 v 0.47745 0.25435 0.25043 v 0.4473 0.36685 0.37543 v 0.47745 0.25435 0.37543 v 0.4473 0.36685 0.50043 v 0.47745 0.25435 0.50043 v 0.36495 0.44921 -0.37457 v 0.36495 0.44921 -0.49957 v 0.36495 0.44921 -0.24957 v 0.36495 0.44921 -0.12457 v 0.36495 0.44921 0.00043 v 0.36495 0.44921 0.12543 v 0.36495 0.44921 0.25043 v 0.36495 0.44921 0.37543 v 0.36495 0.44921 0.50043 v 0.25245 0.47935 -0.37457 v 0.25245 0.47935 -0.49957 v 0.25245 0.47935 -0.24957 v 0.25245 0.47935 -0.12457 v 0.25245 0.47935 0.00043 v 0.25245 0.47935 0.12543 v 0.25245 0.47935 0.25043 v 0.25245 0.47935 0.37543 v 0.25245 0.47935 0.50043 v 0.13995 0.44921 -0.37457 v 0.13995 0.44921 -0.49957 v 0.13995 0.44921 -0.24957 v 0.13995 0.44921 -0.12457 v 0.13995 0.44921 0.00043 v 0.13995 0.44921 0.12543 v 0.13995 0.44921 0.25043 v 0.13995 0.44921 0.37543 v 0.13995 0.44921 0.50043 v 0.05759 0.36685 -0.37457 v 0.05759 0.36685 -0.49957 v 0.05759 0.36685 -0.24957 v 0.05759 0.36685 -0.12457 v 0.05759 0.36685 0.00043 v 0.05759 0.36685 0.12543 v 0.05759 0.36685 0.25043 v 0.05759 0.36685 0.37543 v 0.05759 0.36685 0.50043 v 0.02745 0.25435 -0.37457 v 0.02745 0.25435 -0.49957 v 0.02745 0.25435 -0.24957 v 0.02745 0.25435 -0.12457 v 0.02745 0.25435 0.00043 v 0.02745 0.25435 0.12543 v 0.02745 0.25435 0.25043 v 0.02745 0.25435 0.37543 v 0.02745 0.25435 0.50043 v 0.05759 0.14185 -0.37457 v 0.05759 0.14185 -0.49957 v 0.05759 0.14185 -0.24957 v 0.05759 0.14185 -0.12457 v 0.05759 0.14185 0.00043 v 0.05759 0.14185 0.12543 v 0.05759 0.14185 0.25043 v 0.05759 0.14185 0.37543 v 0.05759 0.14185 0.50043 v 0.13995 0.0595 -0.37457 v 0.13995 0.0595 -0.49957 v 0.13995 0.0595 -0.24957 v 0.13995 0.0595 -0.12457 v 0.13995 0.0595 0.00043 v 0.13995 0.0595 0.12543 v 0.13995 0.0595 0.25043 v 0.13995 0.0595 0.37543 v 0.13995 0.0595 0.50043 v 0.25245 0.02935 -0.37457 v 0.25245 0.02935 -0.49957 v 0.25245 0.02935 -0.24957 v 0.25245 0.02935 -0.12457 v 0.25245 0.02935 0.00043 v 0.25245 0.02935 0.12543 v 0.25245 0.02935 0.25043 v 0.25245 0.02935 0.37543 v 0.25245 0.02935 0.50043 v 0.36495 0.0595 -0.37457 v 0.36495 0.0595 -0.49957 v 0.36495 0.0595 -0.24957 v 0.36495 0.0595 -0.12457 v 0.36495 0.0595 0.00043 v 0.36495 0.0595 0.12543 v 0.36495 0.0595 0.25043 v 0.36495 0.0595 0.37543 v 0.36495 0.0595 0.50043 v 0.4473 0.14185 -0.37457 v 0.4473 0.14185 -0.49957 v 0.4473 0.14185 -0.24957 v 0.4473 0.14185 -0.12457 v 0.4473 0.14185 0.00043 v 0.4473 0.14185 0.12543 v 0.4473 0.14185 0.25043 v 0.4473 0.14185 0.37543 v 0.4473 0.14185 0.50043 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.75717 -0 -0.65322 vn 0.65571 0.37859 -0.65323 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.86601 0.50002 0 vn 1 -0 -0 vn 0.65571 0.37859 0.65323 vn 0.75717 -0 0.65322 vn 0.50002 0.86601 0 vn 0.37859 0.65571 -0.65323 vn 0.50002 0.86601 0 vn 0.50002 0.86601 0 vn 0.50002 0.86601 0 vn 0.50002 0.86601 0 vn 0.50002 0.86601 0 vn 0.50002 0.86601 0 vn 0.37859 0.65571 0.65323 vn 0 1 0 vn 0 0.75717 -0.65322 vn 0 1 0 vn -0 1 0 vn -0 1 0 vn 0 1 0 vn 0 1 0 vn 0 1 0 vn -0 0.75717 0.65322 vn -0.49999 0.86603 -0 vn -0.37857 0.65572 -0.65324 vn -0.49999 0.86603 -0 vn -0.49999 0.86603 -0 vn -0.49999 0.86603 -0 vn -0.49999 0.86603 -0 vn -0.49999 0.86603 -0 vn -0.49999 0.86603 -0 vn -0.37857 0.65572 0.65324 vn -0.86602 0.50001 -0 vn -0.65572 0.37859 -0.65322 vn -0.86602 0.50001 -0 vn -0.86602 0.50001 -0 vn -0.86602 0.50001 -0 vn -0.86602 0.50001 -0 vn -0.86602 0.50001 -0 vn -0.86602 0.50001 -0 vn -0.65572 0.37859 0.65322 vn -1 -0 0 vn -0.75716 -0 -0.65323 vn -1 -0 0 vn -1 -0 0 vn -1 -0 0 vn -1 -0 0 vn -1 -0 0 vn -1 -0 0 vn -0.75716 -0 0.65323 vn -0.86603 -0.49998 0 vn -0.65573 -0.37857 -0.65322 vn -0.86603 -0.49998 0 vn -0.86603 -0.49998 0 vn -0.86603 -0.49998 0 vn -0.86603 -0.49998 0 vn -0.86603 -0.49998 0 vn -0.86603 -0.49998 0 vn -0.65573 -0.37857 0.65322 vn -0.49998 -0.86603 -0 vn -0.37857 -0.65573 -0.65322 vn -0.49998 -0.86603 -0 vn -0.49998 -0.86603 -0 vn -0.49998 -0.86603 -0 vn -0.49998 -0.86603 -0 vn -0.49998 -0.86603 -0 vn -0.49998 -0.86603 -0 vn -0.37857 -0.65573 0.65322 vn 0 -1 -0 vn 0 -0.75716 -0.65323 vn 0 -1 -0 vn 0 -1 -0 vn -0 -1 -0 vn 0 -1 -0 vn -0 -1 -0 vn 0 -1 -0 vn 0 -0.75716 0.65323 vn 0.50001 -0.86602 0 vn 0.37859 -0.65572 -0.65322 vn 0.50001 -0.86602 0 vn 0.50001 -0.86602 0 vn 0.50001 -0.86602 0 vn 0.50001 -0.86602 0 vn 0.50001 -0.86602 0 vn 0.50001 -0.86602 0 vn 0.37859 -0.65572 0.65322 vn 0.86603 -0.49999 -0 vn 0.65572 -0.37857 -0.65324 vn 0.86603 -0.49999 -0 vn 0.86603 -0.49999 -0 vn 0.86603 -0.49999 -0 vn 0.86603 -0.49999 -0 vn 0.86603 -0.49999 -0 vn 0.86603 -0.49999 -0 vn 0.65572 -0.37857 0.65324 vn 0.86603 0.49999 -0 vn 1 0 0 vn 0.75717 0 -0.65322 vn 0.65572 0.37857 -0.65324 vn 0.86603 0.49999 -0 vn 1 0 0 vn 0.86603 0.49999 -0 vn 1 0 0 vn 0.86603 0.49999 -0 vn 1 0 -0 vn 0.86603 0.49999 0 vn 1 0 -0 vn 0.86603 0.49999 0 vn 1 0 -0 vn 0.86603 0.49999 0 vn 1 0 -0 vn 0.65572 0.37857 0.65324 vn 0.75717 0 0.65322 vn 0.50001 0.86602 -0 vn 0.37859 0.65572 -0.65322 vn 0.50001 0.86602 -0 vn 0.50001 0.86602 -0 vn 0.50001 0.86602 -0 vn 0.50001 0.86602 -0 vn 0.50001 0.86602 -0 vn 0.50001 0.86602 -0 vn 0.37859 0.65572 0.65322 vn 0 1 -0 vn -0 0.75716 -0.65323 vn 0 1 -0 vn 0 1 -0 vn 0 1 -0 vn -0 1 -0 vn -0 1 0 vn -0 1 -0 vn 0 0.75716 0.65323 vn -0.49998 0.86603 0 vn -0.37857 0.65573 -0.65322 vn -0.49998 0.86603 0 vn -0.49998 0.86603 0 vn -0.49998 0.86603 0 vn -0.49998 0.86603 0 vn -0.49998 0.86603 0 vn -0.49998 0.86603 0 vn -0.37857 0.65573 0.65322 vn -0.86603 0.49998 0 vn -0.65573 0.37857 -0.65322 vn -0.86603 0.49998 0 vn -0.86603 0.49998 0 vn -0.86603 0.49998 0 vn -0.86603 0.49998 0 vn -0.86603 0.49998 0 vn -0.86603 0.49998 0 vn -0.65573 0.37857 0.65322 vn -1 0 0 vn -0.75716 0 -0.65323 vn -1 0 0 vn -1 0 0 vn -1 0 0 vn -1 0 0 vn -1 0 0 vn -1 -0 -0 vn -0.75716 -0 0.65323 vn -0.86602 -0.50001 0 vn -0.65572 -0.37859 -0.65322 vn -0.86602 -0.50001 0 vn -0.86602 -0.50001 0 vn -0.86602 -0.50001 0 vn -0.86602 -0.50001 0 vn -0.86602 -0.50001 0 vn -0.86602 -0.50001 -0 vn -0.65572 -0.37859 0.65322 vn -0.49999 -0.86603 0 vn -0.37857 -0.65572 -0.65324 vn -0.49999 -0.86603 0 vn -0.49999 -0.86603 0 vn -0.49999 -0.86603 0 vn -0.49999 -0.86603 0 vn -0.49999 -0.86603 0 vn -0.49999 -0.86603 0 vn -0.37857 -0.65572 0.65324 vn -0 -1 0 vn -0 -0.75717 -0.65322 vn -0 -1 0 vn -0 -1 0 vn -0 -1 0 vn -0 -1 0 vn -0 -1 0 vn -0 -1 0 vn 0 -0.75717 0.65322 vn 0.50002 -0.86601 0 vn 0.37859 -0.65571 -0.65323 vn 0.50002 -0.86601 0 vn 0.50002 -0.86601 0 vn 0.50002 -0.86601 0 vn 0.50002 -0.86601 0 vn 0.50002 -0.86601 0 vn 0.50002 -0.86601 0 vn 0.37859 -0.65571 0.65323 vn 0.86601 -0.50002 0 vn 0.65571 -0.37859 -0.65323 vn 0.86601 -0.50002 0 vn 0.86601 -0.50002 0 vn 0.86601 -0.50002 -0 vn 0.86601 -0.50002 -0 vn 0.86601 -0.50002 -0 vn 0.86601 -0.50002 -0 vn 0.65571 -0.37859 0.65323 vt -0.72572 0.60094 vt -0.73837 0.49867 vt -0.74127 0.49867 vt -0.73173 0.60094 vt -0.71387 0.60094 vt -0.73259 0.49867 vt -0.68077 0.60094 vt -0.71552 0.49867 vt -0.49992 0.60094 vt -0.49983 0.49867 vt -0.3192 0.60094 vt -0.28447 0.49867 vt -0.28612 0.60094 vt -0.26741 0.49867 vt -0.27428 0.60094 vt -0.26163 0.49867 vt -0.26826 0.60094 vt -0.25873 0.49867 vt -0.69283 0.6758 vt -0.70641 0.6758 vt -0.66809 0.6758 vt -0.61618 0.6758 vt -0.49997 0.6758 vt -0.38378 0.6758 vt -0.3319 0.6758 vt -0.30716 0.6758 vt -0.29359 0.6758 vt -0.65492 0.70321 vt -0.67499 0.70321 vt -0.62365 0.70321 vt -0.57373 0.70321 vt -0.49998 0.70321 vt -0.42624 0.70321 vt -0.37633 0.70321 vt -0.34507 0.70321 vt -0.325 0.70321 vt -0.62757 0.6758 vt -0.64891 0.6758 vt -0.59636 0.6758 vt -0.55277 0.6758 vt -0.49999 0.6758 vt -0.44721 0.6758 vt -0.40363 0.6758 vt -0.37242 0.6758 vt -0.35108 0.6758 vt -0.61148 0.60094 vt -0.63368 0.60094 vt -0.58179 0.60094 vt -0.54351 0.60094 vt -0.49999 0.60094 vt -0.45647 0.60094 vt -0.41819 0.60094 vt -0.38851 0.60094 vt -0.36631 0.60094 vt -0.60659 0.49867 vt -0.62893 0.49867 vt -0.57739 0.49867 vt -0.54087 0.49867 vt -0.49999 0.49867 vt -0.45911 0.49867 vt -0.4226 0.49867 vt -0.3934 0.49867 vt -0.37106 0.49867 vt -0.61148 0.3964 vt -0.63368 0.3964 vt -0.58179 0.3964 vt -0.54351 0.3964 vt -0.49999 0.3964 vt -0.45647 0.3964 vt -0.41819 0.3964 vt -0.38851 0.3964 vt -0.36631 0.3964 vt -0.62757 0.32152 vt -0.64891 0.32152 vt -0.59636 0.32152 vt -0.55277 0.32152 vt -0.49999 0.32152 vt -0.44721 0.32152 vt -0.40363 0.32152 vt -0.37242 0.32152 vt -0.35108 0.32152 vt -0.65492 0.29412 vt -0.67499 0.29412 vt -0.62365 0.29412 vt -0.57373 0.29412 vt -0.49998 0.29412 vt -0.42624 0.29412 vt -0.37633 0.29412 vt -0.34507 0.29412 vt -0.325 0.29412 vt -0.69283 0.32152 vt -0.70641 0.32152 vt -0.66809 0.32152 vt -0.61618 0.32152 vt -0.49997 0.32152 vt -0.38378 0.32152 vt -0.3319 0.32152 vt -0.30716 0.32152 vt -0.29359 0.32152 vt -0.72572 0.3964 vt -0.73173 0.3964 vt -0.71387 0.3964 vt -0.68077 0.3964 vt -0.49992 0.3964 vt -0.3192 0.3964 vt -0.28612 0.3964 vt -0.27428 0.3964 vt -0.26826 0.3964 vt 0.1115 0.6036 vt 0.1066 0.50133 vt 0.12894 0.50133 vt 0.13369 0.6036 vt 0.08181 0.6036 vt 0.0774 0.50133 vt 0.04354 0.6036 vt 0.04089 0.50133 vt 0.00001 0.6036 vt 0.00001 0.50133 vt -0.04352 0.6036 vt -0.04087 0.50133 vt -0.08179 0.6036 vt -0.07739 0.50133 vt -0.11148 0.6036 vt -0.10659 0.50133 vt -0.13368 0.6036 vt -0.12893 0.50133 vt 0.12758 0.67848 vt 0.14892 0.67848 vt 0.09637 0.67848 vt 0.05279 0.67848 vt 0.00001 0.67848 vt -0.05277 0.67848 vt -0.09636 0.67848 vt -0.12757 0.67848 vt -0.14891 0.67848 vt 0.15493 0.70588 vt 0.175 0.70588 vt 0.12366 0.70588 vt 0.07376 0.70588 vt 0.00002 0.70588 vt -0.07373 0.70588 vt -0.12365 0.70588 vt -0.15492 0.70588 vt -0.17499 0.70588 vt 0.19284 0.67848 vt 0.20641 0.67848 vt 0.1681 0.67848 vt 0.11622 0.67848 vt 0.00003 0.67848 vt -0.11618 0.67848 vt -0.16809 0.67848 vt -0.19283 0.67848 vt -0.20641 0.67848 vt 0.22572 0.6036 vt 0.23174 0.6036 vt 0.21388 0.6036 vt 0.18081 0.6036 vt 0.00008 0.6036 vt -0.18078 0.6036 vt -0.21387 0.6036 vt -0.22572 0.6036 vt -0.23174 0.6036 vt 0.23837 0.50133 vt 0.24127 0.50133 vt 0.23259 0.50133 vt 0.21553 0.50133 vt 0.00017 0.50133 vt -0.21551 0.50133 vt -0.23258 0.50133 vt -0.23837 0.50133 vt -0.24127 0.50133 vt 0.22572 0.39906 vt 0.23174 0.39906 vt 0.21388 0.39906 vt 0.18081 0.39906 vt 0.00008 0.39906 vt -0.18078 0.39906 vt -0.21387 0.39906 vt -0.22572 0.39906 vt -0.23174 0.39906 vt 0.19284 0.3242 vt 0.20641 0.3242 vt 0.1681 0.3242 vt 0.11622 0.3242 vt 0.00003 0.3242 vt -0.11618 0.3242 vt -0.16809 0.3242 vt -0.19283 0.3242 vt -0.20641 0.3242 vt 0.15493 0.29679 vt 0.175 0.29679 vt 0.12366 0.29679 vt 0.07376 0.29679 vt 0.00002 0.29679 vt -0.07373 0.29679 vt -0.12365 0.29679 vt -0.15492 0.29679 vt -0.17499 0.29679 vt 0.12758 0.3242 vt 0.14892 0.3242 vt 0.09637 0.3242 vt 0.05279 0.3242 vt 0.00001 0.3242 vt -0.05277 0.3242 vt -0.09636 0.3242 vt -0.12757 0.3242 vt -0.14891 0.3242 vt 0.1115 0.39906 vt 0.13369 0.39906 vt 0.08181 0.39906 vt 0.04354 0.39906 vt 0.00001 0.39906 vt -0.04352 0.39906 vt -0.08179 0.39906 vt -0.11148 0.39906 vt -0.13368 0.39906 f 1/1/1 2/2/2 3/3/3 f 3/3/3 4/4/4 1/1/1 f 5/5/5 6/6/6 2/2/2 f 2/2/2 1/1/1 5/5/5 f 7/7/7 8/8/8 6/6/6 f 6/6/6 5/5/5 7/7/7 f 9/9/9 10/10/10 8/8/8 f 8/8/8 7/7/7 9/9/9 f 11/11/11 12/12/12 10/10/10 f 10/10/10 9/9/9 11/11/11 f 13/13/13 14/14/14 12/12/12 f 12/12/12 11/11/11 13/13/13 f 15/15/15 16/16/16 14/14/14 f 14/14/14 13/13/13 15/15/15 f 17/17/17 18/18/18 16/16/16 f 16/16/16 15/15/15 17/17/17 f 19/19/19 1/1/1 4/4/4 f 4/4/4 20/20/20 19/19/19 f 21/21/21 5/5/5 1/1/1 f 1/1/1 19/19/19 21/21/21 f 22/22/22 7/7/7 5/5/5 f 5/5/5 21/21/21 22/22/22 f 23/23/23 9/9/9 7/7/7 f 7/7/7 22/22/22 23/23/23 f 24/24/24 11/11/11 9/9/9 f 9/9/9 23/23/23 24/24/24 f 25/25/25 13/13/13 11/11/11 f 11/11/11 24/24/24 25/25/25 f 26/26/26 15/15/15 13/13/13 f 13/13/13 25/25/25 26/26/26 f 27/27/27 17/17/17 15/15/15 f 15/15/15 26/26/26 27/27/27 f 28/28/28 19/19/19 20/20/20 f 20/20/20 29/29/29 28/28/28 f 30/30/30 21/21/21 19/19/19 f 19/19/19 28/28/28 30/30/30 f 31/31/31 22/22/22 21/21/21 f 21/21/21 30/30/30 31/31/31 f 32/32/32 23/23/23 22/22/22 f 22/22/22 31/31/31 32/32/32 f 33/33/33 24/24/24 23/23/23 f 23/23/23 32/32/32 33/33/33 f 34/34/34 25/25/25 24/24/24 f 24/24/24 33/33/33 34/34/34 f 35/35/35 26/26/26 25/25/25 f 25/25/25 34/34/34 35/35/35 f 36/36/36 27/27/27 26/26/26 f 26/26/26 35/35/35 36/36/36 f 37/37/37 28/28/28 29/29/29 f 29/29/29 38/38/38 37/37/37 f 39/39/39 30/30/30 28/28/28 f 28/28/28 37/37/37 39/39/39 f 40/40/40 31/31/31 30/30/30 f 30/30/30 39/39/39 40/40/40 f 41/41/41 32/32/32 31/31/31 f 31/31/31 40/40/40 41/41/41 f 42/42/42 33/33/33 32/32/32 f 32/32/32 41/41/41 42/42/42 f 43/43/43 34/34/34 33/33/33 f 33/33/33 42/42/42 43/43/43 f 44/44/44 35/35/35 34/34/34 f 34/34/34 43/43/43 44/44/44 f 45/45/45 36/36/36 35/35/35 f 35/35/35 44/44/44 45/45/45 f 46/46/46 37/37/37 38/38/38 f 38/38/38 47/47/47 46/46/46 f 48/48/48 39/39/39 37/37/37 f 37/37/37 46/46/46 48/48/48 f 49/49/49 40/40/40 39/39/39 f 39/39/39 48/48/48 49/49/49 f 50/50/50 41/41/41 40/40/40 f 40/40/40 49/49/49 50/50/50 f 51/51/51 42/42/42 41/41/41 f 41/41/41 50/50/50 51/51/51 f 52/52/52 43/43/43 42/42/42 f 42/42/42 51/51/51 52/52/52 f 53/53/53 44/44/44 43/43/43 f 43/43/43 52/52/52 53/53/53 f 54/54/54 45/45/45 44/44/44 f 44/44/44 53/53/53 54/54/54 f 55/55/55 46/46/46 47/47/47 f 47/47/47 56/56/56 55/55/55 f 57/57/57 48/48/48 46/46/46 f 46/46/46 55/55/55 57/57/57 f 58/58/58 49/49/49 48/48/48 f 48/48/48 57/57/57 58/58/58 f 59/59/59 50/50/50 49/49/49 f 49/49/49 58/58/58 59/59/59 f 60/60/60 51/51/51 50/50/50 f 50/50/50 59/59/59 60/60/60 f 61/61/61 52/52/52 51/51/51 f 51/51/51 60/60/60 61/61/61 f 62/62/62 53/53/53 52/52/52 f 52/52/52 61/61/61 62/62/62 f 63/63/63 54/54/54 53/53/53 f 53/53/53 62/62/62 63/63/63 f 64/64/64 55/55/55 56/56/56 f 56/56/56 65/65/65 64/64/64 f 66/66/66 57/57/57 55/55/55 f 55/55/55 64/64/64 66/66/66 f 67/67/67 58/58/58 57/57/57 f 57/57/57 66/66/66 67/67/67 f 68/68/68 59/59/59 58/58/58 f 58/58/58 67/67/67 68/68/68 f 69/69/69 60/60/60 59/59/59 f 59/59/59 68/68/68 69/69/69 f 70/70/70 61/61/61 60/60/60 f 60/60/60 69/69/69 70/70/70 f 71/71/71 62/62/62 61/61/61 f 61/61/61 70/70/70 71/71/71 f 72/72/72 63/63/63 62/62/62 f 62/62/62 71/71/71 72/72/72 f 73/73/73 64/64/64 65/65/65 f 65/65/65 74/74/74 73/73/73 f 75/75/75 66/66/66 64/64/64 f 64/64/64 73/73/73 75/75/75 f 76/76/76 67/67/67 66/66/66 f 66/66/66 75/75/75 76/76/76 f 77/77/77 68/68/68 67/67/67 f 67/67/67 76/76/76 77/77/77 f 78/78/78 69/69/69 68/68/68 f 68/68/68 77/77/77 78/78/78 f 79/79/79 70/70/70 69/69/69 f 69/69/69 78/78/78 79/79/79 f 80/80/80 71/71/71 70/70/70 f 70/70/70 79/79/79 80/80/80 f 81/81/81 72/72/72 71/71/71 f 71/71/71 80/80/80 81/81/81 f 82/82/82 73/73/73 74/74/74 f 74/74/74 83/83/83 82/82/82 f 84/84/84 75/75/75 73/73/73 f 73/73/73 82/82/82 84/84/84 f 85/85/85 76/76/76 75/75/75 f 75/75/75 84/84/84 85/85/85 f 86/86/86 77/77/77 76/76/76 f 76/76/76 85/85/85 86/86/86 f 87/87/87 78/78/78 77/77/77 f 77/77/77 86/86/86 87/87/87 f 88/88/88 79/79/79 78/78/78 f 78/78/78 87/87/87 88/88/88 f 89/89/89 80/80/80 79/79/79 f 79/79/79 88/88/88 89/89/89 f 90/90/90 81/81/81 80/80/80 f 80/80/80 89/89/89 90/90/90 f 91/91/91 82/82/82 83/83/83 f 83/83/83 92/92/92 91/91/91 f 93/93/93 84/84/84 82/82/82 f 82/82/82 91/91/91 93/93/93 f 94/94/94 85/85/85 84/84/84 f 84/84/84 93/93/93 94/94/94 f 95/95/95 86/86/86 85/85/85 f 85/85/85 94/94/94 95/95/95 f 96/96/96 87/87/87 86/86/86 f 86/86/86 95/95/95 96/96/96 f 97/97/97 88/88/88 87/87/87 f 87/87/87 96/96/96 97/97/97 f 98/98/98 89/89/89 88/88/88 f 88/88/88 97/97/97 98/98/98 f 99/99/99 90/90/90 89/89/89 f 89/89/89 98/98/98 99/99/99 f 100/100/100 91/91/91 92/92/92 f 92/92/92 101/101/101 100/100/100 f 102/102/102 93/93/93 91/91/91 f 91/91/91 100/100/100 102/102/102 f 103/103/103 94/94/94 93/93/93 f 93/93/93 102/102/102 103/103/103 f 104/104/104 95/95/95 94/94/94 f 94/94/94 103/103/103 104/104/104 f 105/105/105 96/96/96 95/95/95 f 95/95/95 104/104/104 105/105/105 f 106/106/106 97/97/97 96/96/96 f 96/96/96 105/105/105 106/106/106 f 107/107/107 98/98/98 97/97/97 f 97/97/97 106/106/106 107/107/107 f 108/108/108 99/99/99 98/98/98 f 98/98/98 107/107/107 108/108/108 f 2/2/2 100/100/100 101/101/101 f 101/101/101 3/3/3 2/2/2 f 6/6/6 102/102/102 100/100/100 f 100/100/100 2/2/2 6/6/6 f 8/8/8 103/103/103 102/102/102 f 102/102/102 6/6/6 8/8/8 f 10/10/10 104/104/104 103/103/103 f 103/103/103 8/8/8 10/10/10 f 12/12/12 105/105/105 104/104/104 f 104/104/104 10/10/10 12/12/12 f 14/14/14 106/106/106 105/105/105 f 105/105/105 12/12/12 14/14/14 f 16/16/16 107/107/107 106/106/106 f 106/106/106 14/14/14 16/16/16 f 18/18/18 108/108/108 107/107/107 f 107/107/107 16/16/16 18/18/18 f 18/18/18 17/17/17 27/27/27 f 27/27/27 36/36/36 45/45/45 f 45/45/45 54/54/54 63/63/63 f 63/63/63 72/72/72 81/81/81 f 81/81/81 90/90/90 99/99/99 f 99/99/99 108/108/108 18/18/18 f 18/18/18 27/27/27 45/45/45 f 45/45/45 63/63/63 81/81/81 f 81/81/81 99/99/99 18/18/18 f 18/18/18 45/45/45 81/81/81 f 101/101/101 92/92/92 83/83/83 f 83/83/83 74/74/74 65/65/65 f 65/65/65 56/56/56 47/47/47 f 47/47/47 38/38/38 29/29/29 f 29/29/29 20/20/20 4/4/4 f 4/4/4 3/3/3 101/101/101 f 101/101/101 83/83/83 65/65/65 f 65/65/65 47/47/47 29/29/29 f 29/29/29 4/4/4 101/101/101 f 101/101/101 65/65/65 29/29/29 f 109/109/109 110/110/110 111/111/111 f 111/111/111 112/112/112 109/109/109 f 113/113/113 114/114/114 110/110/110 f 110/110/110 109/109/109 113/113/113 f 115/115/115 116/116/116 114/114/114 f 114/114/114 113/113/113 115/115/115 f 117/117/117 118/118/118 116/116/116 f 116/116/116 115/115/115 117/117/117 f 119/119/119 120/120/120 118/118/118 f 118/118/118 117/117/117 119/119/119 f 121/121/121 122/122/122 120/120/120 f 120/120/120 119/119/119 121/121/121 f 123/123/123 124/124/124 122/122/122 f 122/122/122 121/121/121 123/123/123 f 125/125/125 126/126/126 124/124/124 f 124/124/124 123/123/123 125/125/125 f 127/127/127 109/109/109 112/112/112 f 112/112/112 128/128/128 127/127/127 f 129/129/129 113/113/113 109/109/109 f 109/109/109 127/127/127 129/129/129 f 130/130/130 115/115/115 113/113/113 f 113/113/113 129/129/129 130/130/130 f 131/131/131 117/117/117 115/115/115 f 115/115/115 130/130/130 131/131/131 f 132/132/132 119/119/119 117/117/117 f 117/117/117 131/131/131 132/132/132 f 133/133/133 121/121/121 119/119/119 f 119/119/119 132/132/132 133/133/133 f 134/134/134 123/123/123 121/121/121 f 121/121/121 133/133/133 134/134/134 f 135/135/135 125/125/125 123/123/123 f 123/123/123 134/134/134 135/135/135 f 136/136/136 127/127/127 128/128/128 f 128/128/128 137/137/137 136/136/136 f 138/138/138 129/129/129 127/127/127 f 127/127/127 136/136/136 138/138/138 f 139/139/139 130/130/130 129/129/129 f 129/129/129 138/138/138 139/139/139 f 140/140/140 131/131/131 130/130/130 f 130/130/130 139/139/139 140/140/140 f 141/141/141 132/132/132 131/131/131 f 131/131/131 140/140/140 141/141/141 f 142/142/142 133/133/133 132/132/132 f 132/132/132 141/141/141 142/142/142 f 143/143/143 134/134/134 133/133/133 f 133/133/133 142/142/142 143/143/143 f 144/144/144 135/135/135 134/134/134 f 134/134/134 143/143/143 144/144/144 f 145/145/145 136/136/136 137/137/137 f 137/137/137 146/146/146 145/145/145 f 147/147/147 138/138/138 136/136/136 f 136/136/136 145/145/145 147/147/147 f 148/148/148 139/139/139 138/138/138 f 138/138/138 147/147/147 148/148/148 f 149/149/149 140/140/140 139/139/139 f 139/139/139 148/148/148 149/149/149 f 150/150/150 141/141/141 140/140/140 f 140/140/140 149/149/149 150/150/150 f 151/151/151 142/142/142 141/141/141 f 141/141/141 150/150/150 151/151/151 f 152/152/152 143/143/143 142/142/142 f 142/142/142 151/151/151 152/152/152 f 153/153/153 144/144/144 143/143/143 f 143/143/143 152/152/152 153/153/153 f 154/154/154 145/145/145 146/146/146 f 146/146/146 155/155/155 154/154/154 f 156/156/156 147/147/147 145/145/145 f 145/145/145 154/154/154 156/156/156 f 157/157/157 148/148/148 147/147/147 f 147/147/147 156/156/156 157/157/157 f 158/158/158 149/149/149 148/148/148 f 148/148/148 157/157/157 158/158/158 f 159/159/159 150/150/150 149/149/149 f 149/149/149 158/158/158 159/159/159 f 160/160/160 151/151/151 150/150/150 f 150/150/150 159/159/159 160/160/160 f 161/161/161 152/152/152 151/151/151 f 151/151/151 160/160/160 161/161/161 f 162/162/162 153/153/153 152/152/152 f 152/152/152 161/161/161 162/162/162 f 163/163/163 154/154/154 155/155/155 f 155/155/155 164/164/164 163/163/163 f 165/165/165 156/156/156 154/154/154 f 154/154/154 163/163/163 165/165/165 f 166/166/166 157/157/157 156/156/156 f 156/156/156 165/165/165 166/166/166 f 167/167/167 158/158/158 157/157/157 f 157/157/157 166/166/166 167/167/167 f 168/168/168 159/159/159 158/158/158 f 158/158/158 167/167/167 168/168/168 f 169/169/169 160/160/160 159/159/159 f 159/159/159 168/168/168 169/169/169 f 170/170/170 161/161/161 160/160/160 f 160/160/160 169/169/169 170/170/170 f 171/171/171 162/162/162 161/161/161 f 161/161/161 170/170/170 171/171/171 f 172/172/172 163/163/163 164/164/164 f 164/164/164 173/173/173 172/172/172 f 174/174/174 165/165/165 163/163/163 f 163/163/163 172/172/172 174/174/174 f 175/175/175 166/166/166 165/165/165 f 165/165/165 174/174/174 175/175/175 f 176/176/176 167/167/167 166/166/166 f 166/166/166 175/175/175 176/176/176 f 177/177/177 168/168/168 167/167/167 f 167/167/167 176/176/176 177/177/177 f 178/178/178 169/169/169 168/168/168 f 168/168/168 177/177/177 178/178/178 f 179/179/179 170/170/170 169/169/169 f 169/169/169 178/178/178 179/179/179 f 180/180/180 171/171/171 170/170/170 f 170/170/170 179/179/179 180/180/180 f 181/181/181 172/172/172 173/173/173 f 173/173/173 182/182/182 181/181/181 f 183/183/183 174/174/174 172/172/172 f 172/172/172 181/181/181 183/183/183 f 184/184/184 175/175/175 174/174/174 f 174/174/174 183/183/183 184/184/184 f 185/185/185 176/176/176 175/175/175 f 175/175/175 184/184/184 185/185/185 f 186/186/186 177/177/177 176/176/176 f 176/176/176 185/185/185 186/186/186 f 187/187/187 178/178/178 177/177/177 f 177/177/177 186/186/186 187/187/187 f 188/188/188 179/179/179 178/178/178 f 178/178/178 187/187/187 188/188/188 f 189/189/189 180/180/180 179/179/179 f 179/179/179 188/188/188 189/189/189 f 190/190/190 181/181/181 182/182/182 f 182/182/182 191/191/191 190/190/190 f 192/192/192 183/183/183 181/181/181 f 181/181/181 190/190/190 192/192/192 f 193/193/193 184/184/184 183/183/183 f 183/183/183 192/192/192 193/193/193 f 194/194/194 185/185/185 184/184/184 f 184/184/184 193/193/193 194/194/194 f 195/195/195 186/186/186 185/185/185 f 185/185/185 194/194/194 195/195/195 f 196/196/196 187/187/187 186/186/186 f 186/186/186 195/195/195 196/196/196 f 197/197/197 188/188/188 187/187/187 f 187/187/187 196/196/196 197/197/197 f 198/198/198 189/189/189 188/188/188 f 188/188/188 197/197/197 198/198/198 f 199/199/199 190/190/190 191/191/191 f 191/191/191 200/200/200 199/199/199 f 201/201/201 192/192/192 190/190/190 f 190/190/190 199/199/199 201/201/201 f 202/202/202 193/193/193 192/192/192 f 192/192/192 201/201/201 202/202/202 f 203/203/203 194/194/194 193/193/193 f 193/193/193 202/202/202 203/203/203 f 204/204/204 195/195/195 194/194/194 f 194/194/194 203/203/203 204/204/204 f 205/205/205 196/196/196 195/195/195 f 195/195/195 204/204/204 205/205/205 f 206/206/206 197/197/197 196/196/196 f 196/196/196 205/205/205 206/206/206 f 207/207/207 198/198/198 197/197/197 f 197/197/197 206/206/206 207/207/207 f 208/208/208 199/199/199 200/200/200 f 200/200/200 209/209/209 208/208/208 f 210/210/210 201/201/201 199/199/199 f 199/199/199 208/208/208 210/210/210 f 211/211/211 202/202/202 201/201/201 f 201/201/201 210/210/210 211/211/211 f 212/212/212 203/203/203 202/202/202 f 202/202/202 211/211/211 212/212/212 f 213/213/213 204/204/204 203/203/203 f 203/203/203 212/212/212 213/213/213 f 214/214/214 205/205/205 204/204/204 f 204/204/204 213/213/213 214/214/214 f 215/215/215 206/206/206 205/205/205 f 205/205/205 214/214/214 215/215/215 f 216/216/216 207/207/207 206/206/206 f 206/206/206 215/215/215 216/216/216 f 110/110/110 208/208/208 209/209/209 f 209/209/209 111/111/111 110/110/110 f 114/114/114 210/210/210 208/208/208 f 208/208/208 110/110/110 114/114/114 f 116/116/116 211/211/211 210/210/210 f 210/210/210 114/114/114 116/116/116 f 118/118/118 212/212/212 211/211/211 f 211/211/211 116/116/116 118/118/118 f 120/120/120 213/213/213 212/212/212 f 212/212/212 118/118/118 120/120/120 f 122/122/122 214/214/214 213/213/213 f 213/213/213 120/120/120 122/122/122 f 124/124/124 215/215/215 214/214/214 f 214/214/214 122/122/122 124/124/124 f 126/126/126 216/216/216 215/215/215 f 215/215/215 124/124/124 126/126/126 f 126/126/126 125/125/125 135/135/135 f 135/135/135 144/144/144 153/153/153 f 153/153/153 162/162/162 171/171/171 f 171/171/171 180/180/180 189/189/189 f 189/189/189 198/198/198 207/207/207 f 207/207/207 216/216/216 126/126/126 f 126/126/126 135/135/135 153/153/153 f 153/153/153 171/171/171 189/189/189 f 189/189/189 207/207/207 126/126/126 f 126/126/126 153/153/153 189/189/189 f 209/209/209 200/200/200 191/191/191 f 191/191/191 182/182/182 173/173/173 f 173/173/173 164/164/164 155/155/155 f 155/155/155 146/146/146 137/137/137 f 137/137/137 128/128/128 112/112/112 f 112/112/112 111/111/111 209/209/209 f 209/209/209 191/191/191 173/173/173 f 173/173/173 155/155/155 137/137/137 f 137/137/137 112/112/112 209/209/209 f 209/209/209 173/173/173 137/137/137 gearhead-2-0.701/gamedata/messages.txt000066400000000000000000001406071321074026100175370ustar00rootroot00000000000000%%% GEARHEAD TEXT MESSAGES %%% GENERIC_HINT_STRING_1 GENERIC_HINT_STRING_2 GENERIC_HINT_STRING_3 GENERIC_HINT_STRING_4 GENERIC_HINT_STRING_5 GENERIC_HINT_STRING_6 PLOT_HINT_MEMO <# said "#"> SwitchPartyMode_GoClock SwitchPartyMode_GoTactics SwitchPartyMode_Fail ColorSet_0 ColorSet_1 ColorSet_2 ColorSet_3 ColorSet_4 ColorSet_5 ColorSet_6
RELATIONSHIP_-1 RELATIONSHIP_1 RELATIONSHIP_2 RELATIONSHIP_3 RELATIONSHIP_4 MechaPrize_Announce ROBOTICS_SELECTPARTS_DIRECTIONS BUILD_ROBOT_FAILED BUILD_ROBOT_START BUILD_ROBOT_SUCCESS BUILD_ROBOT_SENTIENT BUILD_ROBOT_TOO_TIRED BUILD_ROBOT_SKILL <# knows #.> BUILD_ROBOT_NO_PARTS BUILD_ROBOT_NOT_SAFE ROBOTICS_BP ROBOTICS_AP ROBOTICS_CP ROBOTICS_PP AHQRANK_5 AHQRANK_4 AHQRANK_3 AHQRANK_2 AHQRANK_1 Cancelled VERBALATTACK_NoMP TALKING_Prompt ARENA_VCHAR_RemoveCharacter ARENA_VCHAR_AssignMecha ARENA_VCHAR_ViewInventory ARENA_VCHAR_AMFP_SelectMecha ARENA_VMEK_SellMecha ARENA_VMEK_SellMechaInv ARENA_VMEK_SMI_SellItem ARENA_VMEK_ViewInventory ARENA_VMEK_EditParts ARENA_VITEM_SellItem ARENA_VITEM_GiveItem ARENA_VITEM_GiveItem_Prompt EXIT TRAINING_ImproveSkill TRAINING_NewSkill TRAINING_ReviewTalents TRAINING_ReviewCyberware TRAINING_NewTalent TRAINING_ImproveStat TRAINING_NotEnoughXP <# doesn't have enough experience points.> TRAINING_Improved <# has improved #.> TRAINING_Learned <# has learned #.> NOFREETALENTS CANTAFFORDTALENT TALENT1 TALENTDESC1 TALENT2 TALENTDESC2 TALENT3 TALENTDESC3 TALENT4 TALENTDESC4 TALENT5 TALENTDESC5 TALENT6 TALENTDESC6 TALENT7 TALENTDESC7 TALENT8 TALENTDESC8 TALENT9 TALENTDESC9 TALENT10 TALENTDESC10 TALENT11 TALENTDESC11 TALENT12 TALENTDESC12 TALENT13 TALENTDESC13 TALENT14 TALENTDESC14 TALENT15 TALENTDESC15 TALENT16 TALENTDESC16 TALENT17 TALENTDESC17 TALENT18 TALENTDESC18 TALENT19 TALENTDESC19 TALENT20 TALENTDESC20 TALENT21 TALENTDESC21 TALENT22 TALENTDESC22 TALENT23 TALENTDESC23 TALENT24 TALENTDESC24 TALENT25 TALENTDESC25 TALENT26 TALENTDESC26 TALENT27 TALENTDESC27 BUYPROMPT1 BUYPROMPT2 BUYPROMPT3 BUYPROMPT4 BUYREPLY1 BUYREPLY2 BUYREPLY3 BUYREPLY4 BUYUPGRADE BUY_YOUHAVEBOUGHT BUY_CANTAFFORD BUYCANCEL1 BUYCANCEL2 BUYCANCEL3 BUYCANCEL4 BUYNOCASH1 BUYNOCASH2 BUYNOCASH3 BUYNOCASH4 SELLPROMPT1 SELLPROMPT2 SELLPROMPT3 SELLPROMPT4 SELLREPLY1 SELLREPLY2 SELLREPLY3 SELLREPLY4 SELLCANCEL1 SELLCANCEL2 SELLCANCEL3 SELLCANCEL4 SELL_YOUHAVESOLD YouFind$ PHONE_NotFound PHONE_Prompt PHONE_GetName PHONE_NoPhone SPR_0 SPR_1 SPR_2 SPR_3 OPR_0 OPR_1 OPR_2 OPR_3 PPR_0 PPR_1 PPR_2 PPR_3 EXTRACT_OK EXTRACT_WRECK INSTALL_OK INSTALL_NOTACTIVE INSTALL_NOROOM INSTALL_NoMoney INSTALL_FAIL INSTALL_WRECK CANT_GET_? YOU_GET_? YOU_STRAP_? HANDLESS_PICKUP YOU_PUT_? GET_WHICH_ITEM? STARTRPG_NewChar <[Create New Character]> REGEN_Hunger <# is hungry.> PROCESSMOVEMENT_Crash <# has crashed! # damage.> PROCESSMOVEMENT_Fall <# has fallen! # damage.> HISTORY_AnnounceVictory <*** VICTORY!!! ***> HISTORY_Skills <# +#> HISTORY_Traits HISTORY_Faction HISTORY_FATALITIES_1 <# people died during your adventure.> HISTORY_FATALITIES_2 HISTORY_FATALITIES_3 HISTORY_FATALITIES_4 HISTORY_FATALITIES_5 HISTORY_FATALITY_1 HISTORY_FATALITY_2 HISTORY_FATALITY_3 HISTORY_FATALITY_4 HISTORY_FATALITY_5 HISTORY_RELATIONS_1 HISTORY_RELATION_1 HISTORY_RELATIONS_2 HISTORY_RELATION_2 HISTORY_RELATIONS_3 HISTORY_RELATION_3 HISTORY_RELATIONS_4 HISTORY_RELATION_4 HISTORY_RELATIONS_5 HISTORY_RELATION_5 STATRANK1 STATRANK2 STATRANK3 STATRANK4 STATRANK5 STATRANK6 STATRANK7 Status_FXDesc1 <# is hurt by poison! # damage.> Status_FXDesc2 <# is on fire! # damage.> FXDESC_CRASH <# has crashed! # damage.> FXDESC_FALL <# has fallen! # damage.> PERFORMANCE_DidWell0 PERFORMANCE_DidWell1 PERFORMANCE_DidWell2 PERFORMANCE_Base <# sings.> PERFORMANCE_Horrible0 PERFORMANCE_Horrible1 PERFORMANCE_Horrible2 PERFORMANCE_NoAudience Cancelled PCUSOP_Prompt PCAS_Prompt PCAS_Cancel < [don't use skill]> PCUS_Prompt PCUS_NotFound MEMO_ReadMemo MEMO_ReadNews MEMO_ReadEMail MEMO_ReadRumors MEMO_Personadex MEMO_NoBrowser MEMO_None MEMO_No_RUMEMO MEMO_No_NEWS MEMO_Next MEMO_Prev MEMO_Call MEMO_CALL_NoPeople <[No People Found]> AS_Email AS_XPV AS_CASHPRIZE AS_YouHaveNoClue HELP_Chara HELP_Mecha HELP_FieldHQ HELP_KeyMap HELP_PersonalHistory HELP_Exit MORETEXT_Prompt < [UP] to go up, [DOWN] to go down, [Q] or [ESC] to exit> PCATTACK_OutOfArc PCATTACK_OutOfRange HELP_Prompt ARENASCRIPT_CheckMechaEquipped SERVICES_Cyber_NoPart SERVICES_Cyber_Cancel SERVICES_Cyber_SelectSlot RANDCHAR_CityDesc RANDCHAR_SGPrompt RANDCHAR_SADesc RANDCHAR_FHPrompt RANDCHAR_FHDesc RANDCHAR_FHAccept RANDCHAR_FHDecline RANDCHAR_SelectStatsCap <# points> RANDCHAR_ASPPrompt <# points; # slots> RANDCHAR_StatPrompt RANDCHAR_ASPDesc RANDCHAR_SkillDesc RANDCHAR_SkillPrompt RANDCHAR_ASPDone < [DONE]> RANDCHAR_STPrompt RANDCHAR_STDesc RANDCHAR_JobDesc RANDCHAR_FactionPrompt RANDCHAR_MechaDesc RANDCHAR_NoFactionPlease <[No starting faction]> RANDCHAR_SMPrompt RANDCHAR_GoalPrompt RANDCHAR_FocusPrompt RANDCHAR_TraitDesc RANDCHAR_TraitPrompt RANDCHAR_TalentPrompt RANDCHAR_SelectSkin RANDCHAR_SelectHair RANDCHAR_SelectClothes RANDCHAR_RomPrompt BACKPACK_ItemTraded BACKPACK_NotTraded BACKPACK_UseInstrument BACKPACK_CantUse BACKPACK_EatItem BACKPACK_YouAreEating <# consumes the #.> BACKPACK_CantBeEaten BACKPACK_FoodSkillBoost BACKPACK_Do_EjectAmmo BACKPACK_Script_USE BACKPACK_CannotUseScript BACKPACK_UseItemScript BACKPACK_UseSkillOnItem BACKPACK_NotHungry BACKPACK_ClueSkillPrompt BACKPACK_CancelSkillUse < Cancel Skill Use> FHQ_SM4P_Prompt PCREPAIR_UseSkill NPCREPAIR_UseSkill <# uses # on #.> PCREPAIR_DEAD < # is beyond your help.> PCREPAIR_NoRepairFuel PCREPAIR_NoDamageDone PCREPAIR_Success <# damage points restored.> PCREPAIR_Failure PCREPAIR_SelectRepairSkill FHQ_ItemMoved FHQ_AssociatePM <# has chosen to pilot this mecha.> FHQ_Transfer FHQ_Rename FHQ_Rename_Prompt FHQ_Rejoin FHQ_EditColor FHQ_Disassemble FHQ_DIS_Doing REJOIN_OK <# rejoins the lance.> REJOIN_DONTWANT <# doesn't want to go with you right now.> AUTOTRAIN_LEARN <# has improved #.> AUTOTRAIN_FAIL <# can't learn anything new yet.> AUTOTRAIN_EVOLVE <# has mutated into #!> Cancelled FACRANK_0 FACRANK_1 FACRANK_2 FACRANK_3 FACRANK_4 FACRANK_5 FACRANK_6 FACRANK_7 FACRANK_8 JOIN_NoPoint JOIN_REFUSE JOIN_BUSY JOIN_Join JOIN_Cancel JOINFE_Need_Space