GH2/0000755000175000017500000000000011402363703010073 5ustar kaolkaolGH2/infodisplay.pp0000644000175000017500000000714011365256066012772 0ustar kaolkaolunit infodisplay; { This unit holds the information browser. } { 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,gearutil; Procedure View_Personadex( GB: GameBoardPtr ); implementation uses ui4gh,narration,interact,arenacfe, {$IFDEF ASCII} vidgfx,vidinfo,vidmenus; {$ELSE} {$IFDEF CUTE} cutegfx,cutemap, {$ELSE} glgfx,glmap, {$ENDIF} glinfo,glmenus; {$ENDIF} type Personadex_Entry = Record NPC,Scene: GearPtr; end; var Dex_List: Array of Personadex_Entry; Dex_Menu: RPGMenuPtr; Info_GB: GameBoardPtr; Procedure PersonadexRedraw; { A specific item was selected, and its location stored in BP_Source. } var n: Integer; begin CombatDisplay( Info_GB ); SetupFHQDisplay; if ( Dex_Menu <> Nil ) then begin n := CurrentMenuItemValue( Dex_Menu ); if n >= 0 then PersonadexInfo( Dex_List[n].NPC , Dex_List[n].Scene , ZONE_ItemsInfo ); end; end; Procedure View_Personadex( GB: GameBoardPtr ); { The PersonaDex allows the PC to view all of his/her relationships } { with NPCs, and some info about the NPCs such as location, motivation, } { and attitude. } Procedure SeekAlongTrack( LList: GearPtr; var N: LongInt ); { Seek NPCs along this track. Store pointers to the NPCs you find. } begin while LList <> Nil do begin if ( LList^.G = GG_Character ) and ( ( NAttValue( LList^.NA , NAG_XXRan , NAS_XXChar_Attitude ) <> 0 ) or ( NAttValue( LList^.NA , NAG_Relationship , 0 ) <> 0 ) ) then begin Dex_List[ N ].NPC := LList; Dex_List[ N ].Scene := FindRootScene( FindActualScene( GB , FindGearScene( LList , GB ) ) ); Inc( N ); end; SeekAlongTrack( LList^.SubCom , N ); SeekAlongTrack( LList^.InvCom , N ); LList := LList^.Next; end; end; var Adv: GearPtr; NumMatches,T: LongInt; begin { Locate the adventure, and determine how many NPCs there are. } Adv := FindRoot( GB^.Scene ); Info_GB := GB; NumMatches := NAttValue( Adv^.NA , NAG_Narrative , NAS_MaxCID ); if NumMatches = 0 then begin DialogMsg( 'ERROR: No CIDs recorded in ' + GearName( Adv ) + '.' ); Exit; end; { Set the length of the NPC_List array. } SetLength( Dex_List , NumMatches ); { Fill the array with NPCs. } NumMatches := 0; SeekAlongTrack( Adv , NumMatches ); SeekAlongTrack( GB^.meks , NumMatches ); { Create the menu. } Dex_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); if NumMatches > 0 then begin for t := 0 to ( NumMatches - 1 ) do begin AddRPGMenuItem( Dex_Menu , GearName( Dex_List[ t ].NPC ) , t ); end; end else begin AddRPGMenuItem( Dex_Menu , MsgString( 'MEMO_CALL_NoPeople' ) , -1 ); end; RPMSortAlpha( Dex_Menu ); { Browse the menu. } T := SelectMenu( Dex_Menu , @PersonadexRedraw ); { Clear the menu and dynamic array. } DisposeRPGMenu( Dex_Menu ); SetLength( Dex_List , 0 ); end; end. GH2/grabgear.pp0000644000175000017500000003541711326004555012222 0ustar kaolkaolunit grabgear; { This unit has one purpose: To seek gears and stick them in } { ArenaScript's grabbed_gear global variable. } { 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; Function GG_LocatePC( GB: GameBoardPtr ): GearPtr; Function GG_LocateNPC( CID: LongInt; GB: GameBoardPtr; Source: GearPtr ): GearPtr; Function GG_LocateItem( NID: LongInt; GB: GameBoardPtr; Source: GearPtr ): GearPtr; Function GG_LocateFaction( FID: Integer; GB: GameBoardPtr; Scene: GearPtr ): GearPtr; Function GG_LocateAdventure( GB: GameBoardPtr; Source: GearPtr ): GearPtr; Function GG_GrabDesig( GB: GameBoardPtr; desig: String ): GearPtr; Function PlotMaster( GB: GameBoardPtr; Source: GearPtr ): GearPtr; Function StoryMaster( GB: GameBoardPtr; Source: GearPtr ): GearPtr; Function Attempt_Gear_Grab( const Cmd: String;var Event: String; GB: GameBoardPtr; Source: GearPtr ): Boolean; implementation uses ability,arenascript,gearutil,interact,narration,texutil,ghprop,ghchars; Function GG_LocatePC( GB: GameBoardPtr ): GearPtr; { Attempt to find the player character. If there's more than one } { master on Team 1, return one of them. } var Bits,PC,Pilot: GearPtr; XPScore,HiXP: LongInt; begin { Begin the search... } PC := Nil; { We are going to cheat a little bit. } { If the interaction menu has been defined, we already know the } { location of the PC since it's stored in I_PC. } if ( IntMenu <> Nil ) and ( I_PC <> Nil ) then begin PC := I_PC; end else if GB <> Nil then begin Bits := GB^.Meks; while ( Bits <> Nil ) do begin if ( NAttValue( Bits^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and IsMasterGear( Bits ) and OnTheMap( GB , Bits ) and GearOperational( Bits ) then begin Pilot := LocatePilot( Bits ); if ( PC = Nil ) and ( Pilot <> Nil ) then begin PC := Bits; HiXP := NattValue( Pilot^.NA , NAG_Experience , NAS_TotalXP ); end else if Pilot <> Nil then begin XPScore := NattValue( Pilot^.NA , NAG_Experience , NAS_TotalXP ); if XPScore > HiXP then begin PC := Bits; HiXP := XPScore; end; end; end; Bits := Bits^.Next; end; end; { If the PC can't be found on the map, search again... } { This time take any Team1 master that has a pilot. } if PC = Nil then begin Bits := GB^.Meks; while ( Bits <> Nil ) and ( PC = Nil ) do begin if ( NAttValue( Bits^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and IsMasterGear( Bits ) and ( LocatePilot( Bits ) <> Nil ) then begin PC := Bits; end; Bits := Bits^.Next; end; end; GG_LocatePC := PC; end; Function GG_LocateNPC( CID: LongInt; GB: GameBoardPtr; Source: GearPtr ): GearPtr; { ATtempt to find a NPC in either the mecha list or in the } { adventure. Return NIL if no such NPC can be found. } var NPC: GearPtr; begin { Error check - no undefined searches!!! } if CID = 0 then Exit( Nil ); NPC := Nil; if ( GB <> Nil ) then NPC := SeekGearByCID( GB^.Meks , CID ); if ( NPC = Nil ) and ( GB^.Scene <> Nil ) then NPC := SeekGearByCID( FindRoot( GB^.Scene ) , CID ); if NPC = Nil then NPC := SeekGearByCID( FindRoot( Source ) , CID ); if ( NPC = Nil ) and ( SCRIPT_DynamicEncounter <> Nil ) then NPC := SeekGearByCID( SCRIPT_DynamicEncounter , CID ); GG_LocateNPC := NPC; end; Function GG_LocateItem( NID: LongInt; GB: GameBoardPtr; Source: GearPtr ): GearPtr; { ATtempt to find a item in either the mecha list or in the } { adventure. Return NIL if no such item can be found. } var Item: GearPtr; begin { Error check - no undefined searches!!! } if NID = 0 then Exit( Nil ); if GB <> Nil then begin Item := SeekGearByIDTag( GB^.Meks , NAG_Narrative , NAS_NID , NID ); if Item = Nil then Item := SeekGearByIDTag( FindRoot( GB^.Scene ) , NAG_Narrative , NAS_NID , NID ); end else begin Item := SeekGearByIDTag( FindRoot( Source ) , NAG_Narrative , NAS_NID , NID ); end; GG_LocateItem := Item; end; Function GG_LocateController( ConID: LongInt; GB: GameBoardPtr; Source: GearPtr ): GearPtr; { Attempt to find a plot controller. } { Return NIL if no such item can be found. } var Item: GearPtr; begin { Error check - no undefined searches!!! } if ConID = 0 then Exit( Nil ); if GB <> Nil then begin Item := SeekGearByIDTag( GB^.Meks , NAG_Narrative , NAS_ControllerID , ConID ); if Item = Nil then Item := SeekGearByIDTag( FindRoot( GB^.Scene ) , NAG_Narrative , NAS_ControllerID , ConID ); end else begin Item := SeekGearByIDTag( FindRoot( Source ) , NAG_Narrative , NAS_ControllerID , ConID ); end; GG_LocateController := Item; end; Function GG_LocateFaction( FID: Integer; GB: GameBoardPtr; Scene: GearPtr ): GearPtr; { Find a faction gear, given its ID number and all the regular } { information passed around by ArenaScript procedures. } begin { Seek the faction. } if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin GG_LocateFaction := SeekFaction( GB^.Scene , FID ); end else if Scene <> Nil then begin GG_LocateFaction := SeekFaction( Scene , FID ); end else begin GG_LocateFaction := Nil; end; end; Function GG_LocateAdventure( GB: GameBoardPtr; Source: GearPtr ): GearPtr; { Find the adventure. } begin if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin GG_LocateAdventure := FindRoot( GB^.Scene ); end else begin GG_LocateAdventure := FindRoot( Source ); end; end; Function GG_GrabDesig( GB: GameBoardPtr; desig: String ): GearPtr; { Seek a gear based on its designation. Since designations are the only } { way I can reliably identify gears in the ATLAS, this grabber will be } { used to grab those gears. } var it: GearPtr; begin { First, search the game board. } it := SeekGearByDesig( GB^.Meks , desig ); { If it wasn't on the game board, search the adventure. } if it = Nil then it := SeekGearByDesig( FindRoot( GB^.Scene ) , desig ); { Return whatever we found. } GG_GrabDesig := it; end; Function PlotMaster( GB: GameBoardPtr; Source: GearPtr ): GearPtr; { Given a scene gear, find the PLOT that it is based off of, } { returning NIL if no such plot exists. Assuming that SCENE is } { based on a plot in the first place, it must be either the } { plot itself, or a descendant of the plot. } var Plot: GearPtr; begin { Note that the master plot may have a G of GG_AbsolutelyNothing, } { if a previous command in the script has set this plot to be } { advanced. } { GH2v0.231: It may also be a plot remnant. } Plot := Source; while ( Plot <> Nil ) and (Plot^.G <> GG_Plot ) and ( Plot^.G <> GG_AbsolutelyNothing ) do Plot := Plot^.Parent; if ( Plot = Nil ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Plot := GB^.Scene; while ( Plot <> Nil ) and (Plot^.G <> GG_Plot ) and ( Plot^.G <> GG_AbsolutelyNothing ) do Plot := Plot^.Parent; end; if ( Plot = Nil ) and ( Source^.G = GG_MetaTerrain ) and ( Source^.Stat[ STAT_Destination ] < 0 ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Plot := FindMetascenePlot( FindRoot( GB^.Scene ) , Source^.Stat[ STAT_Destination ] ); end; if ( Plot = Nil ) and ( NAttValue( Source^.NA , NAG_Narrative , NAS_NID ) <> 0 ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Plot := FindItemPlot( FindRoot( GB^.Scene ) , NAttValue( Source^.NA , NAG_Narrative , NAS_NID ) ); { Maybe it's a quest. Find out. } if Plot = Nil then Plot := FindItemQuest( FindRootScene( GB^.Scene ) , NAttValue( Source^.NA , NAG_Narrative , NAS_NID ) ); end; { This plot could be a quest. Look that up now. } if ( Plot = Nil ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin if NAttValue( Source^.NA , NAG_Narrative , NAS_PlotID ) <> 0 then begin Plot := SeekGearByIDTag( FindRoot( GB^.Scene ) , NAG_PlotStatus , NAttValue( Source^.NA , NAG_Narrative , NAS_PlotID ) , 1 ); end else if NAttValue( GB^.Scene^.NA , NAG_Narrative , NAS_PlotID ) <> 0 then begin Plot := SeekGearByIDTag( FindRoot( GB^.Scene ) , NAG_PlotStatus , NAttValue( GB^.Scene^.NA , NAG_Narrative , NAS_PlotID ) , 1 ); end; end; PlotMaster := Plot; end; Function StoryMaster( GB: GameBoardPtr; Source: GearPtr ): GearPtr; { Given a source gear, find the STORY that it is based off of, } { returning NIL if no such story exists. } var Scene: GearPtr; begin { Note that the master plot may have a G of GG_AbsolutelyNothing, } { if a previous command in the script has set this plot to be } { advanced. } Scene := Source; while ( Scene <> Nil ) and (Scene^.G <> GG_Story ) and ( Scene^.G <> GG_AbsolutelyNothing ) do Scene := Scene^.Parent; if ( Scene = Nil ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Scene := GB^.Scene; while ( Scene <> Nil ) and (Scene^.G <> GG_Story ) and ( Scene^.G <> GG_AbsolutelyNothing ) do Scene := Scene^.Parent; end; { It's possible that if we found an AbsolutelyNothing gear, it's really a plot that } { has just been marked for deletion and is not the story we want. Check for that } { possibility here. } if ( Scene <> Nil ) and ( Scene^.G = GG_AbsolutelyNothing ) and ( Scene^.Parent <> Nil ) and ( Scene^.Parent^.G = GG_Story ) then Scene := Scene^.Parent; if ( Scene = Nil ) and ( Source^.G = GG_MetaTerrain ) and ( Source^.Stat[ STAT_Destination ] < 0 ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Scene := FindMetasceneStory( FindRoot( GB^.Scene ) , Source^.Stat[ STAT_Destination ] ); if Scene = Nil then begin Scene := FindMetascenePlot( FindRoot( GB^.Scene ) , Source^.Stat[ STAT_Destination ] ); while ( Scene <> Nil ) and (Scene^.G <> GG_Story ) and ( Scene^.G <> GG_AbsolutelyNothing ) do Scene := Scene^.Parent; end; end; if ( Scene = Nil ) and ( NAttValue( Source^.NA , NAG_Narrative , NAS_NID ) <> 0 ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Scene := FindItemStory( FindRoot( GB^.Scene ) , NAttValue( Source^.NA , NAG_Narrative , NAS_NID ) ); if Scene = Nil then begin Scene := FindItemPlot( FindRoot( GB^.Scene ) , NAttValue( Source^.NA , NAG_Narrative , NAS_NID ) ); while ( Scene <> Nil ) and (Scene^.G <> GG_Story ) and ( Scene^.G <> GG_AbsolutelyNothing ) do Scene := Scene^.Parent; end; end; StoryMaster := Scene; end; Function GG_FindSubScene( Scene: GearPtr; N: Integer ): GearPtr; { Return the N'th subscene of the provided scene. } begin Scene := Scene^.SubCom; while ( Scene <> Nil ) and ( N > 0 ) do begin if Scene^.G = GG_Scene then Dec( N ); if N > 0 then Scene := Scene^.next; end; GG_FindSubScene := Scene; end; Function Attempt_Gear_Grab( const Cmd : String; var Event: String; GB: GameBoardPtr; Source: GearPtr ): Boolean; { See whether or not CMD refers to a valid Gear-Grabbing command. } { CMD is assumed to be already uppercase. } { If CMD is not a gear-grabbing command, no changes are made. } { Return TRUE if a gear was grabbed, FALSE otherwise. } var it: Boolean; X: LongInt; name: String; begin { Assume this is a gear-grabbing command, for now. } it := True; if CMD = 'GRABSOURCE' then begin Grabbed_Gear := Source; end else if CMD = 'GRABADVENTURE' then begin Grabbed_Gear := GG_LocateAdventure( GB , Source ); end else if CMD = 'GRABDYNAMIC' then begin { Grab the dynamic scene currently under construction. } Grabbed_Gear := SCRIPT_DynamicEncounter; end else if ( CMD = 'GRABCURRENTSCENE' ) and ( GB <> Nil ) then begin Grabbed_Gear := GB^.Scene; end else if CMD = 'GRABFACTION' then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := GG_LocateFaction( X , GB , Source ); end else if ( CMD = 'GRABSCENE' ) and ( GB <> Nil ) then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := FindActualScene( GB , X ); end else if CMD = 'GRABNPC' then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := GG_LocateNPC( X , GB , Source ); end else if CMD = 'GRABLOCAL' then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := LocateMekByUID( gb , X ); end else if CMD = 'GRABTEAM' then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := LocateTeam( gb , X ); end else if CMD = 'GRABITEM' then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := GG_LocateItem( X , GB , Source ); end else if CMD = 'GRABCONTROLLER' then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := GG_LocateController( X , GB , Source ); end else if ( CMD = 'GRABCHATNPC' ) then begin Grabbed_Gear := I_NPC; end else if ( CMD = 'GRABPC' ) and ( GB <> Nil ) then begin Grabbed_Gear := GG_LocatePC( GB ); end else if ( CMD = 'GRABPCPILOT' ) and ( GB <> Nil ) then begin Grabbed_Gear := LocatePilot( GG_LocatePC( GB ) ); end else if ( CMD = 'GRABPLOT' ) and ( Source <> Nil ) then begin Grabbed_Gear := PlotMaster( GB , Source ); end else if ( CMD = 'GRABSTORY' ) and ( Source <> Nil ) then begin Grabbed_Gear := StoryMaster( GB , Source ); end else if ( CMD = 'GRABENTRANCE' ) and ( GB <> Nil ) and ( GB^.Camp <> Nil ) then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := FindSceneEntrance( GB^.Camp^.Source , GB , X ); end else if ( CMD = 'GRABSUBSCENE' ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin X := ScriptValue( Event , GB , Source ); Grabbed_Gear := GG_FindSubScene( GB^.Scene , X ); end else if CMD = 'GRABROOT' then begin Grabbed_Gear := FindRoot( Grabbed_Gear ); end else if CMD = 'GRABROOTSCENE' then begin Grabbed_Gear := FindRootScene( Grabbed_Gear ); end else if CMD = 'GRABTEAMNAME' then begin name := ExtractWord( Event ); Grabbed_Gear := SeekChildByName( GB^.SCENE , name ); if Grabbed_Gear^.G <> GG_Team then Grabbed_Gear := Nil; end else if CMD = 'GRABDESIG' then begin name := ExtractWord( Event ); Grabbed_Gear := GG_GrabDesig( GB , name ); end else if ( CMD = 'GRABPARENT' ) and ( Grabbed_Gear <> Nil ) then begin Grabbed_Gear := Grabbed_Gear^.Parent; end else begin { No command was found matching CMD... return False. } it := False; end; Attempt_Gear_Grab := it; end; end. GH2/ghswag.pp0000644000175000017500000001625011326004555011722 0ustar kaolkaolunit ghswag; { This unit handles various items that will probably be } { carried around by adventurers, but not might be found } { in the tactical game. } { 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; { TREASURE format } { G = GG_Treasure } { S = Undefined } { V = Undefined } { TOOL format } { G = GG_Tool } { S = Skill/Talent Affected } { V = Skill Bonus } { REPAIRFUEL format } { G = GG_Usable } { S = Repair Type } { V = DP Capacity } { CONSUMABLE format } { G = GG_CONSUMABLE } { S = NA } { V = Hunger Points } Const STAT_MoraleBoost = 1; STAT_FoodEffectType = 4; Num_FoodEffectType = 4; FET_Healing = 1; FET_Regeneration = 2; FET_CauseStatus = 3; FET_CureStatus = 4; STAT_FoodEffectMod = 5; STAT_FoodSkillXP = 6; { Does this food provide a bonus to SkillXP? } STAT_FoodSkillXPAmount = 7; { How much skill experience does it provide? } Repair_Cost_Multiplier: Array [0..NumMaterial] of Byte = ( 1, 1, 5 ); Food_Effect_String: Array [1..Num_FoodEffectType] of String = ( '# HEALING 1', '# STATUS 3', '9 STATUS #', '9 CURE #' ); Function ToolDamage( Part: GearPtr ): Integer; Function ToolValue( Part: GearPtr ): Integer; Procedure CheckToolRange( Part: GearPtr ); Function IsLegalToolSub( Equip: GearPtr ): Boolean; Function RepairFuelName( Part: GearPtr ): String; Procedure CheckRepairFuelRange( Part: GearPtr ); Function RepairFuelValue( Part: GearPtr ): LongInt; Procedure CheckFoodRange( Part: GearPtr ); Function FoodMass( Part: GearPtr ): Integer; Function FoodValue( Part: GearPtr ): LongInt; implementation uses ghchars,ghweapon,ui4gh,texutil; Function ToolDamage( Part: GearPtr ): Integer; { Return how much damage this usable gear can withstand. } begin ToolDamage := 1; end; Function ToolValue( Part: GearPtr ): Integer; { Return the value of this usavle gear. } begin ToolValue := Part^.V * Part^.V * 450 + 150; end; Procedure CheckToolRange( Part: GearPtr ); { Examine the various bits of this gear to make sure everything } { is all nice and legal. } begin { Check S - Usable Type; corresponds to a skill } { or a talent. May not target the basic combat skills. } if Part^.S > 0 then begin if Part^.S <= Num_Basic_Combat_Skills then Part^.S := Num_Basic_Combat_Skills + 1 else if Part^.S > NumSkill then Part^.S := NumSkill; end else begin if Part^.S > -1 then Part^.S := -1 else if Part^.S < -NumTalent then Part^.S := -1; end; { Check V - Skill Bonus } if Part^.V < 0 then Part^.V := 0 else if Part^.V > 5 then Part^.V := 5; { Scale must be 0. } Part^.Scale := 0; { No stats defined. } end; Function IsLegalToolSub( Equip: GearPtr ): Boolean; { Return TRUE if EQUIP can be installed into TOOL, or FALSE otherwise. } begin IsLegalToolSub := ( Equip^.G = GG_Weapon ) or ( Equip^.G = GG_PowerSource ) or ( Equip^.G = GG_Computer ); end; Function RepairFuelName( Part: GearPtr ): String; { Returns a default name for some repairfuel. } begin RepairFuelName := MsgString( 'SKILLNAME_' + BStr( Part^.S ) ) + ' Kit'; end; Procedure CheckRepairFuelRange( Part: GearPtr ); { Examine the various bits of this gear to make sure everything } { is all nice and legal. } begin { Check S - Skill Type } if Part^.S < 0 then Part^.S := 0 else if Part^.S > NumMaterial then Part^.S := NumMaterial; end; Function RepairFuelValue( Part: GearPtr ): LongInt; { Return the value of this repair fuel. } begin RepairFuelValue := Part^.V * Repair_Cost_Multiplier[ Part^.S ]; end; Procedure CheckFoodRange( Part: GearPtr ); { Check the range for this consumable gear. } begin { V = Hunger Value } if Part^.V < 0 then Part^.V := 0 else if Part^.V > 60 then Part^.V := 60; { Stat 1 = Morale Boost } if Part^.Stat[ STAT_MoraleBoost ] > 10 then Part^.Stat[ STAT_MoraleBoost ] := 10 else if Part^.Stat[ STAT_MoraleBoost ] < -5 then Part^.Stat[ STAT_MoraleBoost ] := -5; { Stat 4 - Food Effect Type } if Part^.Stat[ STAT_FoodEffectType ] < 0 then Part^.Stat[ STAT_FoodEffectType ] := 0 else if Part^.Stat[ STAT_FoodEffectType ] > Num_FoodEffectType then Part^.Stat[ STAT_FoodEffectType ] := 0; { Stat 5 - Food Effect Mod } if Part^.Stat[ STAT_FoodEffectType ] <> 0 then begin if Part^.Stat[ STAT_FoodEffectType ] = FET_CureStatus then begin if Part^.Stat[ STAT_FoodEffectMod ] < 1 then Part^.Stat[ STAT_FoodEffectMod ] := 1 else if Part^.Stat[ STAT_FoodEffectMod ] > Num_Status_FX then Part^.Stat[ STAT_FoodEffectMod ] := Num_Status_FX; end else begin if Part^.Stat[ STAT_FoodEffectMod ] < 1 then Part^.Stat[ STAT_FoodEffectMod ] := 1 else if Part^.Stat[ STAT_FoodEffectMod ] > Num_Status_FX then Part^.Stat[ STAT_FoodEffectMod ] := Num_Status_FX; end; end else Part^.Stat[ STAT_FoodEffectMod ] := 0; { Stat 6 - Skill XP } if Part^.STAT[ STAT_FoodSkillXP ] > NumSkill then Part^.STAT[ STAT_FoodSkillXP ] := 0 else if Part^.STAT[ STAT_FoodSkillXP ] < 0 then Part^.STAT[ STAT_FoodSkillXP ] := 0; { Stat 7 - Skill XP amount } if Part^.STAT[ STAT_FoodSkillXP ] > 0 then begin if Part^.STAT[ STAT_FoodSkillXPAmount ] < 1 then Part^.STAT[ STAT_FoodSkillXPAmount ] := 1 else if Part^.STAT[ STAT_FoodSkillXPAmount ] > 1000 then Part^.STAT[ STAT_FoodSkillXPAmount ] := 1000; end; end; Function FoodMass( Part: GearPtr ): Integer; { Return the basic mass value for this food. } begin FoodMass := Part^.V div 5; end; Function FoodValue( Part: GearPtr ): LongInt; { Return the cost of this food. } var it,M: LongInt; begin it := Part^.V div 3; Case Part^.Stat[ STAT_FoodEffectType ] of FET_Healing: it := it + 55 * Part^.Stat[ STAT_FoodEffectMod ]; FET_Regeneration: it := it + 45 * Part^.Stat[ STAT_FoodEffectMod ]; FET_CureStatus: it := it + SX_RepCost[ Part^.Stat[ STAT_FoodEffectMod ] ] * 10; end; if Part^.Stat[ STAT_MoraleBoost ] > 0 then begin it := it + ( Part^.Stat[ STAT_MoraleBoost ] * ( 75 - Part^.V ) ); end else begin M := it * ( Part^.Stat[ STAT_MoraleBoost ] + 10 ) div 10; if M < ( it div 2 ) then M := it div 2; it := M; end; { Add the SkillXP cost. } it := it + Part^.STAT[ STAT_FoodSkillXPAmount ] * Credits_Per_XP; FoodValue := it; end; end. GH2/backpack.pp0000644000175000017500000022745111374513724012216 0ustar kaolkaolunit 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} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$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, {$IFDEF CUTE} cutemap,glmenus,glinfo; {$ELSE} glmap,glmenus,glinfo; {$ENDIF} {$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 , Redrawer ); end else if M^.G = GG_Mecha then begin newcolor := SelectColorPalette( colormenu_mode_mecha , portraitname , oldcolor , 100 , 150 , Redrawer ); end else begin newcolor := SelectColorPalette( colormenu_mode_allcolors , portraitname , oldcolor , 100 , 150 , 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_Menu ); if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Female then begin BuildFileMenu( RPM , Graphics_Directory + 'cha_f_*.*' ); end else begin BuildFileMenu( RPM , Graphics_Directory + 'cha_m_*.*' ); 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. } var TI_Menu: RPGMenuPtr; M: GearPtr; Team,N: Integer; begin if ( GB <> Nil ) and not IsSafeArea( GB ) then begin DialogMsg( MsgSTring( 'TRANSFER_NOTHERE' ) ); Exit; 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 ); { 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 ( Team = NAV_DefPlayerTeam ) or ( Team = NAV_LancemateTeam ) then begin if IsMasterGear( M ) and ( ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NaV_LancemateTeam ) ) and ( M <> PC ) then begin AddRPGMenuItem( TI_Menu , TeamMateName( M ) , N ); end; end else begin if IsMasterGear( M ) and ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = Team ) and ( M <> PC ) then begin AddRPGMenuItem( TI_Menu , TeamMateName( M ) , N ); end; 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. } 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 ) and (( GB = Nil ) or IsSafeArea( GB )) 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 := Item; DialogMSG( MsgString( 'FHQ_SelectDestination' ) ); N := SelectMenu( RPM , @ThisWargearRedraw ); 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; 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} {$IFDEF CUTE} CleanSpriteList; {$ELSE} CleanTexList; {$ENDIF} {$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. GH2/nametest.pas0000644000175000017500000000261311326004556012425 0ustar kaolkaolprogram nametest; uses gears,ghchars; var avg_name_length: LongInt; Function NameAlreadyExists( LList: SAttPtr; name: String ): Boolean; { Return TRUE if name is found in LList, or FALSE otherwise. } var IsFound: Boolean; begin IsFound := False; while ( LList <> Nil ) and ( not IsFound ) do begin if LList^.Info = name then IsFound := True; LList := LList^.Next; end; NameAlreadyExists := IsFound; end; Function NamesUntilRepeat: LongInt; { Start generating random names. Stop when a duplicate name is found. } var NList: SAttPtr; N,L: LongInt; name: String; NameRepeated: Boolean; begin NList := nil; N := 0; L := 0; NameRepeated := False; repeat name := UpCase( RandomName ); L := L + Length( name ); Inc( N ); if NameAlreadyExists( NList , name ) then begin writeln( name , ' ' , N ); NameRepeated := True; end else begin StoreSAtt( NList , name ); end; until ( N > 9999 ) or NameRepeated; DisposeSAtt( NList ); if N > 0 then avg_name_length := avg_name_length + ( L div N ); NamesUntilRepeat := N; end; const NumTrials = 5000; var T: Integer; N: LongInt; begin Randomize; N := 0; avg_name_length := 0; for T := 1 to NumTrials do begin N := N + NamesUntilRepeat; end; writeln( 'Average: ' , N div NumTrials ); writeln( 'Average length: ' , avg_name_length div NumTrials ); end. GH2/gearparser.pp0000644000175000017500000014731311376734117012613 0ustar kaolkaolunit gearparser; {This unit reads a text file and converts it into game} {data for GearHead.} { See MDLref.txt for a list of 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 dos,rpgdice,texutil,gears,ghmecha,ghmodule,ghchars,ghweapon,ghsupport,gearutil,locale,interact,ability,ui4gh,ghholder,movement; Const SLOT_Next = 0; SLOT_Sub = 1; SLOT_Inv = 2; var Parser_Macros: SAttPtr; Archetypes_List: GearPtr; WMonList: GearPtr; Standard_Equipment_List: GearPtr; STC_Item_List: GearPtr; Factions_List: GearPtr; Mecha_Theme_List: GearPtr; standard_script_list: GearPtr; Procedure ApplyDictionaryToString( var Info: String; Dictionary: SAttPtr ); Function SkillRankForRenown( Renown: Integer ): Integer; Procedure SetSkillsAtLevel( NPC: GearPtr; Lvl: Integer ); Function SelectMechaByFactionAndRenown( Factions: String; Renown: Integer ): String; Procedure IndividualizeNPC( NPC: GearPtr ); Procedure CheckValidity( var it: GearPtr ); Procedure ApplyCharDesc( NPC: GearPtr; CDesc: String ); Function LoadFile( FName,DName: String ): GearPtr; Function LoadFile( FName: String ): GearPtr; Function LoadGearPattern( FName,DName: String ): GearPtr; Function AggregatePattern( FName,DName: String ): GearPtr; Function LoadRandomSceneContent( FName,DName: String ): GearPtr; Function LoadSingleMecha( FName,DName: String ): GearPtr; Function LoadNewMonster( MonsterName: String ): GearPtr; Function LoadNewNPC( NPCName: String; RandomizeNPCs: Boolean ): GearPtr; Function LoadNewSTC( Desig: String ): GearPtr; Function LoadNewItem( FullName: String ): GearPtr; Procedure RandomLoot( Box: GearPtr; SRP: LongInt; const l_type,l_factions: String ); implementation uses ghswag, {$IFDEF ASCII} vidgfx; {$ELSE} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$ENDIF} Const Recursion_Level: Integer = 0; Procedure SelectThemeAndSpecialty( NPC: GearPtr ); { Set a theme and a specialty skill for this NPC. } const Default_Skill_List = '1 2 3 11 15 16 26 28'; var Faction,Theme: GearPtr; SkillPossibilities: Array [1..NumSkill] of Boolean; SpecSkill,T,N: Integer; SkList: String; begin if NAttValue( NPC^.NA , NAG_Personal , NAS_SpecialistSkill ) <> 0 then begin DialogMsg( 'WARNING: ' + GearName( NPC ) + '/' + SAttValue( NPC^.SA , 'JOB' ) + '/' + BStr( NAttValue( NPC^.NA , NAG_Personal , NAS_FactionID ) ) + ' had spec skill ' + BStr( NAttValue( NPC^.NA , NAG_Personal , NAS_SpecialistSkill ) ) + ' set.' ); end; { Start by locating the faction. This should contain a list of preferred skill specialties. } Faction := SeekCurrentLevelGear( Factions_List , GG_Faction , NAttValue( NPC^.NA , NAG_Personal , NAS_FactionID ) ); if Faction <> Nil then SkList := SAttValue( Faction^.SA , 'Specialist_Skills' ) else SkList := Default_Skill_List; if ( SkList = '' ) or ( Random(10) = 1) then SkList := Default_Skill_List; { Clear the SkillPossibilities array, then fill it with entries from the SkList. } for t := 1 to NumSkill do SkillPossibilities[t] := False; while SkList <> '' do begin T := ExtractValue( SkList ); if ( T >= 1 ) and ( T <= NumSkill ) then SkillPossibilities[t] := True; end; { Count the number of skills, pick one at random. } N := 0; for t := 1 to NumSkill do if SkillPossibilities[t] then Inc( N ); N := Random( N ); SpecSkill := 0; for t := 1 to NumSkill do begin if SkillPossibilities[t] then begin Dec( N ); if N = -1 then begin SpecSkill := T; break; end; end; end; if SpecSkill = 0 then SpecSkill := Random( 5 ) + 1; { Store the specialist skill, and prepare to select the theme. } SetNAtt( NPC^.NA , NAG_Personal , NAS_SpecialistSkill , SpecSkill ); SetNAtt( NPC^.NA , NAG_Skill , SpecSkill , 12 ); { Next, it's time to find a theme for this NPC. The theme selected } { is gonna depend on a bunch of stuff: The NPC's faction, the NPC's job, } { personality traits and yadda yadda yadda... } { All characters can take GENERAL themes. } SkList := 'GENERAL ' + NPCTraitDesc( NPC ) + ' ' + SAttValue( NPC^.SA , 'JOB_DESIG' ); { Add the specialist skill. } SkList := SkList + ' [' + BStr( SpecSkill ) + ']'; if Faction <> Nil then SkList := SkList + ' ' + SAttValue( Faction^.SA , 'DESIG' ); Theme := FindNextComponent( Mecha_Theme_List , SkList ); if Theme <> Nil then SetNAtt( NPC^.NA , NAG_Personal , NAS_MechaTheme , Theme^.S ) else DialogMsg( 'ERROR: No theme found for ' + SkList ); end; Function SkillRankForRenown( Renown: Integer ): Integer; { Given this renown score, return an appropriate skill rank. } const NumSkillLevel = 15; NeededRenown: Array [2..NumSkillLevel] of Integer = ( -20, -13, 7, 14, 21, 28, 35, 43, 52, 62, 73, 85, 98, 112 ); var SkLvl: Integer; begin SkLvl := NumSkillLevel; while ( SkLvl > 1 ) and ( NeededRenown[ SkLvl ] > Renown ) do Dec( SkLvl ); SkillRankForRenown := SkLvl; end; Procedure SetSkillsAtLevel( NPC: GearPtr; Lvl: Integer ); { Set all of this NPC's skills to an appropriate value for } { the requested level. } var SkLvl: Integer; Skill: NAttPtr; begin { If the NPC doesn't have a specialist skill, pick a skill and theme now. } if IsACombatant( NPC ) then begin if NAttValue( NPC^.NA , NAG_Personal , NAS_MechaTheme ) = 0 then SelectThemeAndSpecialty( NPC ); { Combatants automatically get all the basic combat skills. } for SkLvl := 1 to Num_Basic_Combat_Skills do SetNAtt( NPC^.NA , NAG_Skill, SkLvl , 1 ); { They also get all hidden skills. } for SkLvl := 1 to NumSkill do if SkillMan[ SkLvl ].Hidden then SetNAtt( NPC^.NA , NAG_Skill, SkLvl , 1 ); end; { Determine the value to set all skills to. } SkLvl := SkillRankForRenown( lvl ); { Go through the skills and set them. } Skill := NPC^.NA; while Skill <> Nil do begin if Skill^.G = NAG_Skill then begin Skill^.V := SkLvl; end; Skill := Skill^.Next; end; { Apply a slight bonus to the specialist skill. } SkLvl := NAttValue( NPC^.NA , NAG_Personal , NAS_SpecialistSkill ); if ( SkLvl > 0 ) and ( SkLvl <= NumSkill ) then AddNAtt( NPC^.NA , NAG_Skill , SkLvl , 1 ); { Record the NPC's new Renown score. } SetNAtt( NPC^.NA , NAG_CharDescription , NAS_Renowned , Lvl ); end; Function SelectMechaByFactionAndRenown( Factions: String; Renown: Integer ): String; { Select a mecha based on factions and renown. } Function MechaFileMatchWeight( MList: GearPtr ): LongInt; { In order to pass this test, this file must match several } { requirements. There must be mecha capable of travelling in } { all terrain types, there must be at least one mecha matching } { the NPC's faction. } var M: GearPtr; T,N: Integer; Total: Int64; FacFound: Boolean; TerrType: String; Terr_Spec_Found: Array [1..Num_Environment_Types] of Boolean; begin { Initialize the Terr_Spec_Found array } for t := 1 to Num_Environment_Types do Terr_Spec_Found[t] := False; Total := 0; N := 0; FacFound := False; { Go through the list searching for stuff. } M := MList; while M <> Nil do begin if M^.G = GG_Mecha then begin Total := Total + GearValue( M ); if PartAtLeastOneMatch( SAttValue( M^.SA , 'FACTIONS' ) , Factions ) then FacFound := True; TerrType := SAttValue( M^.SA , 'TYPE' ); for t := 1 to Num_Environment_Types do begin if AStringHasBString( TerrType , Environment_Idents[ t ] ) then Terr_Spec_Found[ t ] := True; end; Inc( N ); end; M := M^.Next; end; { Check to see if all the terrain types were found. } for t := 1 to Num_Environment_Types do if not Terr_Spec_Found[t] then FacFound := False; if FacFound and ( N > 0 ) then begin MechaFileMatchWeight := Total div N; end else MechaFileMatchWeight := 0; end; const Min_Max_Cost = 400000; Max_Min_Cost = 750000; var SRec: SearchRec; MechaList: SAttPtr; DList: GearPtr; Minimum_Cost, Maximum_Cost: LongInt; File_Cost: LongInt; MekName: String; begin MechaList := Nil; Maximum_Cost := Calculate_Threat_Points( Renown , 35 ); if Maximum_Cost < Min_Max_Cost then Maximum_Cost := Min_Max_Cost; Minimum_Cost := Maximum_Cost div 2 - 200000; if Minimum_Cost > Max_Min_Cost then Minimum_Cost := Max_Min_Cost; { 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. } DList := LoadFile( SRec.Name , Design_Directory ); File_Cost := MechaFileMatchWeight( DList ); if File_Cost > 0 then begin if ( File_Cost > Minimum_Cost ) and ( File_Cost < Maximum_Cost ) then begin StoreSAtt( MechaList , SRec.Name ); end; end; { Dispose of the list. } DisposeGear( DList ); { Look for the next file in the directory. } FindNext( SRec ); end; if MechaList <> Nil then begin MekName := SelectRandomSAtt( MechaList )^.Info; DisposeSAtt( MechaList ); end else begin MekName := 'buruburu.txt'; end; SelectMechaByFactionAndRenown := MekName; end; Procedure IndividualizeNPC( NPC: GearPtr ); { Randomize up this NPC a bit, to give it that hand-crafted } { NPC look. } { Note that if the NPC has a name by the time it reaches here, its } { personality traits and stats will not be touched. } var N,T,Lvl: Integer; begin { If the NPC doesn't have a body defined, create one. } if NPC^.SubCom = Nil then begin ExpandCharacter( NPC ); end; { Give the NPC a random name + gender + age + personality traits. } if SAttValue( NPC^.SA , 'NAME' ) = '' then begin SetSAtt( NPC^.SA , 'NAME <' + RandomName + '>' ); SetNAtt( NPC^.NA , NAG_CharDescription , NAS_Gender , Random( 2 ) ); if NAttValue( NPC^.NA , NAG_CharDescription , NAS_DAge ) = 0 then AddNAtt( NPC^.NA , NAG_CharDescription , NAS_DAge , 1 + Random( 10 ) - Random( 10 ) + RollStep( 5 ) ); { Give out some personality traits. } { Most NPCs have at least a single trait in the group Sociable,Easygoing,Cheerful } if Random( 5 ) <> 1 then begin if Random( 3 ) = 1 then begin AddReputation( NPC, 3 + Random( 3 ) , -RollStep( 40 ) ); end else begin AddReputation( NPC, 3 + Random( 3 ) , RollStep( 40 ) ); end; end; { Add RollStep(2) other personality traits. } N := RollStep( 2 ); for t := 1 to N do begin { Positive traits are far more common than negative ones. } if Random( 3 ) = 1 then begin if Random( 7 ) = 1 then begin AddReputation( NPC, Random( Num_Personality_Traits ) + 1 , -RollStep( 50 ) ); end else begin AddReputation( NPC, Random( Num_Personality_Traits ) + 1 , -RollStep( 20 ) ); end; end else begin if Random( 7 ) = 1 then begin AddReputation( NPC, Random( Num_Personality_Traits ) + 1 , RollStep( 50 ) ); end else begin AddReputation( NPC, Random( Num_Personality_Traits ) + 1 , RollStep( 20 ) ); end; end; end; { Randomize up those stats a bit. } for t := 1 to NumGearStats do begin NPC^.Stat[T] := NPC^.Stat[T] + Random( 4 ) - Random( 4 ); if Random( 32 ) = 1 then NPC^.Stat[T] := NPC^.Stat[T] + Random( 5 ) - Random( 5 ); if NPC^.Stat[T] < 1 then NPC^.Stat[T] := 1; end; end; { If this is a combatant character, set the skills to match the reputation. } if IsACombatant( NPC ) then begin Lvl := NAttValue( NPC^.NA , NAG_CharDescription , NAS_Renowned ); if Lvl = 0 then begin AddReputation( NPC , Abs( NAS_Renowned ) , Random( 75 ) ); if Random( 3 ) = 1 then AddReputation( NPC , Abs( NAS_Renowned ) , -Random( 25 ) ); Lvl := NAttValue( NPC^.NA , NAG_CharDescription , NAS_Renowned ); end; Lvl := Lvl + 50; if Lvl < 25 then Lvl := 25; SetSkillsAtLevel( NPC , Lvl ); end; { The random personality traits may have affected morale. } SetNAtt( NPC^.NA , NAG_Condition , NAS_MoraleDamage , 0 ); end; Procedure ComponentScan( it: GearPtr ); { This procedure will check each individual component of a mek } { to make sure it is legal. } begin {Loop through all the components.} while it <> Nil do begin {Perform specific checks here.} CheckGearRange( it ); if it^.SubCom <> Nil then ComponentScan(it^.SubCom); if it^.InvCom <> Nil then ComponentScan(it^.InvCom); it := it^.Next; end; end; Procedure CheckMechaMetrics(it: GearPtr); { Examine the components of this mecha to make sure it has everything } { it needs. } { - Exactly one Cockpit } { - Exactly one Body } { - Exactly one engine in the body; If not present, add one. } { - Exactly one gyroscope in the body; If not present, add one. } { If unrecoverable errors are encountered, mark mek as invalid. } var S,SG: GearPtr; Body,CPit: Integer; procedure SubComScan( P: GearPtr ); begin while P <> Nil do begin if P^.G = GG_Cockpit then Inc( CPit ); if P^.SubCom <> Nil then SubComScan( P^.SubCom ); P := P^.Next; end; end; begin { Count the number of body modules. } { ASSERT: All level one subcomponents will be modules, and } { if a body module is found it will be of the correct size. } { The range checker, called before this procedure, should have } { dealt with that already. } S := it^.SubCom; Body := 0; while S <> Nil do begin if ( S^.G = GG_Module ) and ( S^.S = GS_Body ) then begin Inc( Body ); if Body = 1 then begin { Check for the engine. If no engine, install one. } SG := S^.SubCom; while ( SG <> Nil ) and (( SG^.G <> GG_Support ) or ( SG^.S <> GS_Engine )) do SG := SG^.Next; if SG = Nil then begin { Add an engine here. } SG := NewGear( S ); SG^.G := GG_Support; SG^.S := GS_Engine; SG^.V := S^.V; InitGear( SG ); InsertSubCom( S , SG ); end; { Check for the gyro. If no gyro, install one. } SG := S^.SubCom; while ( SG <> Nil ) and (( SG^.G <> GG_Support ) or ( SG^.S <> GS_Gyro )) do SG := SG^.Next; if SG = Nil then begin { Add an engine here. } SG := NewGear( S ); SG^.G := GG_Support; SG^.S := GS_Gyro; SG^.V := 1; InitGear( SG ); InsertSubCom( S , SG ); end; end; end; S := S^.Next; end; if Body <> 1 then begin it^.G := GG_AbsolutelyNothing; end; { Make sure the mecha has exactly one cockpit. } CPit := 0; SubComScan( it^.SubCom ); if CPit <> 1 then begin it^.G := GG_AbsolutelyNothing; end; end; Procedure MetricScan( var head: GearPtr ); { Scan all the parts, looking for parts which need some counting done. } var P,P2: GEarPtr; begin P := Head; while P <> Nil do begin P2 := P^.Next; {Perform specific checks here.} if P^.G = GG_Mecha then CheckMechaMetrics( P ); { After the above checking, this gear might be marked for } { deletion. If so, delete it. } if P^.G = GG_AbsolutelyNothing then begin RemoveGear( Head , P ); end else begin if P^.SubCom <> Nil then MetricScan(P^.SubCom); if P^.InvCom <> Nil then MetricScan(P^.InvCom); end; P := P2; end; end; Procedure CheckValidity( var it: GearPtr ); {Check the gears that have just been loaded and make sure} {that they conform to the game rules. To do this, we will} {scan through every gear in the list, recursing to this} {procedure as necessary.} begin { Do the individual components check. } ComponentScan( it ); { Do the metrics check for specific components. } MetricScan( it ); end; Procedure ApplyCharDesc( NPC: GearPtr; CDesc: String ); { Apply a character description string to this NPC. The character description } { string can contain all kinds of info: personality traits, gender, etc. } var CCD_Cmd: String; { Command extracted from line. } t: Integer; begin { Error check! This command only works for characters. } if ( NPC = Nil ) or ( NPC^.G <> GG_Character ) then Exit; while CDesc <> '' do begin CCD_Cmd := ExtractWord( CDesc ); { Check to see if this is a gender command. } for t := 0 to 1 do begin if CCD_Cmd = UpCase( MsgString( 'GenderName_' + BStr( T ) ) ) then begin SetNAtt( NPC^.NA , NAG_CharDescription , NAS_Gender , T ); end; end; { If not, check to see if it's a personality command. } for t := 1 to Num_Personality_Traits do begin if CCD_Cmd = UpCase( MsgString( 'TRAITNAME_' + BStr( T ) + '_+' ) ) then begin if NAttValue( NPC^.NA , NAG_CharDescription , -T ) < 50 then begin SetNAtt( NPC^.NA , NAG_CharDescription , -T , 50 ); end else begin AddNAtt( NPC^.NA , NAG_CharDescription , -T , 10 ); end; end else if CCD_Cmd = UpCase( MsgString( 'TRAITNAME_' + BStr( T ) + '_-' ) ) then begin if NAttValue( NPC^.NA , NAG_CharDescription , -T ) > -50 then begin SetNAtt( NPC^.NA , NAG_CharDescription , -T , -50 ); end else begin AddNAtt( NPC^.NA , NAG_CharDescription , -T , -10 ); end; end; end; { If not, check to see if it's an age command. } if CCD_Cmd = 'YOUNG' then begin { Set the character's age to something below 20. } while NAttValue( NPC^.NA , NAG_CharDescription , NAS_DAge ) >= 0 do begin AddNAtt( NPC^.NA , NAG_CharDescription , NAS_DAge , -( Random( 6 ) + 1 ) ); end; end else if CCD_Cmd = 'OLD' then begin { Set the character's age to something above 40. } while NAttValue( NPC^.NA , NAG_CharDescription , NAS_DAge ) <= 20 do begin AddNAtt( NPC^.NA , NAG_CharDescription , NAS_DAge , ( Random( 15 ) + RollStep( 5 ) ) ); end; { If setting a relationship, also set the NumConversation } { attribute. I'd like to avoid the original Dune's "I am Duke } { Atriedes, your father" crap. } end else if CCD_Cmd = 'FRIEND' then begin SetNAtt( NPC^.NA , NAG_Relationship , 0 , NAV_Friend ); SetNAtt( NPC^.NA , NAG_Personal , NAS_NumConversation , 100 ); end else if CCD_Cmd = 'FAMILY' then begin SetNAtt( NPC^.NA , NAG_Relationship , 0 , NAV_Family ); SetNAtt( NPC^.NA , NAG_Personal , NAS_NumConversation , 100 ); end else if CCD_Cmd = 'LOVER' then begin SetNAtt( NPC^.NA , NAG_Relationship , 0 , NAV_Lover ); SetNAtt( NPC^.NA , NAG_Personal , NAS_NumConversation , 100 ); end else if CCD_Cmd = 'ARCHENEMY' then begin SetNAtt( NPC^.NA , NAG_Relationship , 0 , NAV_ArchEnemy ); SetNAtt( NPC^.NA , NAG_Personal , NAS_NumConversation , 100 ); end; end; end; Procedure ApplyDictionaryToString( var Info: String; Dictionary: SAttPtr ); { This dictionary contains a bunch of substitution strings. Apply them to } { the string provided. } var P,P2: Integer; SPat,SRep: String; begin P := 1; while P < Length( Info ) do begin if ( Info[P] = '%' ) and ( P < ( Length( Info ) - 1 ) ) then begin { We've found a hash. This could be a replacement string. See what string it is. } SPat := '%'; P2 := P; repeat Inc( P2 ); SPat := SPat + Info[P2]; until ( P2 >= Length( Info ) ) or ( Info[P2] = '%' ); { We now have a string that may very well be something we want to replace. Check it. } SRep := SAttValue( Dictionary , SPat ); if SRep <> '' then begin { The pattern was found in the dictionary. Replace all instances of it. } ReplacePat( Info , SPat , SRep ); P := P + Length( SRep ) - 1; end; end; Inc( P ); end; end; Procedure InsertStandardScript( Part: GearPtr; ScriptRequest: String ); { This part has requested a standard script. } var sr_tag,sr_type,msg: String; sr_script: GearPtr; Dictionary,SA: SAttPtr; T: Integer; begin { Remove the * from the beginning of the request. } DeleteFirstChar( ScriptRequest ); { Get some of the needed information. } sr_tag := RetrieveAPreamble( ScriptRequest ); msg := RetrieveAString( ScriptRequest ); sr_type := ExtractWord( msg ); { Locate the script to be used. } sr_script := SeekGearByName( standard_script_list , sr_type ); if sr_script <> Nil then begin { Create our dictionary. } Dictionary := Nil; AddNAtt( Part^.NA , NAG_GearOps , NAS_MaxStandardScriptID , 1 ); StoreSAtt( Dictionary , '%id% <' + BStr( NAttValue( Part^.NA , NAG_GearOps , NAS_MaxStandardScriptID ) ) + '>' ); for t := 1 to 8 do StoreSAtt( Dictionary , '%' + BStr( T ) + '% <' + ExtractWord( msg ) + '>' ); { Copy the START script into the sr_tag script, copy everything else } { besides the name over directly. } SA := SR_Script^.SA; while SA <> Nil do begin sr_type := UpCase( RetrieveAPreamble( SA^.Info ) ); if sr_type = 'START' then begin msg := RetrieveAString( SA^.Info ); ApplyDictionaryToString( msg , Dictionary ); SetSAtt( Part^.SA , sr_tag + ' <' + msg + '>' ); end else if sr_type <> 'NAME' then begin msg := SA^.Info; ApplyDictionaryToString( msg , Dictionary ); SetSAtt( Part^.SA , msg ); end; SA := SA^.Next; end; DisposeSAtt( Dictionary ); end else begin DialogMsg( 'ERROR: Standard script ' + sr_type + ' not found.' ); end; end; Function ReadGear( var F: Text; RandomizeNPCs: Boolean; const ZZZFName: String ): GearPtr; {F is an open file of type F.} {Start reading information from the file, stopping} {whenever all the info is read.} { Note that the current implementation of this procedure contracts } { out all validity checking to the CheckGearRange procedure } { in gearutil.pp. } const NDum: GearPtr = Nil; var TheLine,OriginalLine,cmd: String; it,C: GearPtr; {IT is the total list which is returned.} {C is the current GEAR being worked on.} dest: Byte; {DESTination of the next GEAR to be added.} { 0 = Sibling; 1 = SubCom; 2 = InvCom } {*** LOCAL PROCEDURES FOR GHPARSER ***} Procedure InstallGear(IG_G,IG_S,IG_V: Integer); {Install a GEAR of the specified type in the currently} {selected installation location. } begin {Determine where the GEAR is to be installed, and check to} {see if this is a legal place to stick it.} {Do the installing} {if C = Nil, we're installing as a sibling at the root level.} {ASSERT: If IT = Nil, then C = Nil as well.} if (dest = 0) or (C = Nil) then begin {NEXT COMPONENT} if C = Nil then C := AddGear(it,NDum) else C := AddGear(C,C^.Parent); end else if dest = 1 then begin {SUB COMPONENT} C := AddGear(C^.SubCom,C); dest := SLOT_Next; end else if dest = 2 then begin {INV COMPONENT} C := AddGear(C^.InvCom,C); dest := SLOT_Next; end; C^.G := IG_G; C^.S := IG_S; C^.V := IG_V; InitGear(C); end; Procedure AssignStat( AS_Slot, AS_Val: Integer ); { Set stat SLOT of the current gear to value VAL. } { Do nothing if there's some reason why this is impossible. } begin { Can only assign a stat if the current gear is defined. } if C <> Nil then begin { Make sure NUM is in the allowable range. } if ( AS_Slot >= 1 ) and ( AS_Slot <= NumGearStats ) then begin C^.Stat[ AS_Slot ] := AS_Val; end; end; end; Procedure AssignNAtt( ANA_G, ANA_S, ANA_V: LongInt ); { Store the described numeric attribute in the current gear. } { Do nothing if there's some reason why this is impossible. } begin { Can only store info if the current gear is defined. } if C <> Nil then begin SetNAtt( C^.NA , ANA_G , ANA_S , ANA_V ); end; end; Procedure CheckMacros( CM_CMD: String ); { This command wasn't found in the list of basic commands. } { Maybe it's part of the macro file? Check and see. } Function GetMacroValue( var GMV_FX: String ): LongInt; { Extract a value from the macro string, or read it } { from the current line. } var GMV_MacDat,GMV_LineDat: String; begin { First, read the data definition. } GMV_MacDat := ExtractWord( GMV_FX ); { If the first character is a ?, this means that we should } { read the value from the regular line. } if ( GMV_MacDat <> '' ) and ( GMV_MacDat[1] = '?' ) then begin { If the data is found, return it. } { Otherwise return the macro default value. } GMV_LineDat := ExtractWord( TheLine ); if GMV_LineDat <> '' then begin GetMacroValue := ExtractValue( GMV_LineDat ); end else begin DeleteFirstChar( GMV_MacDat ); GetMacroValue := ExtractValue( GMV_MacDat ); end; end else begin GetMacroValue := ExtractValue( GMV_MacDat ); end; end; var CM_FX: String; CM_A, CM_B, CM_C: LongInt; begin CM_FX := SAttValue( Parser_Macros , CM_CMD ); { If a macro matching this command was found, process it. } if CM_FX <> '' then begin CM_CMD := UpCase( ExtractWord( CM_FX ) ); if CM_CMD[1] = 'G' then begin { Gear Macro } CM_A := GetMacroValue( CM_FX ); CM_B := GetMacroValue( CM_FX ); CM_C := GetMacroValue( CM_FX ); InstallGear( CM_A , CM_B , CM_C ); end else if CM_CMD[1] = 'S' then begin { Stat Macro } CM_A := GetMacroValue( CM_FX ); CM_B := GetMacroValue( CM_FX ); AssignStat( CM_A , CM_B ); end else if CM_CMD[1] = 'N' then begin { NAtt Macro } CM_A := GetMacroValue( CM_FX ); CM_B := GetMacroValue( CM_FX ); CM_C := GetMacroValue( CM_FX ); AssignNAtt( CM_A , CM_B , CM_C ); end else if CM_CMD[1] = 'M' then begin { MODULE Macro } CM_A := GetMacroValue( CM_FX ); InstallGear( GG_Module , CM_A , MasterSize( C ) ); end; end else if CM_CMD <> '' then begin DialogMsg( 'ERROR: Command ' + UpCase( CM_CMD ) + ' not found in ' + ZZZFName ); DialogMsg( '--"' + OriginalLine + '"' ); end; end; Procedure CMD_Sub; {Set the destination to SUBCOMPONENT} begin dest := SLOT_Sub; end; Procedure CMD_Inv; {Set the destination to INVCOMPONENT} begin dest := SLOT_Inv; end; Procedure CMD_End; {Finish off a range of subcomponents.} {If Dest = 0, move to the parent gear.} {If Dest <> 0, set Dest to 0.} begin if (dest = 0) and (C <> Nil) then C := C^.Parent else dest := 0; end; Procedure CMD_Size; {Set the V field of the current module to the supplied value.} var CS_Size: Integer; begin CS_Size := ExtractValue(TheLine); if C <> Nil then C^.V := CS_Size; end; Procedure CMD_Arch; {Create a new archetypal character gear.} { The rest of this line is the name of the new archetype. } begin InstallGear(GG_Character,0,0); DeleteWhiteSpace( TheLine ); SetSAtt( C^.SA , 'Name <' + TheLine + '>' ); TheLine := ''; end; Procedure CMD_StatLine; { Read all the stats for this gear from a single line. } var CSL_N,CSL_V: Integer; begin { Error Check! } if C = Nil then Exit; CSL_N := 1; while ( TheLine <> '' ) and ( CSL_N <= NumGearStats ) do begin CSL_V := ExtractValue( TheLine ); C^.Stat[CSL_N] := CSL_V; Inc( CSL_N ); end; end; Procedure CMD_SetAlly; { Read this team's allies from the line. } var CSA_AllyID: Integer; begin if ( C = Nil ) then Exit; while ( TheLine <> '' ) do begin CSA_AllyID := ExtractValue( TheLine ); if C^.G = GG_Faction then begin SetNAtt( C^.NA , NAG_FactionScore , CSA_AllyID , 10 ); end else begin SetNAtt( C^.NA , NAG_SideReaction , CSA_AllyID , NAV_AreAllies ); end; end; end; Procedure CMD_SetEnemy; { Read this team's enemies from the line. } var CSE_EnemyID: Integer; begin if ( C = Nil ) then Exit; while ( TheLine <> '' ) do begin CSE_EnemyID := ExtractValue( TheLine ); if C^.G = GG_Faction then begin AddNAtt( C^.NA , NAG_FactionScore , CSE_EnemyID , -10 ); end else begin SetNAtt( C^.NA , NAG_SideReaction , CSE_EnemyID , NAV_AreEnemies ); end; end; end; Procedure META_InsertPartIntoIt( IPII_Part: GEarPtr ); { Insert a part into the current gear-being-loaded by the } { method specified in the dest variable. Afterwards, set C } { to equal this new part. } begin if IPII_Part <> Nil then begin { Stick the part somewhere appropriate. } if (dest = 0) or (C = Nil) then begin {NEXT COMPONENT} { If there is no currently defined C component, } { stick the NPC as the next component at root level. } if C = Nil then LastGear( it )^.Next := IPII_Part else begin LastGear( C )^.Next := IPII_Part; IPII_Part^.Parent := C^.Parent; end; end else if dest = 1 then begin {SUB COMPONENT} InsertSubCom( C , IPII_Part ); end else if dest = 2 then begin {INV COMPONENT} InsertInvCom( C , IPII_Part ); end; dest := SLOT_Next; { Set the current gear to the item's base record. } { Any further modifications will be done there. } C := IPII_Part; end; end; Procedure CMD_NPC; { Search for & then duplicate one of the standard character } { archetypes, inserting it in the gear-under-construction at } { the standard insertion point. } var NPC: GearPtr; begin { NPC cannot be the first gear in a list!!! } if it = Nil then Exit; { Clone the NPC record first. } { Exit the procedure if no appropriate NPC can be found. } DeleteWhiteSpace( TheLine ); { Note that this procedure doesn't randomize the NPCs itself- that } { will have to wait for the second pass which will randomize all NPCs } { together. } NPC := LoadNewNPC( TheLine , False ); if NPC = Nil then begin Exit; end; { Get rid of the NPC name, so the parser doesn't try } { interpreting it as a series of commands. } TheLine := ''; { Stick the NPC somewhere appropriate. } META_InsertPartIntoIt( NPC ); end; Procedure CMD_Monster; { Search for & then duplicate one of the standard monster } { archetypes, inserting it in the gear-under-construction at } { the standard insertion point. } var Mon: GearPtr; begin { Monster cannot be the first gear in a list!!! } if it = Nil then Exit; { Clone the Monster record first. } { Exit the procedure if no appropriate NPC can be found. } DeleteWhiteSpace( TheLine ); Mon := LoadNewMonster( TheLine ); if Mon = Nil then begin Exit; end; { Get rid of the NPC name, so the parser doesn't try } { interpreting it as a series of commands. } TheLine := ''; { Stick the monster somewhere appropriate. } META_InsertPartIntoIt( Mon ); end; Procedure CMD_STC; { Search for & then duplicate one of the standard item } { archetypes, inserting it in the gear-under-construction at } { the standard insertion point. } var STC_Part: GearPtr; begin { Item cannot be the first gear in a list!!! } if it = Nil then Exit; { Clone the item record first. } { Exit the procedure if no appropriate item can be found. } DeleteWhiteSpace( TheLine ); STC_Part := LoadNewSTC( TheLine ); if STC_Part = Nil then begin Exit; end; { Get rid of the item name, so the parser doesn't try } { interpreting it as a series of commands. } TheLine := ''; { Stick the item somewhere appropriate. } META_InsertPartIntoIt( STC_Part ); end; Procedure CMD_Item; { Search for & then duplicate one of the design file } { items, inserting it in the gear-under-construction at } { the standard insertion point. } var STC_Part: GearPtr; begin { Item cannot be the first gear in a list!!! } if it = Nil then Exit; { Clone the item record first. } { Exit the procedure if no appropriate item can be found. } DeleteWhiteSpace( TheLine ); STC_Part := LoadNewItem( TheLine ); if STC_Part = Nil then begin Exit; end; { Get rid of the item name, so the parser doesn't try } { interpreting it as a series of commands. } TheLine := ''; { Stick the item somewhere appropriate. } META_InsertPartIntoIt( STC_Part ); end; Procedure CMD_Scale; { Sets the scale field for the current gear. } var CS_Scale: Integer; begin { Error check- if there is no current gear, exit this procedure. } if ( C = Nil ) then Exit; { Determine what scale to set the current gear to. } CS_Scale := ExtractValue( TheLine ); C^.Scale := CS_Scale; end; Procedure CMD_Set_ID; { Sets the "S" descriptor of this gear. Since this gear has already likely been } { initialized, this can be a bad thing to do... use "SetID" only on virtual gear types } { like personas, metascenes, and so on. } var CS_S: Integer; begin { Error check- if there is no current gear, exit this procedure. } if ( C = Nil ) then Exit; { Determine what scale to set the current gear to. } CS_S := ExtractValue( TheLine ); C^.S := CS_S; end; Procedure CMD_CharDesc; { This procedure allows certain aspects of a character gear to be } { modified. } begin { Call the character description procedure above, then clear the line } { to make sure it's not interpreted as a command sequence. } ApplyCharDesc( C , TheLine ); TheLine := ''; end; Procedure GoIndividualize( LList: GearPtr ); { Individualize any NPCs found along this list or in the children of it. } begin while LList <> Nil do begin if LList^.G = GG_Character then IndividualizeNPC( LList ); GoIndividualize( LList^.SubCom ); GoIndividualize( LList^.InvCom ); LList := LList^.Next; end; end; begin { Initialize variables. } it := Nil; C := Nil; dest := SLOT_Next; { Increase the recursion level; the NPC command uses recursion } { and could get stuck in an endless loop. } Inc( Recursion_Level ); while not EoF(F) do begin {Read the line from disk, and delete leading whitespace.} readln(F,TheLine); DeleteWhiteSpace(TheLine); OriginalLine := TheLine; if ( TheLine = '' ) or ( TheLine[1] = '%' ) then begin { *** COMMENT *** } TheLine := ''; end else if ( TheLine[1] = '*' ) and ( C <> Nil ) and ( ( C^.G > 0 ) or ( C^.G = GG_Faction ) ) then begin { *** STANDARD SCRIPT *** } { This is apparently a standard script. Deal with it. } InsertStandardScript( C , TheLine ); end else if Pos('<',TheLine) > 0 then begin { *** STRING ATTRIBUTE *** } if C <> Nil then SetSAtt(C^.SA,TheLine); end else begin { *** COMMAND LINE *** } {To make things easier upon us, just set the whole} {line to uppercase now.} TheLine := UpCase(TheLine); {Keep processing the line until the end is reached.} while TheLine <> '' do begin CMD := ExtractWord(TheLine); {Branch depending upon what the command is.} if CMD = 'SUB' then CMD_Sub else if CMD = 'INV' then CMD_Inv else if CMD = 'END' then CMD_End else if CMD = 'SIZE' then CMD_Size else if CMD = 'SETID' then CMD_Set_ID else if CMD = 'ARCH' then CMD_Arch else if CMD = 'STATLINE' then CMD_StatLine else if CMD = 'SETALLY' then CMD_SetAlly else if CMD = 'SETENEMY' then CMD_SetEnemy else if CMD = 'NPC' then CMD_NPC else if CMD = 'SCALE' then CMD_Scale else if CMD = 'CHARDESC' then CMD_CharDesc else if CMD = 'MONSTER' then CMD_Monster else if CMD = 'STC' then CMD_STC else if CMD = 'ITEM' then CMD_ITEM else if ( CMD <> '' ) and ( CMD[1] = '%' ) then TheLine := '' else CheckMacros( CmD ); end; end; end; { If the NPCs are to be randomized, do that now. Why not do it at the } { point when they're loaded? Because they can still be modified by other } { commands, that's why. Imagine you create a NPC, then set its faction. } { if the individualization were done directly at the moment of creation, } { then the individualization procedure wouldn't know the NPC's faction. } { This would be bad. So, we do individualization as an extra step at the end. } if RandomizeNPCs then GoIndividualize( it ); {Run a check on each of the Master Gears we have loaded,} {making sure that they are both valid and complete. If} {there are any errors, remove the offending entries.} { See TheRules.txt for a brief outline of things to be checked. } CheckValidity(it); { Decrement the recursion level. } Dec( Recursion_Level ); ReadGear := it; end; { ReadGear } Function LoadFile( FName,DName: String ): GearPtr; { Open and load a text file. } var F: Text; it: GearPtr; begin { Use FSEARCH to confirm the file name. } FName := FSearch( FName , DName ); it := Nil; if FName <> '' then begin { The filename has been found and confirmed. } { Actually load the file. } Assign( F , FName ); Reset( F ); it := ReadGear( F , True , FName ); Close( F ); end; LoadFile := it; end; Function LoadFile( FName: String ): GearPtr; { Open and load a text file. } begin LoadFile := LoadFile( FName , '.' ); end; Function LoadGearPattern( FName,DName: String ): GearPtr; { Attempt to load a gear file from disk. Search for } { pattern matches. } var FList: SAttPtr; it: GearPtr; begin it := Nil; if FName <> '' then begin { Build search list for files that match the source. } FList := CreateFileList( DName + FName ); if FList <> Nil then begin FName := SelectRandomSAtt( FList )^.Info; DisposeSAtt( FList ); it := LoadFile( FName , DName ); end; end; { Return the selected & loaded gears. } LoadGearPattern := it; end; Function AggregatePattern( FName,DName: String ): GearPtr; { Search for the given pattern. Then, load all files that match } { the pattern and concatenate them together. Did I spell that } { right? Probably not. } var FList,F: SAttPtr; part,it: GearPtr; begin it := Nil; if FName <> '' then begin { Build search list for files that match the pattern. } FList := CreateFileList( DName + FName ); F := FList; while F <> Nil do begin part := LoadFile( F^.Info , DName ); AppendGear( it , part ); F := F^.Next; end; DisposeSAtt( FList ); end; { Return the selected & loaded gears. } AggregatePattern := it; end; Function LoadRandomSceneContent( FName,DName: String ): GearPtr; { Search for the given pattern. Then, load all files that match } { the pattern and concatenate them together. Don't randomize NPCs; } { that will be done later. } var FList,F: SAttPtr; part,it: GearPtr; InFile: Text; begin it := Nil; if FName <> '' then begin { Build search list for files that match the pattern. } FList := CreateFileList( DName + FName ); F := FList; while F <> Nil do begin Assign( InFile , DName + F^.Info ); Reset( InFile ); part := ReadGear( InFile , False , FName ); Close( InFile ); AppendGear( it , part ); F := F^.Next; end; DisposeSAtt( FList ); end; { Return the selected & loaded gears. } LoadRandomSceneContent := it; end; Function LoadSingleMecha( FName,DName: String ): GearPtr; { Load a mecha file. Return a single mecha from that file. } { Return Nil if the mecha could not be loaded. } var MList,Mek: GearPtr; begin { Use the above procedure to load a mecha from disk. } MList := LoadGearPattern( FName , DName ); Mek := Nil; if MList <> Nil then begin Mek := CloneGear( SelectRandomGear( MList ) ); DisposeGear( MList ); end; LoadSingleMecha := Mek; end; Function LoadNamedGear( FName,GName: String ): GearPtr; { This function will load a file with the given file name. } { Once that is loaded, it will search for a single gear within } { that file with the given gear name. } var F: Text; G,LList: GearPtr; begin { Open and load the archetypes. } Assign( F , FName ); Reset( F ); LList := ReadGear( F , True , FName ); Close( F ); { Locate the desired archetype. } G := SeekGearByName( LList , GName ); if G <> Nil then G := CloneGear( G ); { Dispose of the list & return the cloned gear. } DisposeGear( LList ); LoadNamedGear := G; end; Function LoadNewMonster( MonsterName: String ): GearPtr; { This function will load the default monster list and } { return a monster of the requested type. } var Mon: GearPtr; begin { Load monster from disk. } if WMonList <> Nil then begin Mon := CloneGear( SeekGearByName( WMonList , MonsterName ) ); end else begin Mon := Nil; end; { If it loaded successfully, set its job to "ANIMAL". } if Mon <> Nil then begin SetSATt( Mon^.SA , 'JOB ' ); end; { Return whatever value was returned. } LoadNewMonster := Mon; end; Function LoadNewNPC( NPCName: String; RandomizeNPCs: Boolean ): GearPtr; { This function will load the NPC archetypes list and } { return a character of the requested type. } var NPC: GearPtr; begin { Attempt to load the NPC from the standard archetypes file. } if Archetypes_List = Nil then begin NPC := LoadNamedGear( Archetypes_File , NPCName ); end else begin NPC := CloneGear( SeekGearByName( Archetypes_List , NPCName ) ); end; { If the NPC was loaded, set its job name and individualize it. } if NPC <> Nil then begin { Store a JOB description. This will be the archetype name. } SetSATt( NPC^.SA , 'JOB <' + NPCName + '>' ); SetSAtt( NPC^.SA , 'NAME <>' ); if RandomizeNPCs then IndividualizeNPC( NPC ); end; { Return the finished product. } LoadNewNPC := NPC; end; Function LoadNewSTC( Desig: String ): GearPtr; { This function will load the STC parts list and } { return an item of the requested type. } var Item: GearPtr; begin { Attempt to load the item from the standard items file. } if STC_Item_List = Nil then Exit( Nil ); Item := CloneGear( SeekGearByDesig( STC_Item_List , Desig ) ); { Return the finished product. } LoadNewSTC := Item; end; Function LoadNewItem( FullName: String ): GearPtr; { This function will check the standard items list } { for a gear with the provided full name. } var Item: GearPtr; begin { Attempt to load the item from the standard items file. } if Standard_Equipment_List = Nil then Exit( Nil ); Item := CloneGear( SeekSibByFullName( Standard_Equipment_List , FullName ) ); { Return the finished product. } LoadNewItem := Item; end; Procedure RandomLoot( Box: GearPtr; SRP: LongInt; const l_type,l_factions: String ); { Fill BOX with SRV (suggested retail price) worth of junk from the standard } { equipment files. } Function ItemIsLegal( I: GearPtr ): Boolean; { Return TRUE if I's type and factions match those requested, or } { FALSE otherwise. Oh, I should also be a legal invcom of BOX. } begin ItemIsLegal := PartAtLeastOneMatch( l_type , SAttValue( I^.SA , 'CATEGORY' ) ) and PartAtLeastOneMatch( l_factions , SAttValue( I^.SA , 'FACTIONS' ) ) and IsLegalInvCom( Box , I ); end; Function RLWeight( Cost,MIC: LongInt ): LongInt; { Return the chance of this item being selected. } begin if Cost < ( MIC div 4 ) then begin RLWeight := 1; end else begin RLWeight := Cost; end; end; Function SelectAnItem: GearPtr; { Select an appropriate item from the standard items list. } var Total: Int64; MIC,Cost: LongInt; I,Selected_Item: GearPtr; begin Selected_Item := Nil; { Calculate Max Item Cost } MIC := ( SRP * 3 ) div 2; if MIC < ( SRP + 1000 ) then MIC := SRP + 1000; { Start by finding the total cost of all legal items. } I := Standard_Equipment_List; Total := 0; while I <> Nil do begin if ItemIsLegal( I ) then begin Cost := GearCost( I ); if ( Cost > 0 ) and ( Cost < MIC ) then Total := Total + RLWeight( Cost , MIC ); end; I := I^.Next; end; { If no items found, exit NIL. } if Total < 1 then Exit( Nil ); { Next, go through the list again, selecting one at random. } I := Standard_Equipment_List; Total := Random( Total ); while ( I <> Nil ) and ( Selected_Item = Nil ) do begin if ItemIsLegal( I ) then begin Cost := GearCost( I ); if ( Cost > 0 ) and ( Cost < MIC ) then begin Total := Total - RLWeight( Cost , MIC ); if Total < 1 then Selected_Item := I; end; end; I := I^.Next; end; SelectAnItem := CloneGear( Selected_Item ); end; Procedure InsertLoot( Box , Item: GearPtr ); { Stick ITEM into BOX, or dispose of it. } var I2: GearPtr; begin if Item^.G = GG_Set then begin while Item^.SubCom <> Nil do begin I2 := Item^.SubCom; DelinkGear( Item^.SubCom , I2 ); InsertLoot( Box, I2 ); end; while Item^.InvCom <> Nil do begin I2 := Item^.InvCom; DelinkGear( Item^.InvCom , I2 ); InsertLoot( Box, I2 ); end; DisposeGear( Item ); end else begin InsertInvCom( Box , Item ); end; end; var Item: GearPtr; begin { Keep processing until we run out of money or objects. } while SRP > 0 do begin Item := SelectAnItem; if Item <> Nil then begin SRP := SRP - GearCost( Item ); InsertLoot( Box , Item ); end else begin { No item found. Set SRP to 0. } SRP := 0; end; end; end; Procedure LoadArchetypes; { Load the default, archetypal gears which may be used in the } { construction of other gears. This includes NPC archetypes and } { so forth. } var F: Text; begin { Open and load the archetypes. } Assign( F , Archetypes_File ); Reset( F ); Archetypes_List := ReadGear( F , False , 'Archetypes' ); Close( F ); end; Procedure SetEquipmentLevels( TestType: Integer ); { Equipment will be ranked by relative value. How is relative value calculated? } { First determine what "class" an item belongs in. Sort all items belonging to this } { class based on value. Finally, break the items into 10 value bands based on their } { position in the list. } { If TESTTYPE is nonzero, the contents of this type will be written to out.txt } type SELRecord = Record Item: GearPtr; { The gear we're talking about. } EClass,ERank: Integer; { What kind of gear is it? What's its current rank? } EValue: LongInt; { What's this gear's value? } end; const IC_MechaGear = -1; IC_MissileWeapon = -2; IC_MeleeWeapon = -3; IC_Food = -4; IC_Medicine = -5; Function ItemClass( Item: GearPtr ): Integer; { Return the class this item belongs to. In general, this will be the } { "G" value of the item, unless it's a set in which case it'll be the } { value of the first invcom. } begin if Item^.G = GG_Set then begin ItemClass := ItemClass( Item^.InvCom ); end else if Item^.G = GG_Mecha then begin ItemClass := 0; end else if Item^.Scale > 0 then begin ItemClass := IC_MechaGear; end else if Item^.G = GG_Weapon then begin if ( Item^.S = GS_Melee ) or ( Item^.S = GS_EMelee ) then ItemClass := IC_MeleeWeapon else ItemClass := IC_MissileWeapon; end else if Item^.G = GG_Consumable then begin if ( Item^.Stat[ STAT_FoodEffectType ] = 0 ) then ItemClass := IC_Food else ItemClass := IC_Medicine; end else begin ItemClass := Item^.G; end; end; Function ELGearCost( Item: GearPtr ): LongInt; { Return the value of this item, or the average value of items } { in a set. } var it: LongInt; begin it := GearCost( Item ); if ( Item^.G = GG_Set ) and ( Item^.InvCom <> Nil ) then it := it div NumSiblingGears( Item^.InvCom ); ELGearCost := it; end; var SEL: Array of SelRecord; Num_Items: Integer; Function ItemRanking( N: Integer ): Integer; { Determine this item's ranking in the current list. } { Adjust the rank of other items to compensate. } var Rank,T: Integer; begin Rank := 1; for T := 0 to ( N-1 ) do begin if SEL[t].EClass = SEL[n].EClass then begin if SEL[t].EValue > SEL[n].EValue then begin { No idea where N places yet, but it's gotta be } { below T. } Inc( SEL[t].ERank ); end else if SEL[t].ERank >= Rank then begin { This item isn't more expensive than N, but has } { a higher rank. Better fix that. } Rank := SEL[t].ERank + 1; end; end; end; ItemRanking := Rank; end; Procedure InitMechaRank( Item: GearPtr ); { Mecha get a rank set directly from their value. Set that now, and } { also mark the category. } const rank_min_val: Array [1..10] of LongInt = ( 0, 100000, 200000, 330000, 481600, 761600, 1201600, 1801600, 2561600, 3481600 ); var mval,t,shoprank: LongInt; begin mval := GearValue( Item ); shoprank := 1; for t := 1 to 10 do if mval > rank_min_val[t] then shoprank := t; SetNAtt( Item^.NA , NAG_GearOps , NAS_ShopRank , ShopRank ); if Item^.G = GG_Mecha then SetSatt( Item^.SA , 'CATEGORY ' ); end; Procedure RateClass( EClass: Integer ); { Count the number of members in this class. Then, assign each member } { a shop rank of 1 to 10 based on its position in the list. While doing this } { zero out each item's value so we don't end up processing the same } { category twice. } var T,N,SRank: Integer; SList: SAttPtr; begin { Error check. } if EClass = 0 then exit; { Step One: Count the members. } N := 0; for t := 0 to ( Num_Items - 1 ) do begin if SEL[t].EClass = EClass then Inc( N ); end; SList := Nil; { Step Two: Set the ratings. } for t := 0 to ( Num_Items - 1 ) do begin if SEL[t].EClass = EClass then begin { We know how many items are in this class, and this item's } { position within that list. From this information we should be able } { to calculate a shop rank for it. } { If there are fewer than 10 items in this class, just use the ranking } { as the shop rank and leave the higher shop ranks empty. } if N <= 10 then begin SRank := SEL[t].ERank; end else begin SRank := ( ( SEL[t].ERank -1 ) * 10 ) div N + 1; end; { Store the rank in the item. } SetNAtt( SEL[t].Item^.NA , NAG_GearOps , NAS_ShopRank , SRank ); if ( TestType <> 0 ) and ( EClass = TestType ) then StoreSAtt( SList , WideStr( SRank , 2 ) + ' ' + WideStr( SEL[t].ERank , 3 ) + ' ' + FullGearName( SEL[t].Item ) + ' ($' + BStr( GearCost( SEL[t].Item ) ) + ')' ); SEL[t].EValue := 0; end; end; if SList <> Nil then begin SortStringList( SList ); SaveStringList( 'out.txt' , SList ); DisposeSAtt( SList ); end; end; var T: Integer; Item: GearPtr; begin { Begin by dimensioning the array. } Num_Items := NumSiblingGears( Standard_Equipment_List ); SetLength( SEL , Num_Items ); { Fill out the basic information. } Item := Standard_Equipment_List; t := 0; while Item <> Nil do begin SEL[t].Item := Item; SEL[t].EClass := ItemClass( Item ); { Determine this item's current ranking, unless it's a mecha } { in which case its position is automatically set. } if ( Item^.G = GG_Mecha ) or ( ( Item^.G = GG_Set ) and ( Item^.InvCom <> Nil ) and ( Item^.InvCom^.G = GG_Mecha ) ) then begin SEL[t].EValue := 0; InitMechaRank( Item ); end else begin SEL[t].EValue := ELGearCost( Item ); SEL[t].ERank := ItemRanking( T ); end; Item := Item^.Next; Inc( T ); end; { Alright, now we have all the items divided into categories and sorted. } for t := 0 to ( Num_Items - 1 ) do begin if SEL[t].EValue <> 0 then begin RateClass( SEL[t].EClass ); end; end; end; initialization Parser_Macros := LoadStringList( Parser_Macro_File ); standard_script_list := LoadFile( 'standard_scripts.txt' , Data_Directory ); STC_Item_List := AggregatePattern( STC_Item_Pattern , Series_Directory ); LoadArchetypes; Standard_Equipment_List := AggregatePattern( '*.txt' , Design_Directory ); SetEquipmentLevels( 0 ); WMonList := AggregatePattern( Monsters_File_Pattern , Series_Directory ); Factions_List := AggregatePattern( 'FACTIONS_*.txt' , Series_Directory ); Mecha_Theme_List := AggregatePattern( 'THEME_*.txt' , Series_Directory ); finalization DisposeSAtt( Parser_Macros ); DisposeGear( Archetypes_List ); DisposeGear( WMonList ); DisposeGear( Standard_Equipment_List ); DisposeGear( STC_Item_List ); DisposeGear( Factions_List ); DisposeGear( Mecha_Theme_List ); DisposeGear( Standard_Script_List ); end. GH2/mpbuilder.pp0000644000175000017500000020762411365256066012445 0ustar kaolkaolunit mpbuilder; { MEGA PLOT ASSEMBLE! It's like a Voltron of narrative content! } { This unit contains the functions and procedures for creating } { big amalgamations of components. } { 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,narration; Type ElementDesc = Record EType: Char; EValue: LongInt; end; { I feel just like Dmitri Mendelev writing this... } ElementTable = Array [1..Num_Plot_Elements] of ElementDesc; var Sub_Plot_List: GearPtr; Procedure ReplaceStrings( Part: GearPtr; Dictionary: SAttPtr ); Function ComponentMenu( CList: GearPtr; var ShoppingList: NAttPtr ): GearPtr; Procedure ClearElementTable( var ET: ElementTable ); Function ExpandDungeon( Dung: GearPtr ): GearPtr; Procedure ConnectScene( Scene: GearPtr; DoInitExits: Boolean ); Function InitMegaPlot( GB: GameBoardPtr; Scope,Slot,Plot: GearPtr; Threat: Integer ): GearPtr; Function LoadQuestFragments: GearPtr; Function AddQuest( Adv,City,QPF_Proto: GearPtr; var Quest_Frags: GearPtr; QReq: String ): Boolean; implementation uses playwright,texutil,gearutil,gearparser,ghchars,randmaps, ui4gh,wmonster,rpgdice,ghprop,ability, {$IFDEF ASCII} vidgfx,vidmenus; {$ELSE} {$IFDEF CUTE} cutegfx,glmenus; {$ELSE} glgfx,glmenus; {$ENDIF} {$ENDIF} Const Num_Sub_Plots = 8; Var standard_trigger_list: SAttPtr; changes_used_so_far: String; MasterEntranceList: GearPtr; Procedure ComponentMenuRedraw; { The redraw for the component selector below. } begin ClrScreen; InfoBox( ZONE_Menu ); InfoBox( ZONE_Info ); InfoBox( ZONE_Caption ); GameMsg( 'Select the next component in the core story.', ZONE_Caption , StdWhite ); RedrawConsole; end; Function ComponentMenu( CList: GearPtr; var ShoppingList: NAttPtr ): GearPtr; { Select one of the components from a menu. } var RPM: RPGMenuPtr; C: GearPtr; N: Integer; SL: NAttPtr; begin RPM := CreateRPGMenu( MenuItem, MenuSelect , ZONE_Menu ); AttachMenuDesc( RPM , ZONE_Info ); SL := ShoppingList; while SL <> Nil do begin C := RetrieveGearSib( CList , SL^.S ); AddRPGMenuItem( RPM , '[' + BStr( SL^.V ) + ']' + GearName( C ) , SL^.S , SAttValue( C^.SA , 'DESC' ) ); SL := SL^.Next; end; N := SelectMenu( RPM , @ComponentMenuRedraw ); SetNAtt( ShoppingList , 0 , N , 0 ); DisposeRPGMenu( RPM ); ComponentMenu := RetrieveGearSib( CList , N ); end; Function NewPlotID( Adv: GearPtr; IsAQuest: Boolean ): LongInt; { Calculate a new unique Plot ID. } { Plots have positive PlotIDs, quests have negative PlotIDs. } begin { Increase the previous ID by one, and return that. } AddNAtt( Adv^.NA , NAG_Narrative , NAS_MaxPlotID , 1 ); if IsAQuest then begin NewPlotID := -NAttValue( Adv^.NA , NAG_Narrative , NAS_MaxPlotID ); end else begin NewPlotID := NAttValue( Adv^.NA , NAG_Narrative , NAS_MaxPlotID ); end; end; Function NewLayerID( Slot: GearPtr ): LongInt; { Calculate a new unique Layer ID. } begin { Increase the previous ID by one, and return that. } AddNAtt( Slot^.NA , NAG_Narrative , NAS_MaxPlotLayer , 1 ); NewLayerID := NAttValue( Slot^.NA , NAG_Narrative , NAS_MaxPlotLayer ); end; Procedure ClearElementTable( var ET: ElementTable ); { Clear this table's stored elements by setting all IDs } { to zero. } var t: Integer; begin for t := 1 to Num_Plot_Elements do begin ET[t].EValue := 0; end; end; Procedure CreatePlotPlaceholder( Slot , Shard: GearPtr ); { Create a new plot gear to hold all of SHARD's elements, and store it } { in SLOT. IT will be marked with a PlotLayer of -(plotlayer), and this marking } { will be used to dispose of all the placeholders after assembly. } var it: GearPtr; T: Integer; EID: LongInt; begin it := NewGear( Slot ); InsertInvCom( Slot , It ); it^.G := GG_Plot; SetSAtt( it^.SA , 'name ' ); SetNAtt( it^.NA , NAG_Narrative , NAS_PlotLayer , -NAttValue( Shard^.NA , NAG_Narrative , NAS_PlotLayer ) ); for t := 1 to Num_Plot_Elements do begin EID := ElementID( Shard , T ); if EID <> 0 then begin SetNAtt( It^.NA , NAG_ElementID , T , EID ); SetSAtt( It^.SA , 'ELEMENT' + BStr( T ) + ' <' + SAttValue( Shard^.SA , 'ELEMENT' + BStr( T ) ) + '>' ); end; end; end; Procedure DeleteSpecificPlaceholder( Slot,Shard: GearPtr ); { Delete the placeholder belonging specifically to this shard. } var p,p2: GearPtr; plotid: LongInt; begin plotid := -NAttValue( Shard^.NA , NAG_Narrative , NAS_PlotLayer ); P := Slot^.InvCom; while P <> Nil do begin P2 := P^.Next; if NAttValue( P^.NA , NAG_Narrative , NAS_PlotLayer ) = PlotID then begin RemoveGear( Slot^.InvCom , P ); end; P := P2; end; end; Procedure DeletePlotPlaceholders( Slot: GearPtr ); { Delete all the plot placeholders. They should be invcoms of SLOT, } { and be marked with negative PlotLayers. } var PP,PP2: GearPtr; begin PP := Slot^.InvCom; while PP <> Nil do begin PP2 := PP^.Next; if NAttValue( PP^.NA , NAG_Narrative , NAS_PlotLayer ) < 0 then RemoveGear( Slot^.InvCom , PP ); PP := PP2; end; end; Function AddSubPlot( GB: GameBoardPtr; Scope,Slot,Plot0,QPF_Proto: GearPtr; var Quest_Frags: GearPtr; SPReq: String; EsSoFar, LayerID, SubPlotSlot: LongInt; IsAQuest,DoDebug: Boolean ): GearPtr; forward; Function InitShard( GB: GameBoardPtr; Scope,Slot,Shard: GearPtr; var Quest_Frags: GearPtr; EsSoFar,PlotID,LayerID,Threat: LongInt; const ParamIn: ElementTable; IsAQuest,DoDebug: Boolean ): GearPtr; { SHARD is a plot fragment candidate. Attempt to add it to the Slot. } { Attempt to add its subplots as well. } { SHARD can only be added if its number of new elements plus the current } { element total is less than the number of total possible elements. } { EsSoFar is the number of elements allocated so far. } { Before initializing a shard, the following will be done: } { - Parameter elements copied over } { - Any character gears present will be randomized } { Initializing includes the following: } { - Set combatant skill levels for quests } { Upon successfully initializing a shard, this procedure will then do the following: } { - Delink the shard from the Slot, and attach all subplots. } { - Create a plot stub and mark it with the PlotID; copy over all elements used by } { this shard and place it as Slot's invcom. This stub is to prevent other shards } { from selecting characters or items used here. } { - Initialize quest metascenes with the PlotID. } { - Return the shard list } { If installation fails, SHARD will be deleted and NIL will be returned. } Procedure DisposeSPList( SPList: GearPtr ); { Delete this subplot list, taking with it any associated placeholders. } var SP: GearPtr; begin while SPList <> Nil do begin DeleteSpecificPlaceholder( Slot , SPList ); SP := SPList; RemoveGear( SPList , SP ); end; end; Procedure ScaleRandomTreasure( LList: GearPtr ); { Scale any random loot values found along this path. } { The treasures found as part of a quest should be } { commesurate with the Difficulty rating of the quest. } var LootValue: LongInt; begin while LList <> Nil do begin if NAttValue( LList^.NA , NAG_Narrative , NAS_RandomLoot ) > 0 then begin LootValue := Calculate_Threat_Points( Threat , 10 + Random( 15 ) ); SetNAtt( LList^.NA , NAG_Narrative , NAS_RandomLoot , LootValue ); end; ScaleRandomTreasure( LList^.SubCom ); ScaleRandomTreasure( LList^.InvCom ); LList := LList^.Next; end; end; Procedure PrepQuestCombatants( LList: GearPtr ); { If this is a quest, scale any combatant NPCs to the proper level. } begin while LList <> Nil do begin if ( LList^.G = GG_Character ) then begin if NotAnAnimal( Llist ) and IsACombatant( LList ) then begin SetSkillsAtLevel( LList , Threat ); end; end; LList := LList^.Next; end; end; Procedure InitializeMapFeatures( LList: GearPtr ); { Mark all map features, their subs and invs, with the PlotID of the } { parent scene. Why do this? So scripts can then locate the quest } { without too much difficulty. } begin while LList <> Nil do begin if NAttValue( LList^.NA , NAG_Narrative , NAS_PlotID ) = 0 then SetNAtt( LList^.NA , NAG_Narrative , NAS_PlotID , PlotID ); if LList^.G = GG_MapFeature then begin InitializeMapFeatures( LList^.SubCom ); InitializeMapFeatures( LList^.InvCom ); end; LList := LList^.Next; end; end; Procedure PrepQuestMetascenes( LList: GearPtr ); { If this is a quest, mark the map features. } begin while LList <> Nil do begin if ( LList^.G = GG_MetaScene ) then begin { Also mark all of the scene's things with the PlotID, so scripts can locate } { the quest easily. } InitializeMapFeatures( LList^.SubCom ); InitializeMapFeatures( LList^.InvCom ); end; LList := LList^.Next; end; end; var InitOK: Boolean; T,NumParam,NumElem: Integer; I,SubPlot,SPList: GearPtr; SPID: LongInt; SPReq,original_changes,EDesc: String; begin { Assign the values to this shard. } SetNAtt( Shard^.NA , NAG_Narrative , NAS_PlotID , PlotID ); SetNAtt( Shard^.NA , NAG_Narrative , NAS_PlotLayer , LayerID ); SetNAtt( Shard^.NA , NAG_Narrative , NAS_DifficultyLevel , Threat ); { Scale the random treasure, based upon the threat value provided. } ScaleRandomTreasure( Shard^.SubCom ); ScaleRandomTreasure( Shard^.InvCom ); { Record the original changes_used_so_far string; if things go sour in this procedure, } { we're going to have to restore it. } original_changes := changes_used_so_far; { Add the changes from this shard. } SPReq := SAttValue( Shard^.SA , 'CHANGES' ); if SPReq <> '' then AddToQuoteString( changes_used_so_far , SPReq ); { Start by copying over all provided parameters. } { Also count the number of parameters passed; it could be useful. } NumParam := 0; for t := 1 to Num_Plot_Elements do begin if ParamIn[ t ].EValue <> 0 then begin SetNAtt( Shard^.NA , NAG_ElementID , T , ParamIn[ t ].EValue ); SetSAtt( Shard^.SA , 'ELEMENT' + BStr( T ) + ' <' + ParamIn[ t ].EType + '>' ); Inc( NumParam ); end; end; { If this is a quest, check the remaining parameters for artifacts. } if IsAQuest then begin for t := ( NumParam + 1 ) to Num_Plot_Elements do begin EDesc := UpCase( SAttValue( Shard^.SA , 'ELEMENT' + BStr( T ) ) ); if ( EDesc <> '' ) and ( EDesc[1] = 'A' ) then begin { This is an artifact request. If no difficulcy context has been } { defined, add one ourselves. } if not AStringHasBString( EDesc , '!' ) then begin EDesc := EDesc + ' ' + DifficulcyContext( Threat ); SetSAtt( Shard^.SA , 'ELEMENT' + BStr( T ) + ' <' + EDesc + '>' ); end; end; end; end; { Next, randomize the NPCs. } { Only do this is we aren't constructing a quest; if we are, then the } { NPCs have already been individualized. } if not IsAQuest then begin I := Shard^.InvCom; while I <> Nil do begin { Character gears have to be individualized. } if ( I^.G = GG_Character ) and NotAnAnimal( I ) then begin IndividualizeNPC( I ); end; I := I^.Next; end; end; { Initialize the subplot list to prevent trouble later on. } SPList := Nil; { Attempt the basic content insertion routine. } if DoDebug then SetSAtt( Shard^.SA , 'name ' ); InitOK := InsertSubPlot( Scope, Slot, Shard , GB ); { If the installation has gone well so far, time to move on. } if InitOK then begin { Count the number of unique elements. If more elements have been } { defined than will fit in a single plot, then loading of this subplot } { will fail. } NumElem := 0; for t := 1 to Num_Plot_Elements do begin if NAttValue( Shard^.NA , NAG_ElementID , T ) <> 0 then begin Inc( NumElem ); end; end; NumElem := NumElem - NumParam + EsSoFar; if NumElem <= Num_Plot_Elements then begin { We have room for the elements. Good. Now move on by installing the subplots. } { Initialize the prefab NPCs for a quest. } if IsAQuest then begin PrepQuestCombatants( Shard^.InvCom ); PrepQuestMetascenes( Shard^.SubCom ); end; { If any of the needed subplots fail, installation of this shard fails } { as well. } { Arena missions may not request subplots. Sorry, that's just how it is. } if Shard^.G <> GG_Scene then begin for t := 1 to Num_Sub_Plots do begin SPReq := SAttValue( Shard^.SA , 'SUBPLOT' + BStr( T ) ); if SPReq <> '' then begin SPID := NewLayerID( Slot ); SubPlot := AddSubPlot( GB , Scope , Slot , Shard , Nil , Quest_Frags , SPReq , NumElem , SPID , T , IsAQuest, DoDebug ); if SubPlot <> Nil then begin { A subplot was correctly installed. Add it to the list. } AppendGear( SPList , SubPlot ); NumElem := NumElem + NAttValue( SubPlot^.NA , NAG_Narrative , NAS_NumSPElementsUsed ); end else begin { The subplot request failed, meaning that this shard fails } { as well. } InitOK := False; RemoveGear( Slot^.InvCom , Shard ); Break; end; end; end; end; end else begin { We have too many elements to merge back into the main plot. } if XXRan_Debug then begin DialogMsg( 'ERROR: ' + GearName( Shard ) + ' has too many elements: ' + BStr( NumElem ) + ' / ' + BStr( EsSoFar ) ); for t := 1 to Num_Plot_Elements do begin DialogMsg( ' ' + BStr( T ) + ' ' + SAttValue( Shard^.SA , 'NAME_' + BStr( T ) ) + ' ' + BStr( ElementID( Shard , T ) ) ); end; end; InitOk := False; RemoveGear( Slot^.InvCom , Shard ); end; end; { Return our result. } if InitOk then begin { Delink the shard. } DelinkGear( Slot^.InvCom , Shard ); { Create the plot placeholder stub, to prevent characters from being } { selected by different parts of the same superplot. } CreatePlotPlaceholder( Slot , Shard ); { Append the SPList to the shard. } AppendGear( Shard , SPList ); SetNAtt( Shard^.NA , NAG_Narrative , NAS_NumSPElementsUsed , NumElem - EsSoFar ); InitShard := Shard; end else begin { Initialization failed. Delete the existing subplots and restore the } { changes_used_so_far list. } changes_used_so_far := original_changes; DisposeSPList( SPList ); InitShard := Nil; end; end; Function QuestIsReusable( Q: GearPtr ): Boolean; { Return TRUE if this quest is reusable, or FALSE otherwise. } begin QuestIsReusable := AStringHasBString( SAttValue( Q^.SA , 'SPECIAL' ) , 'REUSABLE' ); end; Function AddSubPlot( GB: GameBoardPtr; Scope,Slot,Plot0,QPF_Proto: GearPtr; var Quest_Frags: GearPtr; SPReq: String; EsSoFar, LayerID, SubPlotSlot: LongInt; IsAQuest,DoDebug: Boolean ): GearPtr; { A request has been issued for a subplot. Search through the plot } { component list and see if there's anything that matches our criteria. } { Plot0 = the plot requesting the subplot. If this is a quest it may be } { nil, but don't you dare try pulling that crap otherwise. } { QPF_PROTO is a prototype of a prefab element to be inserted into a quest. } { QUEST_FRAGS contains a list of quest fragents. If constructing a quest, these may be used } { in addition to the regular megaplot comps. Once a quest fragment is used, it cannot usually } { be used again. } Function CreateSubPlotList( const CReq: String ): NAttPtr; { Create the list of legal subplots. Start with the regular ones, } { then if appropriate add the not-in-use quest fragments. } { Regular subplots will be added as positive G,S. } { Quest fragments will be added as negative G,S. } var CList: NAttPtr; N,MW: Integer; C: GearPtr; begin { Start by adding the standard components. } CList := CreateComponentList( Sub_Plot_List , CReq ); { Next, add the quest fragments. } if IsAQuest and ( Quest_Frags <> Nil ) then begin N := -1; C := Quest_Frags; while C <> nil do begin if NAttValue( C^.NA , NAG_Narrative , NAS_QuestInUse ) = 0 then begin MW := StringMatchWeight( CReq , SAttValue( C^.SA , 'REQUIRES' ) ); if MW > 0 then begin SetNAtt( CList , N , N , MW ); end; end; Dec( N ); C := C^.Next; end; end; CreateSubPlotList := CList; end; Function SelectNextSubPlot( var ShoppingList: NAttPtr ): GearPtr; { Select a new subplot prototype, and return a pointer to it. } { If a quest fragment is selected, mark it as used. } { Previously, we'd mark a quest fragment as used when it was selected and } { unmark it if the quest initialization failed. This time around it'll stay } { marked for the duration of this quest; if an earlier component can't } { initialize the fragment, chances are good that a later component in the } { same quest won't be able to either. } var it: NAttPtr; N: Integer; Proto: GearPtr; begin { Select one of the components, and delete its entry from the } { shopping list. } it := RandomComponentListEntry( ShoppingList ); N := it^.S; RemoveNAtt( ShoppingList , it ); if N < 0 then begin Proto := RetrieveGearSib( quest_frags , Abs( N ) ); if not QuestIsReusable( Proto ) then SetNAtt( Proto^.NA , NAG_Narrative , NAS_QuestInUse , 1 ); end else begin Proto := RetrieveGearSib( sub_plot_list , N ); end; SelectNextSubPlot := Proto; end; var ShoppingList: NAttPtr; Context,EDesc,SPContext,changes_list: String; ParamList: ElementTable; T,E,Threat: Integer; Shard,QPF_Clone: GearPtr; NotFoundMatch: Boolean; PlotID: LongInt; IsBranchPlot: Boolean; begin { First determine the context. } Context := ExtractWord( SPReq ); DeleteWhiteSpace( SPReq ); { Determine the difficulty rating of this subplot. } if ( SPReq <> '' ) and ( SPReq[1] = '#' ) then begin DeleteFirstChar( SPReq ); T := ExtractValue( SPReq ); if T > 0 then Threat := T; end else if ( Plot0 <> Nil ) then begin threat := NAttValue( Plot0^.NA , NAG_Narrative , NAS_DifficultyLevel ); end else begin threat := 10; end; { Next complete the context. } if Slot^.G = GG_Story then Context := Context + ' ' + StoryContext( GB , Slot ); if IsAQuest then Context := Context + ' ' + QuoteString( SceneContext( Nil , Scope ) ) + ' ' + DifficulcyContext( Threat ); if Plot0 <> Nil then begin SPContext := SAttValue( Plot0^.SA , 'SPContext' ); if SPContext <> '' then Context := Context + ' ' + SPContext; end else begin SPContext := ''; end; { Determine whether this is a regular subplot or a branch plot that will start its own narrative thread. } IsBranchPlot := ( Length( Context ) > 2 ) and ( Context[2] = ':' ); { This will determine whether we inherit the PlotID from Plot0, or generate a new one. } if IsBranchPlot or ( Plot0 = Nil ) then PlotID := NewPlotID( FindRoot( Slot ) , IsAQuest ) else PlotID := NAttValue( Plot0^.NA , NAG_Narrative , NAS_PlotID ); { Store the details for this subplot in Plot0. } if Plot0 <> Nil then begin SetNAtt( Plot0^.NA , NAG_SubPlotLayerID , SubPlotSlot , LayerID ); SetNAtt( Plot0^.NA , NAG_SubPlotPlotID , SubPlotSlot , PlotID ); { Determine the parameters to be sent, and add context info for them. } { We only need parameters if Plot0 = Nil, since root quests take no params. } ClearElementTable( ParamList ); T := 1; while ( SPReq <> '' ) and ( T <= Num_Plot_Elements ) do begin E := ExtractValue( SPReq ); if ( E >= 1 ) and ( E <= Num_Plot_Elements ) then begin { This element is being shared with the subplot. } ParamList[t].EValue := ElementID( Plot0 , E ); EDesc := SAttValue( Plot0^.SA , 'ELEMENT' + BStr( E ) ); if EDesc <> '' then ParamList[t].EType := EDesc[1]; AddElementContext( GB , Plot0 , Context , BStr( T )[1] , E ); Inc( T ); end; end; end else begin { We have no parameters to send. Clear the param list. } ClearElementTable( ParamList ); end; { We have the context. Create the shopping list. } { Positive component values are from the main subplot list. Negative } { component values point to items from the quest_frags list. } ShoppingList := CreateSubPlotList( Context ); if XXRan_Debug and ( Slot^.G = GG_Story ) then begin if NumNAtts( ShoppingList ) < 5 then begin DialogMsg( '[DEBUG] Only ' + BStr( NumNatts( ShoppingList ) ) + ' components for "' + Context + '".' ); end; end; { Based on this shopping list, search for applocable subplots and attempt to } { fit them into the adventure. } NotFoundMatch := True; Shard := Nil; while ( ShoppingList <> Nil ) and NotFoundMatch do begin if XXRan_Wizard and ( ShoppingList <> Nil ) and ( Slot^.G = GG_Story ) and not IsAQuest then begin { DialogMsg( Context );} Shard := CloneGear( ComponentMenu( Sub_Plot_List , ShoppingList ) ); end else if DoDebug and not IsAQuest then begin DialogMsg( Context ); Shard := CloneGear( ComponentMenu( Sub_Plot_List , ShoppingList ) ); end else begin Shard := CloneGear( SelectNextSubPlot( ShoppingList ) ); end; if Shard <> Nil then begin { Make sure this candidate doesn't violate our changes_used_so_far list. } changes_list := SAttValue( Shard^.SA , 'CHANGES' ); if ( changes_list = '' ) or NoQItemsMatch( changes_used_so_far , changes_list ) then begin { Insert the QPF gear, if appropriate. This is naively appended to the } { invcoms, so the Element ID must be the last requested prefab. } if QPF_Proto <> Nil then begin QPF_Clone := CloneGear( QPF_Proto ); InsertInvCom( Shard , QPF_Clone ); end; { See if we can add this one to the list. If not, it will be } { deleted by InitShard. } if SPContext <> '' then SetSAtt( Shard^.SA , 'SPCONTEXT <' + SPContext + '>' ); Shard := InitShard( GB , Scope , Slot , Shard , Quest_Frags , EsSoFar , PlotID , LayerID , Threat , ParamList , IsAQuest , DoDebug ); if Shard <> Nil then NotFoundMatch := False; end else begin { This shard wants to change something we've already changed elsewhere } { in this plot. Better not include it; things could get weird. } DisposeGear( Shard ); end; end; end; { Get rid of the shopping list. } DisposeNAtt( ShoppingList ); { Return our selected subplot. } AddSubPlot := Shard; end; Procedure ReplaceStrings( Part: GearPtr; Dictionary: SAttPtr ); { We have a dictionary of substitute strings, and a part to do the replacements on. } var S: SAttPtr; begin S := Part^.SA; while S <> Nil do begin ApplyDictionaryToString( S^.Info , Dictionary ); S := S^.Next; end; end; Procedure InitListStrings( LList: GearPtr; Dictionary: SAttPtr ); { Run LList, all of its siblings and children, through the ReplaceStrings } { procedure. } begin while LList <> Nil do begin ReplaceStrings( LList , Dictionary ); InitListStrings( LList^.SubCom , Dictionary ); InitListStrings( LList^.InvCom , Dictionary ); LList := LList^.Next; end; end; Procedure MergeElementLists( MasterPlot , SubPlot: GearPtr ); { The element list of SUBPLOT should be merged into MASTERPLOT. } { If a SUBPLOT element is found in MASTERPLOT already, no need } { to merge. Store the master plot element indicies in SubPlot. } { Also copy the PLACE strings here. } var FirstFreeSlot,T,PlotIndex: Integer; EID: LongInt; EDesc: String; Dictionary: SAttPtr; begin { Locate the first free slot in MasterPlot. } FirstFreeSlot := 1; While ( FirstFreeSlot <= Num_Plot_Elements ) and ( ElementID( MasterPlot , FirstFreeSlot ) <> 0 ) do Inc( FirstFreeSlot ); Dictionary := Nil; { Go through the elements of SubPlot. Check to see if they are found in } { MasterPlot. If so, do nothing. If not, add them. } for T := 1 to Num_Plot_Elements do begin EID := ElementID( SubPlot , T ); if EID <> 0 then begin EDesc := SAttValue( SubPlot^.SA , 'ELEMENT' + BStr( T ) ); PlotIndex := PlotElementID( MasterPlot , EDesc[1] , EID ); if PlotIndex = 0 then begin { This element apparently doesn't currently have a } { place in this plot. Add it. } PlotIndex := FirstFreeSlot; Inc( FirstFreeSlot ); SetNAtt( MasterPlot^.NA , NAG_ElementID , PlotIndex , EID ); SetSAtt( MasterPlot^.SA , 'ELEMENT' + BStr( PlotIndex ) + ' <' + EDesc + '>' ); SetSAtt( MasterPlot^.SA , 'TEAM' + BStr( PlotIndex ) + ' <' + SAttValue( SubPlot^.SA , 'TEAM' + BStr( T ) ) + '>' ); end; { We should now have a working PlotIndex. Save it in SubPlot, } { and copy over the PLACE to MasterPlot. } SetNAtt( SubPlot^.NA , NAG_MasterPlotElementIndex , T , PlotIndex ); SetSAtt( Dictionary , '%e' + BStr( T ) + '% <' + BStr( PlotIndex ) + '>' ); end; end; InitListStrings( SubPlot , Dictionary ); DisposeSAtt( Dictionary ); { After initializing the strings, do one more loop to copy over the PLACE info. } for T := 1 to Num_Plot_Elements do begin EID := ElementID( SubPlot , T ); if EID <> 0 then begin PlotIndex := NAttValue( SubPlot^.NA , NAG_MasterPlotElementIndex , T ); EDesc := SAttValue( SubPlot^.SA , 'PLACE' + BStr( T ) ); if EDesc <> '' then SetSAtt( MasterPlot^.SA , 'PLACE' + BStr( PlotIndex ) + ' <' + EDesc + '>' ); end; end; end; Procedure MergePersona( MainPlot , SubPlot , Persona: GearPtr; IsAQuest: Boolean ); { We have a persona that needs to be merged into the main plot. } { If the main plot already has a persona for this character, merge } { this new persona in as a megalist. If no persona currently exists, } { delink this persona from MainPlot and stick in SubPlot. } var MPIndex: Integer; MainPersona: GearPtr; begin { Determine the index of this element in the main plot. } MPIndex := NAttValue( SubPlot^.NA , NAG_MasterPlotElementIndex , Persona^.S ); { Attempt to locate the main persona. } MainPersona := SeekCurrentLevelGear( MainPlot^.SubCom , GG_Persona , MPIndex ); if MainPersona = Nil then begin { No main persona- create one. } if IsAQuest then begin MainPersona := LoadNewSTC( 'PERSONA_BLANK' ); end else begin MainPersona := LoadNewSTC( 'PERSONA_REVERT' ); end; InsertSubCom( MainPlot , MainPersona ); SetSAtt( MainPersona^.SA , 'SPECIAL <' + SAttValue( Persona^.SA , 'SPECIAL' ) + '>' ); { Store the PlotID of this layer, since it's the first to provide a persona for this NPC. } SetNAtt( MainPersona^.NA , NAG_Narrative , NAS_PlotID , NAttValue( SubPlot^.NA , NAG_Narrative , NAS_PlotID ) ); MainPersona^.S := MPIndex; end; { Combine the two plots together. } BuildMegalist( MainPersona , Persona^.SA ); end; Procedure MergeMetascene( MainPlot , SubPlot , MS: GearPtr ); { Combine the sub-metascene with the main metascene. } { If no main metascene exists, simply move and relabel the } { one provided here. } var MPIndex: Integer; MainScene,Thing: GearPtr; begin { Determine the index of this element in the main plot. } MPIndex := NAttValue( SubPlot^.NA , NAG_MasterPlotElementIndex , MS^.S ); { Attempt to locate the main metascene. } MainScene := SeekCurrentLevelGear( MainPlot^.SubCom , GG_MetaScene , MPIndex ); if MainScene = Nil then begin { No main scene- delink, move, and relabel this one. } DelinkGear( SubPlot^.SubCom , MS ); InsertSubCom( MainPlot , MS ); { Store the PlotID of this layer, since it's the first to provide details for this metascene. } SetNAtt( MS^.NA , NAG_Narrative , NAS_PlotID , NAttValue( SubPlot^.NA , NAG_Narrative , NAS_PlotID ) ); MS^.S := MPIndex; end else begin { Combine the two scenes together. } BuildMegalist( MainScene , MS^.SA ); { Copy over all InvComs and SubComs. } while ( MS^.InvCom <> Nil ) do begin Thing := MS^.InvCom; DelinkGear( MS^.InvCom , Thing ); InsertInvCom( MainScene , Thing ); end; while ( MS^.SubCom <> Nil ) do begin Thing := MS^.SubCom; DelinkGear( MS^.SubCom , Thing ); InsertSubCom( MainScene , Thing ); end; end; end; Procedure CombinePlots( MasterPlot, SubPlot: GearPtr; IsAQuest: Boolean ); { Combine SubPlot into MasterPlot, including all elements, scripts, } { personas, metascenes, and so on. } { - Merge element lists } { - Copy PLACE strings from SUBPLOT to MASTERPLOT. } { A place defined in a subplot take precedence over anything } { defined earlier. } { - Megalist scripts } { - Megalist personas } { - Combine MetaScenes } { - Move InvComs } { - Add victory points } var Thing,T2: GearPtr; begin MergeElementLists( MasterPlot , SubPlot ); BuildMegalist( MasterPlot , SubPlot^.SA ); { Take a look at the things in this subplot. } { Deal with them separately, as appropriate. } Thing := SubPlot^.SubCom; while Thing <> Nil do begin T2 := Thing^.Next; if Thing^.G = GG_Persona then begin MergePersona( MasterPlot , SubPlot , Thing , IsAQuest ); end else if Thing^.G = GG_MetaScene then begin MergeMetascene( MasterPlot , SubPlot , Thing ); end; Thing := T2; end; { Move over the InvComs. } while SubPlot^.InvCom <> Nil do begin Thing := SubPlot^.InvCom; DelinkGear( SubPlot^.InvCom , Thing ); InsertInvCom( MasterPlot , Thing ); end; { If the master plot doesn't have a PayRate set, the subplot gets to } { set one. } if NAttValue( MasterPlot^.NA , NAG_ArenaMissionInfo , NAS_PayRate ) = 0 then SetNAtt( MasterPlot^.NA , NAG_ArenaMissionInfo , NAS_PayRate , NAttValue( SubPlot^.NA , NAG_ArenaMissionInfo , NAS_PayRate ) ); end; Function IsStandardTrigger( const S_Head: String ): Boolean; { Return TRUE if S_Head is one of the standard triggers, or FALSE if it } { isn't. } var ST: SAttPtr; MatchFound: Boolean; begin { Go through the list of standard triggers; stop when we find a match. } ST := standard_trigger_list; MatchFound := False; while ( ST <> Nil ) and not MatchFound do begin if HeadMatchesString( ST^.Info , S_Head ) then MatchFound := True; ST := ST^.Next; end; IsStandardTrigger := MatchFound; end; Function AssembleMegaPlot( Slot , SPList: GearPtr; var Quest_Frags: GearPtr; IsAQuest: Boolean ): GearPtr; { SPList is a list of subplots. Assemble them into a single coherent megaplot. } { The first item in the list is the base plot- all other plots get added to it. } { - Delete all placeholder stubs from SLOT } { - Process each fragment in turn. } { - Delink from list } { - Sequester the standard scripts } { - Do string substitutions } { - Combine plots } { - If a non-reusable quest fragment, delete the prototype } { - Insert the finished plot into slot as an invcom } Procedure PrepStandardScripts( SubPlot: GearPtr ); { A subplot's standard scripts (those attached to basic game triggers, as } { listed in ASLRef.txt) will only be called when the subplot is active. } var sline: SAttPtr; { our counter for moving through the list. } s_Head,s_Script: String; begin sline := SubPlot^.SA; while sline <> Nil do begin S_Head := RetrieveAPreamble( sline^.Info ); if IsStandardTrigger( S_Head ) then begin { This is a standard script. It needs to be moved to a new location, } { and this original line needs to be replace with a redirect. } { First, get the script info. } S_Script := RetrieveAString( sline^.Info ); { Next, install the redirect. } sline^.info := S_Head + ' '; { Finally, place the original script in its new home. } SetSAtt( SubPlot^.SA , '.%id%_' + S_Head + ' <' + S_Script + '>' ); end; sline := sline^.next; end; end; Procedure InitMetasceneFactions( SubPlot: GearPtr ); { Certain metascenes may have a faction defined. Don't use that numeric faction; } { instead, use the element being referred to. } var MScene: GearPtr; FID: Integer; begin MScene := SubPlot^.SubCom; while MScene <> Nil do begin if MScene^.G = GG_MetaScene then begin FID := NAttValue( MScene^.NA , NAG_Personal , NAS_FactionID ); if FID <> 0 then begin SetNAtt( MScene^.NA , NAG_Personal , NAS_FactionID , ElementID( SubPlot , FID ) ); end; end; MScene := MScene^.Next; end; end; Procedure DoStringSubstitutions( SubPlot: GearPtr; IsMasterPlot: Boolean ); { Do the string substitutions for this subplot. Basically, } { create the dictionary and pass it on to the substituter. } var Dictionary: SAttPtr; T: Integer; begin { Begin creating. } Dictionary := Nil; SetSAtt( Dictionary , '%plotid% <' + BStr( NAttValue( SubPlot^.NA , NAG_Narrative , NAS_PlotID ) ) + '>' ); SetSAtt( Dictionary , '%id% <' + BStr( NAttValue( SubPlot^.NA , NAG_Narrative , NAS_PlotLayer ) ) + '>' ); SetSAtt( Dictionary , '%threat% <' + BStr( NAttValue( SubPlot^.NA , NAG_Narrative , NAS_DifficultyLevel ) ) + '>' ); for t := 1 to Num_Sub_Plots do begin SetSAtt( Dictionary , '%id' + BStr( T ) + '% <' + Bstr( NAttValue( SubPlot^.NA , NAG_SubPlotLayerID , T ) ) + '>' ); SetSAtt( Dictionary , '%plotid' + BStr( T ) + '% <' + Bstr( NAttValue( SubPlot^.NA , NAG_SubPlotPlotID , T ) ) + '>' ); end; for t := 1 to Num_Plot_Elements do begin { If dealing with the main plot, do substitutions for the Element Indicies now. } if IsMasterPlot then SetSAtt( Dictionary , '%E' + BStr( T ) + '% <' + BStr( T ) + '>' ); SetSAtt( Dictionary , '%' + BStr( T ) + '% <' + BStr( ElementID( SubPlot , T ) ) + '>' ); SetSAtt( Dictionary , '%name' + BStr( T ) + '% <' + SAttValue( SubPlot^.SA , 'name_' + BStr( T ) ) + '>' ); end; { Run the provided subplot through the convertor. } InitListStrings( SubPlot , Dictionary ); DisposeSAtt( Dictionary ); end; Procedure InitMPSubs( MasterPlot: GearPtr ); { All personas and metascenes must be marked with the PlotID } { of the subplot wot spawned 'em. } var LList: GearPtr; begin LList := MasterPlot^.SubCom; while LList <> Nil do begin if ( LList^.G = GG_Persona ) or ( LList^.G = GG_MetaScene ) then begin SetNAtt( LList^.NA , NAG_Narrative , NAS_PlotID , NAttValue( MasterPlot^.NA , NAG_Narrative , NAS_PlotID ) ); end; LList := LList^.Next; end; end; Procedure DeleteQuestPrototype( Plot: GearPtr ); { It's possible that this subplot is based on a quest fragment. } { If said fragment isn't reusable, remove it from the list. } var Frag: GearPtr; begin if ( Quest_Frags <> Nil ) and ( Plot^.S > 0 ) then begin Frag := SeekCurrentLevelGear( Quest_Frags , GG_Plot , Plot^.S ); if ( Frag <> Nil ) and ( not QuestIsReusable( Frag ) ) then begin RemoveGear( Quest_Frags , Frag ); end; end; end; Procedure ResetQuestPrototypes; { Clear the INUSE frag from all remaining prototypes. } var Frag: GearPtr; begin Frag := Quest_Frags; while Frag <> Nil do begin SetNAtt( Frag^.NA , NAG_Narrative , NAS_QuestInUse , 0 ); Frag := Frag^.Next; end; end; var MasterPlot,SubPlot: GearPtr; begin { Delete the placeholders. } DeletePlotPlaceholders( Slot ); { Extract the master plot. It should be the first one in the list. } MasterPlot := SPList; DelinkGear( SPList , MasterPlot ); DoStringSubstitutions( MasterPlot , True ); InitMetasceneFactions( MasterPlot ); InitMPSubs( MasterPlot ); InsertInvCom( Slot , MasterPlot ); DeleteQuestPrototype( MasterPlot ); { Store the PlotID being used. Do so for all subplot IDs as well. } SetNAtt( MasterPlot^.NA , NAG_PlotStatus , NAttValue( MasterPlot^.NA , NAG_Narrative , NAS_PlotID ) , 1 ); { Keep processing until we run out of subplots. } while SPList <> Nil do begin SubPlot := SPList; DelinkGear( SPList , SubPlot ); AddSAtt( MasterPlot^.SA , 'SUBPLOT_NAME' , GearName( SubPlot ) ); SetNAtt( MasterPlot^.NA , NAG_PlotStatus , NAttValue( SubPlot^.NA , NAG_Narrative , NAS_PlotID ) , 1 ); { Do the substitutions for standard triggers here. } PrepStandardScripts( SubPlot ); InitMetasceneFactions( SubPlot ); DeleteQuestPrototype( SubPlot ); DoStringSubstitutions( SubPlot , False ); CombinePlots( MasterPlot, SubPlot , IsAQuest ); DisposeGear( SubPlot ); end; { After assembling the plot, clear any INUSE tags left over on the } { quest frags. } ResetQuestPrototypes; { Return the finished plot. } AssembleMegaPlot := MasterPlot; end; Procedure MoveElements( GB: GameBoardPtr; Adv,Plot: GearPtr; IsAQuest: Boolean ); { There are a bunch of elements in this plot. Some of them need to be moved. } { Make it so. } { GB may be nil, but Adv must be a component of the adventure. } var T,PlaceIndex: Integer; PlaceCmd,EDesc,TeamName,DebugRec: String; Element,Dest,MF,Team,MS,Thing,DScene,Dest0: GearPtr; InSceneNotElement: Boolean; EID: LongInt; begin { Loop through all elements, looking for stuff to move. } for t := 1 to Num_Plot_ELements do begin PlaceCmd := SAttValue( Plot^.SA , 'PLACE' + BStr( T ) ); if PlaceCmd <> '' then begin EDesc := SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ); DebugRec := PlaceCmd; if ( EDesc <> '' ) and ( UpCase( EDesc[1] ) = 'S' ) then begin { I can't believe you just asked me to move a scene... } { What you really must want is for me to move an encounter } { attached to a metascene. Yeah, that must be it. } EID := ElementID( Plot , T ); if EID < 0 then begin Element := FindSceneEntrance( FindRoot( Adv ) , GB , EID ); end else begin Element := Nil; end; end else begin { Just find the regular element. } Element := SeekPlotElement( FindRoot( Adv ) , Plot , T , GB ); end; InSceneNotElement := ( PlaceCmd[1] = '~' ); if InSceneNotElement then DeleteFirstChar( PlaceCmd ); if PlaceCmd = '/' then begin Dest := SeekCurrentLevelGear( FindRoot( Adv )^.InvCom , GG_PlotThingSet , 0 ); InSceneNotElement := False; end else begin PlaceIndex := ExtractValue( PlaceCmd ); Dest := SeekPlotElement( FindRoot( Adv ) , Plot , PlaceIndex , GB ); end; TeamName := RetrieveBracketString( PlaceCmd ); if Element = Nil then begin DialogMsg( 'ERROR- Element ' + BStr( T ) + ' of ' + GearName( Plot ) + ' not found for movement.' ); Exit; end; { Next, delink the gear for movement... but there's a catch. } { We don't want the delinker to give our element an OriginalHome } { if it's a prefab element, because we want to do that ourselves } { now in a bit. } { Don't delink if we have a scene- in that case, we're just here to transfer } { over the metascene stuff. } if Element^.G <> GG_Scene then begin if ( Element^.Parent <> Nil ) and ( Element^.Parent^.G = GG_Plot ) and IsInvCom( Element ) then begin DelinkGear( Element^.Parent^.InvCom , Element ); end else begin DelinkGearForMovement( GB , Element ); end; end; if InSceneNotElement and (( Dest = Nil ) or ( Dest^.G <> GG_Scene )) then begin { If the destination is a metascene, locate its entrance. } if ( Dest = Nil ) or ( Dest^.G = GG_MetaScene ) then begin Dest := FindSceneEntrance( FindRoot( Adv ) , GB , ElementID( Plot , PlaceIndex ) ); end; { Try to find the associated scene now. } if ( Dest <> Nil ) and not IsAQuest then begin Dest := FindActualScene( GB , FindSceneID( Dest , GB ) ); end; end; if ( Dest <> Nil ) then begin if ( Dest^.G <> GG_Scene ) and ( Dest^.G <> GG_MetaScene ) and IsLegalInvCom( Dest , Element ) then begin { If E can be an InvCom of Dest, stick it there. } InsertInvCom( Dest , Element ); end else begin { If Dest isn't a scene, find the scene DEST is in itself } { and stick E in there. } Dest0 := Dest; while ( Dest <> Nil ) and ( not IsAScene( Dest ) ) do Dest := Dest^.Parent; if Dest = Nil then begin DialogMsg( 'ERROR: ' + GearName( Dest0 ) + ' selected as place for ' + GearName( Element ) ); Exit; end; if IsMasterGear( Element ) then begin if TeamName <> '' then begin Team := SeekChildByName( Dest , TeamName ); if ( Team <> Nil ) and ( Team^.G = GG_Team ) then begin SetNAtt( Element^.NA , NAG_Location , NAS_Team , Team^.S ); end else begin ChooseTeam( Element , Dest ); end; end else begin ChooseTeam( Element , Dest ); end; end; { If a Metascene map feature has been defined as this element's home, } { stick it there instead of in the scene proper. Such an element will } { always be MiniMap component #1, so set that value here too. } if ( Dest^.G = GG_MetaScene ) then begin MF := SeekGearByDesig( Dest^.SubCom , 'HOME ' + BStr( T ) ); if MF <> Nil then begin Dest := MF; SetNAtt( Element^.NA , NAG_ComponentDesc , NAS_ELementID , 1 ); end; end; { If this is a quest, then this element might have some supplemental } { scene content. Better take a look. } if IsAQuest and IsAScene( Dest ) then begin MS := SeekCurrentLevelGear( Plot^.SubCom , GG_MetaScene , T ); if MS <> Nil then begin { Store the destination scene- we'll need it later. } DScene := Dest; { This metascene may also contain a home for this element. } MF := SeekGearByDesig( MS^.SubCom , 'HOME' ); if ( MF <> Nil ) and ( Element^.G <> GG_Scene ) then begin Dest := MF; SetNAtt( Element^.NA , NAG_ComponentDesc , NAS_ELementID , 1 ); end; { Copy over all InvComs and SubComs. } while ( MS^.InvCom <> Nil ) do begin Thing := MS^.InvCom; DelinkGear( MS^.InvCom , Thing ); InsertInvCom( DScene , Thing ); end; while ( MS^.SubCom <> Nil ) do begin Thing := MS^.SubCom; DelinkGear( MS^.SubCom , Thing ); InsertSubCom( DScene , Thing ); end; end; end; { If this is a prefab element and we're deploying } { to a metascene, assign an OriginalHome value of -1 } { to make sure it doesn't get deleted when the plot } { ends. } if NAttValue( Element^.NA , NAG_ParaLocation , NAS_OriginalHome ) = 0 then begin if Dest^.G = GG_MetaScene then SetNAtt( Element^.NA , NAG_ParaLocation , NAS_OriginalHome , -1 ); end; if ( GB <> Nil ) and ( Dest = GB^.Scene ) then begin EquipThenDeploy( GB , Element , True ); end else if Element^.G <> GG_Scene then begin InsertInvCom( Dest , Element ); end; end; end else begin DialogMsg( 'ERROR: Destination not found for ' + GearName( Element ) + '/' + GearName( Plot ) + ' PI:' + BStr( PlaceIndex ) ); DialogMsg( DebugRec ); InsertInvCom( Plot , Element ); end; end; end; end; Procedure DeployPlot( GB: GameBoardPtr; Slot,Plot: GearPtr; Threat: Integer ); { Actually add the plot to the adventure. Set it in place, move any elements as } { requested. } { - Insert persona fragments as needed } { - Deploy elements as indicated by PLACE strings } begin PrepAllPersonas( FindRoot( Slot ) , Plot , GB , NAttValue( Slot^.NA , NAG_Narrative , NAS_MaxPlotLayer ) + 1 ); if Plot^.G <> GG_Scene then begin MoveElements( GB , FindRoot( Slot ) , Plot , False ); PrepMetascenes( FindRoot( Slot ) , Plot , GB ); end; end; Function ExpandDungeon( Dung: GearPtr ): GearPtr; { Expand this dungeon. Return the "goal scene", which is the lowest level generated. } { Add sub-levels, branches, and goal requests. } { Note that this procedure will not assign SceneIDs nor will it connect the levels } { with entrances. } var name_base,type_base: String; branch_number: Integer; sub_scenes: GearPtr; LowestLevel: GearPtr; Function ExtractSubScenes: GearPtr; { Remove any scenes that are subcoms of the dungeon, and } { return them in a list. } var it,S,S2: GearPtr; begin it := Nil; S := Dung^.SubCom; while S <> Nil do begin S2 := S^.Next; if S^.G = GG_Scene then begin DelinkGear( Dung^.SubCom , S ); AppendGear( it , S ); end; S := S2; end; ExtractSubScenes := it; end; Procedure EliminateClonedScenes( DL: GearPtr ); { When cloning the prototype dungeon level, don't copy } { the sub-scenes as well. } var S,S2: GearPtr; begin S := DL^.SubCom; while S <> Nil do begin S2 := S^.Next; if S^.G = GG_Scene then begin RemoveGear( DL^.SubCom , S ); end; S := S2; end; end; Procedure AddNewDungeonLevel( S: GearPtr; Branch: Integer ); var S2,T: GearPtr; begin S2 := CloneGear( S ); { Eliminate any sub-scenes of S2. } EliminateClonedScenes( S2 ); InsertSubCom( S , S2 ); { We don't want to use the main dungeon entrance type for this entrance, } { so copy the DEntrance string instead. } SetSAtt( S2^.SA , 'ENTRANCE <' + SAttValue( S^.SA , 'DENTRANCE' ) + '>' ); { Increase the dungeon level. } AddNAtt( S2^.NA , NAG_Narrative , NAS_DungeonLevel , 1 ); if NAttValue( S2^.NA , NAG_Narrative , NAS_DungeonLevel ) > NAttValue( LowestLevel^.NA , NAG_Narrative , NAS_DungeonLevel ) then LowestLevel := S2; SetNAtt( S2^.NA , NAG_Narrative , NAS_DungeonBranch , Branch ); { Increase the difficulcy level. } T := S2^.SubCom; while T <> Nil do begin if ( T^.G = GG_Team ) and ( T^.Stat[ STAT_WanderMon ] > 0 ) then begin T^.Stat[ STAT_WanderMon ] := T^.Stat[ STAT_WanderMon ] + 1 + Random( 3 ) + Random( 2 ); { Add the context description for the difficulcy level. } SetSAtt( S2^.SA , 'type <' + type_base + ' ' + DifficulcyContext( T^.Stat[ STAT_WanderMon ] ) ); end; T := T^.Next; end; end; Procedure ExpandThisLevel( S: GearPtr ); { Search for dungeons among the adventure's scenes. If you find any, } { maybe expand them by adding sub-dungeons. } const Branch_Suffix: Array [1..10] of char = ( 'a','b','c','d','e','f','g','h','i','j' ); dungeon_goal_content_string = 'SOME 1 # SUB *DUNGEON_GOAL'; var S2: GearPtr; Branch: Integer; begin Branch := NAttValue( S^.NA , NAG_Narrative , NAS_DungeonBranch ); if ( S^.G = GG_Scene ) and AStringHasBString( SAttValue( S^.SA , 'TYPE' ) , 'DUNGEON' ) and ( SAttValue( S^.SA , 'DENTRANCE' ) <> '' ) then begin if NAttValue( S^.NA , NAG_Narrative , NAS_DungeonLevel ) < ( RollStep( 3 ) + 1 ) then begin { Maybe add a branch. } AddNewDungeonLevel( S , Branch ); if ( Random( 5 ) = 1 ) and ( Branch_Number < 9 ) then begin AddNewDungeonLevel( S , Branch_Number + 1 ); Inc( Branch_Number ); end; end else begin { If not adding a deeper level, add DungeonGoal content. } AddSAtt( S^.SA , 'CONTENT' , ReplaceHash( dungeon_goal_content_string , BSTr( NAttValue( S^.NA , NAG_Narrative , NAS_DungeonLevel ) * 10 + 15 ) ) ) end; { Name the dungeon. } if Branch = 0 then begin SetSAtt( S^.SA , 'name <' + name_base + ', L' + BStr( NAttValue( S^.NA , NAG_Narrative , NAS_DungeonLevel ) + 1 ) ); end else begin SetSAtt( S^.SA , 'name <' + name_base + ', L' + BStr( NAttValue( S^.NA , NAG_Narrative , NAS_DungeonLevel ) + 1 ) + Branch_Suffix[ Branch ] ); end; end; S2 := S^.SubCom; while S2 <> Nil do begin ExpandThisLevel( S2 ); S2 := S2^.Next; end; end; begin { Record some information, initialize some variables. } name_base := GearName( Dung ); if Full_RPGWorld_Info then DialogMsg( 'Expanding ' + name_base ); SetSAtt( Dung^.SA , 'DUNGEONNAME <' + name_base + '>' ); type_base := SAttValue( Dung^.SA , 'TYPE' ); sub_scenes := ExtractSubScenes; LowestLevel := Dung; Branch_Number := 0; ExpandThisLevel( Dung ); if Sub_Scenes <> Nil then InsertSubCom( lowestLevel , Sub_Scenes ); ExpandDungeon := lowestlevel; end; Procedure ConnectScene( Scene: GearPtr; DoInitExits: Boolean ); { SCENE needs to be connected to its parent scene. This means that any } { entrances in PARENT pointing to SCENE have to be given the correct } { destination number, and any entrances in SCENE leading back to PARENT } { also have to be given the correct destination number. } { PRECON: Scene and its parent must have already been given scene IDs. } Function FindEntranceByName( EG: GearPtr; Name: String ): GearPtr; { Find an entrance with the provided name. } { This may be in one of the parent scene's subcoms, or one of its } { map feature's subcoms or invcoms. } var it: GearPtr; begin it := Nil; Name := UpCase( Name ); while ( EG <> Nil ) and ( it = Nil ) do begin if ( EG^.G = GG_MetaTerrain ) and ( UpCase( SAttValue( EG^.SA , 'NAME' ) ) = Name ) then it := EG else if ( EG^.G = GG_MapFeature ) then begin it := FindEntranceByName( EG^.SubCom , Name ); if ( it = Nil ) then it := FindEntranceByName( EG^.InvCom , Name ); end; EG := EG^.Next; end; FindEntranceByName := it; end; Procedure InitExits( S,E: GearPtr ); { Locate exits with a nonzero destination, then give them the proper } { destination of the parent scene. } begin while E <> Nil do begin if E^.G = GG_MapFeature then begin InitExits( S , E^.SubCom ); InitExits( S , E^.InvCom ); end else if ( E^.G = GG_MetaTerrain ) and ( E^.Stat[ STAT_Destination ] <> 0 ) then begin if ( S^.Parent^.G = GG_Scene ) then begin E^.Stat[ STAT_Destination ] := S^.Parent^.S; end else if S^.Parent^.G = GG_MetaScene then begin { We must be dealing with a quest scene. No problem- } { I know exactly where its SceneID is. } E^.Stat[ STAT_Destination ] := ElementID( S^.Parent^.Parent , S^.Parent^.S ); end; end; E := E^.Next; end; end; var E,Loc: GearPtr; Entrance,EName: String; begin { Insert entrance to super-scene. } E := FindEntranceByName( Scene^.Parent^.SubCom , GearName( Scene ) ); if E = Nil then begin Entrance := SAttValue( Scene^.SA , 'DUNGEONNAME' ); if Entrance <> '' then begin E := FindEntranceByName( Scene^.Parent^.SubCom , Entrance ); end; end; if ( E <> Nil ) and ( E^.G = GG_MetaTerrain ) then begin { A named entrance was found. Initialize it. } E^.Stat[ STAT_Destination ] := Scene^.S; end else begin { No entrance for this scene was specified. Better create one. } E := FindNextComponent( MasterEntranceList , SAttValue( Scene^.SA , 'ENTRANCE' ) ); if E <> Nil then begin E := CloneGear( E ); if ( E^.S = GS_MetaBuilding ) or ( E^.S = GS_MetaEncounter ) then begin EName := SAttValue( Scene^.SA , 'DUNGEONNAME' ); if EName = '' then EName := GearName( Scene ); SetSAtt( E^.SA , 'NAME <' + EName + '>' ); end; E^.Stat[ STAT_Destination ] := Scene^.S; if Scene^.Parent^.G <> GG_World then E^.Scale := Scene^.Parent^.V; if NAttValue( Scene^.NA , NAG_LOcation , NAS_X ) <> 0 then begin SetNAtt( E^.NA , NAG_Location , NAS_X , NAttValue( Scene^.NA , NAG_LOcation , NAS_X ) ); SetNAtt( E^.NA , NAG_Location , NAS_Y , NAttValue( Scene^.NA , NAG_LOcation , NAS_Y ) ); end; { Insert "E" as an InvCom of the parent scene. } { If E isn't a building or the parent scene isn't a world, } { also insert a subzone for E so it won't be stuck randomly somewhere. } if ( E^.S = GS_MetaBuilding ) or ( E^.S = GS_MetaEncounter ) or ( Scene^.Parent^.G = GG_World ) then begin InsertInvCom( Scene^.Parent , E ); end else begin Loc := NewSubZone( Scene^.Parent ); InsertSubCom( Loc , E ); end; end; end; { Initialize exits back to the upper level. } if DoInitExits then begin InitExits( Scene , Scene^.SubCom ); InitExits( Scene , Scene^.InvCom ); end; end; Procedure PrepQuestDungeon( Adv,SceneProto: GearPtr ); { Prepare this dungeon, please. To do this we'll need to expand the dungeon } { by several levels, assign unique IDs to all our new scenes, and connect } { them all to each other. } { The SceneID which has already been assigned will be the SceneID of the } { goal level. The ScenePrototype, which will serve as the entry level, } { will be given a new SceneID. Make sure that you use this new SceneID } { for assigning the entrance. } { The procedure for expanding a quest dungeon is as follows: } { 1 - Remove non-original subs and invs, saving them for the goal level. } { 2 - Expand the dungeon. } { 3 - Assign SceneIDs as needed and connect the scenes. } { At the same time, record the ID of the entry level. } { 4 - Reinstall the subs and invs from step 1 into the goal level. } var GoalLevel,NOSubs,NOInvs: GearPtr; EntryLevelID: Integer; Procedure AssignSceneIDs( SList: GearPtr ); { Assign unique IDs to all the scenes in this list and all of } { their children scenes. Also do the connections, as long as we're here. } { On top of that, record the entry level ID. Got all that? Good. } begin while SList <> Nil do begin if ( SList^.G = GG_Scene ) then begin if SList <> GoalLevel then SList^.S := NewSceneID( Adv ); { Record the entry level ID. } SetNAtt( SList^.NA , NAG_Narrative , NAS_DungeonEntrance , EntryLevelID ); ConnectScene( SList , True ); end; if SList <> GoalLevel then AssignSceneIDs( SList^.SubCom ); SList := SList^.next; end; end; Procedure InitPrototype; { The prototype must be initialized. } { Things to do: } { - Set the L1 Difficulty rating } { - Strip out the non-original SubComs and InvComs. } Function StripNonOriginals( var LList: GearPtr ): GearPtr; { Remove anything from this list that doesn't have the WasQDOriginal tag. } { Return the list of removed items. } var LL,LL2,OutList: GearPtr; begin LL := LList; OutList := Nil; while LL <> Nil do begin LL2 := LL^.Next; if NAttValue( LL^.NA , NAG_Narrative , NAS_QuestDungeonPlacement ) <> NAV_WasQDOriginal then begin DelinkGear( LList , LL ); AppendGear( OutList , LL ); end; LL := LL2; end; StripNonOriginals := OutList; end; var Team: GearPtr; begin { Assign the Difficulty number. } Team := SceneProto^.SubCom; while Team <> Nil do begin if ( Team^.G = GG_Team ) and ( Team^.Stat[ STAT_WanderMon ] > 0 ) then begin Team^.Stat[ STAT_WanderMon ] := NAttValue( SceneProto^.NA , NAG_Narrative , NAS_DifficultyLevel ) - 10; if Team^.Stat[ STAT_WanderMon ] < 4 then Team^.Stat[ STAT_WanderMon ] := 2 + Random( 3 ); end; Team := Team^.Next; end; { Strip out the non-original subs and invs. } NOSubs := StripNonOriginals( SceneProto^.SubCom ); NOInvs := StripNonOriginals( SceneProto^.InvCom ); end; Procedure ReinstallSubsAndInvs; { Reinstall the subs and invs, placing them in either the goal or the } { entry levels. } var part: GearPtr; begin { Begin with the subs. } while NoSubs <> Nil do begin part := NoSubs; DelinkGear( NoSubs , part ); if NAttValue( Part^.NA , NAG_Narrative , NAS_QuestDungeonPlacement ) = NAV_ForEntryLevel then begin InsertSubCom( SceneProto , part ); end else begin InsertSubCom( GoalLevel , part ); end; end; { Finish with the invs. } while NOInvs <> Nil do begin part := NoInvs; DelinkGear( NoInvs , part ); if NAttValue( Part^.NA , NAG_Narrative , NAS_QuestDungeonPlacement ) = NAV_ForEntryLevel then begin InsertInvCom( SceneProto , part ); end else begin InsertInvCom( GoalLevel , part ); end; end; end; begin { **************** } { *** STEP ONE *** } { **************** } { Start by initializing the dungeon prototype. } InitPrototype; { **************** } { *** STEP TWO *** } { **************** } { Next expand the dungeon. } GoalLevel := ExpandDungeon( SceneProto ); { ****************** } { *** STEP THREE *** } { ****************** } { Next, pass out the UniqueIDs. } { Also take this opportunity to connect everything. } SceneProto^.S := NewSceneID( Adv ); EntryLevelID := SceneProto^.S; AssignSceneIDs( SceneProto^.SubCom ); { ***************** } { *** STEP FOUR *** } { ***************** } { Re-insert the NOSubs and NOInvs into the finished dungeon. } ReinstallSubsAndInvs; end; Procedure InstallQuestScenes( Adv , City , Quest: GearPtr ); { QUEST probably contains a number of metascenes which we have to deal with. } { If these are newly-defined scenes, they get placed in the adventure. Otherwise } { they get combined with existing scenes. } { 1 - Locate the destination for each scene. } { - If a destination cannot be found, assign it to the city. } { - Clear the PLACE attribute after reading it. } { 2 - Move scene to its destination, and change type. } { - Perform additional initialization. } { 3 - Expand dungeons. } { - If this isn't a dungeon, initialize any WMon teams that may exist. } { - Element ID will be the SceneID of the goal level. } { 4 - Locate and initialize entrances. } { - Make sure dungeon entrances point to the entrance, not the goal level. } { - If no entrance can be found, use default ConnectScene procedure. } { 5 - Locate and initialize exits. } Procedure PrepWMonTeams( Scene: GearPtr ); { Check for monster teams. Set appropriate threat levels. } var Team: GearPtr; begin Team := Scene^.SubCom; while Team <> Nil do begin if ( Team^.G = GG_Team ) and ( Team^.Stat[ STAT_WanderMon ] > 0 ) then begin Team^.Stat[ STAT_WanderMon ] := NAttValue( Scene^.NA , NAG_Narrative , NAS_DifficultyLevel ); if Team^.Stat[ STAT_WanderMon ] < 3 then Team^.Stat[ STAT_WanderMon ] := 3; end; Team := Team^.Next; end; end; Procedure InitializeEntrance( Scene: GearPtr; SIDtoSeek: Integer ); { Initialize the entrances for this scene. Note that because of dungeons, } { the SceneID to seek might not be the same as the current SceneID of the } { scene. Therefore, search for the provided SceneID, but set the SceneID } { of the provided scene. } var Entrance: GearPtr; EDesig: String; FoundAnEntrance: Boolean; begin { Haven't started... therefore, we haven't found an entrance yet. } FoundAnEntrance := False; { Create the designation that we're looking for. } EDesig := 'ENTRANCE ' + BStr( SIDtoSeek ); { Now that we have this, start searching for entrances until we } { run out of them. There may be more than one. } repeat ENtrance := SeekGearByDesig( Quest , EDesig ); if ENtrance = Nil then begin Entrance := SeekGearByDesig( Adv , EDesig ); end; if Entrance <> Nil then begin FoundAnEntrance := True; Entrance^.Stat[ STAT_Destination ] := Scene^.S; SetSAtt( Entrance^.SA , 'DESIG ' ); end; until Entrance = Nil; { If we haven't found any entrances, or if we're requesting one, } { call the automatic scene connector. } if ( SAttValue( Scene^.SA , 'ENTRANCE' ) <> '' ) or not FoundAnEntrance then begin { Don't bother initializing the exits, because we're doing that ourselves below. } ConnectScene( Scene , False ); end; end; Procedure InitExits( S,E: GearPtr ); { Locate exits with a nonzero destination, then give them the proper } { destination of the parent scene. } begin while E <> Nil do begin if E^.G = GG_MapFeature then begin InitExits( S , E^.SubCom ); InitExits( S , E^.InvCom ); end else if ( E^.G = GG_MetaTerrain ) and ( E^.Stat[ STAT_Destination ] = -1 ) then begin if ( S^.Parent^.G = GG_Scene ) then begin E^.Stat[ STAT_Destination ] := S^.Parent^.S; end else if S^.Parent^.G = GG_MetaScene then begin { We must be dealing with a quest scene. No problem- } { I know exactly where its SceneID is. } E^.Stat[ STAT_Destination ] := ElementID( S^.Parent^.Parent , S^.Parent^.S ); end; end; E := E^.Next; end; end; var QS,QS2,Dest: GearPtr; EDesc,DDesc: String; N,EIn: Integer; begin { Loop through all the subcoms looking for potential quest scenes. } QS := Quest^.SubCom; while QS <> Nil do begin QS2 := QS^.Next; if QS^.G = GG_MetaScene then begin { Find out whether this is a quest scene or not. If not, } { then it's just a list of contents to stuff into one of the } { pre-existing scenes. } EDesc := SAttValue( Quest^.SA , 'ELEMENT' + BStr( QS^.S ) ); if ( EDesc <> '' ) and ( UpCase( EDesc[1] ) = 'Q' ) then begin { **************** } { *** STEP ONE *** } { **************** } { We've got a live one. Start by locating the destination. } DDesc := SAttValue( Quest^.SA , 'PLACE' + BStr( QS^.S ) ); N := ExtractValue( DDesc ); Dest := SeekPlotElement( Adv , Quest , N , Nil ); if ( Dest = Nil ) or not IsAScene( Dest ) then Dest := City; { Remove the PLACE string, so the element placer doesn't try to move it. } SetSAtt( Quest^.SA , 'PLACE' + BStr( QS^.S ) + ' <>' ); { **************** } { *** STEP TWO *** } { **************** } { Move the new scene to its destination. Change it from a metascene } { into an actual scene. Update its element description in the quest. } DelinkGear( Quest^.SubCom , QS ); InsertSubCom( Dest , QS ); { Record the element index. } EIn := QS^.S; SetSAtt( Quest^.SA , 'ELEMENT' + BStr( EIn ) + ' ' ); QS^.G := GG_Scene; QS^.S := ElementID( Quest , EIn ); { Also copy over the HABITAT, if this scene doesn't have one. } if SAttValue( QS^.SA , 'HABITAT' ) = '' then SetSAtt( QS^.SA , 'HABITAT <' + SAttValue( City^.SA , 'HABITAT' ) + '>' ); { ****************** } { *** STEP THREE *** } { ****************** } { If this scene is a dungeon, expand it. The current SceneID will be } { retained by the goal level; check QS^.S to find the ID of the entry. } if AStringHasBString( SAttValue( QS^.SA , 'TYPE' ) , 'DUNGEON' ) then PrepQuestDungeon( Adv, QS ) else PrepWMonTeams( QS ); { ***************** } { *** STEP FOUR *** } { ***************** } { Locate and initialize the scene's entrance. } { Just in case this is a dungeon, don't forget to use QS^.S rather than } { the ElementID. } InitializeEntrance( QS , ElementID( Quest , EIn ) ); { ***************** } { *** STEP FIVE *** } { ***************** } { Locate and initialize the scene's exits. These should point to the } { parent scene. } InitExits( QS , QS^.SubCom ); InitExits( QS , QS^.InvCom ); end; end; QS := QS2; end; end; Procedure DeployQuest( Adv , City , Quest: GearPtr ); { Deploy this quest. } Procedure ConvertPersonas; { Change the quest personas from plot-style element-indexed ones to } { regular style CID-indexed ones. } var P: GearPtr; begin P := Quest^.SubCom; while P <> Nil do begin if P^.G = GG_Persona then begin P^.S := ElementID( Quest , P^.S ); end; P := P^.Next; end; end; begin { Remove the quest from the adventure, and stick it into the city. } DelinkGear( Adv^.InvCom , Quest ); InsertSubCom( City , Quest ); PrepAllPersonas( Adv , Quest , Nil , NAttValue( Adv^.NA , NAG_Narrative , NAS_MaxPlotLayer ) + 1 ); ConvertPersonas; InstallQuestScenes( Adv , City , Quest ); MoveElements( Nil , Adv , Quest , True ); end; Function InitMegaPlot( GB: GameBoardPtr; Scope,Slot,Plot: GearPtr; Threat: Integer ): GearPtr; { We've just been handed a prospective megaplot. } { Create all subplots, and initialize everything. } { 1 - Create list of components } { 2 - Merge all components into single plot } { 3 - Insert persona fragments } { 4 - Deploy elements as indicated by PLACE strings } var SPList,FakeFrags: GearPtr; PlotID,LayerID: LongInt; FakeParams: ElementTable; begin { The plot we've been handed will serve as the base component. The first thing } { to do, then, is to initialize it via the InitShard procedure. This will also } { give us a list of subplots. If InitShard fails, PLOT will be deleted. } { First, we need to clear SLOT's current Plot Layer ID to start fresh, then } { request a new later ID from Slot and a Plot ID from the adventure. } PlotID := NewPlotID( FindRoot( Slot ) , False ); SetNAtt( Slot^.NA , NAG_Narrative , NAS_MaxPlotLayer , 0 ); LayerID := NewLayerID( Slot ); { Initialize some of the variables we're going to need. } changes_used_so_far := ''; FakeFrags := Nil; ClearElementTable( FakeParams ); SPList := InitShard( GB , Scope , Slot , Plot , FakeFrags , 0 , PlotID , LayerID , Threat , FakeParams , False , ( GearName( Plot ) = 'DEBUG' ) ); { Now that we have the list, assemble it. } if SPList <> Nil then begin Plot := AssembleMegaPlot( Slot , SPList , FakeFrags , False ); DeployPlot( GB , Slot , Plot , Threat ); end; InitMegaPlot := SPList; end; Procedure InitPlaceStrings( P: GearPtr ); { Initialize all the place strings of the standard subplots. } { To be comprehended, place strings need to point to the master plot } { element slot, but for human readability it's better to point them } { at the subplot element slot. This procedure converts any subplot } { element slots to master plot slot references. } var T: Integer; PlaceCmd,DestSlot: String; HasTilde: Boolean; begin while P <> Nil do begin for t := 1 to Num_Plot_Elements do begin PlaceCmd := SAttValue( P^.SA , 'PLACE' + BStr( T ) ); DeleteWhiteSpace( PlaceCmd ); if ( PlaceCmd <> '' ) and ( PlaceCmd[1] <> '/' ) then begin if PlaceCmd[1] = '~' then begin HasTilde := True; DeleteFirstChar( PlaceCmd ); end else HasTilde := False; if PlaceCmd[1] <> '%' then begin DestSlot := ExtractWord( PlaceCmd ); PlaceCmd := '%e' + DestSlot + '% ' +PlaceCmd; end; if HasTilde then PlaceCmd := '~' + PlaceCmd; SetSAtt( P^.SA , 'PLACE' + BStr( T ) + ' <' + PlaceCmd + '>' ); end; end; P := P^.Next; end; end; Function LoadQuestFragments: GearPtr; { Load and initialize the quest fragments. } Procedure AssignMasterListIDNumbers( M: GearPtr ); { Each fragment in the master list needs a unique ID number, stored } { in its "S" descriptor. } var ID: Integer; begin ID := 0; while M <> Nil do begin M^.S := ID; Inc( ID ); M := M^.Next; end; end; var Frags: GearPtr; begin Frags := AggregatePattern( 'QUEST_*.txt' , Series_Directory ); { Initialize the quest fragments. } AssignMasterListIDNumbers( Frags ); InitPlaceStrings( Frags ); LoadQuestFragments := Frags; end; Function AddQuest( Adv,City,QPF_Proto: GearPtr; var Quest_Frags: GearPtr; QReq: String ): Boolean; { Add a quest to the provided city. } { QPF_Proto is a prototype for a prefab element to be added to a quest. } { Quest_Frags is the list of quest fragments. Some of them may get deleted here. } { QReq is the quest request taken from the ATLAS. } var QList,Quest: GearPtr; begin { Initialize some of the global variables. } changes_used_so_far := ''; { Step One- Select a starting fragment. } QList := AddSubPlot( Nil, City, Adv, Nil, QPF_Proto , Quest_Frags, QReq, 0, NewLayerID( Adv ), 0, True, False ); { This will give us a list of quest fragments. Assemble them. } if QList <> Nil then begin Quest := AssembleMegaPlot( Adv , QList , Quest_Frags , True ); DeployQuest( Adv , City , Quest ); AddQuest := True; end else begin AddQuest := False; end; end; initialization { Load the list of subplots from disk. } Sub_Plot_List := LoadRandomSceneContent( 'MEGA_*.txt' , series_directory ); standard_trigger_list := LoadStringList( Data_Directory + 'standard_triggers.txt' ); InitPlaceStrings( Sub_Plot_List ); MasterEntranceList := AggregatePattern( 'ENTRANCE_*.txt' , Series_Directory ); finalization { Dispose of the list of subplots. } DisposeGear( Sub_Plot_List ); DisposeSAtt( standard_trigger_list ); DisposeGear( MasterEntranceList ); end. GH2/scriptbuilder.pas0000644000175000017500000000232311326004555013455 0ustar kaolkaolProgram scriptbuilder; { This program is meant to act as an editor for content files. } { 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} Procedure SBMainMenu; { This procedure will allow a new content file to be created, a previously created project to be } { loaded, or the program to be exited. } begin end; begin { The main program just calls the main menu. } SBMainMenu; end. GH2/vidgfx.pp0000644000175000017500000007054511333736447011752 0ustar kaolkaolunit vidgfx; { 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 Video,Keyboard,ui4gh,texutil,gears; Type vgfx_rect = Record X,Y,W,H: Byte; end; vgfx_zone = Record X_Anchor,X_Justify,W: Integer; Y_Anchor,Y_Justify,H: Integer; end; RedrawProcedureType = Procedure; Const vg_Pen: Byte = 0; { Will be initialied to proper value below. } { The real values for ZONE_Console get set at initialization below. } ZONE_Console: vgfx_rect = ( x:1; y:21; w:80; h:5 ); Console_History_Length = 240; NormMode: TVideoMode = ( Col: 80; Row: 25; Color: True ); RightColumnWidth = 25; ANC_Low = 0; ANC_Mid = 1; ANC_High = 2; ZONE_CharGenMenu: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -25; W: 24; Y_Anchor: ANC_Mid; Y_Justify: -3; H: 10; ); ZONE_CharGenPrompt: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -25; W: 24; Y_Anchor: ANC_Mid; Y_Justify: -10; H: 6; ); ZONE_CharGenCaption: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -25; W: 24; Y_Anchor: ANC_Mid; Y_Justify: 9; H: 2; ); ZONE_CharGenDesc: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 74; Y_Anchor: ANC_High; Y_Justify: -3; H: 3; ); ZONE_Caption: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -8; W: 16; Y_Anchor: ANC_Low; Y_Justify: 3; H: 3; ); ZONE_Info: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -RightColumnWidth + 1; W: RightColumnWidth - 2; Y_Anchor: ANC_Mid; Y_Justify: -10; H: 7; ); ZONE_Menu: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -RightColumnWidth + 1; W: RightColumnWidth - 2; Y_Anchor: ANC_Mid; Y_Justify: -2; H: 10; ); ZONE_Menu1: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -RightColumnWidth + 1; W: RightColumnWidth - 2; Y_Anchor: ANC_Mid; Y_Justify: -2; H: 5; ); ZONE_Menu2: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -RightColumnWidth + 1; W: RightColumnWidth - 2; Y_Anchor: ANC_Mid; Y_Justify: 4; H: 4; ); ZONE_SubCaption: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -RightColumnWidth + 1; W: RightColumnWidth - 2; Y_Anchor: ANC_Mid; Y_Justify: 7; H: 1; ); ZONE_Clock: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -RightColumnWidth + 2; W: RightColumnWidth - 4; Y_Anchor: ANC_Mid; Y_Justify: 8; H: 1; ); ZONE_GetItemMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -16; W: 32; Y_Anchor: ANC_Mid; Y_Justify: - 4; H: 8; ); ZONE_ShopCaption: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 9; H: 2; ); ZONE_ShopMsg: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 6; H: 6; ); ZONE_ShopMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: 1; H: 7; ); ZONE_ItemsInfo: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: 5; W: 33; Y_Anchor: ANC_Mid; Y_Justify: -9; H: 14; ); ZONE_ItemsPCInfo: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: 5; W: 33; Y_Anchor: ANC_Mid; Y_Justify: 6; H: 2; ); ZONE_EqpMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 6; H: 6; ); ZONE_InvMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: 1; H: 5; ); ZONE_BackpackInstructions: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 9; H: 2; ); ZONE_FieldHQMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -37; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 9; H: 15; ); InteractAreaWidth = 75; Interact_X_Justify = -37; Interact_Y_Justify = -9; { The name zone includes the JobAgeGender description. } ZONE_InteractName: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: Interact_X_Justify; W: InteractAreaWidth; Y_Anchor: ANC_Mid; Y_Justify: Interact_Y_Justify; H: 2; ); ZONE_InteractStatus: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: Interact_X_Justify; W: InteractAreaWidth; Y_Anchor: ANC_Mid; Y_Justify: Interact_Y_Justify + 2; H: 1; ); ZONE_InteractMsg: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: Interact_X_Justify; W: InteractAreaWidth; Y_Anchor: ANC_Mid; Y_Justify: Interact_Y_Justify + 3; H: 5; ); ZONE_InteractMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: Interact_X_Justify; W: InteractAreaWidth; Y_Anchor: ANC_Mid; Y_Justify: Interact_Y_Justify + 8; H: 7; ); { *** INTERNAL USE ONLY *** } ZONE_InteractTotal: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: Interact_X_Justify; W: InteractAreaWidth; Y_Anchor: ANC_Mid; Y_Justify: Interact_Y_Justify; H: 15; ); { *** INTERNAL USE ONLY *** } ZONE_MemoText: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 6; H: 8; ); ZONE_MemoMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: 3; H: 3; ); { The SelectArenaMission display zones: } ZONE_SAMText: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: 0; H: 5; ); ZONE_SAMMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: -6; H: 5; ); ZONE_UsagePrompt: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: - 8; H: 10; ); ZONE_UsageMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: 3; H: 7; ); { Note that in ASCII mode, RightInfo and LeftInfo aren't to the right and left, } { but instead occupy the Menu1 and Menu2 zones. } ZONE_RightInfo: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: 1; W: 20; Y_Anchor: ANC_Low; Y_Justify: 3; H: 7; ); ZONE_LeftInfo: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -21; W: 20; Y_Anchor: ANC_Low; Y_Justify: 3; H: 7; ); ZONE_CharacterDisplay: vgfx_zone = ( X_Anchor: ANC_Low; X_Justify: 1; W: 52; Y_Anchor: ANC_Low; Y_Justify: 1; H: 19; ); ZONE_WorldMap: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -13; W: 25; Y_Anchor: ANC_Mid; Y_Justify: - 8; H: 15; ); ZONE_MonologueInfo: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: -8; H: 1 ); ZONE_MonologueText: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: -6; H: 10 ); ZONE_ArenaPilotMenu: vgfx_zone = ( X_Anchor: ANC_Low; X_Justify: 2; W: 20; Y_Anchor: ANC_Low; Y_Justify: 1; H: 15 ); ZONE_ArenaMechaMenu: vgfx_zone = ( X_Anchor: ANC_Low; X_Justify: 23; W: 20; Y_Anchor: ANC_Low; Y_Justify: 1; H: 15 ); ZONE_ArenaInfo: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -34; W: 33; Y_Anchor: ANC_Low; Y_Justify: 1; H: 10; ); ZONE_PCStatus: vgfx_zone = ( X_Anchor: ANC_High; X_Justify: -34; W: 33; Y_Anchor: ANC_Low; Y_Justify: 12; H: 4; ); ZONE_Dialog: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -39; W: 80; Y_Anchor: ANC_Low; Y_Justify: 12; H: 3; ); ZONE_ConcertAudience: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -30; W: 60; Y_Anchor: ANC_Mid; Y_Justify: -7; H: 2; ); ZONE_ConcertCaption: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -30; W: 60; Y_Anchor: ANC_Mid; Y_Justify: -4; H: 4; ); ZONE_ConcertMenu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -30; W: 60; Y_Anchor: ANC_Mid; Y_Justify: 1; H: 4; ); ZONE_ConcertDesc: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -30; W: 60; Y_Anchor: ANC_Mid; Y_Justify: 6; H: 1; ); ZONE_Title_Screen_Top: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -15; W: 30; Y_Anchor: ANC_Mid; Y_Justify: -10; H: 2; ); ZONE_Title_Screen_Title: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -15; W: 30; Y_Anchor: ANC_Mid; Y_Justify: -10; H: 1; ); ZONE_Title_Screen_Version: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -15; W: 30; Y_Anchor: ANC_Mid; Y_Justify: -9; H: 1; ); ZONE_Title_Screen_Menu: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -15; W: 30; Y_Anchor: ANC_Mid; Y_Justify: -7; H: 15; ); { *** STANDARD COLORS *** } StdBlack: Byte = Black; StdWhite: Byte = White; MenuItem: Byte = Cyan; MenuSelect: Byte = LightCyan; TerrainGreen: Byte = Green; PlayerBlue: Byte = LightBlue; AllyPurple: Byte = LightMagenta; EnemyRed: Byte = Red; NeutralGrey: Byte = LightGray; InfoGreen: Byte = Green; InfoHiLight: Byte = LightGreen; TextboxGrey: Byte = DarkGray; AttackColor: Byte = LightRed; NeutralBrown: Byte = Yellow; BorderBlue: Byte = Blue; MelodyYellow: Byte = Yellow; { STATE VARIABLES - USE WITH CAUTION } { External setting of these vars is not supported, but reading should } { be okay most of the time. } vg_FGColor: Byte = LightGray; vg_BGColor: Byte = Black; vg_X: Byte = 1; { Cursor Position. } vg_Y: Byte = 1; { Cursor Position. } vg_Window: vgfx_rect = ( x:1 ; y:1 ; w:80 ; h:25 ); Var Console_History: SAttPtr; Procedure DoFlip; Function RPGKey: Char; Function IsMoreKey( A: Char ): Boolean; Procedure MoreKey; Procedure ClrZone( const Z: vgfx_Rect ); Procedure ClrScreen; Procedure TextColor( C: Byte ); Procedure TextBackground( C: Byte ); Procedure TextOut(X,Y : Word;Const S : String); Procedure ClipZone( Z: vgfx_rect ); Procedure MaxClipZone; Function ZoneToRect( Z: VGFX_Zone ): VGFX_Rect; Procedure DrawGlyph( img: Char; X,Y,FG,BG: Byte ); Procedure GameMSG( msg: string; Z: vgfx_rect; C: Byte ); Procedure GameMSG( msg: string; Z: vgfx_zone; C: Byte ); Procedure CMessage( const msg: String; Z: VGFX_Rect; C: Byte ); Procedure CMessage( const msg: String; Z: VGFX_Zone; C: Byte ); Procedure RedrawConsole; Procedure DialogMSG(msg: string); Function MoreHighFirstLine( LList: SAttPtr ): Integer; Procedure MoreText( LList: SAttPtr; FirstLine: Integer ); Function GetStringFromUser( const Prompt: String; Redraw: RedrawProcedureType ): String; Procedure SetupMemoDisplay; Procedure DrawBPBorder; Procedure SetupFHQDisplay; Procedure DrawGetItemBorder; Procedure SetupInteractDisplay( C: Byte ); Procedure SetupServicesDisplay; Procedure InfoBox( MyDest: VGFX_Rect ); Procedure InfoBox( Z: VGFX_Zone ); Procedure ClockBorder; Procedure SetupArenaDisplay; Procedure SetupArenaMissionMenu; Procedure SetupConcertDisplay; Procedure SetupTitleScreenDisplay; implementation Procedure DoFlip; { Update the screen. } begin UpdateScreen( False ); end; Function RawKey: Char; {Read a keypress from the keyboard. Convert it into a form} {that my other procedures would be willing to call useful.} var getit: Char; TK: TKeyEvent; begin TK := TranslateKeyEvent( GetKeyEvent ); if GetKeyEventFlags( TK ) = kbASCII then begin getit := GetKeyEventChar( TK ); end else if GetKeyEventFlags( TK ) = kbFnKey then begin case GetKeyEventCode( TK ) of kbdUp: getit := KeyMap[ KMC_North ].KCode; {Up Cursor Key} kbdHome: getit := KeyMap[ KMC_NorthWest ].KCode; {Home Cursor Key} kbdPgUp: getit := KeyMap[ KMC_NorthEast ].KCode; {PageUp Cursor Key} kbdDown: getit := KeyMap[ KMC_South ].KCode; {Down Cursor Key} kbdEnd: getit := KeyMap[ KMC_SouthWest ].KCode; {End Cursor Key} kbdPgDn: getit := KeyMap[ KMC_SouthEast ].KCode; {PageDown Cursor Key} kbdLeft: getit := KeyMap[ KMC_West ].KCode; {Left Cursor Key} kbdRight: getit := KeyMap[ KMC_East ].KCode; {Right Cursor Key} end; end else begin getit := ' '; end; RawKey := getit; end; Function RPGKey: Char; { Basically, call RAWKEY then convert the result. } var getit: Char; begin getit := RawKey; case getit of #8: getit := #27; { Convert backspace to escape. } #10: getit := ' '; { Convert enter to space. } #13: getit := ' '; { Convert enter to space. } end; RPGKey := getit; end; Function IsMoreKey( A: Char ): Boolean; { Return TRUE if A is a "more" key, that should skip to the next message in a list. } begin IsMoreKey := ( A = ' ' ) or ( A = #27 ); end; Procedure MoreKey; { Wait for the user to press either the space bar or the ESC key. } var A: Char; begin { Keep reading keypresses until either a space or an ESC is found. } repeat A := RPGKey; until IsMoreKey( A ); end; Function BufferPos( X,Y: Integer ): Integer; { Translate screen coordinates X,Y into a video buffer index. } begin BufferPos := (X-1)+(Y-1)*ScreenWidth; end; Procedure ClrZone( const Z: vgfx_Rect ); { Clear the specified zone. } const ClrChar: TVideoCell = Ord(' ')+($07 shl 8); var X,Y,P: Integer; begin for X := Z.X to ( Z.X + Z.W - 1 ) do begin for Y := Z.Y to ( Z.Y + Z.H - 1 ) do begin P := BufferPos( X , Y ); if ( P >= 0 ) and ( P < ( ScreenWidth * ScreenHeight ) ) then VideoBuf^[ BufferPos( X , Y ) ] := ClrChar; end; end; end; Procedure ClrScreen; { Clear the entire screen. Yay! } var sz: vgfx_rect; begin sz.X := 1; sz.Y := 1; sz.H := screenheight; sz.W := screenwidth; ClrZone( sz ); end; Procedure VClrEOL; { Clear from the current write position to the end of the current line. } var sz: vgfx_rect; begin sz.X := VG_X; sz.Y := VG_Y; sz.H := 1; sz.W := screenwidth - VG_X; if sz.W > ( vg_window.X + vg_window.W - 1 ) then sz.W := ( vg_window.X + vg_window.W - VG_X ); ClrZone( sz ); end; Procedure CalcPen; { Calculate the color bit value, based on the requested FGPen and BGPen. } begin VG_Pen := VG_FGColor + ( VG_BGColor shl 4 ); end; Procedure TextColor( C: Byte ); { Set the foreground color. } begin VG_FGColor := C; CalcPen; end; Procedure TextBackground( C: Byte ); { Set the background color. } begin VG_BGColor := C; CalcPen; end; Procedure TextColorBackground( FG,BG: Byte ); { Set the foreground color. } begin VG_FGColor := FG; VG_BGColor := BG; CalcPen; end; Function InWindow( X , Y: Byte ): Boolean; { Return TRUE if X,Y is in the window, or FALSE otherwise. } begin InWindow := ( X >= vg_window.X ) and ( Y >= vg_window.Y ) and ( X < ( vg_window.X + vg_window.w ) ) and ( Y < ( vg_window.y + vg_window.h ) ); end; Procedure TextOut(X,Y : Word;Const S : String); { Write text to the screen at the listed coordinates. This procedure } { was ripped more or less exactly from the FPC documentation. } Var P,I,M : Word; begin P:=((X-1)+(Y-1) * ScreenWidth); M:=Length(S); If ( P + M ) > ScreenWidth*ScreenHeight then M:=ScreenWidth*ScreenHeight-P; For I:=1 to M do if InWindow( X + I - 1 , Y ) then VideoBuf^[P+I-1]:=Ord(S[i])+( VG_Pen shl 8 ); end; Procedure ClipZone( Z: vgfx_rect ); { Set the clipping bounds to this defined zone. } begin vg_window := Z; vg_x := Z.X; vg_y := Z.Y; end; Procedure VGotoXY( X,Y: Integer ); { Set the write position to the requested coordinates. } begin vg_x := X; vg_y := Y; end; Procedure MaxClipZone; { Restore the clip area to the maximum possible area. } begin vg_window.X := 1; vg_window.Y := 1; vg_window.W := ScreenColumns; vg_Window.H := ScreenRows; end; Procedure DrawGlyph( img: Char; X,Y,FG,BG: Byte ); { Draw a character at the requested location with the given foreground and } { background colors. } var I: Integer; begin I := ( Y-1 ) * ScreenWidth + X - 1; if InWindow( X,Y ) and ( I < VideoBufSize ) then begin TextColorBackground( FG , BG ); VideoBuf^[ I ] := Ord( img )+( VG_Pen shl 8 ); end; end; Procedure VWrite( Const S: String ); { Write to the screen using Video. } Var P,I,M : Word; begin P:=((VG_X-1)+(VG_Y-1) * ScreenWidth); M:=Length(S); If ( P + M ) > ScreenWidth*ScreenHeight then M:=ScreenWidth*ScreenHeight-P; For I:=1 to M do begin if InWindow( vg_x , vg_y ) then begin VideoBuf^[P+I-1]:=( Ord(S[i]) )+( VG_Pen shl 8 ); end; Inc( vg_X ); end; end; Procedure VWriteln( Const S: String ); { Write to the screen using Video. Move to the next line afterwards. } Var P,I,M : Word; begin P:=((VG_X-1)+(VG_Y-1) * ScreenWidth); M:=Length(S); If ( P + M ) > ScreenWidth*ScreenHeight then M:=ScreenWidth*ScreenHeight-P; For I:=1 to M do begin if InWindow( vg_x , vg_y ) then begin VideoBuf^[P+I-1]:=Ord(S[i])+( VG_Pen shl 8 ); end; Inc( vg_X ); end; vg_X := vg_window.X; InC( vg_y ); end; Function ZoneToRect( Z: VGFX_Zone ): VGFX_Rect; { Convert the provided zone to a rect. } var it: VGFX_Rect; begin it.W := Z.W; it.H := Z.H; case Z.X_Anchor of ANC_Low: it.X := 1; ANC_Mid: it.X := ScreenColumns div 2; ANC_High: it.X := ScreenColumns; end; it.X := it.X + Z.X_Justify; case Z.Y_Anchor of ANC_Low: it.Y := 1; ANC_Mid: it.Y := ScreenRows div 2; ANC_High: it.Y := ScreenRows; end; it.Y := it.Y + Z.Y_Justify; ZoneToRect := it; end; Procedure GameMSG( msg: string; Z: vgfx_rect; C: Byte ); {Prettyprint the string MSG with color C in screen zone Z.} var NextWord: String; THELine: String; {The line under construction.} LC: Boolean; {Loop Condition.} begin { CLean up the message a bit. } DeleteWhiteSpace( msg ); TextColorBackground( C , Black ); {Clear the message area, and set clipping bounds.} ClrZone( Z ); ClipZone( Z ); {THELine = The first word in this iteration} THELine := ExtractWord( msg ); {Start the main processing loop.} while TheLine <> '' do begin {Set the LoopCondition to True.} LC := True; { Start building the line. } repeat NextWord := ExtractWord( Msg ); if Length(THEline + ' ' + NextWord) < Z.W then THEline := THEline + ' ' + NextWord else LC := False; until (not LC) or (NextWord = '') or ( TheLine[Length(TheLine)] = #13 ); { If the line ended due to a line break, deal with it. } if ( TheLine[Length(TheLine)] = #13 ) then begin { Display the line break as a space. } TheLine[Length(TheLine)] := ' '; NextWord := ExtractWord( msg ); end; { Output the line. } if NextWord = '' then begin VWrite(THELine); end else begin VWriteLn(THELine); end; { Prepare for the next iteration. } TheLine := NextWord; end; { while msg <> '' } {Restore the clip window to its maximum size.} MaxClipZone; end; Procedure GameMSG( msg: string; Z: vgfx_zone; C: Byte ); { Convert the zone to a rect and send it to the above procedure. } begin GameMsg( msg , ZoneToRect( Z ) , C ); end; Procedure CMessage( const msg: String; Z: VGFX_Rect; C: Byte ); { Display MSG centered in zone Z. } var X,Y: Integer; begin { Figure out the coordinates for centered display. } X := Z.X + ( Z.W div 2 ) - ( Length( msg ) div 2 ); Y := Z.Y + Z.H div 2; { Actually do the output. } ClrZone( Z ); ClipZone( Z ); if X < 1 then X := 1; if Y < 1 then Y := 1; VGotoXY( X , Y ); TextColor( C ); VWrite(msg); MaxClipZone; end; Procedure CMessage( const msg: String; Z: VGFX_Zone; C: Byte ); { Convert the zone to a rect, and print the message. } begin CMessage( msg , ZoneToRect( Z ) , C ); end; Procedure RedrawConsole; { Draw the console to the screen. } var T: Integer; N: Integer; begin TextColorBackground( Green , Black ); N := NumSAtts( Console_History ); ClipZone( ZONE_Console ); for t := 1 to ZONE_Console.H do begin if ( t + N - ZONE_Console.H ) > 0 then begin VWriteLn( RetrieveSAtt( Console_History , ( t + N - ZONE_Console.H ) )^.Info ); end else begin VWriteLn( ' ' ); end; end; MaxClipZone; end; Procedure DialogMSG(msg: string); {not const-able} { Print a message in the scrolling dialog box. } var NextWord: String; THELine: String; {The line under construction.} LC: Boolean; {Loop Condition.} SA: SAttPtr; begin { CLean up the message a bit. } DeleteWhiteSpace( msg ); msg := '> ' + msg; {THELine = The first word in this iteration} THELine := ExtractWord( msg ); {Start the main processing loop.} while TheLine <> '' do begin {Set the LoopCondition to True.} LC := True; { Start building the line. } repeat NextWord := ExtractWord( Msg ); if Length(THEline + ' ' + NextWord) < ZONE_Console.W then THEline := THEline + ' ' + NextWord else LC := False; until (not LC) or (NextWord = '') or ( TheLine[Length(TheLine)] = #13 ); { If the line ended due to a line break, deal with it. } if ( TheLine[Length(TheLine)] = #13 ) then begin { Display the line break as a space. } TheLine[Length(TheLine)] := ' '; NextWord := ExtractWord( msg ); end; { Output the line. } if TheLine <> '' then begin if NumSAtts( Console_History ) >= Console_History_Length then begin SA := Console_History; RemoveSAtt( Console_History , SA ); end; StoreSAtt( Console_History , TheLine ); end; { Prepare for the next iteration. } TheLine := NextWord; end; { while msg <> '' } { Redraw the dialog area. } RedrawConsole; end; Function MoreHighFirstLine( LList: SAttPtr ): Integer; { Determine the highest possible FirstLine value. } var it: Integer; begin it := NumSAtts( LList ) - ( ScreenHeight - 3 ); if it < 1 then it := 1; MoreHighFirstLine := it; end; Procedure MoreText( LList: SAttPtr; FirstLine: Integer ); { Browse this text file across the majority of the screen. } { Clear the screen upon exiting, though restoration of the } { previous display is someone else's responsibility. } Procedure DisplayTextHere; var CLine: SAttPtr; { Current Line } begin { Error check. } if FirstLine < 1 then FirstLine := 1 else if FirstLine > MoreHighFirstLine( LList ) then FirstLine := MoreHighFirstLine( LList ); VGotoXY( 1 , 1 ); CLine := RetrieveSATt( LList , FirstLine ); while ( VG_Y < ( ScreenHeight - 1 ) ) do begin VClrEOL; if CLine <> Nil then begin vwriteln( Copy( CLine^.Info , 1 , ScreenWidth - 1 ) ); CLine := CLine^.Next; end else begin vwriteln( '' ); end; end; DoFlip; end; var A: Char; begin ClrScreen; VGotoXY( 1 , ScreenHeight ); TextColorBackground( LightGreen , Black ); VWrite( MsgString( 'MORETEXT_Prompt' ) ); { Display the screen. } TextColor( LightGray ); DisplayTextHere; repeat { Get input from user. } A := RPGKey; { Possibly process this input. } if A = KeyMap[ KMC_South ].KCode then begin Inc( FirstLine ); DisplayTextHere; end else if A = KeyMap[ KMC_North ].KCode then begin Dec( FirstLine ); DisplayTextHere; end; until ( A = #27 ) or ( A = 'Q' ); { CLear the display area. } ClrScreen; end; Function GetStringFromUser( const Prompt: String; Redraw: RedrawProcedureType ): String; { Does what it says. } const AllowableCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890()-=_+,.?"'; MaxInputLength = 39; ZONE_TextInputFull: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: -1; H: 2; ); ZONE_TextInputPrompt: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: -1; H: 1; ); ZONE_TextInput: vgfx_zone = ( X_Anchor: ANC_Mid; X_Justify: -20; W: 40; Y_Anchor: ANC_Mid; Y_Justify: 0; H: 1; ); var A: Char; it: String; begin { Initialize string. } it := ''; Redraw; InfoBox( ZONE_TextInputFull ); { Give us a nice blinky cursor, please. } SetCursorType( crBlock ); repeat { Set up the display. } CMessage( Prompt , ZONE_TextInputPrompt , White ); CMessage( it , ZONE_TextInput , Green ); TextColor( LightGreen ); SetCursorPos( VG_X - 1 , VG_Y - 1 ); DoFlip; A := RawKey; if ( A = #8 ) and ( Length( it ) > 0 ) then begin it := Copy( it , 1 , Length( it ) - 1 ); end else if ( Pos( A , AllowableCharacters ) > 0 ) and ( Length( it ) < MaxInputLength ) then begin it := it + A; end; until ( A = #13 ) or ( A = #27 ); { Get rid of the cursor, again. } SetCursorType( crHidden ); GetStringFromUser := it; end; Procedure SetupMemoDisplay; { Draw a border for the memo browser. } begin InfoBox( ZONE_MemoText ); InfoBox( ZONE_MemoMenu ); end; Procedure DrawBPBorder; { Draw the backpack border. } begin InfoBox( ZONE_EqpMenu ); InfoBox( ZONE_InvMenu ); InfoBox( ZONE_BackpackInstructions ); InfoBox( ZONE_ItemsInfo ); InfoBox( ZONE_ItemsPCInfo ); end; Procedure SetupFHQDisplay; { Draw the backpack border. } begin InfoBox( ZONE_FieldHQMenu ); InfoBox( ZONE_BackpackInstructions ); InfoBox( ZONE_ItemsInfo ); InfoBox( ZONE_ItemsPCInfo ); end; Procedure DrawGetItemBorder; { Draw the get items border. } begin InfoBox( ZONE_GetItemMenu ); end; Procedure SetupInteractDisplay( C: Byte ); { Draw the backpack border. } begin ClrZone( ZoneToRect( ZONE_InteractTotal ) ); InfoBox( ZONE_InteractTotal ); end; Procedure SetupServicesDisplay; { Draw the display for the services interface. } begin InfoBox( ZONE_ShopCaption ); InfoBox( ZONE_ShopMsg ); InfoBox( ZONE_ShopMenu ); InfoBox( ZONE_ItemsInfo ); InfoBox( ZONE_ItemsPCInfo ); end; Procedure InfoBox( MyDest: VGFX_Rect ); { Draw a box around the specified location. } var X,Y: Integer; begin ClrZone( MyDest ); if MyDest.X > 0 then Dec( MyDest.X ); if MyDest.Y > 0 then Dec( MyDest.Y ); MyDest.W := MyDest.W + 2; MyDest.H := MyDest.H + 2; for X := ( MyDest.X + 1 ) to ( MyDest.X + MyDest.W - 2 ) do begin DrawGlyph( '-' , X , MyDest.Y , BorderBlue , StdBlack ); DrawGlyph( '-' , X , MyDest.Y + MyDest.H - 1 , BorderBlue , StdBlack ); end; for y := ( MyDest.Y + 1 ) to ( MyDest.Y + MyDest.H - 2 ) do begin DrawGlyph( '|' , MyDest.X , Y , BorderBlue , StdBlack ); DrawGlyph( '|' , MyDest.X + MyDest.W - 1 , Y , BorderBlue , StdBlack ); end; DrawGlyph( '+' , MyDest.X , MyDest.Y , BorderBlue , StdBlack ); DrawGlyph( '+' , MyDest.X + MyDest.W - 1 , MyDest.Y , BorderBlue , StdBlack ); DrawGlyph( '+' , MyDest.X + MyDest.W - 1 , MyDest.Y + MyDest.H - 1 , BorderBlue , StdBlack ); DrawGlyph( '+' , MyDest.X , MyDest.Y + MyDest.H - 1 , BorderBlue , StdBlack ); end; Procedure InfoBox( Z: VGFX_Zone ); { Draw a box around the specified location. } var MyDest: VGFX_Rect; begin MyDest := ZoneToRect( Z ); InfoBox( MyDest ); end; Procedure ClockBorder; { Draw the setup for the clock. } var MyDest: VGFX_Rect; begin MyDest := ZoneToRect( ZONE_Clock ); DrawGlyph( '[' , MyDest.X - 1 , MyDest.Y , BorderBlue , Black ); DrawGlyph( ']' , MyDest.X + MyDest.W + 1 , MyDest.Y , BorderBlue , Black ); end; Procedure SetupArenaDisplay; { Setup the borders for the menus. } begin ClrScreen; InfoBox( ZONE_ArenaInfo ); InfoBox( ZONE_ArenaPilotMenu ); InfoBox( ZONE_ArenaMechaMenu ); InfoBox( ZONE_PCStatus ); RedrawConsole; end; Procedure SetupArenaMissionMenu; { This sets up the select arena mission menu. } begin InfoBox( ZONE_SAMText ); InfoBox( ZONE_SAMMenu ); end; Procedure SetupConcertDisplay; { Set up the concert display. } begin InfoBox( ZONE_ConcertAudience ); InfoBox( ZONE_ConcertCaption ); InfoBox( ZONE_ConcertMenu ); InfoBox( ZONE_ConcertDesc ); end; Procedure SetupTitleScreenDisplay; { Set up the concert display. } begin ClrScreen; InfoBox( ZONE_Title_Screen_Top ); InfoBox( ZONE_Title_Screen_Menu ); CMessage( 'GearHead II' , ZONE_Title_Screen_Title , StdWhite ); end; initialization InitVideo; InitKeyboard; CalcPen; NormMode.Col := ScreenColumns; NormMode.Row := ScreenRows; SetVideoMode( NormMode ); SetCursorType( crHidden ); Console_History := Nil; ZONE_Console.W := ScreenColumns; ZONE_Console.Y := ScreenRows - 4; ZONE_RightInfo := ZONE_Menu1; ZONE_LeftInfo := ZONE_Menu2; ZONE_Caption := ZONE_Info; ZONE_SubCaption := ZONE_Clock; ZONE_Dialog := ZONE_PCStatus; ZONE_CharGenMenu := ZONE_Menu1; ZONE_CharGenCaption := ZONE_Menu2; ZONE_CharGenPrompt := ZONE_Info; finalization {$IFNDEF ASCII} ClrScreen; DoFlip; {$ENDIF} DoneVideo; DoneKeyboard; DisposeSAtt( Console_History ); end. GH2/robotics.pp0000644000175000017500000005156211365256066012304 0ustar kaolkaolunit robotics; { This unit handles the Robotics skill. Take some spare parts } { and build yourself a plastic pal who's fun to be with. } { 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} { Overview: To build a robot, first you select a list of components. These components provide you with a pool of points which are then spent to purchase a frame and customize it. The four types of points are Build, Armor, Computer, and Power. Build is the generic currency, needed for everything. Armor contributes to BOD and the final armor value of the robot. Computer contributes to PER, CRA, KNO, and CHA Power contributes to REF, SPD, and EGO } interface uses gears,locale; Procedure BuildRobot( GB: GameBoardPtr; PC: GearPtr ); implementation uses gearutil,ghchars,texutil,arenacfe,ability,ui4gh,menugear, rpgdice,ghmodule,ghholder,ghmovers,action,narration,interact, arenascript,gearparser, {$IFDEF ASCII} vidgfx,vidmenus,vidinfo; {$ELSE} {$IFDEF CUTE} cutegfx,glmenus,glinfo; {$ELSE} glgfx,glmenus,glinfo; {$ENDIF} {$ENDIF} const Num_Robot_Skill = 9; Robot_Skill: Array [1..Num_Robot_Skill] of Byte = ( NAS_Awareness, NAS_Toughness, NAS_Medicine , NAS_Repair, NAS_SpotWeakness, NAS_Stealth, NAS_Science, NAS_MechaEngineering, NAS_CodeBreaking ); var Robotics_GB: GameBoardPtr; Robotics_Menu: RPGMenuPtr; Robotics_Source,Robotics_Parts: GearPtr; Robotic_Forms: GearPtr; Robotics_Instructions,Robotics_Info: String; Function R_BuildPoints( LList: GearPtr ): LongInt; { Build points are the fundamental currency of Robotics. } var BP: LongInt; begin BP := 0; while LList <> Nil do begin if LList^.G = GG_RepairFuel then begin BP := BP + LList^.V; end else if ( LList^.G = GG_Computer ) or ( LList^.G = GG_PowerSource ) then begin BP := BP + LList^.V * 25; end else begin BP := BP + GearMaxDamage( LList ) + GearMaxArmor( LList ) + GearMass( LList ); end; BP := BP + R_BuildPoints( LList^.SubCom ); LList := LList^.Next; end; R_BuildPoints := BP; end; Function R_ArmorPoints( LList: GearPtr ): LongInt; { Armor points determine the armor rating of the finished robot. } var BP: LongInt; begin BP := 0; while LList <> Nil do begin BP := BP + ( GearMaxDamage( LList ) div 10 ) + GearMaxArmor( LList ); BP := BP + R_ArmorPoints( LList^.SubCom ); LList := LList^.Next; end; R_ArmorPoints := BP; end; Function R_ComputerPoints( LList: GearPtr ): LongInt; { Computer points give a bonus to certain stats, and are needed for certain builds. } var BP: LongInt; begin BP := 0; while LList <> Nil do begin if LList^.G = GG_Computer then BP := BP + LList^.V; BP := BP + R_ComputerPoints( LList^.SubCom ); LList := LList^.Next; end; R_ComputerPoints := BP; end; Function R_PowerPoints( LList: GearPtr ): LongInt; { Power points give a bonus to certain stats, and are needed for certain builds. } var BP: LongInt; begin BP := 0; while LList <> Nil do begin if LList^.G = GG_PowerSource then BP := BP + LList^.V; BP := BP + R_PowerPoints( LList^.SubCom ); LList := LList^.Next; end; R_PowerPoints := BP; end; Procedure RobotPartRedraw; { Redraw procedure for the robot part selector. } var N: Integer; Part: GearPtr; begin if Robotics_GB <> Nil then CombatDisplay( Robotics_GB ); { We're going to be using the same border as the inventory panel. } DrawBPBorder; { Show details of the item currently being examined. } if ( Robotics_Menu <> Nil ) and ( Robotics_Source <> Nil ) then begin N := CurrentMenuItemValue( Robotics_Menu ); if N > 0 then begin Part := RetrieveGearSib( Robotics_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( Robotics_GB , Part , ZONE_ItemsInfo ); end; end; end; { Display the info and instructions strings. } GameMsg( Robotics_Instructions , ZONE_BackpackInstructions , InfoHilight ); GameMsg( Robotics_Info , ZONE_EqpMenu , InfoHilight ); end; Function IngredientsDesc( Ingredients: GearPtr ): String; { Return a string describing the build points gained from this list of } { ingredients. } begin IngredientsDesc := MsgString( 'ROBOTICS_BP' ) + BStr( R_BuildPoints( Ingredients ) ) + #13 + ' ' + MsgString( 'ROBOTICS_AP' ) + BStr( R_ArmorPoints( Ingredients ) ) + #13 + ' ' + MsgString( 'ROBOTICS_CP' ) + BStr( R_ComputerPoints( Ingredients ) ) + #13 + ' ' + MsgString( 'ROBOTICS_PP' ) + BStr( R_PowerPoints( Ingredients ) ); end; Function IsGoodRobotPart( Part: GearPtr ): Boolean; { Return TRUE if this part can be installed in a robot, or FALSE otherwise. } begin if ( Part^.G = GG_Weapon ) or ( Part^.G = GG_Shield ) or ( Part^.G = GG_ExArmor ) or ( Part^.G = GG_Computer ) or ( Part^.G = GG_Powersource ) or ( Part^.G = GG_Tool ) then begin IsGoodRobotPart := True; end else if ( Part^.G = GG_RepairFuel ) then begin IsGoodRobotPart := ( ( Part^.S = 15 ) or ( Part^.S = 23 ) ); end else if Part^.G = GG_Harness then begin IsGoodRobotPart := ( R_ComputerPoints( Part^.SubCom ) > 0 ) or ( R_PowerPoints( Part^.SubCom ) > 0 ); end else begin IsGoodRobotPart := False; end; end; Function SelectRobotParts( GB: GameBoardPtr; PC: GearPtr ): GearPtr; { Select up to 10 parts to build a robot with. } { Delink them from the INVENTORY and return them as a list. } var Part,P2: GearPtr; N: Integer; begin Robotics_GB := GB; Robotics_Parts := Nil; Robotics_Instructions := MsgString( 'Robotics_SelectParts_Directions' ); repeat Robotics_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); Robotics_Info := IngredientsDesc( Robotics_Parts ); Robotics_Source := PC^.InvCom; Part := PC^.InvCom; N := 1; while Part <> Nil do begin if IsGoodRobotPart( Part ) then begin AddRPGMenuItem( Robotics_Menu , GearName( Part ) , N ); end; Part := Part^.Next; Inc( N ); end; RPMSortAlpha( Robotics_Menu ); AlphaKeyMenu( Robotics_Menu ); AddRPGMenuItem( Robotics_Menu , MsgString( 'EXIT' ) , -1 ); N := SelectMenu( Robotics_Menu , @RobotPartRedraw ); DisposeRPGMenu( Robotics_Menu ); if N > -1 then begin Part := RetrieveGearSib( PC^.InvCom , N ); DelinkGear( PC^.InvCom , Part ); while Part^.InvCom <> Nil do begin P2 := Part^.InvCom; DelinkGear( Part^.InvCom , P2 ); InsertInvCom( PC , P2 ); end; AppendGear( Robotics_Parts , Part ); end; until ( NumSiblingGears( Robotics_Parts ) > 9 ) or ( N = -1 ); SelectRobotParts := Robotics_Parts; end; Function BuildPointsNeeded( Rob: GearPtr ): LongInt; { Return the number of build points needed to make this robot. } var s: Integer; { Stat counter } BP: LongInt; begin BP := 0; for s := 1 to 8 do begin BP := BP + Rob^.Stat[ S ] * 10; end; BuildPointsNeeded := BP; end; Function RobotStatSpecialPointsNeeded( StatVal: Integer ): Integer; { High baseline stats may require special materials. The usual amount } { is one special point of materials per 2 points of stat over 4. } begin if StatVal > 5 then begin RobotStatSpecialPointsNeeded := ( StatVal - 4 ) div 2; end else begin RobotStatSpecialPointsNeeded := 0; end; end; Function ArmorPointsNeeded( Rob: GearPtr ): LongInt; { Return the number of build points needed to make this robot. } begin ArmorPointsNeeded := RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Body ] ); end; Function ComputerPointsNeeded( Rob: GearPtr ): LongInt; { Return the number of build points needed to make this robot. } begin ComputerPointsNeeded := RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Perception ] ) + RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Craft ] ) + RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Knowledge ] ) + Rob^.STAT[ STAT_Charm ] - 1; end; Function PowerPointsNeeded( Rob: GearPtr ): LongInt; { Return the number of build points needed to make this robot. } begin PowerPointsNeeded := RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Reflexes ] ) + RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Speed ] ) + RobotStatSpecialPointsNeeded( Rob^.STAT[ STAT_Ego ] ); end; Function R_SkillLevelNeeded( Rob: GearPtr ): Integer; { Determine the minimum Robotics skill needed to attempt this robot. } var T,SkLvl: Integer; begin { The basic skill level needed is the highest stat. } SkLvl := 1; for t := 1 to 8 do begin if Rob^.Stat[ t ] > SkLvl then SkLvl := Rob^.Stat[ t ]; end; { If this is a sentient robot, minimum skill level is 11. } if ( Rob^.Stat[ STAT_Charm ] > 1 ) then begin if SkLvl < 8 then SkLvl := 11 else SkLvl := SkLvl + 3; end; R_SkillLevelNeeded := SkLvl; end; Function SelectRobotForm( GB: GameBoardPtr; PC,Ingredients: GearPtr ): GearPtr; { Given the provided list of ingredients and the PC's skill level, select } { one of the legal robot forms to try and build. } var BP,AP,CP,PP: LongInt; { Build, Armor, Computer, and Power points. } SkRank,N: Integer; { The PC's skill rank, and a counter. } Rob: GearPtr; { Robot Body Form } begin Robotics_Source := Robotic_Forms; Robotics_Parts := Ingredients; { Start by calculating the number of points we're dealing with. } BP := R_BuildPoints( Ingredients ); AP := R_ArmorPoints( Ingredients ); CP := R_ComputerPoints( Ingredients ); PP := R_PowerPoints( Ingredients ); SkRank := SkillRank( PC , NAS_Science ); { Create the menu. Determine which forms the PC can choose from. } Robotics_Menu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_InvMenu ); Robotics_Instructions := MsgString( 'Robotics_SelectForm_Directions' ); N := 1; Rob := Robotic_Forms; while Rob <> Nil do begin if ( BuildPointsNeeded( Rob ) <= BP ) and ( ArmorPointsNeeded( Rob ) <= AP ) and ( ComputerPointsNeeded( Rob ) <= CP ) and ( PowerPointsNeeded( Rob ) <= PP ) and ( R_SkillLevelNeeded( Rob ) <= SkRank ) then begin { This robot can be built. We have the technology. } AddRPGMenuItem( Robotics_Menu , GearName( Rob ) , N ); end; Rob := Rob^.Next; Inc( N ); end; RPMSortAlpha( Robotics_Menu ); AlphaKeyMenu( Robotics_Menu ); AddRPGMenuItem( Robotics_Menu , MsgString( 'EXIT' ) , -1 ); { We now have a menu. Select a form. } Robotics_Source := Robotic_Forms; Robotics_Info := IngredientsDesc( Ingredients ); N := SelectMenu( Robotics_Menu , @RobotPartRedraw ); DisposeRPGMenu( Robotics_Menu ); { At this exact point, the variable ROB must be Nil because of the while } { loop above. Because we're returning a clone of ROB, if menu selection } { was cancelled we don't have any more work to do. } if N > -1 then begin Rob := CloneGear( RetrieveGearSib( Robotic_Forms , N ) ); end; SelectRobotForm := Rob; end; Function RandomRobotName: String; { Generate random St*r-W*rs sounding robot name. } const NumLetter = 30; Letters: Array [1..NumLetter] of char = ( 'A','B','C','D','E', 'F','G','H','I','J', 'K','L','M','N','O', 'P','Q','R','S','T', 'U','V','W','X','Y', 'Z','C','P','D','R' ); Function AlphaNum: String; { Generate a random sequence of letters and numbers. } var msg: String; begin msg := Letters[ Random( NumLetter ) + 1 ]; if Random( 2 ) = 1 then msg := msg + Letters[ Random( NumLetter ) + 1 ]; if Random( 2 ) = 1 then msg := msg + BStr( Random( 10 ) ) else msg := BStr( Random( 10 ) ) + msg; if Random( 10 ) = 1 then msg := msg + BStr( Random( 10 ) ) else if Random( 9 ) = 1 then msg := BStr( Random( 10 ) ) + msg else if Random( 8 ) = 1 then msg := msg + Letters[ Random( NumLetter ) + 1 ] else if Random( 8 ) = 1 then msg := Letters[ Random( NumLetter ) + 1 ] + msg; AlphaNum := msg; end; Function JustNum: String; { Return a random sequence of numbers. } begin JustNum := BStr( Random( 499 ) + Random( 489 ) + 10 ); end; var name: String; begin name := AlphaNum; repeat if Random( 2 ) = 1 then begin name := name + '-' + AlphaNum; end else begin name := name + '-' + JustNum; end; until ( Length( name ) > 10 ) or ( Random( 2 ) = 1 ); RandomRobotName := name; end; Function UseRobotics( GB: GameBoardPtr; PC,Ingredients,Form: GearPtr ): GearPtr; { Given the above list of ingredients, the PC will try to construct a robot. } { This function returns the robot, or NIL if construction failed. } { The calling procedure should place the robot on the map or dispose of it. } { FORM is the form being attempted. It should be a free-floating gear at this point. } var BP,AP,CP,PP: LongInt; { Build, Armor, Computer, and Power points. } SkRoll,SkRank,T,RobotSize,ArmorVal: Integer; Part: GearPtr; begin { Add the stamina decrease here. } AddMentalDown( PC , 10 ); { Pay the point cost for the robot. } BP := R_BuildPoints( Ingredients ) - BuildPointsNeeded( Form ); AP := R_ArmorPoints( Ingredients ) - ArmorPointsNeeded( Form ); CP := R_ComputerPoints( Ingredients ) - ComputerPointsNeeded( Form ); PP := R_PowerPoints( Ingredients ) - PowerPointsNeeded( Form ); { Start with allocating the robot's base gear. } SetNAtt( Form^.NA , NAG_GearOps , NAS_Material , NAV_Metal ); SetSAtt( Form^.SA , 'TYPE ' ); SetSAtt( Form^.SA , 'JOB ' ); SetSAtt( Form^.SA , 'NAME <' + RandomRobotName + '>' ); SetNAtt( Form^.NA , NAG_CharDescription , NAS_DAge , -19 ); SetSAtt( Form^.SA , 'ROGUECHAR ' ); SetSAtt( Form^.SA , 'SDL_COLORS <80 80 85 170 155 230 6 42 120>' ); { Make a skill roll for the robot stats. You only get one skill roll; } { the target number is the stat in question. If your skill roll for any } { of the stats fail, you can make up for it by spending build points. } SkRoll := SkillRoll( GB , PC , NAS_Science , STAT_Knowledge , R_SkillLevelNeeded( Form ) + 5 , ToolBonus( PC , -NAS_Robotics ) , True , True ); SkRank := SkillRank( PC , NAS_Science ); { Check the skill roll against each of the form's stats. } { If we finish with non-negative build points, all is well. } for t := 1 to 8 do begin if SkRoll <= Form^.Stat[ T ] then begin BP := BP - ( Form^.Stat[ T ] - SkRoll ) * 15; end else if ( SkRoll > 10 ) and ( ( T <> STAT_Charm ) or ( Form^.Stat[ T ] > 1 ) ) then begin Form^.Stat[ T ] := Form^.Stat[ T ] + Random( ( SkRoll - 7 ) div 2 ); end; end; { If the robot has been created successfully, move on and perform the rest of the } { initialization. } if BP > 0 then begin { Apply any special effects associated with the ingredients. } { Spend any remaining BP, AP, CP, and PP on perks. } { Record ArmorVal before spending AP. } ArmorVal := AP div 5 + 1; while AP > 0 do begin if Random( 3 ) = 1 then begin AddNAtt( Form^.NA , NAG_Skill , NAS_Vitality , 1 ); AP := AP - ( NAttValue( Form^.NA , NAG_Skill , NAS_Vitality ) + 2 ); end else begin Inc( Form^.Stat[ STAT_Body ] ); AP := AP - 5; end; end; while CP > 0 do begin if Random( 10 ) = 1 then begin { Add a new skill. } AddNAtt( Form^.NA , NAG_Skill , Robot_Skill[ Random( Num_RObot_Skill ) + 1 ] , 1 ); CP := CP - 2; end else if Random( 3 ) = 1 then begin Inc( Form^.Stat[ STAT_Perception ] ); CP := CP - 3; end else if Random( 2 ) = 1 then begin Inc( Form^.Stat[ STAT_Craft ] ); CP := CP - 3; end else begin Inc( Form^.Stat[ STAT_Knowledge ] ); CP := CP - 3; end; end; while PP > 0 do begin if Random( 3 ) = 1 then begin Inc( Form^.Stat[ STAT_Reflexes ] ); PP := PP - 5; end else if Random( 2 ) = 1 then begin Inc( Form^.Stat[ STAT_Speed ] ); PP := PP - 4; end else begin Inc( Form^.Stat[ STAT_Ego ] ); PP := PP - 4; end; end; { Initialize the limbs- set size and armor value. } Part := Form^.SubCom; RobotSize := MasterSize( Form ); if ArmorVal > ( RobotSize + 1 ) then ArmorVal := ( RobotSize + 1 ); while Part <> Nil do begin if Part^.G = GG_Module then begin Part^.V := RobotSize; Part^.Stat[ STAT_Armor ] := ArmorVal; end; Part := Part^.Next; end; { Set the basic skills for the robot. } for t := 4 to 6 do SetNAtt( Form^.NA , NAG_Skill , T , Random( SkRank ) + 1 ); { If this robot is self-aware, set a job and assign a CID. } if Form^.Stat[ STAT_Charm ] > 1 then begin SetNAtt( Form^.NA , NAG_Personal , NAS_CID , NewCID( FindRoot( GB^.Scene ) ) ); AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( Form^.NA , NAG_Personal , NAS_CID ) , 20 ); SetSAtt( Form^.SA , 'JOB ' ); { Robots typically acquire the personality traits of their creator. } for t := 1 to Num_Personality_Traits do begin if Random( 3 ) <> 1 then begin SetNAtt( Form^.NA , NAG_CharDescription , -T , NAttValue( PC^.NA , NAG_CharDescription , -T ) ); end else if Random( 5 ) > SkRoll then begin SetNAtt( Form^.NA , NAG_CharDescription , -T , -NAttValue( PC^.NA , NAG_CharDescription , -T ) ); end; end; { Intelligent robots start with mecha combat skills. } for t := 1 to 3 do SetNAtt( Form^.NA , NAG_Skill , T , Random( SkRank ) + 1 ); end; end else begin { Robot construction has failed. } DisposeGear( Form ); { As a cold consolation to the PC, give back repair fuel equal to about 80% } { of the build points put into the robot. } BP := ( R_BuildPoints( Ingredients ) * 4 ) div 5; if BP > 30000 then BP := 30000 else if BP < 1 then BP := 1; Part := LoadNewSTC( 'SPAREPARTS-1' ); if Part <> Nil then begin if IsLegalInvCom( PC , Part ) then begin Part^.V := BP; InsertInvCom( PC , Part ); end else begin DisposeGear( Part ); end; end; end; { Advance time by the required amount. } WaitAMinute( GB , PC , ReactionTime( PC ) * 10 ); { Get rid of the ingredients list. } DisposeGear( Ingredients ); UseRobotics := Form; end; Procedure BuildRobot( GB: GameBoardPtr; PC: GearPtr ); { Start performing on a musical instrument. First this procedure } { will seek the best instrument currently held, then it will set } { up the continuous action. } var Ingredients,Form,Robot: GearPtr; T: Integer; begin if CurrentMental( PC ) < 1 then begin DialogMsg( MsgString( 'BUILD_ROBOT_TOO_TIRED' ) ); Exit; end else if not IsSafeArea( GB ) then begin DialogMsg( MsgString( 'BUILD_ROBOT_NOT_SAFE' ) ); Exit; end; PC := LocatePilot( PC ); DialogMsg( MsgString( 'BUILD_ROBOT_START' ) ); Ingredients := SelectRobotParts( GB , PC ); { If no ingredients were selected, no robot will be built. } if Ingredients = Nil then begin DialogMsg( MsgString( 'BUILD_ROBOT_NO_PARTS' ) ); Exit; end; Form := SelectRobotForm( GB , PC , Ingredients ); { If no form was selected, no robot will be built and the ingredients } { will be returned to the PC. } if Form = Nil then begin InsertInvCom( PC , Ingredients ); Exit; end; Robot := UseRobotics( GB , PC , Ingredients , Form ); if Robot = Nil then begin DialogMsg( MsgString( 'BUILD_ROBOT_FAILED' ) ); end else begin SetNAtt( Robot^.NA , NAG_Location , NAS_Team , NAV_LancemateTeam ); SetNAtt( Robot^.NA , NAG_Relationship , 0 , NAV_ArchAlly ); SetNAtt( Robot^.NA , NAG_CharDescription , NAS_CharType , NAV_CTLancemate ); DeployGear( GB , Robot , True ); if NAttValue( Robot^.NA , NAG_Personal , NAS_CID ) <> 0 then begin if NumLancemateSlots( GB^.Scene , PC ) < LancematesPresent( GB ) then RemoveLancemate( GB , Robot , False ); DialogMsg( ReplaceHash( MsgString( 'BUILD_ROBOT_SENTIENT' ) , GearName( Robot ) ) ); end else begin if PetsPresent( GB ) > PartyPetSlots( PC ) then RemoveLancemate( GB , Robot , False ); DialogMsg( ReplaceHash( MsgString( 'BUILD_ROBOT_SUCCESS' ) , GearName( Robot ) ) ); end; { Give the PC a rundown on the new robot's skills. } for t := 1 to Num_Robot_Skill do begin if NAttValue( Robot^.NA , NAG_Skill , Robot_Skill[ T ] ) > 0 then begin DialogMsg( ReplaceHash( ReplaceHash( MsgString( 'BUILD_ROBOT_SKILL' ) , GearName( Robot ) ) , MsgString( 'SKILLNAME_' + BStr( Robot_Skill[ t ] ) ) ) ); end; end; end; end; initialization { Load the robotic forms from disk. } Robotic_Forms := AggregatePattern( 'ROBOTS_*.txt' , Series_Directory ); finalization { Dispose of the robotic forms. } DisposeGear( Robotic_Forms ); end. GH2/services.pp0000644000175000017500000021671211376734117012303 0ustar kaolkaolunit services; { This is an offshoot of the ArenaTalk/ArenaScript interaction } { stuff. It's supposed to handle shops & other cash transactions } { for the GearHead RPG engine. } { 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 num_standard_schemes = 5; standard_lot_colors: Array [0..num_standard_schemes-1] of string = ( '152 172 183 199 188 162 200 0 200', { Coral, Gull Grey, Purple } ' 80 80 85 130 144 114 200 200 0', { Dark Grey, Battleship Grey, Yellow } ' 66 121 179 210 215 80 205 25 0', { Default player colors } '201 205 229 49 91 161 0 200 0', { Aero Blue, Azure, Green } '240 240 240 208 34 51 50 50 150' { White, Red Goes Fasta, Blue } ); SATT_SaleTag = 'SALETAG'; var { The following vars are primarily needed by the interaction routines in } { arenascript.pp, but they're needed here too and this is the higher level } { unit so here they are. } CHAT_Message: String; { The message in the interact window. } CHAT_React: Integer; { How the NPC feels about the PC. } I_Endurance: Integer; { How much of the PC's crap the NPC is } { willing to take. When it reaches 0, the NPC says goodbye. } Function Random_Mecha_Colors: String; Function RepairMasterCost( Master: GearPtr; Material: Integer ): LongInt; Function ReloadMasterCost( M: GearPtr; ReloadGeneralInv: Boolean ): LongInt; Procedure DoReloadMaster( M: GearPtr; ReloadGeneralInv: Boolean ); Procedure OpenShop( GB: GameBoardPtr; PC,NPC: GearPtr; Stuff: String ); Procedure OpenSchool( GB: GameBoardPtr; PC,NPC: GearPtr; Stuff: String ); Procedure ExpressDelivery( GB: GameBoardPtr; PC,NPC: GearPtr ); Procedure ShuttleService( GB: GameBoardPtr; PC,NPC: GearPtr ); implementation uses ability,arenacfe,backpack,gearutil,ghchars,ghmodule,gearparser, ghswag,ghweapon,interact,menugear,rpgdice,skilluse,texutil, description,narration,ui4gh,ghprop, customization, {$IFDEF ASCII} vidgfx,vidmap,vidmenus,vidinfo; {$ELSE} {$IFDEF CUTE} cutegfx,cutemap,glmenus,glinfo,colormenu; {$ELSE} glgfx,glmap,glmenus,glinfo,colormenu; {$ENDIF} {$ENDIF} Const MaxShopItems = 21; { Maximum number of items in a shop. } { Repair Mode Constants } RM_Medical = 1; { Repair MEAT for SF:0 } RM_GeneralRepair = 2; { Repair METAL and BIOTECH for SF:0 } RM_MechaRepair = 3; { Repair METAL and BIOTECH for higher SF } var SERV_GB: GameBoardPtr; SERV_PC,SERV_NPC,SERV_Info: GearPtr; SERV_Menu: RPGMenuPtr; Standard_Caliber_List: GearPtr; Procedure ServiceRedraw; { Redraw the screen for whatever service is going to go on. } var Part: GearPtr; begin CombatDisplay( SERV_GB ); SetupServicesDisplay; if ( SERV_Info <> Nil ) and ( Serv_Menu <> Nil ) then begin Part := RetrieveGearSib( SERV_Info , CurrentMenuItemValue( SERV_Menu ) ); if Part <> Nil then begin BrowserInterfaceInfo( SERV_GB , Part , ZONE_ItemsInfo ); end; end else if Serv_Info <> Nil then begin BrowserInterfaceInfo( SERV_GB , SERV_Info , ZONE_ItemsInfo ); end; if SERV_NPC <> Nil then NPCPersonalInfo( SERV_NPC , ZONE_ShopCaption ); CMessage( '$' + BStr( NAttValue( SERV_PC^.NA , NAG_Experience , NAS_Credits ) ) , ZONE_ItemsPCInfo , InfoHilight ); GameMsg( CHAT_Message , ZONE_ShopMsg , InfoHiLight ); end; Procedure SellStuffRedraw; { Redraw the screen for whatever service is going to go on. } var N: Integer; Part: GearPtr; begin CombatDisplay( SERV_GB ); SetupServicesDisplay; if ( SERV_Info <> Nil ) and ( Serv_Menu <> Nil ) then begin N := CurrentMenuItemValue( SERV_Menu ); if N > 0 then begin Part := LocateGearByNumber( SERV_Info , N ); if Part <> Nil then begin BrowserInterfaceInfo( SERV_GB , Part , ZONE_ItemsInfo ); end; end; end else if Serv_Info <> Nil then begin BrowserInterfaceInfo( SERV_GB , SERV_Info , ZONE_ItemsInfo ); end; if SERV_NPC <> Nil then NPCPersonalInfo( SERV_NPC , ZONE_ShopCaption ); CMessage( '$' + BStr( NAttValue( SERV_PC^.NA , NAG_Experience , NAS_Credits ) ) , ZONE_ItemsPCInfo , InfoHilight ); GameMsg( CHAT_Message , ZONE_ShopMsg , InfoHiLight ); end; Procedure ServicesBackpackRedraw; { A redrawer for the backpack, as accessed from services. } { Just do the combat display and call it even. } begin CombatDisplay( SERV_GB ); end; Function ScalePrice( GB: GameBoardPtr; PC,NPC: GearPtr; Price: Int64 ): LongInt; { Modify the price listed based upon the PC's shopping skill, } { faction membership, and whatever else. } var ShopRk,ShopTr,R: Integer; { ShopRank and ShopTarget } PriceMod: LongInt; Adv: GearPtr; begin { Start with the assumption that we're paying 100%. } PriceMod := 100; { First, let's modify this by Shopping skill. } { Determine the Shopping skill rank of the buyer. } ShopRk := SkillValue( PC , NAS_Shopping , STAT_Charm ); { Determine the shopping target number, which should be the EGO } { stat of the storekeeper. } if ( NPC = Nil ) or ( NPC^.G <> GG_Character ) then ShopTr := 10 else begin { Target is based on both the Ego of the shopkeeper } { and also on the relationship with the PC. } ShopTr := CStat( NPC , STAT_Ego ); R := ReactionScore( Nil , PC , NPC ); if R > 0 then begin ShopTr := ShopTr - ( R div 5 ); end else if R < 0 then begin { It's much harder to haggle if the shopkeep } { doesn't like you. } ShopTr := ShopTr + Abs( R ) div 2; { If the dislike ventures into hate, there's a markup. } if R < -20 then PriceMod := PriceMod + Abs( R ) div 5; end; end; { If ShopRk beats ShopTr, lower the asking price. } if ShopRk > ShopTr then begin { Every point of shopping skill that the unit has } { gives a 2% discount to whatever is being purchased. } ShopRk := ( ShopRk - ShopTr ) * 2; if ShopRk > 50 then ShopRk := 50; PriceMod := PriceMod - ShopRk; end; { Next, let's take a look at factions. } if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and ( NPC <> Nil ) then begin { ArchEnemies get a 20% markup, allies get a 10% discount. } Adv := FindRoot( GB^.Scene ); if IsArchEnemy( Adv , NPC ) then begin PriceMod := PriceMod + 20; end else if IsArchAlly( Adv , NPC ) then begin PriceMod := PriceMod - 10; end; end; { Calculate the final price. } Price := ( Price * PriceMod ) div 100; if Price < 1 then Price := 1; ScalePrice := Price; end; Function MaxShopRank( Shopkeeper: GearPtr ): Integer; { Return the maximum shop rank normally stocks. } var MSR: Integer; begin MSR := SkillRank( Shopkeeper , NAS_Shopping ) - 3; if MSR < 3 then MSR := 3; MaxShopRank := MSR; end; Function PurchasePrice( GB: GameBoardPtr; PC,NPC,Item: GearPtr ): LongInt; { Determine the purchase price of ITEM as being sold by NPC } { to PC. } begin { Scale the base cost for the item. } PurchasePrice := ScalePrice( GB , PC , NPC , GearCost( Item ) ); end; Procedure ShoppingXP( PC , Part: GearPtr ); { The PC has just purchased PART. Give some XP to the PC's shopping } { skill, then print a message if appropriate. } var Price: LongInt; begin { Find the price of the gear. This must be positive or it'll } { crash the logarithm function. } Price := GearCost( Part ); if Price < 1 then Price := 1; if DoleSkillExperience( PC , NAS_Shopping , Round( Ln( Price ) * 5 ) + 1 ) then begin DialogMsg( MsgString( 'SHOPPING_SkillAdvance' ) ); end; end; Function ShopTolerance( GB: GameBoardPtr; NPC: GearPtr ): Integer; { Tolerance measures the legality/illegality of items. This function } { returns the maximum legality level stocked by this shopkeeper. } var Scene: GearPtr; Tolerance: Integer; begin if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Scene := FindRootScene( GB^.Scene ); if AStringHasBSTring( SAttValue( GB^.Scene^.SA , 'SPECIAL' ) , 'UNREGULATED' ) then begin Tolerance := NAttValue( GB^.Scene^.NA , NAG_GearOps , NAS_Legality ); end else begin Tolerance := NAttValue( Scene^.NA , NAG_GearOps , NAS_Legality ); end; { Criminal shopkeepers have a higher than normal tolerance. } if NAttValue( NPC^.NA , NAG_CharDescription , NAS_Lawful ) < 0 then begin Tolerance := Tolerance - ( NAttValue( NPC^.NA , NAG_CharDescription , NAS_Lawful ) div 2 ) + 5; end; end else begin Tolerance := 0; end; ShopTolerance := Tolerance; end; procedure BuyAmmoClips( GB: GameBoardPtr; PC,NPC,Weapon: GearPtr ); { Allow spare clips to be purchased for this weapon. } { If possible, add some special clip types. } var AmmoList: GearPtr; Tolerance: Integer; Function HasUniqueType( Ammo: GearPtr ): Boolean; { Return TRUE if Ammo has a TYPE attribute tag which WEAPON lacks. } var WType,AType,msg: String; UTypeFound: Boolean; begin WType := WeaponAttackAttributes( Weapon ); AType := SAttValue( Ammo^.SA , 'TYPE' ); { If you've got an ammo that does nothing and normal ammo that does something, } { that ammo counts as having a unique type. } if ( AType = '' ) and ( WType <> '' ) then Exit( True ); UTypeFound := False; while ( AType <> '' ) and not UTypeFound do begin msg := ExtractWord( AType ); if not AStringHasBString( WType , msg ) then UTypeFound := True; end; HasUniqueType := UTypeFound; end; Procedure AddAmmoToList( Proto: GearPtr ); { Create a clone of this ammunition and add it to the list. } { If appropriate, add some ammo variants. We will assume for the } { purpose of this procedure that no item will ever have multiple } { ammo clips of the same type; i.e. you would not have a 5mm rifle } { which also had an integrated 5mm rifle built into it. If you } { design a weapon like that you are a truly terrible person, and } { I wash my hands of you. } var A,ATmp,AVar,VarList: GearPtr; begin A := CloneGear( Proto ); AppendGear( AmmoList , A ); { Depending on the caliber of this ammo, and the shopkeeper's stats, } { maybe add some variants. } VarList := SeekGearByName( Standard_Caliber_List , SAttValue( A^.SA , 'CALIBER' ) ); if VarList <> Nil then begin AVar := VarList^.SubCom; while AVar <> Nil do begin if ( NAttValue( AVar^.NA , NAG_GearOps , NAS_Legality ) <= Tolerance ) and HasUniqueType( AVar ) then begin ATmp := CloneGear( A ); SetSAtt( ATmp^.SA , 'name <' + GearName( A ) + ' (' + GearName( AVar ) + ')>' ); SetSAtt( ATmp^.SA , 'type <' + SAttValue( AVar^.SA , 'TYPE' ) + '>' ); AppendGear( AmmoList , ATmp ); end; AVar := AVar^.Next; end; end; end; Procedure LookForAmmo( LList: GearPtr ); { Search along this linked list looking for ammo. If you find } { any, copy it and add it to the list. Then, add any ammo varieties } { allowed by the shopkeeper's skill level and tolerance. } begin while LList <> Nil do begin if LList^.G = GG_Ammo then begin AddAmmoToList( LList ); end; LookForAmmo( LList^.SubCom ); LList := LList^.Next; end; end; var ShopMenu: RPGMenuPtr; Ammo: GearPtr; N: Integer; Cost: LongInt; begin { Step One: Create the list of ammo. } AmmoList := Nil; Tolerance := ShopTolerance( GB , NPC ); LookForAmmo( Weapon^.SubCom ); { Step Two: Create the shopping menu. } ShopMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); N := 1; Ammo := AmmoList; while Ammo <> Nil do begin AddRPGMenuItem( ShopMenu , GearName( Ammo ) + ' ($' + BStr( PurchasePrice( GB , PC , NPC , Ammo ) ) + ')' , N ); Inc( N ); Ammo := Ammo^.Next; end; RPMSortAlpha( ShopMenu ); AlphaKeyMenu( ShopMenu ); AddRPGMenuItem( ShopMenu , MsgString( 'EXIT' ) , -1 ); { Step Three: Keep shopping until the PC selects exit. } repeat SERV_Info := AmmoList; SERV_Menu := ShopMenu; N := SelectMenu( ShopMenu , @ServiceRedraw ); if N > 0 then begin Ammo := RetrieveGearSib( AmmoList , N ); Cost := PurchasePrice( GB , PC , NPC , Ammo ); if NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) >= Cost then begin { Copy the gear, then stick it in inventory. } Ammo := CloneGear( Ammo ); GivePartToPC( GB , Ammo , PC ); { Reduce the buyer's cash by the cost of the gear. } AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , -Cost ); CHAT_Message := MsgString( 'BUYREPLY' + BStr( Random( 4 ) + 1 ) ); DialogMSG( ReplaceHash( MsgString( 'BUY_YOUHAVEBOUGHT' ) , GearName( Ammo ) ) ); { Give some XP to the PC's SHOPPING skill. } ShoppingXP( PC , Ammo ); end else begin { Not enough cash to buy... } DialogMSG( ReplaceHash( MsgString( 'BUY_CANTAFFORD' ) , GearName( Ammo ) ) ); CHAT_Message := MsgString( 'BUYNOCASH' + BStr( Random( 4 ) + 1 ) ); end; end; until N = -1; { Upon exiting, dispose of the ammo list. } DisposeRPGMenu( ShopMenu ); DisposeGear( AmmoList ); end; procedure PurchaseGearMenu( GB: GameBoardPtr; PC,NPC,Part: GearPtr ); { The PC may or may not want to buy PART. } { Show the price of this gear, and ask whether or not the } { player wants to make this purchase. } { If this item contains any SF:0 ammunition, offer to sell some } { backup clips as well. } var YNMenu: RPGMenuPtr; Cost: LongInt; N: Integer; msg: String; begin Cost := PurchasePrice( GB , PC , NPC , Part ); YNMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AddRPGMenuItem( YNMenu , 'Buy ' + GearName( Part ) + ' ($' + BStr( Cost ) + ')' , 1 ); if ( Part^.SubCom <> Nil ) or ( Part^.InvCom <> Nil ) then AddRPGMenuItem( YNMenu , MsgString( 'SERVICES_BrowseParts' ) , 2 ); if ( SeekSubsByG( Part^.SubCom , GG_Ammo ) <> Nil ) and ( Part^.Scale = 0 ) then AddRPGMenuItem( YNMenu , MsgString( 'SERVICES_BuyClips' ) , 3 ); AddRPGMenuItem( YNMenu , 'Search Again' , -1 ); msg := MSgString( 'BuyPROMPT' + Bstr( Random( 4 ) + 1 ) ); msg := ReplaceHash( msg , GearName( Part ) ); msg := ReplaceHash( msg , BStr( Cost ) ); CHAT_Message := Msg; repeat Serv_Info := Part; Serv_Menu := Nil; N := SelectMenu( YNMenu , @ServiceRedraw ); if N = 1 then begin if NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) >= Cost then begin { Copy the gear, then stick it in inventory. } Part := CloneGear( Part ); GivePartToPC( GB , Part , PC ); { Reduce the buyer's cash by the cost of the gear. } AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , -Cost ); CHAT_Message := MsgString( 'BUYREPLY' + BStr( Random( 4 ) + 1 ) ); if ( NPC <> Nil ) and ( NAttValue( Part^.NA , NAG_GearOps , NAS_ShopRank ) >= ( MaxShopRank( NPC ) div 2 ) ) then begin if DoleSkillExperience( NPC , NAS_Shopping , Random( SkillAdvCost( NPC , NAttValue( Part^.NA , NAG_GearOps , NAS_ShopRank ) + 3 ) ) + 1 ) then begin CHAT_Message := CHAT_Message + ' ' + MsgString( 'BUYUPGRADE' ); end; end; DialogMSG( ReplaceHash( MsgString( 'BUY_YOUHAVEBOUGHT' ) , GearName( Part ) ) ); { Give some XP to the PC's SHOPPING skill. } ShoppingXP( PC , Part ); end else begin { Not enough cash to buy... } DialogMSG( ReplaceHash( MsgString( 'BUY_CANTAFFORD' ) , GearName( Part ) ) ); CHAT_Message := MsgString( 'BUYNOCASH' + BStr( Random( 4 ) + 1 ) ); end; end else if N = 2 then begin MechaPartBrowser( Part , @ServiceRedraw ); end else if N = 3 then begin BuyAmmoClips( GB , PC , NPC , Part ) end else if N = -1 then begin CHAT_Message := MsgString( 'BUYCANCEL' + BStr( Random( 4 ) + 1 ) ); end; until ( N <> 2 ) and ( N <> 3 ); DisposeRPGMenu( YNMenu ); end; Function SellGear( var LList,Part: GearPtr; PC,NPC: GearPtr; const Categories: String ): Boolean; { The unit may or may not want to sell PART. } { Show the price of this gear, and ask whether or not the } { player wants to make this sale. } var YNMenu: RPGMenuPtr; Cost: Int64; R,ShopRk,ShopTr: Integer; N: Integer; WasStolen: Boolean; msg: String; begin { First - check to see whether or not the item is stolen. } { Most shopkeepers won't buy stolen goods. The PC has to locate } { a fence for illicit transactions. } WasStolen := NAttValue( Part^.NA , NAG_NArrative , NAS_Stolen ) <> 0; if WasStolen then begin N := NAttValue( NPC^.NA , NAG_CharDescription , NAS_Lawful ); Cost := NAttValue( NPC^.NA , NAG_CharDescription , NAS_Heroic ); if Cost > 0 then N := N + Cost; if N >= 0 then begin { This shopkeeper won't buy stolen items. } CHAT_Message := MsgString( 'SERVICES_StolenResponse' ); DialogMsg( MsgString( 'SERVICES_StolenDesc' ) ); { If the shopkeeper doesn't already hate the PC, } { then the PC's reputation and relation scores } { may both get damaged. } if ( PC <> Nil ) and ( NAttValue( PC^.NA , NAG_ReactionScore , NAttValue( NPC^.NA , NAG_PErsonal , NAS_CID ) ) >= -20 ) then begin AddReputation( PC , 2 , -1 ); if N > Random( 200 ) then AddReputation( PC , 6 , -1 ); AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( NPC^.NA , NAG_PErsonal , NAS_CID ) , -( Random( 6 ) + 1 ) ); end; Exit( False ); end; end; Cost := GearCost( Part ); if Destroyed( Part ) then Cost := Cost div 3; { If this part matches the category of the shopkeeper, it's worth more money. } { Actually, it works so that selling inappropriate items are penalized. } if not ( ( Part^.Scale < 1 ) and PartAtLeastOneMatch ( Categories , SAttValue( Part^.Sa , 'CATEGORY' ) ) ) then begin Cost := ( Cost * 2 ) div 3; end; { Determine shopping rank. } ShopRk := SkillValue( PC , NAS_Shopping , STAT_Charm ); { Determine shopping target. } if ( NPC = Nil ) or ( NPC^.G <> GG_Character ) then ShopTr := 10 else begin { Target is based on both the Ego of the shopkeeper } { and also on the relationship with the PC. } ShopTr := NPC^.Stat[ STAT_Ego ]; R := ReactionScore( Nil , PC , NPC ); if R > 0 then begin ShopTr := ShopTr - ( R div 5 ); end else if R < 0 then begin { It's much harder to haggle if the shopkeep } { doesn't like you. } ShopTr := ShopTr + Abs( R ) div 2; end; end; { Every point of shopping skill that the unit has } { gives a 1% bonus to the money gained. } ShopRk := ShopRk - ShopTR; if ShopRk > 40 then ShopRk := 40 else if ShopRk < 0 then ShopRk := 0; Cost := ( Cost * (20 + ShopRk ) ) div 100; if Cost < 1 then Cost := 1; YNMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AddRPGMenuItem( YNMenu , 'Sell ' + GearName( Part ) + ' ($' + BStr( Cost ) + ')' , 1 ); AddRPGMenuItem( YNMenu , 'Maybe later' , -1 ); { Query the menu - Sell it or not? } msg := MSgString( 'SELLPROMPT' + Bstr( Random( 4 ) + 1 ) ); msg := ReplaceHash( msg , BStr( Cost ) ); msg := ReplaceHash( msg , GearName( Part ) ); CHAT_Message := Msg; SERV_Menu := Nil; SERV_Info := Part; N := SelectMenu( YNMenu , @ServiceRedraw ); if N = 1 then begin { Increase the buyer's cash by the price of the gear. } AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , Cost ); CHAT_Message := MSgString( 'SELLREPLY' + Bstr( Random( 4 ) + 1 ) ); msg := MSgString( 'SELL_YOUHAVESOLD' ); msg := ReplaceHash( msg , GearName( Part ) ); msg := ReplaceHash( msg , BStr( Cost ) ); DialogMSG( msg ); { If the item was stolen, trash the PC's reputation here. } if WasStolen then begin AddReputation( PC , 2 , -5 ); end; RemoveGear( LList , Part ); end else begin CHAT_Message := MSgString( 'SELLCANCEL' + Bstr( Random( 4 ) + 1 ) ); end; DisposeRPGMenu( YNMenu ); SERV_Info := Nil; SellGear := N = 1; end; Function RepairMasterCost( Master: GearPtr; Material: Integer ): LongInt; { Return the expected cost of repairing every component of } { MASTER which is made of MATERIAL. } var it: LongInt; begin it := TotalRepairableDamage( Master , Material ) * Repair_Cost_Multiplier[ Material ]; RepairMasterCost := it; end; Function RepairMasterByModeCost( Part: GearPtr; RepMode: Integer ): LongInt; { Determine how much it will cost to repair this master using the specified repair mode. } { If the mode doesn't affect this master, return 0. } var Cost: LongInt; begin Cost := 0; if RepMode = RM_Medical then begin Cost := RepairMasterCost( Part , NAV_Meat ); end else if ( RepMode = RM_GeneralRepair ) and ( Part^.Scale = 0 ) then begin Cost := RepairMasterCost( Part , NAV_Metal ) + RepairMasterCost( Part , NAV_Biotech ); end else if ( RepMode = RM_MechaRepair ) and ( Part^.Scale > 0 ) then begin Cost := RepairMasterCost( Part , NAV_Metal ) + RepairMasterCost( Part , NAV_Biotech ); end; RepairMasterByModeCost := Cost; end; Function RepairAllCost( GB: GameBoardPtr; RepMode: Integer ): LongInt; { Determine the cost of repairing every item belonging to Team 1. } var Part: GearPtr; Cost: longInt; begin { Initialize values. } Part := GB^.Meks; Cost := 0; { Browse through each gear on the board, adding the cost to repair } { each Team 1 mek or character. } while Part <> Nil do begin if ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) then begin { Only repair mecha which have pilots assigned!!! } { If the PC had to patch up all that salvage every time... Brr... } if ( Part^.G <> GG_Mecha ) or ( SAttValue( Part^.SA , 'PILOT' ) <> '' ) then begin Cost := Cost + RepairMasterByModeCost( Part , RepMode ); end; end; Part := Part^.Next; end; RepairAllCost := Cost; end; Procedure DoRepairMaster( GB: GameBoardPtr; Master,Repairer: GearPtr; Material: Integer ); { Remove the damage counters from every component of MASTER which } { can be affected using the provided SKILL. } var TRD: LongInt; begin { Repair this part, if appropriate. } TRD := TotalRepairableDamage( Master , Material ); ApplyRepairPoints( Master , Material , TRD , True ); { Wait an amount of time. } QuickTime( GB , AP_Minute * 5 ); end; Procedure DoRepairMasterByMode( GB: GameBoardPtr; Part,NPC: GearPtr; RepMode: Integer ); { Determine how much it will cost to repair this master using the specified repair mode. } { If the mode doesn't affect this master, return 0. } begin if RepMode = RM_Medical then begin DoRepairMaster( GB , Part , NPC , NAV_Meat ); end else if ( RepMode = RM_GeneralRepair ) and ( Part^.Scale = 0 ) then begin DoRepairMaster( GB , Part , NPC , NAV_Metal ); DoRepairMaster( GB , Part , NPC , NAV_Biotech ); end else if ( RepMode = RM_MechaRepair ) and ( Part^.Scale > 0 ) then begin DoRepairMaster( GB , Part , NPC , NAV_Metal ); DoRepairMaster( GB , Part , NPC , NAV_Biotech ); end; end; Procedure DoRepairAll( GB: GameBoardPtr; NPC: GearPtr; RepMode: Integer ); { Repair every item belonging to Team 1. } var Part: GearPtr; begin { Initialize values. } Part := GB^.Meks; { Browse through each gear on the board, repairing } { each Team 1 mek or character. } while Part <> Nil do begin if ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) then begin { Only repair mecha which have pilots assigned!!! } { If the PC had to patch up all that salvage every time... Brr... } if ( Part^.G <> GG_Mecha ) or ( SAttValue( Part^.SA , 'PILOT' ) <> '' ) then begin DoRepairMasterByMode( GB , Part , NPC , RepMode ); end; end; Part := Part^.Next; end; end; Procedure RepairAllFrontEnd( GB: GameBoardPtr; PC, NPC: GearPtr; RepMode: Integer ); { Run the REPAIR ALL procedure, and charge the PC for the work done. } { If the PC doesn't have enough money to repair everything roll to } { see if the NPC will do this work for free. } const NumRepairSayings = 5; var msg: String; Cost,Cash: LongInt; R: Integer; begin { Determine the cost of repairing everything, and also } { the amount of cash the PC has. } Cost := ScalePrice( GB , PC , NPC , RepairAllCost( GB , RepMode ) ); Cash := NAttValue( PC^.NA, NAG_Experience , NAS_Credits ); R := ReactionScore( Nil , PC , NPC ); msg := ''; { See whether or not the PC will be charged for this repair. } { If the NPC likes the PC well enough, the service will be free. } if ( Random( 200 ) + 10 ) < R then begin { The NPC will do the PC a favor, and do this one for free. } msg := MsgString( 'SERVICES_RAFree' ); Cost := 0; end else if ( Cash < Cost ) and ( R > ( 10 + NPC^.Stat[ STAT_Ego ] ) ) then begin msg := MsgString( 'SERVICES_RACantPay' ); AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) , -Random( 10 ) ); Cost := 0; end; if Cost < Cash then begin DoRepairAll( GB , NPC , RepMode ); AddNAtt( PC^.NA, NAG_Experience , NAS_Credits , -Cost ); if msg = '' then msg := MsgString( 'SERVICES_RADoRA' + BStr( Random( NumRepairSayings ) + 1 ) ); end else begin msg := MsgString( 'SERVICES_RALousyBum' ); end; CHAT_Message := msg; end; Procedure RepairOneFrontEnd( GB: GameBoardPtr; Part, PC, NPC: GearPtr; RepMode: Integer ); { Run the REPAIR MASTER procedure, and charge the PC for the work done. } { If the PC doesn't have enough money to repair everything roll to } { see if the NPC will do this work for free. } const NumRepairSayings = 5; var Cost,Cash: LongInt; R: Integer; begin { Determine the cost of repairing everything, and also } { the amount of cash the PC has. } Cost := ScalePrice( GB , PC , NPC , RepairMasterByModeCost( PArt , RepMode ) ); Cash := NAttValue( PC^.NA, NAG_Experience , NAS_Credits ); R := ReactionScore( Nil , PC , NPC ); { See whether or not the PC will be charged for this repair. } { If the NPC likes the PC well enough, the service will be free. } if ( Random( 90 ) + 10 ) < R then begin { The NPC will do the PC a favor, and do this one for free. } CHAT_Message := MsgString( 'SERVICES_RAFree' ); Cost := 0; end else if ( Cash < Cost ) and ( R > 10 ) then begin CHAT_Message := MsgString( 'SERVICES_RACantPay' ); AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) , -Random( 5 ) ); Cost := 0; end; if Cost < Cash then begin DoRepairMasterByMode( GB , Part , NPC , RepMode ); AddNAtt( PC^.NA, NAG_Experience , NAS_Credits , -Cost ); CHAT_Message := MSgString( 'SERVICES_RADoRA' + BStr( Random( NumRepairSayings ) + 1 ) ); end else begin CHAT_Message := MsgString( 'SERVICES_RALousyBum' ); end; end; Function ReloadMagazineCost( Mag: GearPtr ): LongInt; { Calculate the cost of reloading this magazine. } var Spent: Integer; it: LongInt; begin it := 0; if Mag^.G = GG_Ammo then begin Spent := NAttValue( Mag^.NA , NAG_WeaponModifier , NAS_AmmoSpent ); if Spent > 0 then begin it := ( ComponentValue( Mag , True , True ) * Spent ) div Mag^.Stat[ STAT_AmmoPresent ]; if it < 5 then it := 5; end; end; if it > 0 then begin { Reduce the reload cost by a factor of 5- apparently, magazines are really expensive. } it := it div 5; if it < 1 then it := 1; end; ReloadMagazineCost := it; end; Function ReloadMasterCost( M: GearPtr; ReloadGeneralInv: Boolean ): LongInt; { Return the cost of refilling all magazines held by M. } var Part: GearPtr; it: LongInt; begin it := ReloadMagazineCost( M ); Part := M^.SubCom; while Part <> Nil do begin it := it + ReloadMasterCost( Part , ReloadGeneralInv ); Part := Part^.Next; end; if ReloadGeneralInv or not IsMasterGear( M ) then begin Part := M^.InvCom; while Part <> Nil do begin it := it + ReloadMasterCost( Part , ReloadGeneralInv ); Part := Part^.Next; end; end; ReloadMasterCost := it; end; Procedure DoReloadMaster( M: GearPtr; ReloadGeneralInv: Boolean ); { Clear all ammo usage by M. } var Part: GearPtr; begin { If this is an ammunition gear, set the number of shots fired to 0. } if M^.G = GG_Ammo then SetNAtt( M^.NA , NAG_WeaponModifier , NAS_AmmoSpent , 0 ); { Check SubComs and InvComs. } Part := M^.SubCom; while Part <> Nil do begin DoReloadMaster( Part , ReloadGeneralInv ); Part := Part^.Next; end; if ReloadGeneralInv or not IsMasterGear( M ) then begin Part := M^.InvCom; while Part <> Nil do begin DoReloadMaster( Part , ReloadGeneralInv ); Part := Part^.Next; end; end; end; Function ReloadCharsCost( GB: GameBoardPtr; PC,NPC: GearPtr; ReloadGeneralInv: Boolean ): LongInt; { Calculate the cost of reloading every PC's ammunition. } var it: LongInt; Part: GearPtr; begin it := 0; Part := GB^.Meks; while Part <> Nil do begin if ( ( NATtVAlue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) ) and ( Part^.G = GG_Character ) then begin it := it + ReloadMasterCost( Part , ReloadGeneralInv ); end; Part := Part^.Next; end; { SCale the price for the PC's shopping skill. } if it > 0 then it := ScalePrice( GB , PC , NPC , it ); ReloadCharsCost := it; end; Procedure DoReloadChars( GB: GameBoardPtr; PC,NPC: GearPtr; ReloadGeneralInv: Boolean ); { Calculate the cost of reloading every PC's ammunition. } var COst: LongInt; Part: GearPtr; begin Cost := ReloadCharsCost( GB , PC , NPC , ReloadGeneralInv ); if Cost <= NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) then begin Part := GB^.Meks; while Part <> Nil do begin if ( ( NATtVAlue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) ) and ( Part^.G = GG_Character ) then begin DoReloadMaster( Part , ReloadGeneralInv ); end; Part := Part^.Next; end; AddNAtt( PC^.NA, NAG_Experience , NAS_Credits , -Cost ); { Print the message. } CHAT_Message := MsgString( 'SERVICES_ReloadChars' ); end else begin { Player can't afford the reload. } CHAT_Message := MsgString( 'SERVICES_RALousyBum' ); end; end; Function ReloadMechaCost( GB: GameBoardPtr; PC,NPC: GearPtr; ReloadGeneralInv: Boolean ): LongInt; { Calculate the cost of reloading every mek's ammunition. } var it: LongInt; Part: GearPtr; begin it := 0; Part := GB^.Meks; while Part <> Nil do begin if ( NATtVAlue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and ( Part^.G = GG_Mecha ) then begin it := it + ReloadMasterCost( Part , ReloadGeneralInv ); end; Part := Part^.Next; end; { SCale the price for the PC's shopping skill. } if it > 0 then it := ScalePrice( GB , PC , NPC , it ); ReloadMechaCost := it; end; Procedure DoReloadMecha( GB: GameBoardPtr; PC,NPC: GearPtr; ReloadGeneralInv: Boolean ); { Calculate the cost of reloading every PC's ammunition. } var COst: LongInt; Part: GearPtr; begin Cost := ReloadMechaCost( GB , PC , NPC , ReloadGeneralInv ); if Cost <= NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) then begin Part := GB^.Meks; while Part <> Nil do begin if ( NATtVAlue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and ( Part^.G = GG_Mecha ) then begin DoReloadMaster( Part , ReloadGeneralInv ); end; Part := Part^.Next; end; AddNAtt( PC^.NA, NAG_Experience , NAS_Credits , -Cost ); { Print the message. } CHAT_Message := MsgString( 'SERVICES_ReloadMeks' ); end else begin { Player can't afford the reload. } CHAT_Message := MsgString( 'SERVICES_RALousyBum' ); end; end; Function RechargeCost( GB: GameBoardPtr; PC,NPC: GearPtr ): LongInt; { Calculate the cost of reloading every PC's ammunition. } Function RechargeTrackCost( Part: GearPtr ): LongInt; { Return the number of spent power points along this track and counting all children. } var it: LongInt; begin it := 0; while Part <> Nil do begin it := it + NAttValue( Part^.NA , NAG_Condition , NAS_PowerSpent ) + RechargeTrackCost( Part^.SubCom ) + RechargeTrackCost( Part^.InvCom ); Part := Part^.Next; end; RechargeTrackCost := it; end; var it: LongInt; Part: GearPtr; begin it := 0; Part := GB^.Meks; while Part <> Nil do begin if ( NATtVAlue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) then begin it := it + NAttValue( Part^.NA , NAG_Condition , NAS_PowerSpent ) + RechargeTrackCost( Part^.SubCom ) + RechargeTrackCost( Part^.InvCom ); end; Part := Part^.Next; end; if it > 0 then begin { Every 100 points of power costs 1 credit. } it := it div 100; if it < 1 then it := 1; { SCale the price for the PC's shopping skill. } it := ScalePrice( GB , PC , NPC , it ); end; RechargeCost := it; end; Procedure DoRecharge( GB: GameBoardPtr; PC,NPC: GearPtr ); { Recharge the PC's power sources. } Procedure DoRechargeTrack( Part: GearPtr ); { Recharge everything along this track. } begin while Part <> Nil do begin SetNAtt( Part^.NA , NAG_Condition , NAS_PowerSpent , 0 ); DoRechargeTrack( Part^.SubCom ); DoRechargeTrack( Part^.InvCom ); Part := Part^.Next; end; end; var COst: LongInt; Part: GearPtr; begin Cost := RechargeCost( GB , PC , NPC ); if Cost <= NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) then begin Part := GB^.Meks; while Part <> Nil do begin if ( NATtVAlue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) or ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) then begin SetNAtt( Part^.NA , NAG_Condition , NAS_PowerSpent , 0 ); DoRechargeTrack( Part^.SubCom ); DoRechargeTrack( Part^.InvCom ); end; Part := Part^.Next; end; AddNAtt( PC^.NA, NAG_Experience , NAS_Credits , -Cost ); { Print the message. } CHAT_Message := MsgString( 'SERVICES_DoRecharge' ); end else begin { Player can't afford the recharge. } CHAT_Message := MsgString( 'SERVICES_RALousyBum' ); end; end; Function Random_Mecha_Colors: String; { Return some random colors for this mecha. } begin {$IFDEF ASCII} random_mecha_colors := standard_lot_colors[ random( num_standard_schemes ) ]; {$ELSE} if Random( 3 ) = 1 then begin random_mecha_colors := standard_lot_colors[ random( num_standard_schemes ) ]; end else begin random_mecha_colors := RandomColorString( CS_PrimaryMecha ) + ' ' + RandomColorString( CS_SecondaryMecha ) + ' ' + RandomColorString( CS_Detailing ); end; {$ENDIF} end; Function CreateWaresList( GB: GameBoardPtr; NPC: GearPtr; Stuff: String ): GearPtr; { Fabricate the list of items this NPC has for sale. } Function IsGoodWares( I: GearPtr; Tolerance: LongInt ): Boolean; { Return TRUE if this item is appropriate for NPC's shop, } { FALSE if it isn't. An item is appropriate if: } { - one of its CATEGORY tags may be found in STUFF. } { - its unscaled value doesn't exceed the shopkeep's rating. } { - its faction is either the storekeeper or the town's faction. } { - its modified legality level is greater or equal to the town's level. } const LowLegalityLevel = 0; var NGW: Boolean; Tag,Category,Desc: String; N: LongInt; Scene,Fac: GearPtr; begin { Begin by assuming TRUE. } NGW := True; { Search through STUFF to see if Item's type matches the CATEGORY. } Category := SAttValue( I^.SA , 'CATEGORY' ); NGW := not PartAtLeastOneMatch ( Stuff , Category ); Scene := FindRootScene( GB^.Scene ); { Make sure this item is cleared for the shopkeeper's faction, and the faction } { of the city. Items marked with the GENERAL tag are clear for all factions. } if not NGW then begin N := 0; desc := 'GENERAL'; Fac := SeekFaction( GB^.Scene , NAttValue( NPC^.NA , NAG_Personal , NAS_FactionID ) ); if Fac <> Nil then desc := SAttValue( Fac^.SA , 'DESIG' ) + ' ' + desc; { Scene here points to the root scene. } if ( Scene <> Nil ) and ( Fac = Nil ) then begin Fac := SeekFaction( GB^.Scene , NAttValue( Scene^.NA , NAG_Personal , NAS_FactionID ) ); if Fac <> Nil then desc := SAttValue( Fac^.SA , 'DESIG' ) + ' ' + desc; end; Category := SAttValue( I^.SA , 'FACTIONS' ); while Category <> '' do begin Tag := ExtractWord( Category ); if AStringHasBString( desc , Tag ) then Inc( N ); end; { If there wasn't at least one faction match, this item is no good. } NGW := N < 1; end; { Make sure this item is legal. } { Mecha don't have to be checked for legality. } if ( Scene <> Nil ) and ( I^.G <> GG_Mecha ) and not NGW then begin { Scene should point to the root scene here, since we found it above. } { If the current scene is marked for a modified legality level, } { use the local tolerance value instead. } NGW := NAttValue( I^.NA , NAG_GearOps , NAS_Legality ) > Tolerance; end; IsGoodWares := not NGW; end; Function GetWaresListItem( Wares: NAttPtr; ShopRank , N: Integer ): NAttPtr; { Return a pointer to the Nth entry of the provided ShopRank. } var it: NAttPtr; begin it := Nil; while ( Wares <> Nil ) and ( it = Nil ) do begin if Wares^.V = ShopRank then begin Dec( N ); if N = 0 then it := Wares; end; Wares := Wares^.Next; end; GetWaresListItem := it; end; Procedure InitShopItem( I: GearPtr ); { Certain items may need some initialization. } var mecha_colors: String; Fac: GearPtr; Discount: Integer; begin if I^.G = GG_Mecha then begin { To start with, determine this merchant's lot color. This is the color all } { the mecha in the sales lot are painted. If the NPC has a faction this will } { be the faction color. Otherwise, check to see if he has a color stored. } { Otherwise pick a color scheme at random and save it. } mecha_colors := SAttValue( NPC^.SA , 'mecha_colors' ); if mecha_colors = '' then begin Fac := SeekFaction( GB^.Scene , NAttValue( NPC^.NA , NAG_Personal , NAS_FactionID ) ); if Fac <> Nil then mecha_colors := SAttValue( Fac^.SA , 'mecha_colors' ); if mecha_colors = '' then begin mecha_colors := Random_Mecha_Colors; end; SetSAtt( NPC^.SA , 'mecha_colors <' + mecha_colors + '>' ); end; { NEW v0.310- If the storekeeper knows MECHA ENGINEERING, maybe } { modify this mecha! } if ( Random( 2 ) = 1 ) and ( NAttValue( NPC^.NA , NAG_Skill , NAS_MechaEngineering ) > 0 ) then begin ShopkeeperModifyMek( NPC , I ); end; SetSAtt( I^.SA , 'sdl_colors <' + mecha_colors + '>' ); end; { New v0.625- Maybe this item will be on sale! } if ( Random( 20 ) = 1 ) then begin Discount := ( Random( 5 ) + 1 ) * 5; MarkGearsWithNAtt( I , NAG_GearOps , NAS_CostAdjust , -Discount ); MarkGearsWithSAtt( I , SATT_SaleTag + ' <' + ReplaceHash( MSgString( 'SALETAG_Discount' ) , BStr( Discount ) ) + '>' ); end; end; var Wares,I: GearPtr; { List of items for sale. } NPCSeed,NPCRestock,Tolerance,MSR: LongInt; N,ShopRank,ItemPts: Integer; WList,ILink: NAttPtr; Num_Items_By_Rank: Array [1..10] of Integer; begin { Set the random seed to something less than random... } NPCSeed := NAttValue( NPC^.NA , NAG_PErsonal , NAS_RandSeed ); NPCRestock := NAttValue( NPC^.NA , NAG_PErsonal , NAS_RestockTime ); if NPCSeed = 0 then begin NPCSeed := Random( 2000000000 ) + 1; NPCRestock := Random( 86400 ) + 1; SetNAtt( NPC^.NA , NAG_PErsonal , NAS_RandSeed , NPCSeed ); SetNAtt( NPC^.NA , NAG_PErsonal , NAS_RestockTime , NPCRestock ); end; RandSeed := ( ( GB^.ComTime + NPCRestock ) div 86400 ) + NPCSeed; { We've already got everything loaded from disk, in Standard_Equipment_List. } { Create a component list of legal parts. } { Calculate the shopkeeper's tolerance and maximum shop rank. } Tolerance := ShopTolerance( GB , NPC ); MSR := MaxShopRank( NPC ); { Initialize the Num_Items_By_Rank array. } for n := 1 to 10 do Num_Items_By_Rank[n] := 0; { Create a list of potential wares for the shopkeeper. } { Follow the same format as the component list from gearutil.pp: } { G=0, S=ID of the item, V=weight of the item. } { Also, fill out the NumItemsByShoprank array while we're here. } Wares := Nil; WList := Nil; I := Standard_Equipment_List; N := 1; while I <> Nil do begin if IsGoodWares( I , Tolerance ) then begin ShopRank := NAttValue( I^.NA , NAG_GearOps , NAS_ShopRank ); if ( ShopRank >= 1 ) and ( ShopRank <= 10 ) then begin SetNAtt( WList , 0 , N , ShopRank ); Inc( Num_Items_By_Rank[ ShopRank ] ); end else begin DialogMsg( 'ERROR: ' + GearName( I ) + ' has ShopRank ' + BStr( ShopRank ) ); end; end; Inc( N ); I := I^.Next; end; { We've got a random number of points with which to generate items. } ItemPts := 11 + Random( 15 ); while ItemPts > 0 do begin { Select a shop rank. } ShopRank := Random( MSR ) + 1; if ShopRank > 10 then ShopRank := 10; while ( ShopRank < 10 ) and ( Random( 8 ) = 1 ) and ( Num_Items_By_Rank[ ShopRank + 1 ] > 0 ) do begin Inc( ShopRank ); Dec( ItemPts ); end; { Make sure there are items at this shoprank. If not, move down. } while ( ShopRank > 0 ) and ( Num_Items_By_Rank[ ShopRank ] < 1 ) do Dec( ShopRank ); { Select one of the items at random. } if ShopRank > 0 then begin N := Random( Num_Items_By_Rank[ ShopRank ] ) + 1; { Locate the NAtt pointer to this item. } ILink := GetWaresListItem( WList , ShopRank , N ); N := ILink^.S; RemoveNAtt( WList , ILink ); Dec( Num_Items_By_Rank[ ShopRank ] ); { Clone it, initialize it, and add it to the list. } I := CloneGear( RetrieveGearSib( Standard_Equipment_List , N ) ); InitShopItem( I ); AppendGear( Wares , I ); { If this is a mecha, ItemPts will run out faster. } if I^.G = GG_Mecha then Dec( ItemPts ); end; Dec( ItemPts ); end; { Get rid of the shopping list. } DisposeNAtt( WList ); { Re-randomize the random seed. } Randomize; { Return the list we've created. } CreateWaresList := Wares; end; Procedure BrowseWares( GB: GameBoardPtr; PC,NPC: GearPtr; Wares: GearPtr ); { Take a look through the items this NPC has for sale. } { First, construct the shop list. Then, browse each item, } { potentially buying whichever one strikes your fancy. } var RPM: RPGMenuPtr; { Buying menu. } I: GearPtr; N: Integer; msg,msg2: String; begin { Create the browsing menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); I := Wares; N := 1; while I <> Nil do begin msg := FullGearName( I ); { Add extra information, depending upon item type. } if I^.G = GG_Weapon then begin msg := msg + ' (DC:' + BStr( ScaleDC( I^.V , I^.Scale ) ) + ')'; end else if ( I^.G = GG_ExArmor ) or ( I^.G = GG_Shield ) then begin msg := msg + ' [AC:' + BStr( GearMaxArmor( I ) ) + ']'; end else if I^.G = GG_Ammo then begin msg := msg + ' (' + BStr( I^.Stat[ STAT_AmmoPresent ] ) + ')'; end else if I^.G = GG_Software then begin msg := 'SW: ' + msg; end; { Add extra information, depending upon item scale. } if ( I^.G <> GG_Mecha ) and ( I^.Scale > 0 ) then begin msg := msg + '(SF' + BStr( I^.Scale ) + ')'; end; { Add the sale tag, if it exists. } msg2 := SAttValue( I^.SA , SATT_SALETAG ); if msg2 <> '' then msg := msg + ' (' + msg2 + ')'; { Pad the message. } {$IFDEF ASCII} while Length( msg + ' $' + BStr( PurchasePrice( GB , PC , NPC , I ) ) ) < ( ZONE_ShopMenu.W - 5 ) do msg := msg + ' '; {$ELSE} while TextLength( GAME_FONT , ( msg + ' $' + BStr( PurchasePrice( GB , PC , NPC , I ) ) ) ) < ( ZONE_ShopMenu.W - 50 ) do msg := msg + ' '; {$ENDIF} { Add it to the menu. } AddRPGMenuItem( RPM , msg + ' $' + BStr( PurchasePrice( GB , PC , NPC , I ) ) , N ); Inc( N ); I := I^.Next; end; RPMSortAlpha( RPM ); { Error check - if for some reason we are left with a blank } { menu, better leave this procedure. } if RPM^.NumItem < 1 then begin DisposeRPGMenu( RPM ); Exit; end; RPM^.Mode := RPMNoCleanup; Repeat SERV_Menu := RPM; SERV_Info := Wares; { Display the trading stats. } N := SelectMenu( RPM , @ServiceRedraw ); if N > 0 then begin PurchaseGearMenu( GB , PC , NPC , RetrieveGearSib( Wares , N ) ); end; until N = -1; SERV_Menu := Nil; SERV_Info := Nil; DisposeRPGMenu( RPM ); end; Procedure SellStuff( GB: GameBoardPtr; PCInv,PCChar,NPC: GearPtr; const Categories: String ); { The player wants to sell some items to this NPC. } { PCInv points to the team-1 gear whose inventory is to be sold. } { PCChar points to the actual player character. } var RPM: RPGMenuPtr; MI,N: Integer; Part : GearPtr; begin MI := 1; repeat { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); RPM^.Mode := RPMNoCleanup; BuildInventoryMenu( RPM , PCInv , True ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Exit' ) , -1 ); SetItemByPosition( RPM , MI ); { Get a choice from the menu, then record the current item } { number. } SERV_Menu := RPM; SERV_Info := PCInv; N := SelectMenu( RPM , @SellStuffRedraw ); MI := RPM^.SelectItem; { Dispose of the menu. } DisposeRPGMenu( RPM ); { If N is positive, prompt to sell that item. } if N > -1 then begin Part := LocateGearByNumber( PCInv , N ); SellGear( Part^.Parent^.InvCom , Part , PCChar , NPC , Categories ); end; until N = -1; SERV_Menu := Nil; SERV_Info := Nil; end; Function ShopkeeperCanRepairMecha( NPC: GearPtr ): Boolean; { Return TRUE if the shopkeeper can repair mecha, or FALSE otherwise. } begin ShopkeeperCanRepairMecha := ( NPC <> Nil ) and ( NAttValue( NPC^.NA , NAG_Skill , NAS_Repair ) > 5 ); end; Procedure ThisMechaWasSelected( GB: GameBoardPtr; MekNum: Integer; PC,NPC: GearPtr ); { Do all the standard shopping options with this mecha. } { IMPORTANT: A mecha can only be sold if it's not currently on the map! } { Otherwise, the PC could potentially sell himself if in the cockpit... } var RPM: RPGMenuPtr; Mek: GearPtr; N: Integer; Cost: LongInt; begin { Find the mecha. } Mek := RetrieveGearSib( GB^.Meks , MekNum ); repeat { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); { Add options, depending on the mek. } if not OnTheMap( GB , Mek ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_Sell' ) + GearName( Mek ) , 1 ); if ShopkeeperCanRepairMecha( NPC ) then begin Cost := RepairMasterByModeCost( Mek , RM_MechaRepair ); if Cost > 0 then begin AddRPGMenuItem( RPM , MsgString( 'SERVICES_DoMechaRepair' ) + ' [$' + BStr( ScalePrice( GB , PC , NPC , Cost ) ) + ']' , 2 ); end; end; AddRPGMenuItem( RPM , MsgString( 'SERVICES_SellMekInv' ) , 4 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_BrowseParts' ) , 3 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Exit' ) , -1 ); SERV_Menu := Nil; SERV_Info := Mek; N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); if N = 1 then begin { Sell the mecha. } if SellGear( GB^.Meks , Mek , PC , NPC , '' ) then N := -1; end else if N = 2 then begin { Repair the mecha. } RepairOneFrontEnd( GB , Mek , PC , NPC , RM_MechaRepair ); end else if N = 3 then begin { Use the parts browser. } MechaPartBrowser( Mek , @ServiceRedraw ); end else if N = 4 then begin { Sell items. } SellStuff( GB , Mek , PC , NPC , '' ); end; until N = -1; SERV_Info := Nil; end; Function CreateMechaMenu( GB: GameBoardPtr ): RPGMenuPtr; { Create a menu listing all the Team1 meks on the board. } var RPM: RPGMenuPtr; N: Integer; Mek: GearPtr; msg,msg2: String; begin { Allocate a menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); { Add each mek to the board. } N := 1; Mek := GB^.Meks; while Mek <> Nil do begin { If this gear is a mecha, and it belongs to the PC, } { add it to the menu. } if ( NAttValue( Mek^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and not GearActive( Mek ) then begin msg := TeamMateName( Mek ); msg2 := SAttValue( Mek^.SA , SATT_SALETAG ); if msg2 <> '' then msg := msg + ' (' + msg2 + ')'; AddRPGMenuItem( RPM , msg , N ); end; Inc( N ); Mek := Mek^.Next; end; RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Exit' ) , -1 ); CreateMechaMenu := RPM; end; Procedure BrowseMecha( GB: GameBoardPtr; PC,NPC: GearPtr ); { The Player is going to take a look through his mecha list, maybe } { sell some of them, maybe repair some of them... } var RPM: RPGMenuPtr; N: Integer; begin repeat { Create the browsing menu. } RPM := CreateMechaMenu( GB ); { Select an item from the menu, then get rid of the menu. } SERV_Info := GB^.Meks; SERV_Menu := RPM; N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); { If a mecha was selected, take a look at it. } if N > 0 then begin ThisMechaWasSelected( GB , N , PC , NPC ); end; until N = -1; SERV_Info := Nil; SERV_Menu := Nil; end; Procedure InstallCyberware( GB: GameBoardPtr; PC , NPC: GearPtr ); { The NPC will attempt to install cyberware into the PC. } { - The PC will select which item to install. } { - If appropriate, the PC will select where to install. } { - NPC will make rolls to reduce trauma rating of part. } { - Time will be advanced by 6 hours. } { - Part will be transferred and installed. } Procedure ClearCyberSlot( Slot,Item: GearPtr ); { Clear any items currently using ITEM's CyberSlot } { from Slot's list of subcomponents. } var SC,SC2: GearPtr; CyberSlot: String; begin CyberSlot := UpCase( SAttValue( Item^.SA , SAtt_CyberSlot ) ); if CyberSlot <> '' then begin SC := Slot^.SubCom; while SC <> Nil do begin SC2 := SC^.Next; if UpCase( SAttValue( SC^.SA , SAtt_CyberSlot ) ) = CyberSlot then begin RemoveGear( Slot^.SubCom , SC ); end; SC := SC2; end; end; end; var RPM: RPGMenuPtr; N: Integer; Item,Slot: GearPtr; Procedure CreateCyberMenu; { Check through PC's inventory, adding items which bear } { the "CYBER" tag to the menu. } var Part: GearPtr; begin Part := LocatePilot( PC )^.InvCom; while Part <> Nil do begin if ( Part^.G = GG_Modifier ) and ( Part^.V = GV_CharaModifier ) then begin AddRPGMenuItem( RPM , GearName( Part ) , FindGearIndex( PC , Part ) ); end; Part := Part^.Next; end; end; Function WillingToPay: Boolean; { The name is a bit misleading. This function checks to } { see if the PC can pay, then if the PC agrees to the } { price will then take his money. } var Cost: LongInt; begin Cost := SkillAdvCost( Nil , NAttValue( NPC^.NA , NAG_Skill , NAS_Medicine ) ) * 2; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Cyber_Pay_Yes' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Cyber_Pay_No' ) , -1 ); CHAT_Message := ReplaceHash( MsgString( 'SERVICES_Cyber_Pay' ) , BStr( Cost ) ); N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); if N = 1 then begin if NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) >= Cost then begin AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , -Cost ); WillingToPay := True; end else begin WillingToPay := False; end; end else begin WillingToPay := False; end; end; Procedure PerformInstallation; { Actually stick the part into the PC. } var SkRoll,Trauma: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Cyber_WaitPrompt' ) , -1 ); ClearCyberSlot( Slot , Item ); DelinkGear( Item^.Parent^.InvCom , Item ); InsertSubCom( Slot , Item ); if GB <> Nil then QuickTime( GB , 7200 + Random( 3600 ) ); AddStaminaDown( PC , Random( 8 ) + Random( 8 ) + Random( 8 ) + 3 ); AddMentalDown( PC , Random( 8 ) + Random( 8 ) + Random( 8 ) + 3 ); ApplyCyberware( LocatePilot( PC ) , Item ); { Add some cyberdisfunction points now. } SkRoll := RollStep( SkillValue( NPC , NAS_Medicine , STAT_Knowledge ) ); if SkRoll < 1 then SkRoll := 1; Trauma := ( TraumaValue( Item ) * 10 ) div SkRoll; AddNAtt( FindMaster( Slot )^.NA , NAG_Condition , NAS_CyberTrauma , Trauma ); CHAT_Message := MsgString( 'SERVICES_Cyber_Wait' ); N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); CHAT_Message := MsgString( 'SERVICES_Cyber_Done' + BStr( Random( 3 ) + 1 ) ); DialogMsg( ReplaceHash( MsgString( 'SERVICES_Cyber_Confirmation' ) , GearName( Item ) ) ); end; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); CreateCyberMenu; if RPM^.NumItem > 0 then begin CHAT_Message := MsgString( 'SERVICES_Cyber_SelectPart' ); N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); if N > 0 then begin Item := LocateGearByNumber( PC , N ); if Item <> Nil then begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); BuildSubMenu( RPM , PC , Item , False ); if RPM^.NumItem = 1 then begin Slot := LocateGearByNumber( PC , RPM^.FirstItem^.Value ); end else if RPM^.NumItem > 1 then begin CHAT_Message := MsgString( 'SERVICES_Cyber_SelectSlot' ); N := SelectMenu( RPM , @ServiceRedraw ); if N > 0 then begin Slot := LocateGearByNumber( PC , N ); end else begin Slot := Nil; end; end else begin Slot := Nil; end; DisposeRPGMenu( RPM ); if Slot <> Nil then begin if WillingToPay then begin PerformInstallation; end else begin CHAT_Message := MsgString( 'SERVICES_Cyber_Cancel' ); end; end else begin CHAT_Message := MsgString( 'SERVICES_Cyber_Cancel' ); end; end; end else begin CHAT_Message := MsgString( 'SERVICES_Cyber_Cancel' ); end; end else begin CHAT_Message := MsgString( 'SERVICES_Cyber_NoPart' ); DisposeRPGMenu( RPM ); end; end; Procedure OpenShop( GB: GameBoardPtr; PC,NPC: GearPtr; Stuff: String ); { Let the shopping commence! This procedure is called when } { a conversation leads to a transaction... This is the top } { level of the shopping menu, and should offer the following } { choices: } { - Browse Wares } { - Repair All / Treat Injuries (depening on NPC skills) } { - Reload All (if this is a weapon shop) } { - Take a look at this... (to sell/repair/reload items in Inv) } var RPM: RPGMenuPtr; Wares: GearPtr; N: Integer; Cost,C1,C2: LongInt; begin SERV_GB := GB; SERV_NPC := NPC; SERV_PC := PC; { Gather up all the PC's mechas and salvage. } GatherFieldHQ( GB ); { Generate the list of stuff in the store. } Wares := CreateWaresList( GB , NPC , Stuff ); repeat { Start by allocating the menu. } { This menu will use the same dimensions as the interaction } { menu, since it branches from there. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); { Add the basic options. } if Wares <> Nil then AddRPGMenuItem( RPM , 'Browse Wares' , 0 ); { Add options for each of the repair skills. } if NAttValue( NPC^.NA , NAG_Skill , NAS_Medicine ) > 0 then begin Cost := RepairAllCost( GB , RM_Medical ); if Cost > 0 then begin AddRPGMenuItem( RPM , MsgString( 'SERVICES_DoMedicalRepair' ) + ' [$' + BStr( ScalePrice( GB , PC , NPC , Cost ) ) + ']' , RM_Medical ); end; end; if NAttValue( NPC^.NA , NAG_Skill , NAS_Repair ) > 0 then begin Cost := RepairAllCost( GB , RM_GeneralRepair ); if Cost > 0 then begin AddRPGMenuItem( RPM , MsgString( 'SERVICES_DoGeneralRepair' ) + ' [$' + BStr( ScalePrice( GB , PC , NPC , Cost ) ) + ']' , RM_GeneralRepair ); end; { If the shopkeeper knows Basic Repair, allow Reload Chars. } C1 := ReloadCharsCost( GB , PC , NPC , False ); C2 := ReloadCharsCost( GB , PC , NPC , True ); if ( C1 > 0 ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_ReloadCharsPrompt' ) + ' [$' + BStr( C1 ) + ']' , -4 ); if C2 > C1 then AddRPGMenuItem( RPM , MsgString( 'SERVICES_ReloadChars+Prompt' ) + ' [$' + BStr( C2 ) + ']' , -11 ); if ShopkeeperCanRepairMecha( NPC ) then begin Cost := RepairAllCost( GB , RM_MechaRepair ); if Cost > 0 then begin AddRPGMenuItem( RPM , MsgString( 'SERVICES_DoMechaRepair' ) + ' [$' + BStr( ScalePrice( GB , PC , NPC , Cost ) ) + ']' , RM_MechaRepair ); end; { If the shopkeeper knows Mecha Repair, allow reload mecha. } C1 := ReloadMechaCost( GB , PC , NPC , False ); C2 := ReloadMechaCost( GB , PC , NPC , True ); if ( C1 > 0 ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_ReloadMeksPrompt' ) + ' [$' + BStr( C1 ) + ']' , -3 ); if C2 > C1 then AddRPGMenuItem( RPM , MsgString( 'SERVICES_ReloadMeks+Prompt' ) + ' [$' + BStr( C2 ) + ']' , -10 ); end; end; { Also if the shopkeeper knows Basic Repair, allow recharging of batteries. } if ( RechargeCost( GB , PC , NPC ) > 0 ) and ( NAttValue( NPC^.NA , NAG_Skill , NAS_Repair ) > 0 ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_RechargePrompt' ) + ' [$' + BStr( RechargeCost( GB , PC , NPC ) ) + ']' , -9 ); if AStringHasBString( Stuff, 'DELIVERY' ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_ExpressDelivery' ) , -8 ); { If the shopkeeper knows Cybertech, allow the implantation } { of modules. } if (( NAttValue( NPC^.NA , NAG_Skill , NAS_Medicine ) > 0 ) and ( NAttValue( NPC^.NA , NAG_Skill , NAS_Science ) > 0 )) or AStringHasBString( Stuff , 'BodyMod' ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_CybInstall' ) , -7 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_SellStuff' ) , -5 ); if AStringHasBString( Stuff, 'MECHA' ) or ShopkeeperCanRepairMecha( NPC ) then AddRPGMenuItem( RPM , MsgString( 'SERVICES_MechaService' ) , -2 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Inventory' ) , -6 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_Exit' ) , -1 ); { Display the trading stats. } N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); if N > 0 then begin RepairAllFrontEnd( GB , PC , NPC , N ); end else if N = 0 then begin BrowseWares( GB, PC , NPC , Wares ); end else if N = -2 then begin BrowseMecha( GB , PC , NPC ); end else if N = -3 then begin DoReloadMecha( GB , PC , NPC , False ); end else if N = -4 then begin DoReloadChars( GB , PC , NPC , False ); end else if N = -5 then begin SellStuff( GB , PC , PC , NPC , Stuff ); end else if N = -6 then begin BackpackMenu( GB , PC , True , @ServicesBackpackRedraw ); end else if N = -7 then begin InstallCyberware( GB , PC , NPC ); end else if N = -8 then begin ExpressDelivery( GB , PC , NPC ); end else if N = -9 then begin DoRecharge( GB , PC , NPC ); end else if N = -10 then begin DoReloadMecha( GB , PC , NPC , True ); end else if N = -11 then begin DoReloadChars( GB , PC , NPC , True ); end; until N = -1; DisposeGear( Wares ); end; Procedure OpenSchool( GB: GameBoardPtr; PC,NPC: GearPtr; Stuff: String ); { Let the teaching commence! I was thinking, at first, of } { including skill training as a sub-bit of the shopping procedure, } { but abandoned this since I'd like a bit more control over } { the process. } { The going rate for training is $100 = 1XP. } { This rate is not affected by Shopping skill, though a good } { reaction score with the teacher can increase the number of XP } { gained. } const XPStep: Array [1..40] of Integer = ( 1,2,3,4,5, 6,7,8,9,10, 12,15,20,25,50, 75,100,150,200,250, 500,750,1000,1500,2000, 2500,3000,3500,4000,4500, 5000,6000,7000,8000,9000, 10000,12500,15000,20000,25000 ); Knowledge_First_Bonus = 14; Knowledge_First_Penalty = 8; var SkillMenu,CostMenu: RPGMenuPtr; Skill,N: Integer; Cash: LongInt; DSLTemp: Boolean; begin SERV_GB := GB; SERV_NPC := NPC; SERV_PC := PC; { When using a school, can always learn directly. } DSLTemp := Direct_Skill_Learning; Direct_Skill_Learning := True; { Step One: Create the skills menu. } SkillMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AttachMenuDesc( SkillMenu , ZONE_ItemsInfo ); while Stuff <> '' do begin N := ExtractValue( Stuff ); if ( N >= 1 ) and ( N <= NumSkill ) then begin AddRPGMenuItem( SkillMenu , MsgString( 'SKILLNAME_' + BStr( N ) ) , N , SkillDescription( N ) ); end; end; RPMSortAlpha( SkillMenu ); AddRPGMenuItem( SkillMenu , MsgString( 'SCHOOL_Exit' ) , -1 ); repeat { Get a selection from the menu. } Skill := SelectMenu( SkillMenu , @ServiceRedraw ); { If a skill was chosen, do the training. } if ( Skill >= 1 ) and ( Skill <= NumSkill ) then begin { Create the CostMenu, and see how much the } { player wants to spend. } CostMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); Cash := NAttValue( PC^.NA , NAG_Experience , NAS_Credits ); { Add menu entries for each of the cost values } { that the PC can afford. } for N := 1 to 40 do begin if XPStep[ N ] * Credits_Per_XP <= Cash then begin AddRPGMenuItem( CostMenu , '$' + BStr( XPStep[ N ] * Credits_Per_XP ) , N ); end; end; { Add the exit option, so that we'll never have } { an empty menu. } AddRPGMenuItem( CostMenu , MsgString( 'SCHOOL_ExitCostSelector' ) , -1 ); Chat_Message := MsgString( 'SCHOOL_HowMuch' ); N := SelectMenu( CostMenu , @ServiceRedraw ); DisposeRPGMenu( CostMenu ); { If CANCEL wasn't selected, take away the cash } { and give the PC some experience. } if N <> -1 then begin CHAT_Message := MsgString( 'SCHOOL_TeachingInProgress' ); AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , -( XPStep[ N ] * Credits_Per_XP ) ); { Calculate the number of XPs earned. } if NPC <> Nil then begin Cash := ( XPStep[ N ] * ( 400 + ReactionScore( GB^.Scene , PC , NPC ) ) ) div 400; end else begin Cash := XPStep[ N ]; end; { Add bonus for high Knowledge stat, } { or penalty for low Knowledge stat. } if CStat( PC , STAT_Knowledge ) >= Knowledge_First_Bonus then begin Cash := ( Cash * ( 100 + ( CStat( PC , STAT_Knowledge ) - Knowledge_First_Bonus + 1 ) * 5 ) ) div 100; end else if CStat( PC , STAT_Knowledge ) <= Knowledge_First_Penalty then begin Cash := ( Cash * ( 100 - ( Knowledge_First_Penalty - CStat( PC , STAT_Knowledge ) + 1 ) * 10 ) ) div 100; if Cash < 1 then Cash := 1; end; if DoleSkillExperience( PC , Skill , Cash ) then begin DialogMsg( MsgString( 'SCHOOL_Learn' + BStr( Random( 5 ) + 1 ) ) ); end; { Training takes time. } while ( N > 0 ) and ( GB <> Nil ) do begin QuickTime( GB , 100 + Random( 100 ) ); Dec( N ); end; end; end; until Skill = -1; { Restore the Direct_Skill_Learning setting. } Direct_Skill_Learning := DSLTemp; DisposeRPGMenu( SkillMenu ); end; Procedure FillExpressMenu( GB: GameBoardPtr; RPM: RPGMenuPtr ); { Search through the world for gears belonging to the PC. } var N: Integer; CurrentCity,World: GearPtr; { PROCEDURES BLOCK } Function FXMRootScene( Part: GearPtr ): GearPtr; { Find the root scene of this part, assuming it's in a regular scene and not } { on the gameboard or anywhere strange. } begin while ( Part <> Nil ) and not ( ( Part^.Parent <> Nil ) and ( Part^.Parent^.G <> GG_Scene ) ) do begin Part := Part^.Parent; end; FXMRootScene := Part; end; Procedure CheckAlongPath( Part: GearPtr; AddToMenu: Boolean ); { CHeck along the path specified. } begin while Part <> Nil do begin Inc(N); if ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) and AddToMenu then AddRPGMenuItem( RPM , FullGearName( Part ) + ' (' + GearName( FXMRootScene( Part ) ) + ')' , N ); if Part = CurrentCity then begin { Don't add parts from the current location. } CheckAlongPath( Part^.InvCom , False ); CheckAlongPath( Part^.SubCom , False ); end else begin CheckAlongPath( Part^.InvCom , AddToMenu ); CheckAlongPath( Part^.SubCom , AddToMenu ); end; Part := Part^.Next; end; end; begin N := 0; CurrentCity := FindRootScene( GB^.Scene ); World := FindWorld( GB , GB^.Scene ); CheckAlongPath( World^.InvCom , True ); CheckAlongPath( World^.SubCom , True ); end; { FillExpressMenu } Function DeliveryCost( Mek: GearPtr ): LongInt; { Return the cost to deliver this mecha from one location } { to the next. Cost is determined by mass. } var C,T: LongInt; begin { Base value is the mass of the mek. } C := GearMass( Mek ); { This gets multiplied upwards as the mass of the mecha increases. } for t := 1 to Mek^.Scale do C := C * 5; { Return the finished cost. } DeliveryCost := C; end; Procedure ExpressDelivery( GB: GameBoardPtr; PC,NPC: GearPtr ); { The PC needs some mecha delivered from out of town. } { Better search the entire adventure and find every mecha } { belonging to the PC. } var RPM: RPGMenuPtr; N: Integer; Mek: GearPtr; Cost: LongInt; begin SERV_GB := GB; SERV_NPC := NPC; SERV_PC := PC; repeat RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); FillExpressMenu( GB , RPM ); RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); if N > -1 then begin Mek := LocateGearByNumber( FindWorld( GB , GB^.Scene ) , N ); if Mek <> Nil then begin Cost := ScalePrice( GB , PC , NPC , DeliveryCost( Mek ) ); RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AddRPGMenuItem( RPM , ReplaceHash( MsgString( 'SERVICES_MoveYes' ) , GearName( Mek ) ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'SERVICES_MoveNo' ) , -1 ); Chat_Message := ReplaceHash( MsgString( 'SERVICES_MovePrompt' + BStr( Random( 3 ) + 1 ) ) , BStr( Cost ) ); N := SelectMenu( RPM , @ServiceRedraw ); DisposeRPGMenu( RPM ); if N = 1 then begin { The PC wants to move this mecha. } if NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) >= Cost then begin AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , -Cost ); if IsInvCom( Mek ) then begin DelinkGear( Mek^.Parent^.InvCom , Mek ); end else if IsSubCom( Mek ) then begin DelinkGear( Mek^.Parent^.SubCom , Mek ); end; DeployGear( GB , Mek , False ); Chat_Message := MsgString( 'SERVICES_MoveDone' + BStr( Random( 3 ) + 1 ) ); end else begin Chat_Message := MsgString( 'SERVICES_MoveNoCash' ); end; end; N := 0; end; end; until N = -1; end; Procedure ShuttleService( GB: GameBoardPtr; PC,NPC: GearPtr ); { The PC will be able to travel to a number of different cities. } function FindLocalGate( World: GearPtr; SceneID: Integer ): GearPtr; { This is a nice simple non-recursive list search, } { since the gate should be at root level. } var Part,TheGate: GearPtr; begin Part := World^.InvCom; TheGate := Nil; while ( Part <> Nil ) and ( TheGate = Nil ) do begin if ( Part^.G = GG_MetaTerrain ) and ( Part^.Stat[ STAT_Destination ] = SceneID ) then begin TheGate := Part; end; Part := Part^.Next; end; FindLocalGate := TheGate; end; Function WorldMapRange( World: GearPtr; X0,Y0,X1,Y1: Integer ): Integer; begin if WorldWrapsX( World ) and ( Abs( X0 - X1 ) > ( World^.Stat[ STAT_MapWidth ] div 2 ) ) then begin if X1 > X0 then begin X1 := X1 - World^.Stat[ STAT_MapWidth ]; end else begin X0 := X0 - World^.Stat[ STAT_MapWidth ]; end; end; if WorldWrapsY( World ) and ( Abs( Y1 - Y0 ) > ( World^.Stat[ STAT_MapHeight ] div 2 ) ) then begin if Y1 > Y0 then begin Y1 := Y1 - World^.Stat[ STAT_MapHeight ]; end else begin Y0 := Y0 - World^.Stat[ STAT_MapHeight ]; end; end; WorldMapRange := Range( X0 , Y0 , X1 , Y1 ); end; Function TravelCost( World,Entrance: GearPtr; X0 , Y0: Integer ): LongInt; { Calculate the travel cost from the original location to the } { destination city. } var X1,Y1: Integer; begin if Entrance = Nil then begin TravelCost := 50000; end else begin { Determine the X,Y coords of the destination on the world map. } { If the map is a wrapping-type map, maybe modify for the shortest } { possible distance. } X1 := NAttValue( Entrance^.NA , NAG_Location , NAS_X ); Y1 := NAttValue( Entrance^.NA , NAG_Location , NAS_Y ); TravelCost := WorldMapRange( World , X1 , Y1 , X0 , Y0 ) * 200 + 250; end; end; const MaxShuttleRange = 150; var World,City,Fac,Entrance: GearPtr; X0,Y0,N,Cost: LongInt; RPM: RPGMenuPtr; begin SERV_GB := GB; SERV_NPC := NPC; SERV_PC := PC; { Create a shopping list of the available scenes. These must not be } { enemies of the current scene, must be located on the same world, } { must be within a certain range, and must have "DESTINATION" in their } { TYPE string attribute. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ShopMenu ); AttachMenuDesc( RPM , ZONE_ItemsInfo ); World := FindWorld( GB , GB^.Scene ); City := World^.SubCom; Entrance := FindLocalGate( World , FindRootScene( GB^.Scene )^.S ); if Entrance <> Nil then begin X0 := NAttValue( Entrance^.NA , NAG_Location , NAS_X ); Y0 := NAttValue( Entrance^.NA , NAG_Location , NAS_Y ); end else begin X0 := 1; Y0 := 1; end; Fac := SeekFaction( GB^.Scene , GetFactionID( FindRootScene( GB^.Scene ) ) ); while City <> Nil do begin { Do the faction check. } if ( City <> FindRootScene( GB^.Scene ) ) and ( ( Fac = Nil ) or ( NAttValue( Fac^.NA , NAG_FactionScore , GetFactionID( City ) ) >= 0 ) ) then begin { Do the range check. } Entrance := FindLocalGate( World , City^.S ); if AStringHasBString( SAttValue( City^.SA , 'TYPE' ) , 'DESTINATION' ) then begin AddRPGMenuItem( RPM , GearName( City ) + ' ($' + BStr( TravelCost( World, Entrance , X0 , Y0 ) ) + ')' , City^.S , SAttValue( City^.SA , 'DESC' ) ); end; end; City := City^.Next; end; { Sort the menu. } RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); { Add the cancel option. } AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); repeat { Perform the menu selection. } N := SelectMenu( RPM , @ServiceRedraw ); { If a destination was selected, see if it's possible to go there, deduct the PC's } { money, etc. } if N > -1 then begin Entrance := FindLocalGate( World , N ); Cost := TravelCost( World , Entrance , X0 , Y0 ); if NAttValue( PC^.NA , NAG_Experience , NAS_Credits ) >= Cost then begin GB^.QuitTheGame := True; GB^.ReturnCode := N; AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , -Cost ); TransitTime( GB , Cost * 10 ); end else begin { Not enough cash to buy... } CHAT_Message := MsgString( 'BUYNOCASH' + BStr( Random( 4 ) + 1 ) ); end; end; until GB^.QuitTheGame or ( N = -1 ); DisposeRPGMenu( RPM ); end; initialization SERV_GB := Nil; SERV_NPC := Nil; Standard_Caliber_List := AggregatePattern( 'CALIBER_*.txt' , Data_Directory ); finalization DisposeGear( Standard_Caliber_List ); end. GH2/glmenus.pp0000644000175000017500000005717211365256066012135 0ustar kaolkaolunit glmenus; {$MODE FPC} { BUGS - If SelectMenu is handed an empty menu, all heck will } { break loose. This can be a particular problem for SelectFile. } { 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 sdl,sdl_ttf,dos,texutil,ui4gh, {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} const {These two constants are used to tell the SELECT procedure whether or not} {the user is allowed to cancel.} RPMNormal = 0; RPMNoCancel = 1; RPMEscCancel = 2; { Menu can be cancelled, but not by mouse click. } RPMNoCleanup = 2; {If you want the menu left on the screen after we've finished, use this.} type RPGMenuKeyPtr = ^RPGMenuKey; RPGMenuKey = Record k: Char; value: integer; {The value returned when this key is pressed.} next: RPGMenuKeyPtr; end; RPGMenuItemPtr = ^RPGMenuItem; RPGMenuItem = Record msg: string; {The text which appears in the menu} value: integer; {A value, returned by SelectMenu. -1 is reserved for Cancel} desc: string; {Pointer to the item description. If Nil, no desc.} next: RPGMenuItemPtr; end; RPGMenu = Record active: boolean; itemcolor,selcolor,dtexcolor: TSDL_Color; Menu_Zone,Desc_Zone: TSDL_Rect; mode: Byte; topitem,selectitem,numitem: integer; {fields holding info about the status of the menu.} FirstItem: RPGMenuItemPtr; FirstKey: RPGMenuKeyPtr; end; RPGMenuPtr = ^RPGMenu; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer; const desc: string): RPGMenuItemPtr; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer): RPGMenuItemPtr; Procedure DisposeRPGMenuItem( var LList: RPGMenuItemPtr ); Procedure ClearMenu( RPM: RPGMenuPtr ); Procedure RemoveRPGMenuItem(RPM: RPGMenuPtr; var LMember: RPGMenuItemPtr); Procedure AddRPGMenuKey(RPM: RPGMenuPtr; k: Char; value: Integer); Function CreateRPGMenu(icolor,scolor: TSDL_Color; Z: TSDL_Rect): RPGMenuPtr; Procedure AttachMenuDesc( RPM: RPGMenuPtr; Z: TSDL_Rect ); Procedure DisposeRPGMenu(var RPM: RPGMenuPtr); Procedure DisplayMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ); Function RPMLocateByPosition(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; Function RPMLocateByValue(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; Function SelectMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): integer; Procedure RPMSortAlpha(RPM: RPGMenuPtr); Function CurrentMenuItemValue( RPM: RPGMenuPtr ): Integer; Function SetItemByValue( RPM: RPGMenuPtr ; V: Integer ): RPGMenuItemPtr; Procedure SetItemByPosition( RPM: RPGMenuPtr ; N: Integer ); Procedure BuildFileMenu( RPM: RPGMenuPtr; const SearchPattern: String ); Function SelectFile( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): String; implementation Function LastMenuItem(MIList: RPGMenuItemPtr): RPGMenuItemPtr; {This procedure will find the last item in the linked list.} begin {While the menu item is pointing to a next menu item, it's not the last. duh.} {So, move through the list until we hit a Nil pointer.} while MIList^.next <> Nil do begin {Check the next one.} MIList := MIList^.next; end; {We've found a MI.next = Nil. Yay! Return its address.} LastMenuItem := MIList; end; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer; const desc: string): RPGMenuItemPtr; {This procedure will add an item to the RPGToolMenu.} {The new item will be added as the last item in the list.} var it: ^RPGMenuItem; {Here's a pointer for the item we're creating.} temp: RPGMenuItemPtr; begin {Allocate memory for it.} New(it); {Check to make sure that the allocation succeeded.} if it = Nil then begin exit( Nil ); end; {Initialize it to the correct values.} it^.msg := msg; it^.value := value; it^.next := Nil; it^.desc := desc; {The desc field is assigned the value of PChar since it} {is assumed that we arent responsible for the allocation,} {disposal, or contents of this string.} {Locate the last item in the list, then assign "it" to it.} {If the list is currently empty, stick "it" in as the first item.} if RPM^.firstitem = Nil then begin RPM^.firstitem := it; end else begin temp := LastMenuItem(RPM^.FirstItem); temp^.next := it; end; {Increment the NumItem field.} Inc(RPM^.numitem); AddRPGMenuItem := it; end; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer): RPGMenuItemPtr; { Just like the above, but no desc. } begin AddRPGMenuItem := AddRPGMenuItem( RPM , msg , value , '' ); end; Procedure DisposeRPGMenuItem( var LList: RPGMenuItemPtr ); { Get rid of this list of items. } { WARNING - If you call this procedure for a menu, it will not } { change the value of the NumItems field!!! This will cause } { problems when trying to use the menu. Unless you know exactly } { what you're doing, use the ClearMenu procedure instead. } var NextItem: RPGMenuItemPtr; begin while LList <> Nil do begin NextItem := LList^.Next; Dispose( LList ); LList := NextItem; end; end; Procedure ClearMenu( RPM: RPGMenuPtr ); { Deallocate all the items in this menu, and set the number of } { items to 0. } begin DisposeRPGMenuItem( RPM^.FirstItem ); RPM^.NumItem := 0; RPM^.SelectItem := 1; RPM^.topitem := 1; end; Procedure RemoveRPGMenuItem(RPM: RPGMenuPtr; var LMember: RPGMenuItemPtr); {Locate and extract member LMember from list LList.} {Then, dispose of LMember.} var a,b: RPGMenuItemPtr; begin { Make sure LMember isn't Nil } if LMember = Nil then Exit; {Initialize A and B} B := RPM^.FirstItem; A := Nil; {Locate LMember in the list. A will thereafter be either Nil,} {if LMember if first in the list, or it will be equal to the} {element directly preceding LMember.} while (B <> LMember) and (B <> Nil) do begin A := B; B := B^.next; end; if B = Nil then begin {Major FUBAR. The member we were trying to remove can't} {be found in the list.} writeln('ERROR- RemoveLink asked to remove a link that doesnt exist.'); end else if A = Nil then begin {There's no element before the one we want to remove,} {i.e. it's the first one in the list.} RPM^.FirstItem := B^.Next; Dispose(B); end else begin {We found the attribute we want to delete and have another} {one standing before it in line. Go to work.} A^.next := B^.next; Dispose(B); end; { Reduce the number of items in the menu. } Dec(RPM^.NumItem); end; Procedure AddRPGMenuKey(RPM: RPGMenuPtr; k: Char; value: Integer); {Add a dynamically defined RPGMenuKey to the menu.} var it: RPGMenuKeyPtr; begin New(it); if it = Nil then begin exit; end; {Initialize the values.} it^.k := k; it^.value := value; it^.next := RPM^.FirstKey; RPM^.FirstKey := it; end; Function CreateRPGMenu(icolor,scolor: TSDL_Color; Z: TSDL_Rect): RPGMenuPtr; {This function creates a new RPGMenu record, and returns the address.} var it: ^RPGMenu; {Here's a pointer for the menu we're making.} begin {Allocate memory for it.} New(it); {Check to make sure that we've actually initialized something.} if it = Nil then exit( Nil ); {Initialize the elements of the record.} it^.itemcolor := icolor; it^.selcolor := scolor; it^.Menu_Zone := Z; it^.Desc_Zone.W := 0; {A width value of 0 means there is no desc window.} it^.Mode := RPMNormal; it^.FirstItem := Nil; it^.FirstKey := Nil; it^.dtexcolor := icolor; it^.active := False; {TopItem refers to the highest item on the screen display.} it^.topitem := 1; {SelectItem refers to the item currently being pointed at by the selector.} it^.selectitem := 1; {NumItem refers to the total number of items currently in the linked list.} it^.numitem := 0; {Return the address.} CreateRPGMenu := it; end; Procedure AttachMenuDesc( RPM: RPGMenuPtr; Z: TSDL_Rect ); { Set the area for description items to zone Z. } begin RPM^.Desc_Zone := Z; end; Procedure DisposeRPGMenu(var RPM: RPGMenuPtr); {This procedure is called when you want to get rid of the menu. It will deallocate} {the memory for the RPGMenu record and also for all of the linked RPGMenuItems.} var c,d: RPGMenuKeyPtr; begin {Check to make sure that we've got a valid pointer here.} if RPM = Nil then begin exit; end; {Save the location of the first menu item...} DisposeRPGMenuItem( RPM^.FirstItem ); c := RPM^.FirstKey; {... then get rid of the menu record.} Dispose(RPM); RPM := Nil; {Keep processing the menu items until we hit a Nil nextitem.} while c <> Nil do begin d := c^.next; Dispose(c); c := d; end; end; Function RPMLocateByPosition(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; {Locate the i'th element of the item list, then return its address.} var a: RPGMenuItemPtr; {Our pointer} t: integer; {Our counter} begin {Error check, first off.} if i > RPM^.numitem then begin exit( Nil ); end; a := RPM^.FirstItem; t := 1; if i > 1 then begin for t := 2 to i do a := a^.next; end; RPMLocateByPosition := a; end; Function RPMLocateByValue(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; {Locate the i'th element of the item list, then return its address.} var t,a: RPGMenuItemPtr; {Our counter and a pointer} begin a := Nil; t := RPM^.FirstItem; while ( a = Nil ) and ( t <> Nil ) do begin if t^.value = i then a := t; t := t^.Next; end; RPMLocateByValue := a; end; Function MenuHeight( RPM: RPGMenuPtr ): Integer; { Return the height of the menu, in text rows. } var MH: Integer; begin MH := ( RPM^.Menu_Zone.h div TTF_FontLineSkip( game_font ) ); if MH < 1 then MH := 1; MenuHeight := MH; end; Procedure RPMRefreshDesc(RPM: RPGMenuPtr); {Refresh the menu description box, if appropriate.} begin {Check to make sure that this menu has a description box, first off.} if RPM^.Desc_Zone.W > 0 then begin CMessage( RPMLocateByPosition(RPM,RPM^.selectitem)^.desc , RPM^.Desc_Zone , RPM^.dtexcolor ); end; end; Procedure DisplayMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ); {Display the menu on the screen.} var topitem: RPGMenuItemPtr; a: RPGMenuItemPtr; {A pointer to be used while printing.} t: integer; height: integer; {The width of the menu display.} NextColor: TSDL_Color; MyDest: TSDL_Rect; Y,DY: Integer; begin {Error check- make sure the menu has items in it.} if RPM^.FirstItem = Nil then Exit; { If a redraw procedure has been specified, call it. } if ReDrawer <> Nil then ReDrawer; SDL_SetClipRect( Game_Screen , @RPM^.Menu_Zone ); {Calculate the height of the menu.} height := MenuHeight( rpm ); {Locate the top of the menu.} topitem := RPMLocateByPosition(RPM,RPM^.topitem); MyDest.X := RPM^.Menu_Zone.X; Y := RPM^.Menu_Zone.Y; DY := TTF_FontLineSkip( game_font ); MyDest.W := RPM^.Menu_Zone.W; a := topitem; for t := 1 to Height do begin MyDest.Y := Y; Y := Y + DY; {If we're at the currently selected item, highlight it.} if ((t + RPM^.topitem - 1) = RPM^.selectitem) and RPM^.Active then NextColor := RPM^.selcolor else NextColor := RPM^.itemcolor; QuickText( a^.msg , MyDest , NextColor , game_font ); a := a^.next; {Check to see if we've prematurely encountered the end of the list.} if a = Nil then break; end; {Restore the window to its regular size.} SDL_SetClipRect( Game_Screen , Nil ); {If there's an associated Desc field, display it now.} RPMRefreshDesc(RPM); end; Procedure RPMReposition( RPM: RPGMenuPtr; FullScroll: Boolean ); {The selected item has just changed, and is no longer visible on screen.} {Adjust the RPGMenu's topitem field to an appropriate value.} begin {When this function is called, there are two possibilities: either the} {selector has moved off the bottom of the page or the top.} if RPM^.selectitem < RPM^.topitem then begin {The selector has moved off the bottom of the list. The new page} {display should start with SelectItem on the bottom.} if FullScroll then begin RPM^.topitem := RPM^.selectitem - MenuHeight( RPM ) + 1; end else begin RPM^.topitem := RPM^.selectitem; end; {Error check- if this moves topitem below 1, that's bad.} if RPM^.topitem < 1 then RPM^.topitem := 1; end else begin {The selector has moved off the top of the list. The new page should} {start with SelectItem at the top, unless this would make things look} {funny.} if FullScroll then begin if ((RPM^.selectitem + MenuHeight( RPM ) - 1) > RPM^.numitem) then begin {There will be whitespace at the bottom of the menu if we assign} {SelectItem to TopItem. Make TopItem equal to the effective last} {page.} RPM^.topitem := RPM^.numitem - MenuHeight( RPM ) + 1; if RPM^.topitem < 1 then RPM^.topitem := 1; end else RPM^.topitem := RPM^.selectitem; end else if ((RPM^.topitem + MenuHeight( RPM ) - 1) < RPM^.numitem) then begin Inc( RPM^.TopItem ); end; end; end; Procedure RPMUpKey( RPM: RPGMenuPtr; FullScroll: Boolean ); {Someone just pressed the UP key, and we're gonna process that input.} {PRECONDITIONS: RPM has been initialized properly, and is currently being} { displayed on the screen.} begin {Decrement the selected item by one.} Dec(RPM^.selectitem); {If this causes it to go beneath one, wrap around to the last item.} if RPM^.selectitem = 0 then RPM^.selectitem := RPM^.numitem; {If the movement takes the selected item off the screen, do a redisplay.} {Otherwise, indicate the newly selected item.} if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem) >= MenuHeight( RPM )) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,FullScroll); end; end; Procedure RPMDownKey( RPM: RPGMenuPtr; FullScroll: Boolean ); {Someone just pressed the DOWN key, and we're gonna process that input.} {PRECONDITIONS: RPM has been initialized properly, and is currently being} { displayed on the screen.} begin {Increment the selected item.} Inc(RPM^.selectitem); {If this takes the selection out of bounds, restart at the first item.} if RPM^.selectitem = RPM^.numitem + 1 then RPM^.selectitem := 1; {If the movement takes the selected item off the screen, do a redisplay.} {Otherwise, indicate the newly selected item.} if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem) >= MenuHeight( RPM ) ) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,FullScroll); end; end; Function SelectMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): integer; {This function will allow the user to browse through the menu and will} {return a value based upon the user's selection.} var getit: char; {Character used to store user input} r,I: integer; {The value we'll be sending back.} m: RPGMenuKeyPtr; UK: Boolean; {Has a special MenuKey been pressed?} OldMouseX, OldMouseY: Integer; { TUNGINOBI: I got sick of the mouse cursor getting } { in the way of the keyboard, so this will only } { change the menu item if the mouse has moved. } begin {The menu is now active!} RPM^.Active := True; {Show the menu to the user.} DisplayMenu( RPM , ReDrawer ); DoFlip; {TUNGINOBI: Initialise the mouse movement variables} OldMouseX := Mouse_X; OldMouseY := Mouse_Y; {Initialize UK} UK := False; {Start the loop. Remain in this loop until either the player makes a selection} {or cancels the menu using the ESC key.} repeat DisplayMenu(RPM,ReDrawer); DoFlip; {Read the input from the keyboard.} getit := RPGKey; {Certain keys need processing- if so, process them.} case getit of {Selection Movement Keys} RPK_Up: RPMUpKey( RPM , True ); RPK_Down: RPMDownKey( RPM , True ); RPK_TimeEvent: if Mouse_Active then begin { If the mouse pointer is around } { the menu, we may have to do something. } if ( Mouse_X >= RPM^.Menu_Zone.X ) and ( Mouse_X <= ( RPM^.Menu_Zone.X + RPM^.Menu_Zone.W ) ) and (( Mouse_X <> OldMouseX ) or ( Mouse_Y <> OldMouseY )) then begin if ( Mouse_Y < ( RPM^.Menu_Zone.Y ) ) then begin if ( RPM^.SelectItem > 1 ) then RPMUpKey( RPM , False ); end else if ( Mouse_Y > ( RPM^.Menu_Zone.Y + RPM^.Menu_Zone.H ) ) then begin if ( (RPM^.selectitem - RPM^.topitem) < MenuHeight( RPM ) ) and ( RPM^.selectitem < RPM^.numitem ) then RPMDownKey( RPM , False ); end else begin I := ( Mouse_Y - RPM^.Menu_Zone.Y ) div TTF_FontLineSkip( game_font ); SetItemByPosition( RPM , RPM^.TopItem + I ); { Upon setting an item directly, freeze the mouse. } OldMouseX := Mouse_X; OldMouseY := Mouse_Y; end; end; end; RPK_MouseButton: { If the mouse hit happened inside } { the menu area, it was a selection. } { Otherwise it was a cancel. } if ( Mouse_X >= RPM^.Menu_Zone.X ) and ( Mouse_X <= ( RPM^.Menu_Zone.X + RPM^.Menu_Zone.W )) and ( Mouse_Y >= RPM^.Menu_Zone.Y ) and ( Mouse_Y <= ( RPM^.Menu_Zone.Y + RPM^.Menu_Zone.H )) then begin getit := ' '; end else begin if ( RPM^.Mode <> RPMNoCancel ) and ( RPM^.Mode <> RPMEscCancel ) then getit := #27 else getit := ' '; end; RPK_RightButton: if ( RPM^.Mode <> RPMNoCancel ) and ( RPM^.Mode <> RPMEscCancel ) then getit := #27; {If we receive an ESC, better check to make sure we're in a} {cancelable menu. If not, convert the ESC to an unused key.} #27: If RPM^.Mode = RPMNoCancel then getit := 'Q'; { If we get a backspace, conver that to ESC. } #8: If RPM^.Mode <> RPMNoCancel then getit := #27; { Convert enter to space. } #13,#10: getit := ' '; end; {Check to see if a special MENU KEY has been pressed.} if RPM^.FirstKey <> Nil then begin m := RPM^.FirstKey; while m <> Nil do begin if getit = m^.k then begin UK := True; r := m^.value; end; m := m^.next; end; end; {Check for a SPACE or ESC.} until (getit = ' ') or (getit = #27) or UK; {The menu is no longer active.} RPM^.Active := False; {We have to send back a different value depending upon whether a selection} {was made or the menu was cancelled. If an item was selected, return its} {value field. The value always returned by a cancel will be -1.} {If a MenuKey was pressed, r already contains the right value.} if getit = ' ' then begin r := RPMLocateByPosition(RPM,RPM^.selectitem)^.value; end else if not UK then r := -1; SelectMenu := r; end; Procedure RPMSortAlpha(RPM: RPGMenuPtr); {Given a menu, RPM, sort its items based on the alphabetical} {order of their msg fields.} {I should mention here that I haven't written a sorting} {algorithm in years, and only once on a linked list (CS assignment).} {I think this is an insertion sort... I checked on internet for} {examples of sorting techniques, found a bunch of contradictory} {information, and decided to just write the easiest thing that} {would work. Since we're dealing with a relatively small number} {of items here, speed shouldn't be that big a concern.} var sorted: RPGMenuItemPtr; {The sorted list} a,b,c,d: RPGMenuItemPtr;{Counters. We always need them, you know.} youshouldstop: Boolean; {Can you think of a better name?} begin {Initialize A and Sorted.} a := RPM^.firstitem; Sorted := Nil; while a <> Nil do begin b := a; {b is to be added to sorted} a := a^.next; {increase A to the next item in the menu} {Give b's Next field a value of Nil.} b^.next := nil; {Locate the correct position in Sorted to store b} if Sorted = Nil then {This is the trivial case- Sorted is empty.} Sorted := b else if UpCase( b^.msg ) < Upcase( Sorted^.msg ) then begin {b should be the first element in the list.} c := sorted; sorted := b; sorted^.next := c; end else begin {c and d will be used to move through Sorted.} c := Sorted; {Locate the last item lower than b} youshouldstop := false; repeat d := c; c := c^.next; if c = Nil then youshouldstop := true else if UpCase( c^.msg ) > UpCase( b^.msg ) then begin youshouldstop := true; end; until youshouldstop; b^.next := c; d^.next := b; end; end; RPM^.firstitem := Sorted; end; Function CurrentMenuItemValue( RPM: RPGMenuPtr ): Integer; { Determine the value of the current menu item, and return it. } { Return 0 if the item is not found. } var Item: RPGMenuItemPtr; begin item := RPMLocateByPosition( RPM , RPM^.SelectItem ); if item = Nil then begin CurrentMenuItemValue := 0; end else begin CurrentMenuItemValue := item^.value; end; end; Function SetItemByValue( RPM: RPGMenuPtr ; V: Integer ): RPGMenuItemPtr; { Search through the list, and set the SelectItem } { field to the first menu item which matches V. } var T: Integer; MI: RPGMenuItemPtr; begin if RPM = Nil then exit; MI := RPM^.FirstItem; T := 1; while (MI <> Nil) and (MI^.Value <> V) do begin MI := MI^.Next; Inc( T ); end; if MI <> Nil then begin RPM^.SelectItem := T; if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem) > MenuHeight( RPM ) ) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,True); end; end; SetItemByValue := MI; end; Procedure SetItemByPosition( RPM: RPGMenuPtr ; N: Integer ); { Search through the list, and set the SelectItem } { field to the Nth menu item. } begin if RPM = Nil then exit; if N <= RPM^.NumItem then begin RPM^.SelectItem := N; if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem + 1) > MenuHeight( RPM ) ) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,True); end; end; end; Procedure BuildFileMenu( RPM: RPGMenuPtr; const SearchPattern: String ); { Do a DosSearch for files matching SearchPattern, then add } { each of the files found to the menu. } var F: SearchRec; N: Integer; begin N := 1; FindFirst( SearchPattern , AnyFile , F ); While DosError = 0 do begin AddRPGMenuItem( RPM , F.Name , N ); Inc(N); FindNext( F ); end; end; Function SelectFile( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): String; { RPM is a menu created by the BuildFileMenu procedure. } { So, select one of the items and return the item name, which } { should be a filename. } var N: Integer; { The number of the file selected. } Name: String; { The name of the filename selected. } begin { Do the menu selection first. } N := SelectMenu( RPM , ReDrawer ); if N = -1 then begin { Selection was canceled. So, return an empty string. } Name := ''; end else begin { Locate the selected element of the menu. } Name := RPMLocateByPosition(RPM,RPM^.SelectItem)^.msg; end; SelectFile := Name; end; end. GH2/arenaplay.pp0000644000175000017500000013134411376734117012431 0ustar kaolkaolunit 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} {$IFDEF CUTE} cutemap,cutegfx; {$ELSE} glmap,glgfx; {$ENDIF} {$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. GH2/chargen.pp0000644000175000017500000015170311326004555012054 0ustar kaolkaolunit 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, {$IFDEF CUTE} cutegfx,glinfo,glmenus; {$ELSE} glgfx,glinfo,glmenus; {$ENDIF} {$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 ); 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; RCDescMessage := MsgString( 'RANDCHAR_SkillDesc' ); RCPromptMessage := ''; RCCaption := ''; 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; AttachMenuDesc( RPM , ZONE_CharGenPrompt ); { 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 ); 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 ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCDescMessage := MsgString( 'RANDCHAR_CityDesc' ); RCPromptMessage := ''; RCCaption := MsgString( 'RANDCHAR_CityPrompt' ); { 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 RCPromptMessage := ''; RCDescMessage := MsgString( 'RANDCHAR_FHDesc' ); RCCaption := MsgString( 'RANDCHAR_FHPrompt' ); 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 ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCPromptMessage := ''; RCDescMessage := MsgString( 'RANDCHAR_JobDesc' ); RCCaption := MsgString( 'RANDCHAR_JobPrompt' ); { 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 ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCCaption := MsgString( 'RANDCHAR_FactionPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_FactionDesc' ); { 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; RCDescMessage := MsgString( 'RANDCHAR_ASPDesc' ); RCPromptMessage := ''; 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; AttachMenuDesc( RPM , ZONE_CharGenPrompt ); { 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 ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); { 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; RCCaption := MsgString( 'RANDCHAR_TalentPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_TalentDesc' ); RPM^.Mode := RPMNoCancel; RPMSortAlpha( RPM ); ALphaKeyMenu( RPM ); 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 ); AttachMenuDesc( RPM , ZONE_CharGenPrompt ); RCCaption := MsgString( 'RANDCHAR_MechaPrompt' ); RCDescMessage := MsgString( 'RANDCHAR_MechaDesc' ); RPM^.Mode := RPMNoCancel; { 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 RCDescMessage := MsgString( 'RANDCHAR_TraitDesc' ); RCPromptMessage := MsgString( 'RANDCHAR_FocusPrompt' ); DoTraitType( Focus_List ); RCPromptMessage := MsgString( 'RANDCHAR_GoalPrompt' ); 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, @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 begin PList := CreateFileList( Graphics_Directory + 'por_f_*.*' ); end; RCDescMessage := ''; RCPromptMessage := MsgString( 'RANDCHAR_PicturePrompt' ); RCCaption := ''; P := 1; repeat {$IFDEF CUTE} CleanSpriteList; {$ELSE} CleanTexList; {$ENDIF} 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 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; SetSAtt( PC^.SA , 'SDL_COLORS <49 91 161 252 212 195 150 112 89>' ); { 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 ); 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 ); end else begin EasyStatPoints( PC , StatPt ); end; { Allocate skill points. } if M = MODE_Regular then begin AllocateSkillPoints( PC , SkillPt ); CharacterDisplay( PC , Nil ); 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; SelectMecha( Egg , PC , M = MODE_Regular ); { Set personality traits. } if M = MODE_Regular then begin SetTraits( PC ); CharacterDisplay( PC , Nil ); 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 ); 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. GH2/playwright.pp0000644000175000017500000026547211401151246012642 0ustar kaolkaolunit playwright; { This unit handles the insertion of random plots. } { It has a lot of procedures for selecting random elements from the } { adventure based on criteria provided by the plot to be inserted. } { 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,mpbuilder; { G = GG_Plot } { S = ID Number (not nessecarily unique) } { v0.240 - Elements are stored as numeric attributes } const GS_XRanStory = 1; NAG_ArenaMissionInfo = 20; NAS_PayRate = 1; { Determines how much money the unit will earn upon } { completing the mission. } NAS_Pay = 2; { Holds the actual calculated pay value. } NAS_IsCoreMission = 3; { If nonzero, this is a core mission. } var persona_fragments: GearPtr; Standard_Plots,Standard_Moods: GearPtr; Dramatic_Choices: GearPtr; Procedure BuildMegalist( Dest: GearPtr; AddOn: SAttPtr ); Function SceneContext( GB: GameBoardPtr; Scene: GearPtr ): String; Function SceneDesc( Scene: GearPtr ): String; Function NumFreeScene( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String ): Integer; Function FindFreeScene( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String; Num: Integer ): GearPtr; Function SearchForScene( Adventure , Plot: GearPtr; GB: GameBoardPtr; Desc: String ): GearPtr; Procedure AddGearXRContext( GB: GameBoardPtr; Adv,Part: GearPtr; var Context: String; palette_entry_code: Char ); Procedure AddElementContext( GB: GameBoardPtr; Story: GearPtr; var Context: String; palette_entry_code: Char; Element_Num: Integer ); Function StoryContext( GB: GameBoardPtr; Story: GearPtr ): String; Function SeekUrbanArea( RootScene: GearPtr ): GearPtr; Procedure InsertPFrags( Plot,Persona: GearPtr; const Context: String; ID: Integer ); Procedure PrepAllPersonas( Adventure,Plot: GearPtr; GB: GameBoardPtr; MinID: Integer ); Procedure PrepMetaScenes( Adventure,Plot: GearPtr; GB: GameBoardPtr ); Function PersonalContext( Adv,NPC: GearPtr ): String; Function DifficulcyContext( Threat: Integer ): String; Function InsertStory( Slot,Story: GearPtr; GB: GameBoardPtr ): Boolean; Function InsertSubPlot( Scope,Slot,SubPlot: GearPtr; GB: GameBoardPtr ): Boolean; Function InsertPlot( Scope,Slot,Plot: GearPtr; GB: GameBoardPtr; Threat: Integer ): Boolean; Function InsertMood( City,Mood: GearPtr; GB: GameBoardPtr ): Boolean; Function InsertRSC( Source,Frag: GearPtr; GB: GameBoardPtr ): Boolean; Procedure EndPlot( GB: GameBoardPtr; Adv,Plot: GearPtr ); Function AddRandomPlot( GB: GameBoardPtr; Slot: GearPtr; Context: String; EList: ElementTable; Threat: Integer ): Boolean; Procedure PrepareNewComponent( Story: GearPtr; GB: GameBoardPtr ); Function InsertArenaMission( Source,Mission: GearPtr; ThreatAtGeneration: Integer ): Boolean; Procedure UpdatePlots( GB: GameBoardPtr; Renown: Integer ); Procedure UpdateMoods( GB: GameBoardPtr ); Procedure CreateChoiceList( GB: GameBoardPtr; Story: GearPtr ); Procedure ClearChoiceList( Story: GearPtr ); implementation uses ui4gh,rpgdice,texutil,gearutil,interact,ability,gearparser,ghchars,narration,ghprop, arenascript,chargen,wmonster, {$IFDEF ASCII} vidgfx,vidmenus; {$ELSE} {$IFDEF CUTE} cutegfx,glmenus; {$ELSE} glgfx,glmenus; {$ENDIF} {$ENDIF} Type PWSearchResult = Record thing: GearPtr; match: Integer; end; var Fast_Seek_Element: Array [0..1,1..Num_Plot_Elements] of GearPtr; Procedure BuildMegalist( Dest: GearPtr; AddOn: SAttPtr ); { Combine the scripts listed in ADDON into LLIST. } { If a script with the same label already exists in LLIST, the new } { script from ADDON supercedes it, while the old script gets moved to } { a new label. } var SPop,Key,Current: String; SPopSA: SAttPtr; begin SPop := 'na'; while AddOn <> Nil do begin { If there's currently a SAtt in the megalist with this } { key, it has to be "pushed" to a new position. } Key := UpCase( RetrieveAPreamble( AddOn^.Info ) ); if ( Key <> 'REQUIRES' ) and ( Key <> 'DESC' ) and ( Key <> 'DESIG' ) and ( Key <> 'SPECIAL' ) and not ( HeadMatchesString( 'ELEMENT' , Key ) or HeadMatchesString( 'TEAM' , Key ) or HeadMatchesString( 'CONTENT' , Key ) or HeadMatchesString( 'CONTEXT' , Key ) or HeadMatchesString( 'MINIMAP' , Key ) or HeadMatchesString( 'QUEST' , Key ) or HeadMatchesString( 'SCENE' , Key ) or HeadMatchesString( 'NAME' , Key ) or HeadMatchesString( 'PLACE' , Key ) ) then begin Current := AS_GetString( Dest , Key ); if Current <> '' then begin SPopSA := AddSAtt( Dest^.SA , Key , Current ); SPop := RetrieveAPreamble( SPopSA^.Info ); end else begin SPop := 'na'; end; ReplacePat( AddOn^.Info , '%pop%' , SPop ); SetSAtt( Dest^.SA , AddOn^.Info ); end; AddOn := AddOn^.Next; end; end; Function SeekDramaticChoice( N: Integer ): GearPtr; { Return a pointer to Dramatic Choice N. } { If no such choice can be found, return NIL. } var C: GearPtr; begin C := Dramatic_Choices; while ( C <> Nil ) and ( C^.V <> N ) do C := C^.Next; SeekDramaticChoice := C; end; Function SceneContext( GB: GameBoardPtr; Scene: GearPtr ): String; { Return a string describing the context of this scene. } var CType,TType: String; C: GearPtr; begin CType := SAttValue( Scene^.SA , 'CONTEXT' ) + ' ' + SATtValue( Scene^.SA , 'DESIG' ) + ' ' + SAttValue( Scene^.SA , 'TYPE' ); TType := SAttValue( Scene^.SA , 'TERRAIN' ); if TType = '' then TType := 'GROUND'; CType := CType + ' ' + TType; if ( Scene^.G = GG_Scene ) and IsSubCom( Scene ) then CType := CType + ' STATIC' else CType := CType + ' DYNAMIC'; { Add the faction context. } C := SeekFaction( FindRoot( Scene ) , NAttValue( Scene^.NA , NAG_Personal , NAS_FactionID ) ); if C <> Nil then AddTraits( CType , SAttValue( C^.SA , 'DESIG' ) ) else AddTraits( CType , 'NOFAC' ); { Next add the data for the city we're located in, its faction, and the } { world that it's located in. } C := FindRootScene( Scene ); if C <> Nil then begin if C <> Scene then AddTraits( CType , SAttValue( C^.SA , 'DESIG' ) ); AddTraits( CType , SAttValue( C^.SA , 'PERSONATYPE' ) ); C := SeekFaction( FindRoot( Scene ) , NAttValue( C^.NA , NAG_Personal , NAS_FactionID ) ); if C <> Nil then AddTraits( CType , SAttValue( C^.SA , 'DESIG' ) ); C := FindRootScene( Scene )^.Parent; if ( C <> Nil ) and ( C^.G = GG_World ) then begin AddTraits( CType , SAttValue( C^.SA , 'DESIG' ) ); end; end; SceneContext := CType; end; Function FilterElementDescription( var IDesc: String ): String; { Given this element description, break it up into the } { intrinsic and relative description strings. } { Note that the tag "NEVERFAIL" is edited out at this stage. } var ZeroDesc,cmd,RDesc: String; begin ZeroDesc := IDesc; RDesc := ''; IDesc := ''; while ZeroDesc <> '' do begin cmd := ExtractWord( ZeroDesc ); if ( cmd <> '' ) and ( cmd[1] = '!' ) then begin RDesc := RDesc + ' ' + cmd; { If this relative command requires a value, } { copy that over as well. } { The two that don't are !Global and !Lancemate. } if ( UpCase( cmd )[2] <> 'G' ) and ( UpCase( cmd )[2] <> 'L' ) then begin cmd := ExtractWord( ZeroDesc ); RDesc := RDesc + ' ' + cmd; end; end else if ( cmd <> '' ) and ( UpCase( cmd ) <> 'NEVERFAIL' ) then begin IDesc := IDesc + ' ' + cmd; end; end; FilterElementDescription := RDesc; end; Function GetFSE( N: Integer ): GearPtr; { Get the FastSeekElement requested. } begin if N > 0 then begin GetFSE := Fast_Seek_Element[ 1 , N ] end else begin GetFSE := Fast_Seek_Element[ 0 , Abs( N ) ]; end; end; Function GetFSEID( Story , Plot: GearPtr; N: Integer ): Integer; { Return the ID of the FSE listed. } begin if N > 0 then begin GetFSEID := ElementID( Plot , N ); end else if Story <> Nil then begin GetFSEID := ElementID( Story , N ); end else begin GetFSEID := 0; end; end; Function PWAreEnemies( Adv, Part: GearPtr; N: Integer ): Boolean; { Return TRUE if the faction of element N is an enemy of the } { faction of PART. } var Fac: GearPtr; F0,F1: Integer; begin F0 := GetFactionID( GetFSE( N ) ); F1 := GetFactionID( Part ); { A faction is never its own enemy. } if F0 = F1 then Exit( False ); Fac := SeekFaction( Adv , F1 ); if Fac <> Nil then begin PWAreEnemies := NAttValue( Fac^.NA , NAG_FactionScore , F0 ) < 0; end else begin PWAreEnemies := False; end; end; Function PWAreAllies( Adv, Part: GearPtr; N: Integer ): Boolean; { Return TRUE if the faction of element N is an ally of the } { faction of PART. } var Fac: GearPtr; F0,F1: Integer; begin F0 := GetFactionID( GetFSE( N ) ); F1 := GetFactionID( Part ); { A faction is always allied with itself. } if F0 = F1 then Exit( True ); Fac := SeekFaction( Adv , F1 ); if Fac <> Nil then begin PWAreAllies := NAttValue( Fac^.NA , NAG_FactionScore , F0 ) > 0; end else begin PWAreAllies := False; end; end; Function PartMatchesRelativeCriteria( Adv,Plot,Part: GearPtr; GB: GameBoardPtr; Desc: String ): Boolean; { Return TRUE if the part matches the relative criteria } { provided, or FALSE if it does not. } var it: Boolean; cmd: String; Q: Char; begin { Assume TRUE unless shown otherwise. } it := True; { Lancemates can only be selected if they're asked for by the plot. } if AStringHasBString( Desc , '!LANCEMATE' ) then it := ( Part^.G = GG_Character ) and ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) else it := ( NAttValue( Part^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ); { Check all the bits in the description string. } While ( Desc <> '' ) and it do begin Cmd := ExtractWord( Desc ); if Cmd <> '' then begin DeleteFirstChar( cmd ); Q := UpCase( cmd )[ 1 ]; if ( Q = 'N' ) and ( Plot <> Nil ) then begin { L1 must equal L2. } it := it and ( FindRootScene( FindActualScene( GB , FindSceneID( Part , GB ) ) ) = FindRootScene( FindActualScene( GB , FindSceneID( GetFSE( ExtractValue( Desc ) ) , GB ) ) ) ); end else if ( Q = 'F' ) and ( Plot <> Nil ) then begin { L1 must not equal L2. } it := it and ( FindRootScene( FindActualScene( GB , FindSceneID( Part , GB ) ) ) <> FindRootScene( FindActualScene( GB , FindSceneID( GetFSE( ExtractValue( Desc ) ) , GB ) ) ) ); end else if Q = 'M' then begin { MEMBER. This part must belong to the } { requested faction. } it := it and ( GetFactionID( Part ) = ExtractValue( Desc ) ); end else if ( Q = 'C' ) and ( Plot <> Nil ) then begin { COMRADE. This part must belong to the } { same faction as the requested element. } it := it and ( GetFactionID( GetFSE( ExtractValue( Desc ) ) ) = GetFactionID( Part ) ); end else if ( Q = 'X' ) and ( Plot <> Nil ) then begin { eXclude. This part must not be an ally } { of the faction of the requested element. } it := it and ( not PWAreAllies( Adv, Part , ExtractValue( Desc ) ) ); end else if ( Q = 'O' ) and ( Plot <> Nil ) then begin { Okay. This part must not be an enemy } { of the faction of the requested element. } it := it and ( not PWAreEnemies( Adv, Part , ExtractValue( Desc ) ) ); end else if ( Q = 'A' ) and ( Plot <> Nil ) then begin { Ally. This part must not be an ally } { of the faction of the requested element. } it := it and PWAreAllies( Adv, Part , ExtractValue( Desc ) ); end else if ( Q = 'E' ) and ( Plot <> Nil ) then begin { ENEMY. The faction of the requested } { element must be hated by this part's } { faction. } it := it and PWAreEnemies( Adv, Part , ExtractValue( Desc ) ); end; end; end; { Return the result. } PartMatchesRelativeCriteria := it; end; Function NPCMatchesDesc( Adv,Plot,NPC: GearPtr; const IDesc,RDesc: String; GB: GameBoardPtr ): Boolean; { Return TRUE if the supplied NPC matches this description } { string, FALSE otherwise. Note that an extra check is performed } { to prevent animals from being chosen for plots, which could } { otherwise happen if the animal in question has a Character ID. } { Thank you Fluffy the Stegosaurus. } var it: Boolean; begin { DESC should contain a string list of all the stuff we want } { our NPC to have. Things like gender, personality traits, } { et cetera. Most of these things are intrinsic to the NPC, } { but some of them are defined relative to other elements of } { this plot. } it := PartMatchesCriteria( XNPCDesc( GB , Adv , NPC ) , IDesc ); if it then it := PartMatchesRelativeCriteria( Adv, Plot, NPC, GB, RDesc ); NPCMatchesDesc := it and NotAnAnimal( NPC ); end; Function CharacterSearch( Adv, Plot: GearPtr; Desc: String; GB: GameBoardPtr ): GearPtr; { Search high and low looking for a character that matches } { the provided search description! } { GH2- note that ADV probably isn't the adventure proper. It's the limit for our element } { search, probably the city in which this plot will take place. } var NPC: GearPtr; NumMatches: Integer; Results: Array of PWSearchResult; IDesc,RDesc: String; CheckGlobal,SeekLancemate: Boolean; Procedure CheckAlongPath( P: GearPtr ); { Check along this path looking for characters. If a match is found, } { add it to the array. } var CID: LongInt; begin while P <> Nil do begin if ( P^.G = GG_Character ) and NPCMatchesDesc( Adv, Plot, P , IDesc , RDesc , GB ) then begin { Next, check to make sure it has an assigned CID. } CID := NAttValue( P^.NA , NAG_Personal , NAS_CID ); if ( CID <> 0 ) then begin Results[ NumMatches ].thing := P; Results[ NumMatches ].match := 1; Inc( NumMatches ); end; end; { Don't check the contents of the content set- if it hasn't yet been added to the } { adventure, we don't want it. } { Also, don't check the contents of metascenes- either they're already involved in } { a plot or they're off-limits. } if ( P^.G <> GG_ContentSet ) and ( P^.G <> GG_MetaScene ) then begin CheckAlongPath( P^.SubCom ); CheckAlongPath( P^.InvCom ); end; P := P^.Next; end; end; begin { Step one- size the array. We have a good estimate for how many characters are in the adventure in } { the MaxCID value. } NumMatches := NAttValue( FindRoot( Adv )^.NA , NAG_Narrative , NAS_MaxCID ); if NumMatches = 0 then begin DialogMsg( 'ERROR: No CIDs recorded in ' + GearName( FindRoot( Adv ) ) + '.' ); Exit( Nil ); end; SetLength( Results , NumMatches ); { Determine the scope- if searching globablly, do so. } CheckGlobal := AStringHasBString( Desc , '!G' ); if CheckGlobal then Adv := FindRoot( Adv ); SeekLancemate := AStringHasBString( Desc , '!L' ); { Filter the relative description from the instrinsic description. } IDesc := Desc; RDesc := FilterElementDescription( IDesc ); { Step two- search the adventure looking for characters. } NumMatches := 0; CheckAlongPath( Adv^.SubCom ); if Adv^.G = GG_Scene then CheckAlongPath( Adv^.InvCom ); { Check the gameboard as well, as long as it's not a metascene or a temporary scene. } { Actually, you can go ahead and search temp scenes as long as we're looking for a lancemate. } if ( GB <> Nil ) and ( SeekLancemate or not SceneIsTemp( GB^.Scene ) ) then begin CheckAlongPath( GB^.Meks ); end; { Check the invcomponents of the adventure only if global } { NPCs are allowed by the DESC string. } if CheckGlobal then begin CheckAlongPath( FindRoot( Adv )^.InvCom ); end; if NumMatches > 0 then begin NPC := Results[ Random( NumMatches ) ].Thing; end else begin NPC := Nil; end; CharacterSearch := NPC; end; { Character Search } Function SceneDesc( Scene: GearPtr ): String; { Create a description string for this scene. } var it: String; begin if ( Scene = Nil ) or not IsAScene( Scene ) then begin it := ''; end else begin it := SAttValue( Scene^.SA , 'TYPE' ) + ' ' + SAttValue( Scene^.SA , 'CONTEXT' ) + ' SCALE' + BStr( Scene^.V ); end; SceneDesc := QuoteString( it ); end; Function XSceneDesc( Scene: GearPtr ): String; { Return the regular scene description plus a few extra bits. } var it: String; FID: LongInt; Adv,Fac: GearPtr; begin { Just copying the code above for now- it's not complicated, and should } { save time from creating/copying two strings. If you are reading this } { source code to learn how to program, DO NOT DO THIS YOURSELF!!! It's a } { bad thing and will probably come back to bite me in the arse later on. } { Also, feel free to use the word "arse" in your comments. } if ( Scene = Nil ) or not IsAScene( Scene ) then begin it := ''; end else begin it := SAttValue( Scene^.SA , 'TYPE' ) + ' ' + SAttValue( Scene^.SA , 'CONTEXT' ) + ' SCALE' + BStr( Scene^.V ); Adv := FindRoot( Scene ); FID := NAttValue( Scene^.NA , NAG_Personal , NAS_FactionID ); Fac := SeekFaction( Adv , FID ); if Fac <> Nil then it := it + ' ' + SATtValue( Fac^.SA , 'DESIG' ) else it := it + ' NOFAC'; if ( FID <> 0 ) and ( FID = NAttValue( Adv^.NA , NAG_Personal , NAS_FactionID ) ) then it := it + ' PCFAC'; end; XSceneDesc := QuoteString( it ); end; Function NumFreeScene( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String ): Integer; { Find out how many scenes match the provided description. } var RDesc: String; Function CheckAlongPath( P: GearPtr ): Integer; var N: Integer; begin N := 0; while P <> Nil do begin if ( P^.G = GG_Scene ) and PartMatchesCriteria( XSceneDesc( P ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, P, GB, RDesc ) then begin Inc( N ); end; N := N + CheckAlongPath( P^.SubCom ); P := P^.Next; end; CheckAlongPath := N; end; begin RDesc := FilterElementDescription( Desc ); if ( Adventure^.G = GG_Scene ) and PartMatchesCriteria( XSceneDesc( Adventure ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, Adventure, GB, RDesc ) then begin NumFreeScene := CheckAlongPath( Adventure^.SubCom ) + 1; end else begin NumFreeScene := CheckAlongPath( Adventure^.SubCom ); end; end; Function FindFreeScene( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String; Num: Integer ): GearPtr; { Find the NUM'th scene that matches the provided description. } var S2: GearPtr; RDesc: String; { PROCEDURES BLOCK. } Procedure CheckAlongPath( Part: GearPtr ); { CHeck along the path specified. } begin while ( Part <> Nil ) and ( S2 = Nil ) do begin { Decrement N if this gear matches our description. } if ( Part^.G = GG_Scene ) and PartMatchesCriteria( XSceneDesc( Part ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, Part, GB, RDesc ) then begin Dec( Num ); if Num = 0 then S2 := Part; end; if Num > 0 then CheckAlongPath( Part^.SubCom ); Part := Part^.Next; end; end; begin S2 := Nil; RDesc := FilterElementDescription( Desc ); if ( Adventure^.G = GG_Scene ) and PartMatchesCriteria( XSceneDesc( Adventure ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, Adventure, GB, RDesc ) then begin if Num = 1 then begin S2 := Adventure; end else begin Dec( Num ); CheckAlongPath( Adventure^.SubCom ); end; end else begin CheckAlongPath( Adventure^.SubCom ); end; FindFreeScene := S2; end; Function SearchForScene( Adventure , Plot: GearPtr; GB: GameBoardPtr; Desc: String ): GearPtr; { Try to find a scene matching the description. } var NumElements: Integer; begin if AStringHasBString( Desc , '!G' ) then Adventure := FindRoot( Adventure ); NumElements := NumFreeScene( Adventure , Plot , GB , Desc ); if NumElements > 0 then begin { Pick one of the free scenes at random. } SearchForScene := FindFreeScene( Adventure , Plot , GB , Desc , Random( NumElements ) + 1 ); end else begin SearchForScene := Nil; end; end; Function NumFreeEntryPoints( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String ): Integer; { Find out how many scenes match the provided description. } var RDesc: String; Function CheckAlongPath( P: GearPtr ): Integer; var N: Integer; begin N := 0; while P <> Nil do begin if ( P^.G = GG_MetaTerrain ) and ( P^.S <> GS_MetaEncounter ) and ( P^.Stat[ STAT_Destination ] < 0 ) and PartMatchesCriteria( SAttValue( P^.SA , 'REQUIRES' ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, P, GB, RDesc ) then begin if MetaSceneNotInUse( Adventure , P^.Stat[ STAT_Destination ] ) then Inc( N ); end; N := N + CheckAlongPath( P^.SubCom ); N := N + CheckAlongPath( P^.InvCom ); P := P^.Next; end; CheckAlongPath := N; end; begin RDesc := FilterElementDescription( Desc ); NumFreeEntryPoints := CheckAlongPath( Adventure^.SubCom ) + CheckAlongPath( GB^.Meks ); end; Function FindFreeEntryPoint( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String; N: Integer ): GearPtr; { Find out how many scenes match the provided description. } var E2: GearPtr; RDesc: String; Procedure CheckAlongPath( P: GearPtr ); begin while ( P <> Nil ) and ( E2 = Nil ) do begin if ( P^.G = GG_MetaTerrain ) and ( P^.S <> GS_MetaEncounter ) and ( P^.Stat[ STAT_Destination ] < 0 ) and PartMatchesCriteria( SAttValue( P^.SA , 'REQUIRES' ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, P, GB, RDesc ) and MetaSceneNotInUse( Adventure , P^.Stat[ STAT_Destination ] ) then begin Dec( N ); if N = 0 then E2 := P; end; if E2 = Nil then CheckAlongPath( P^.SubCom ); if E2 = Nil then CheckAlongPath( P^.InvCom ); P := P^.Next; end; end; begin E2 := Nil; RDesc := FilterElementDescription( Desc ); CheckAlongPath( Adventure^.SubCom ); if E2 = Nil then CheckAlongPath( GB^.Meks ); FindFreeEntryPoint := E2; end; Function SearchForMetasceneEntryPoint( Adventure , Plot: GearPtr; GB: GameBoardPtr; Desc: String ): GearPtr; { Locate a metaterrain gear which has a negative destination number that's } { currently unused. } var NumElements: Integer; begin NumElements := NumFreeEntryPoints( Adventure , Plot , GB , Desc ); if NumElements > 0 then begin { Pick one of the free scenes at random. } SearchForMetasceneEntryPoint := FindFreeEntryPoint( Adventure , Plot , GB , Desc , Random( NumElements ) + 1 ); end else begin SearchForMetasceneEntryPoint := Nil; end; end; function FactionDesc( Adv,Fac: GearPtr ): String; { Return a description of the provided faction. } var it: String; begin { Error check- make sure the adventure really is the adventure. } Adv := FindRoot( Adv ); { Basic description is the faction's TYPE string attribute. } it := SATtValue( Fac^.SA , 'TYPE' ) + ' ' + SATtValue( Fac^.SA , 'CONTEXT' ) + ' ' + SATtValue( Fac^.SA , 'DESIG' ); if IsArchEnemy( Adv, Fac ) then it := it + ' ARCHENEMY'; if IsArchAlly( Adv, Fac ) then it := it + ' ARCHALLY'; if Fac^.S = NAttValue( Adv^.NA , NAG_Personal , NAS_FactionID ) then it := it + ' PCFAC'; FactionDesc := it; end; Function NumFreeFaction( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String ): Integer; { Find out how many factions match the provided description. } var Fac: GearPtr; N: Integer; RDesc: String; begin Fac := Adventure^.InvCom; N := 0; RDesc := FilterElementDescription( Desc ); while Fac <> Nil do begin if ( Fac^.G = GG_Faction ) and PartMatchesCriteria( FactionDesc( Adventure,Fac ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, Fac, GB, RDesc ) then Inc( N ); Fac := Fac^.Next; end; NumFreeFaction := N; end; Function FindFreeFaction( Adventure,Plot: GearPtr; GB: GameBoardPtr; Desc: String; Num: Integer ): GearPtr; { Find the NUM'th scene that matches the provided description. } var Fac,F2: GearPtr; RDesc: String; begin Fac := Adventure^.InvCom; F2 := Nil; RDesc := FilterElementDescription( Desc ); while Fac <> Nil do begin if ( Fac^.G = GG_Faction ) and PartMatchesCriteria( FactionDesc( Adventure,Fac ) , Desc ) and PartMatchesRelativeCriteria( Adventure, Plot, Fac, GB, RDesc ) then begin Dec( Num ); if Num = 0 then F2 := Fac; end; Fac := Fac^.Next; end; FindFreeFaction := F2; end; Function DeployNextPrefabElement( GB: GameBoardPtr; Adventure,Plot: GearPtr; N: Integer; MovePrefabs: Boolean ): GearPtr; { Deploy the next element, give it a unique ID number if } { appropriate, then return a pointer to the it. } var E,Dest,Target: GearPtr; { Element & Destination. } D,P: Integer; Place,SubStr: String; ID: LongInt; InSceneNotElement: Boolean; begin { Find the first uninitialized entry in the list. } { This is gonna be our next element. } E := Plot^.InvCom; While ( E <> Nil ) and ( SAttValue( E^.SA , 'ELEMENT' ) <> '' ) do begin E := E^.Next; end; if E <> Nil then begin { Give our new element a unique ID, and store its ID in the Plot. } { Characters who aren't animals get CIDs, encounters get scene IDs, } { everything else gets NIDs. } if ( E^.G = GG_Character ) and NotAnAnimal( E ) then begin ID := NewCID( Adventure ); SetNAtt( E^.NA , NAG_Personal , NAS_CID , ID ); SetSAtt( Plot^.SA , 'ELEMENT' + BStr( N ) + ' ' ); SetSAtt( E^.SA , 'ELEMENT ' ); end else if ( E^.G = GG_MetaTerrain ) and ( E^.S = GS_MetaEncounter ) and not AStringHasBString( SAttValue( E^.SA , 'SPECIAL' ) , 'NOMSID' ) then begin ID := NewMetaSceneID( Adventure ); E^.Stat[ STAT_Destination ] := ID; SetSAtt( Plot^.SA , 'ELEMENT' + BStr( N ) + ' ' ); SetSAtt( E^.SA , 'ELEMENT ' ); { Maybe set this element's name. } Place := GearName( E ); ReplacePat( Place , '%r%' , RandomName ); SetSAtt( E^.SA , 'name <' + Place + '>' ); end else begin ID := NewNID( Adventure ); SetNAtt( E^.NA , NAG_Narrative , NAS_NID , ID ); SetSAtt( Plot^.SA , 'ELEMENT' + BStr( N ) + ' ' ); SetSAtt( E^.SA , 'ELEMENT ' ); end; SetNAtt( Plot^.NA , NAG_ElementID , N , ID ); If MovePrefabs then begin { Find out if we have to put this element somewhere else. } { Memes automatically get frozen. } if E^.G = GG_Meme then begin Place := '/'; end else if ( E^.G = GG_Secret ) or ( E^.G = GG_CityMood ) then begin Place := ''; end else begin Place := SAttValue( Plot^.SA , 'PLACE' + BStr( N ) ); end; { If we have to put it somewhere, do so now. } { Otherwise leave it where it is. } if Place <> '' then begin { Delink the element from the plot. } DelinkGear( Plot^.InvCom , E ); { Determine its target location. } { If the PLACE string starts with a tilde, we want to } { place the prefab element in the same scene as the relative } { element rather than the element itself. } InSceneNotElement := Place[1] = '~'; if InSceneNotElement then DeleteFirstChar( Place ); { If the PLACE is /, then this element should start play frozen. } if Place[1] = '/' then begin Dest := SeekCurrentLevelGear( FindRoot( Adventure )^.InvCom , GG_PlotThingSet , 0 ); InSceneNotElement := False; end else begin D := ExtractValue( Place ); if ( Abs( D ) >= 1 ) and ( Abs( D ) <= Num_Plot_Elements ) then begin Dest := GetFSE( D ); end else begin DialogMsg( 'ERROR: Illegal place for element ' + BStr( N ) + ' in plot ' + GearName( Plot ) ); end; end; if InSceneNotElement and (( Dest = Nil ) or ( Dest^.G <> GG_Scene )) then begin { If the destination is a metascene, locate its entrance. } if ( Dest = Nil ) or ( Dest^.G = GG_MetaScene ) then begin Dest := FindSceneEntrance( Adventure , GB , GetFSEID( Plot^.Parent , Plot , D ) ); end; { Try to find the associated scene now. } if Dest <> Nil then begin Dest := FindActualScene( GB , FindSceneID( Dest , GB ) ); end; end; if Dest = Nil then begin { An invalid location was specified... } DialogMsg( 'ERROR: No dest found for ' + GearName( E ) + ' in ' + GearName( Plot ) ); DisposeGear( E ); end else if ( Dest^.G <> GG_Scene ) and ( Dest^.G <> GG_MetaScene ) and IsLegalInvCom( Dest , E ) then begin { If E can be an InvCom of Dest, stick it there. } InsertInvCom( Dest , E ); end else begin { If Dest isn't a scene, find the scene DEST is in itself } { and stick E in there. } while ( Dest <> Nil ) and ( not IsAScene( Dest ) ) do Dest := Dest^.Parent; if Dest <> Nil then begin { Maybe set this item next to another item. } P := Pos( '!N' , Place ); if P > 0 then begin SubStr := copy( Place , P , 255 ); ExtractWord( SubStr ); ID := ExtractValue( SubStr ); Target := GetFSE( ID ); if ( Target <> Nil ) and ( ( Target^.G = GG_Scene ) or ( Target^.G = GG_MetaScene ) ) then begin if ID > 0 then begin Target := FindSceneEntrance( Adventure , GB , ElementID( Plot , ID ) ); end else begin Target := FindSceneEntrance( Adventure , GB , ElementID( Plot^.Parent , Abs( ID ) ) ); end; end; if Target <> Nil then begin SetNAtt( E^.NA , NAG_ParaLocation , NAS_X , NAttValue( Target^.NA , NAG_Location , NAS_X ) ); SetNAtt( E^.NA , NAG_ParaLocation , NAS_Y , NAttValue( Target^.NA , NAG_Location , NAS_Y ) ); end; end; { If the destination is the current scene, } { we'll want to deploy this element right away. } { Otherwise just shove it in the invcoms. } if Dest = GB^.Scene then begin EquipThenDeploy( GB , E , True ); end else begin InsertInvCom( Dest , E ); end; { If E is a character, this brings us to the next problem: } { we need to assign a TEAM for E to be a member of. } if E^.G = GG_Character then begin SetSAtt( E^.SA , 'TEAMDATA <' + Place + '>' ); ChooseTeam( E , Dest ); end; { If DEST is a metascene, give E an OriginalHome } { of -1 so it isn't deleted when the scene ends. } if ( Dest^.G = GG_MetaScene ) or IsInvCom( Dest ) then SetNAtt( E^.NA , NAG_ParaLocation , NAS_OriginalHome , -1 ); end else begin { Couldn't find a SCENE gear. Get rid of E. } DialogMsg( 'ERROR: No dest found for ' + GearName( E ) + ' in ' + GearName( Plot ) ); DisposeGear( E ); end; end; { If Dest ... } end; { if Place <> '' } end; { if MovePrefabs } end; DeployNextPrefabElement := E; end; Function SelectArtifact( Adventure: GearPtr; Desc: String ): GearPtr; { Select an artifact at random. If one can be found, delink it from the } { artifact collection and return its address. } var AC,A,Part: GearPtr; N: Integer; begin { Step One: Find the artifact set. } AC := SeekCurrentLevelGear( Adventure^.InvCom , GG_ArtifactSet , 0 ); A := Nil; if AC <> Nil then begin { Count the number of appropriate gears. } Part := AC^.InvCom; N := 0; while Part <> Nil do begin if PartMatchesCriteria( SAttValue( Part^.SA , 'REQUIRES' ) , Desc ) then Inc( N ); Part := Part^.next; end; { Next, grab one at random. } if N > 0 then begin N := Random( N ); Part := AC^.InvCom; while ( Part <> Nil ) and ( A = Nil ) do begin if PartMatchesCriteria( SAttValue( Part^.SA , 'REQUIRES' ) , Desc ) then begin if N = 0 then A := Part; Dec( N ); end; Part := Part^.next; end; end; end; if A <> Nil then DelinkGear( AC^.InvCom , A ); SelectArtifact := A; end; Function NewContentNPC( Adv,Story,Plot: GearPtr; desc: String ): GearPtr; { Create a new NPC. Give it whatever traits are needed. } var NPC,Fac: GearPtr; FacID,HTID: LongInt; begin { First, we need to determine the NPC's faction and home town. } { These will be stored as separate elements. } FacID := ExtractValue( desc ); if FacID <> 0 then begin Fac := GetFSE( FacID ); FacID := GetFactionID( Fac ); end; HTID := ExtractValue( desc ); if HTID <> 0 then HTID := GetFSEID( Story , Plot , HTID ); { We have the info. Time to create our NPC. } NPC := RandomNPC( Adv , FacID , HTID ); { Do the individualization. } IndividualizeNPC( NPC ); { Customize the character. } ApplyChardesc( NPC , Desc ); { Return the result. } NewContentNPC := NPC; end; Function PrepareQuestSceneElement( Adventure , Plot: GearPtr; N: Integer ): GearPtr; { We've received a request for a new permanent scene. Assign a SceneID for it. } { Also, copy over the difficulty rating from the plot, and mark all current } { subs and invs as original content. } { Also make sure to give the scene a unique name. } Procedure MarkChildrenAsOriginal( LList: GearPtr ); { Mark all the children of this quest scene as original, so if it's a } { dungeon other things which get added can be set aside and placed in } { the goal level. } begin while LList <> Nil do begin if NAttValue( LList^.NA , NAG_Narrative , NAS_QuestDungeonPlacement ) = 0 then begin SetNAtt( LList^.NA , NAG_NArrative , NAS_QuestDungeonPlacement , NAV_WasQDOriginal ); end; LList := LList^.Next; end; end; var ID,Tries: Integer; QS,PScene: GearPtr; BaseName,UniqueName: String; begin ID := NewSceneID( Adventure ); SetNAtt( Plot^.NA , NAG_ElementID , N , ID ); QS := SeekCurrentLevelGear( Plot^.SubCom , GG_MetaScene , N ); if QS <> Nil then begin { Set the difficulty level, unless this has been overridden. } if NAttValue( QS^.NA , NAG_NArrative , NAS_DifficultyLevel ) = 0 then SetNAtt( QS^.NA , NAG_NArrative , NAS_DifficultyLevel , NAttValue( Plot^.NA , NAG_Narrative , NAS_DifficultyLevel ) ); { Mark the children as originals. } MarkChildrenAsOriginal( QS^.SubCom ); MarkChildrenAsOriginal( QS^.InvCom ); { Assign a unique name for this scene. } { Generate a unique name for this scene. } BaseName := SAttValue( QS^.SA , 'NAME' ); UniqueName := ReplaceHash( BaseName , RandomName ); Tries := 0; repeat PScene := SeekGearByName( Adventure , UniqueName ); if PScene <> Nil then UniqueName := ReplaceHash( BaseName , RandomName ); Inc( Tries ); until ( PScene = Nil ) or ( Tries > 500 ); if Tries > 500 then begin { We tried, and failed, to get a unique name. } UniqueName := 'Ique ' + BStr( ID ); DialogMsg( 'ERROR: Scene ' + GearName( Plot ) + ' (' + BStr( ID ) + ') couldn''t generate unique name.' ); end; SetSAtt( QS^.SA , 'NAME <' + UniqueName + '>' ); end; PrepareQuestSceneElement := QS; end; Function FindElement( Adventure,Plot: GearPtr; N: Integer; GB: GameBoardPtr ; MovePrefabs, Debug: Boolean ): Boolean; { Locate and store the Nth element for this plot. } { Return TRUE if a suitable element could be found, or FALSE } { if no suitable element exists in the adventure & this plot } { will have to be abandoned. } var Element: GearPtr; Desc,EKind: String; OK: Boolean; NumElements: Integer; begin { Error check } if ( N < 1 ) or ( N > Num_Plot_Elements ) then Exit( False ); { Find the description for this element. } desc := UpCase( SAttValue( Plot^.SA , 'ELEMENT' + BStr( N ) ) ); DeleteWhiteSpace( Desc ); { Initialize OK to TRUE. } OK := True; if desc <> '' then begin EKind := ExtractWord( Desc ); if EKind[1] = 'C' then begin { This element is a CHARACTER. Find one. } { IMPORTANT!!!: Character being sought muct not have a plot already!!! } if ( Plot <> Nil ) then desc := 'NOTUSED ' + desc; Element := CharacterSearch( Adventure , Plot , Desc , GB ); if Element <> Nil then begin { Store the NPC's ID in the plot. } SetNAtt( Plot^.NA , NAG_ElementID , N , NAttValue( Element^.NA , NAG_Personal , NAS_CID ) ); Fast_Seek_Element[ 1 , N ] := Element; end else begin { No free NPCs were found. Bummer. } OK := False; end; end else if EKind[1] = 'S' then begin { This element is a SCENE. Find one. } { Pick one of the free scenes at random. } Element := SearchForScene( Adventure , Plot , GB , Desc ); if Element <> Nil then begin { Store the Scene ID in the plot. } SetNAtt( Plot^.NA , NAG_ElementID , N , Element^.S ); Fast_Seek_Element[ 1 , N ] := Element; end else begin { No free scenes were found. Bummer. } OK := False; end; end else if EKind[1] = 'M' then begin { This element is a METASCENE. Find one. } { Pick one of the free scenes at random. } Element := SearchForMetasceneEntryPoint( Adventure , Plot , GB , Desc ); if Element <> Nil then begin { Store the Scene ID in the plot. } SetNAtt( Plot^.NA , NAG_ElementID , N , Element^.Stat[ STAT_Destination ] ); Fast_Seek_Element[ 1 , N ] := FindMetascene( Adventure , Element^.Stat[ STAT_Destination ] ); { Change its type to SCENE, since it will be treated as a scene hereafter. } SetSAtt( PLOT^.SA , 'ELEMENT' + BStr( N ) + ' ' ); end else begin { No free scenes were found. Bummer. } OK := False; end; end else if EKind[1] = 'F' then begin { Faction element. } NumElements := NumFreeFaction( FindRoot( Adventure ) , Plot , GB , Desc ); if NumElements > 0 then begin { Pick one of the free scenes at random. } Element := FindFreeFaction( FindRoot( Adventure ) , Plot , GB , Desc , Random( NumElements ) + 1 ); Fast_Seek_Element[ 1 , N ] := Element; { Store the Scene ID in the plot. } SetNAtt( Plot^.NA , NAG_ElementID , N , Element^.S ); end else begin { No free scenes were found. Bummer. } OK := False; end; end else if EKind[1] = 'P' then begin { PreFab element. Check Plot/InvCom and } { retrieve it. } Element := DeployNextPrefabElement( GB , Adventure , Plot , N , MovePrefabs ); Fast_Seek_Element[ 1 , N ] := Element; OK := Element <> Nil; end else if EKind[1] = 'Q' then begin { Quest Scene. Find the metascene template being used. } Element := PrepareQuestSceneElement( Adventure , Plot , N ); Fast_Seek_Element[ 1 , N ] := Element; OK := Element <> Nil; end else if EKind[1] = 'A' then begin { Artifact. Select one at random, then deploy it as a prefab element. } Element := SelectArtifact( FindRoot( Adventure ) , desc ); if Element <> Nil then begin { We now want to deploy the artifact as though it were a } { prefab element. } Element^.Next := Plot^.InvCom; Plot^.InvCom := Element; Element^.Parent := Plot; DeployNextPrefabElement( GB , Adventure , Plot , N , MovePrefabs ); Fast_Seek_Element[ 1 , N ] := Element; OK := True; end else OK := False; end else if EKind[1] = 'N' then begin { New NPC. Create and format one, then deploy it as a prefab element. } Element := NewContentNPC( Adventure , Plot^.Parent , Plot , desc ); if Element <> Nil then begin { We now want to deploy the artifact as though it were a } { prefab element. } Element^.Next := Plot^.InvCom; Plot^.InvCom := Element; Element^.Parent := Plot; DeployNextPrefabElement( GB , Adventure , Plot , N , MovePrefabs ); Fast_Seek_Element[ 1 , N ] := Element; OK := True; end else OK := False; end else if ( EKind[1] = '.' ) then begin if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin { Insert the current scene as this element. } SetSAtt( PLOT^.SA , 'ELEMENT' + BStr( N ) + ' ' ); SetNAtt( PLOT^.NA , NAG_ElementID , N , RealSceneID( GB^.Scene ) ); Fast_Seek_Element[ 1 , N ] := GB^.Scene; OK := True; end else if Adventure^.G = GG_Scene then begin { Insert the current scope as this element. } SetSAtt( PLOT^.SA , 'ELEMENT' + BStr( N ) + ' ' ); SetNAtt( PLOT^.NA , NAG_ElementID , N , RealSceneID( Adventure ) ); Fast_Seek_Element[ 1 , N ] := Adventure; OK := True; end else OK := False; end; end; if Debug or ( GearName( Plot ) = 'DEBUG' ) then begin if not OK then DialogMsg( 'PLOT ERROR: ' + BStr( N ) + ' Element Not Found! ' + GearName( Plot ) ) else if desc <> '' then DialogMsg( 'PLOT ELEMENT ' + BStr( N ) + ': ' + BStr( ElementID( Plot , N ) ) + ' ' + GearName( Element ) ); end; FindElement := OK; end; Function SeekUrbanArea( RootScene: GearPtr ): GearPtr; { Locate an urban area contained within this scene. It may either be the } { scene itself or a subscene thereof. An urban area is somewhere that buildings } { may safely be placed. } Function IsUrbanArea( Scene: GearPtr ): Boolean; begin IsUrbanArea := ( Scene^.G = GG_Scene ) and AStringHasBString( SAttValue( Scene^.SA , 'TYPE' ) , 'URBAN' ); end; Function SearchSubScenes( LList: GearPtr ): GearPtr; { Search this list of sub-scenes to locate an urban scene. } var Scene: GearPtr; begin Scene := Nil; while ( Scene = Nil ) and ( LList <> Nil ) do begin if IsUrbanArea( LList ) then Scene := LList; if Scene = Nil then SearchSubScenes( LList^.SubCom ); LList := LList^.Next; end; SearchSubScenes := Scene; end; begin if IsUrbanArea( RootScene ) then Exit( RootScene ); SeekUrbanArea := SearchSubScenes( RootScene^.SubCom ); end; Procedure CreateElement( Adventure,Plot: GearPtr; N: Integer; GB: GameBoardPtr ); { Create and store the Nth element for this plot. } var Element,Destination,Faction,EntranceList,Dest2: GearPtr; Desc,EKind,SubStr,job: String; ID,P: Integer; begin { Error check } if ( N < 1 ) or ( N > Num_Plot_Elements ) then Exit; { Find the description for this element. } desc := UpCase( SAttValue( Plot^.SA , 'ELEMENT' + BStr( N ) ) ); DeleteWhiteSpace( Desc ); { Locate the !Near clause, if it exists. } P := Pos( '!N' , desc ); if P > 0 then begin SubStr := copy( desc , P , 255 ); ExtractWord( SubStr ); ID := ExtractValue( SubStr ); Destination := GetFSE( ID ); if ( Destination <> Nil ) and ( Destination^.G <> GG_MetaScene ) then begin Destination := FindActualScene( GB , FindSceneID( Destination , GB ) ); end; end else begin Destination := Nil; end; { Locate the !Comrade clause, if it exists. } { If no !Comrade found, try !Ally as well. } P := Pos( '!C' , desc ); if P = 0 then P := Pos( '!A' , desc ); if P > 0 then begin SubStr := copy( desc , P , 255 ); ExtractWord( SubStr ); ID := ExtractValue( SubStr ); Faction := GetFSE( ID ); if ( Faction <> Nil ) then begin Faction := SeekFaction( Adventure , GetFactionID( Faction ) ); end; end else begin Faction := Nil; end; if desc <> '' then begin EKind := ExtractWord( Desc ); if EKind[1] = 'C' then begin { This element is a CHARACTER. Create one. } job := SAttValue( Plot^.SA , 'NEVERFAIL' + BStr( N ) ); if job = '' then begin Element := RandomNPC( FindRoot( Adventure ) , 0 , RealSceneID( FindRootScene( GB^.Scene ) ) ); { Do the individualization. } IndividualizeNPC( Element ); job := SAttValue( Element^.SA , 'job' ); end else Element := LoadNewNPC( job , True ); if Element = Nil then Element := LoadNewNPC( 'MECHA PILOT' , True ); SetSAtt( Element^.SA , 'job <' + job + '>' ); SetSAtt( Element^.SA , 'TEAMDATA ' ); { Customize the character. } ApplyChardesc( Element , Desc ); { Store the NPC's ID in the plot. } ID := NewCID( Adventure ); SetNAtt( Element^.NA , NAG_Personal , NAS_CID , ID ); SetNAtt( Plot^.NA , NAG_ElementID , N , ID ); Fast_Seek_Element[ 1 , N ] := Element; end else if EKind[1] = 'M' then begin { MetaScene element. } { Create a building to use as the entrance, give it a unique ID number, } { then change the element type to Scene. } FilterElementDescription( Desc ); if Faction <> Nil then desc := desc + ' ' + SAttValue( Faction^.SA , 'DESIG' ); EntranceList := AggregatePattern( 'ENTRANCE_*.txt' , Series_Directory ); Element := CloneGear( FindNextComponent( EntranceList , Desc ) ); DisposeGear( EntranceList ); if Element = Nil then Element := LoadNewSTC( 'BUILDING' ); SetSAtt( Element^.SA , 'NAME <' + RandomBuildingName( Element ) + '>' ); { This is gonna be a new building, so don't stick it just anywhere. } { We want to place this element in the URBAN area of the scene. } { Note that if no urban area can be found, NEVERFAIL will actually } { fail. Woah! } Dest2 := SeekUrbanArea( Destination ); if Dest2 <> Nil then begin Destination := Dest2; ID := NewMetaSceneID( Adventure ); Element^.Stat[ STAT_Destination ] := ID; Element^.Scale := Dest2^.V; SetNAtt( Plot^.NA , NAG_ElementID , N , ID ); SetSAtt( Plot^.SA , 'ELEMENT' + BStr( N ) + ' ' ); end else begin DisposeGear( Element ); Element := Nil; end; end else begin Element := Nil; DialogMsg( 'ERROR- CreateElement asked to create element of type ' + EKind +'.' ); DialogMsg( 'Resultant plot ' + GearName( Plot ) + ' may fail.' ); end; { After this point, don't expect to use "desc" any more. It may have been } { modified or chopped into pieces above. } if Element <> Nil then begin if Destination <> Nil then begin if GB^.Scene = Destination then begin EquipThenDeploy( GB , Element , True ); end else begin InsertInvCom( Destination , Element ); end; if IsAScene( Destination ) and IsMasterGear( Element ) then begin ChooseTeam( Element , Destination ); end; end else begin InsertInvCom( Plot , Element ); end; if Faction <> Nil then begin SetNAtt( Element^.NA , NAG_Personal , NAS_FactionID , Faction^.S ); end; { Indicate that this is a prefab element, so if the plot fails it'll be deleted. } SetSAtt( Plot^.SA , 'ELEMENT' + BStr( N ) + ' <' + SAttValue( Plot^.SA , 'ELEMENT' + BStr( N ) ) + ' PREFAB>' ); end; end; end; Procedure AddGearXRContext( GB: GameBoardPtr; Adv,Part: GearPtr; var Context: String; palette_entry_code: Char ); { Add the context information for PART to CONTEXT. } var F: GearPtr; msg,m2: String; T: Integer; begin if Part <> Nil then begin Context := Context + ' ' + palette_entry_code + ':++'; { Add additional information about PART. } msg := SAttValue( Part^.SA , 'DESIG' ); if msg <> '' then Context := Context + ' ' + palette_entry_code + ':' + msg; msg := SAttValue( Part^.SA , 'CONTEXT' ); while msg <> '' do begin m2 := ExtractWord( msg ); if m2 <> '' then Context := Context + ' ' + palette_entry_code + ':' + m2; end; { If Part isn't a faction itself, add the designation of its faction. } if Part^.G <> GG_Faction then begin F := SeekFaction( Adv , NAttValue( Part^.NA , NAG_Personal , NAS_FactionID ) ); if F <> Nil then begin msg := SAttValue( F^.SA , 'DESIG' ); if msg <> '' then Context := Context + ' ' + palette_entry_code + ':' + msg; end; end else begin F := Part; end; if F <> Nil then begin { If this faction is also the PC's faction, add a PCFAC tag. } if F^.S = NAttValue( FindRoot( Adv )^.NA , NAG_Personal , NAS_FactionID ) then Context := Context + ' ' + palette_entry_code + ':PCFAC'; end else begin { No faction was found. } Context := Context + ' ' + palette_entry_code + ':NOFAC'; end; { If Part is a character, add relationship info. Unless the } { relationship is ENEMY, in which case it's covered below. } if Part^.G = GG_Character then begin Case NAttValue( Part^.NA , NAG_Relationship , 0 ) of NAV_Family: Context := Context + ' ' + palette_entry_code + ':FAMILY'; NAV_Lover: Context := Context + ' ' + palette_entry_code + ':LOVER'; NAV_Friend: Context := Context + ' ' + palette_entry_code + ':FRIEND'; end; m2 := SAttValue( Part^.SA , 'JOB_DESIG' ); if m2 <> '' then Context := Context + ' ' + palette_entry_code + ':' + m2; { If the part is a lancemate, add that info. } if NAttValue( Part^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam then begin Context := Context + ' ' + palette_entry_code + ':LANCE'; end; { Add the heroic/villainous component. } T := NAttValue( Part^.NA , NAG_CharDescription , NAS_Heroic ); if T > 0 then Context := Context + ' ' + palette_entry_code + ':GOOD_' else if T < 0 then Context := Context + ' ' + palette_entry_code + ':EVIL_'; { Add the character arc and attitude values. } AddXXCharContext( Part , Context , palette_entry_code ); end else if Part^.G = GG_Scene then begin m2 := SAttValue( Part^.SA , 'TERRAIN' ); if m2 <> '' then Context := Context + ' ' + palette_entry_code + ':' + m2; end; if IsArchEnemy( Adv , Part ) then Context := Context + ' ' + palette_entry_code + ':ENEMY'; if IsArchAlly( Adv , Part ) then Context := Context + ' ' + palette_entry_code + ':ALLY'; end else begin Context := Context + ' ' + palette_entry_code + ':--'; end; end; Procedure AddElementContext( GB: GameBoardPtr; Story: GearPtr; var Context: String; palette_entry_code: Char; Element_Num: Integer ); { Add the context details for element T of STORY. } var IsMetaScene: Boolean; Adv,E: GearPtr; msg: String; begin IsMetaScene := False; Adv := FindRoot( Story ); { If this element is a scene, tell whether it's a regular scene } { or a metascene. } msg := SAttValue( Story^.SA , 'ELEMENT' + BStr( Element_Num ) ); if ( msg <> '' ) and ( UpCase( msg[1] ) = 'S' ) then begin if ElementID( Story , Element_Num ) >= 0 then begin Context := Context + ' ' + palette_entry_code + ':perm'; end else begin Context := Context + ' ' + palette_entry_code + ':meta'; IsMetaScene := True; end; end; E := SeekPlotElement( Adv , Story , Element_Num , GB ); if ( E = Nil ) and IsMetaScene and ( FindSceneEntrance( Adv , GB , ElementID( Story , Element_Num ) ) <> Nil ) then begin Context := Context + ' ' + palette_entry_code + ':++'; end else begin AddGearXRContext( GB , Adv , E , Context , palette_entry_code ); end; end; Function DifficulcyContext( Threat: Integer ): String; { Return a string describing this difficulcy level. } begin if Threat > 80 then begin DifficulcyContext := '!Ex'; end else if Threat > 60 then begin DifficulcyContext := '!Hi'; end else if Threat > 40 then begin DifficulcyContext := '!Md'; end else if Threat > 20 then begin DifficulcyContext := '!Lo'; end else begin DifficulcyContext := '!Ne'; end; end; Function StoryPlotRequest( Story: GearPtr ): String; { Return the plotrequest to be used by this story. The plot request } { depends on the provided XXRAN_PATTERN attribute and the dramatic } { choice made by the player. } var it: String; C: GearPtr; begin { Start with the plot request. } it := SAttValue( Story^.SA , 'XXRAN_PATTERN' ); { Attach the current choice to the plot_request. } C := SeekDramaticChoice( NAttValue( Story^.NA , NAG_XXRan , NAS_DramaticChoice ) ); if C <> Nil then begin it := it + SAttValue( C^.SA , 'DESIG' ); end else begin it := it + 'INTRO'; end; StoryPlotRequest := it; end; Function StoryContext( GB: GameBoardPtr; Story: GearPtr ): String; { Describe the context of this story in a concise string. } const Merit_Badge_Tag: Array [1..NumMeritBadge] of String = ( 'STAR_', 'CHAMP', '', '', 'MORAL', '' ); var it,msg: String; T: Integer; LList: GearPtr; begin { Start with the basic context. } it := StoryPlotRequest( Story ) + ' ' + SAttValue( Story^.SA , 'CONTEXT' ); { Add tags for the choices made so far. } LList := Dramatic_Choices; while LList <> Nil do begin if NAttValue( Story^.NA , NAG_Completed_DC , LList^.V ) > 0 then it := it + ' :' + SAttValue( LList^.SA , 'DESIG' ); LList := LList^.Next; end; { Add tags for the merit badges earned by the PC. } if ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin { Use LList to hold the adventure, for now. } LList := FindRoot( GB^.Scene ); for t := 1 to NumMeritBadge do begin if ( Merit_Badge_Tag[ t ] <> '' ) and HasMeritBadge( LList , T ) then begin it := it + ' C:' + Merit_Badge_Tag[ t ]; end; end; end; { Add a description for the difficulcy rating. } it := it + ' ' + DifficulcyContext( NAttValue( Story^.NA , NAG_Narrative , NAS_DifficultyLevel ) ); { Add the extra random palette entries. } if ( Story^.G = GG_Story ) and ( Story^.S = GS_XRANStory ) then begin for t := 1 to Num_Plot_Elements do begin { If this element represents to a palette entry, add its info } { to the context. } msg := SAttValue( Story^.SA , 'palette_entry_code' + BStr( T ) ); if msg <> '' then AddElementContext( GB , Story , it , msg[1] , T ); end; end; StoryContext := it; end; Procedure InsertPFrags( Plot,Persona: GearPtr; const Context: String; ID: Integer ); { Prepare the persona for use in the game. Search its string attributes and } { add any persona fragments that are requested. } Function SpaceyString( msg: String ): String; { Return a version of msg with all the quotes replaced by } { spaces. } var T: Integer; begin for t := 1 to Length( msg ) do if msg[t] = '"' then msg[t] := ' '; SpaceyString := msg; end; var F: GearPtr; { The persona fragment to insert. } S,FS: SAttPtr; head,info,TypeLabel: String; Param: Array [1..8] of String; T: Integer; begin S := Persona^.SA; while S <> Nil do begin if S^.Info[1] = '*' then begin { This is a fragment request. Start by determining the relevant data. } head := RetrieveAPreamble( S^.Info ); DeleteFirstChar( head ); info := RetrieveAString( S^.info ); TypeLabel := ExtractWord( info ); for t := 1 to 8 do Param[t] := ExtractWord( Info ); { Next, search for a matching fragment... } F := FindNextComponent( persona_fragments , TypeLabel + ' ' + Context ); if F <> Nil then begin { If one was found, prep it for inclusion in the persona. } F := CloneGear( F ); FS := F^.SA; while FS <> Nil do begin { Format the strings. Tasks: } { - Rename "START" to "HEAD" } { - Replace %1% through %8% with parameters } { - Replace %id% with BStr( ID ) } if UpCase( RetrieveAPreamble( FS^.Info ) ) = 'START' then begin FS^.Info := head + ' <' + RetrieveAString( FS^.Info ) + '>'; end; ReplacePat( FS^.Info , '%id%' , BStr( ID ) ); ReplacePat( FS^.Info , '%1%' , Param[1] ); ReplacePat( FS^.Info , '%2%' , Param[2] ); ReplacePat( FS^.Info , '%3%' , Param[3] ); ReplacePat( FS^.Info , '%4%' , Param[4] ); ReplacePat( FS^.Info , '%5%' , Param[5] ); ReplacePat( FS^.Info , '%6%' , Param[6] ); ReplacePat( FS^.Info , '%7%' , Param[7] ); ReplacePat( FS^.Info , '%8%' , Param[8] ); FS := FS^.Next; end; { Copy everything to the main persona. } FS := F^.SA; while FS <> Nil do begin SetSAtt( Persona^.SA , FS^.Info ); FS := FS^.Next; end; { Increment the ID counter. } Inc( ID ); DisposeGear( F ); end else begin DialogMsg( 'ERROR: No persona fragment found for ' + TypeLabel + ' ' + SpaceyString( Context ) ); end; end; { - Replace %E1% through %E8% with element IDs } if Plot <> Nil then begin for t := 1 to Num_Plot_Elements do begin ReplacePat( S^.Info , '%E' + BStr( T ) + '%' , BStr( ElementID( Plot , T ) ) ); end; end; S := S^.Next; end; end; Function PersonalContext( Adv,NPC: GearPtr ): String; { Return the context for this NPC. The context should include the trait } { description and the faction designation. } { This function is used for selecting persona fragments. } var it: String; Theme: Integer; begin if NPC = Nil then exit( '' ); it := XNPCDesc( Nil , Adv , NPC ); { Add the theme here. } Theme := NAttValue( NPC^.NA , NAG_Personal , NAS_MechaTheme ); it := it + ' [MT' + BStr( Theme ) + ']'; PersonalContext := it; end; Procedure PrepAllPersonas( Adventure,Plot: GearPtr; GB: GameBoardPtr; MinID: Integer ); { Prepare the personas of this plot. } { Also store the mission recharge time for any NPCs involved. } var P,P2,NPC: GearPtr; Context: String; begin P := Plot^.SubCom; Context := SAttValue( Plot^.SA , 'CONTEXT' ); if ( Plot^.Parent <> Nil ) and ( Plot^.Parent^.G = GG_Story ) then begin Context := Context + StoryContext( GB , Plot^.Parent ); { end else begin Context := Context + SAttValue( Plot^.SA , 'CONTEXT' ); } end; while P <> Nil do begin P2 := P^.Next; if P^.G = GG_Persona then begin NPC := SeekPlotElement( Adventure , Plot , P^.S , GB ); if ( NPC <> Nil ) and ( GB <> Nil ) then SetNAtt( NPC^.NA , NAG_Personal , NAS_PlotRecharge , GB^.ComTime + 86400 ); InsertPFrags( Plot , P , Context + PersonalContext( Adventure , NPC ) , MinID ); end; P := P2; end; end; Procedure PrepMetaScenes( Adventure,Plot: GearPtr; GB: GameBoardPtr ); { Maps are stored by name, so each metascene needs a unique name. } { Since it already has a unique ID number this shouldn't be much trouble. } var M,Entrance,T: GearPtr; Context: String; begin if ( Plot^.Parent <> Nil ) and ( Plot^.Parent^.G = GG_Story ) then begin Context := StoryContext( GB , Plot^.Parent ); end else begin Context := SAttValue( Plot^.SA , 'CONTEXT' ) + ' ' + DifficulcyContext( NAttValue( Plot^.NA , NAG_Narrative , NAS_DifficultyLevel ) ); end; M := Plot^.SubCom; while M <> Nil do begin if ( M^.G = GG_MetaScene ) and ( M^.S >= 1 ) and ( M^.S <= Num_Plot_Elements ) then begin { Store the entrance of the metascene. We'll need it later. } Entrance := FindSceneEntrance( Adventure , GB , ElementID( Plot , M^.S ) ); if Entrance <> Nil then begin SetNAtt( M^.NA , NAG_Narrative , NAS_EntranceScene , FindSceneID( Entrance , GB ) ); end; { Prep the local personas. } T := M^.SubCom; while T <> Nil do begin if ( T^.G = GG_Persona ) and ( T^.S >= 1 ) and ( T^.S <= Num_Plot_Elements ) then begin { Replace S with the CID of element T^.S. } InsertPFrags( Plot , T , Context + ' ' + PersonalContext( Adventure , GetFSE( T^.S ) ) , 1 ); T^.S := ElementID( Plot , T^.S ); end; T := T^.Next; end; SetSAtt( M^.SA , 'NAME ' ); SetSAtt( M^.SA , 'CONTEXT <' + SAttValue( M^.SA , 'CONTEXT' ) + ' ' + Context + '>' ); end; M := M^.Next; end; end; Procedure InitPlot( Adventure,Plot: GearPtr; GB: GameBoardPtr ); { Initialize this plot. } { - Format rumor strings. } { - Provide unique names for all metascenes. } begin PrepMetaScenes( Adventure , Plot , GB ); PrepAllPersonas( Adventure , Plot , GB , 1 ); end; Procedure InitRandomLoot( LList: GearPtr ); { Search this list for random loot requests. Upon finding one, } { fill the resultant gear with sstuff. } const Default_Category = 'TREASURE'; var LootVal: LongInt; Loot_Category, Loot_Factions: String; begin while LList <> Nil do begin InitRandomLoot( LList^.SubCom ); InitRandomLoot( LList^.InvCom ); LootVal := NAttValue( LList^.NA , NAG_Narrative , NAS_RandomLoot ); if LootVal > 0 then begin { A request for random loot has been placed. Find the loot } { categories and the loot faction, then proceed to stuff. } Loot_Category := SAttValue( LList^.SA , 'LOOT_CATEGORY' ); if Loot_Category = '' then Loot_Category := Default_Category; Loot_Factions := 'GENERAL ' + SAttValue( LList^.SA , 'LOOT_FACTIONS' ); RandomLoot( LList , LootVal , Loot_Category , Loot_Factions ); SetNAtt( LList^.NA , NAG_Narrative , NAS_RandomLoot , 0 ); end; LList := LList^.Next; end; end; Procedure InitPrefabMoods( Slot, Plot: GearPtr ); { Moods can grab elements from either the plot that spawned them or the story } { that spawned the plot. Do that now. } var LList,Grab_Source: GearPtr; t,N: LongInt; desc: String; begin LList := Plot^.InvCom; while LList <> Nil do begin if LList^.G = GG_CityMood then begin for t := 1 to Num_Plot_Elements do begin { If an element grab is requested, process that now. } desc := SAttValue( LList^.SA , 'ELEMENT' + BStr( T ) ); if ( desc <> '' ) and ( UpCase( desc[1] ) = 'G' ) then begin ExtractWord( desc ); N := ExtractValue( desc ); { If we got a positive value, grab from the plot. Otherwise grab from } { the presumed story. } if N > 0 then Grab_Source := Plot else Grab_Source := Slot; N := Abs( N ); desc := SAttValue( Grab_Source^.SA , 'ELEMENT' + BStr( N ) ); if Desc = '' then begin DialogMsg( 'ERROR: ' + GearName( LList ) + ' tried to grab empty element ' + BStr( N ) + ' from ' + GearName( Grab_Source ) ); end else begin { Only copy over the first character of the element description, } { since that's all we need, and also because copying a PREFAB tag } { may result in story elements being unnessecarily deleted. } { Copy QuestScenes as regular scenes, since by the time this mood is deployed } { that's what they'll be. } if UpCase( desc[1] ) = 'Q' then desc[1] := 'S'; SetSAtt( LList^.SA , 'ELEMENT' + BStr( T ) + ' <' + desc[1] + '>' ); SetNAtt( LList^.NA , NAG_ElementID , T , ElementID( Grab_Source , N ) ); end; end; end; { For t ... } end; LList := LList^.Next; end; end; Function MatchPlotToAdventure( Scope,Slot,Plot: GearPtr; GB: GameBoardPtr; DoFullInit,MovePrefabs,Debug: Boolean ): Boolean; { This PLOT gear is meant to be inserted as an INVCOM of Slot. } { Perform the insertion, select unselected elements, and make sure } { that everything fits. } { SLOT must be a descendant of the adventure. } { SCOPE is usually the adventure- the the higest level at which element searches will take place. } { IMPORTANT: PLOT must not already be inserted into SLOT!!! } { This procedure also works for Stories. } var T: Integer; E: STring; Adventure,PFE: GearPtr; { Prefab Element } EverythingOK,OKNow: Boolean; begin { Error Check } if ( Plot = Nil ) or ( Slot = Nil ) then Exit; EverythingOK := True; { We need to stick the PLOT into the SLOT to prevent } { the FindElement procedure from choosing the same item for } { multiple elements. } InsertInvCom( Slot , Plot ); { Locate the adventure. It must be the root of Slot. } Adventure := FindRoot( Slot ); { Select Actors } { First clear the FastSeek array. } for t := 1 to Num_Plot_Elements do begin Fast_Seek_Element[ 0 , t ] := Nil; Fast_Seek_Element[ 1 , t ] := Nil; end; if ( Slot^.G = GG_Story ) then begin for t := 1 to Num_Plot_Elements do begin Fast_Seek_Element[ 0 , t ] := SeekPlotElement( Adventure , Slot , T , GB ); end; end else if Scope^.G = GG_CityMood then begin { We've been handed a mood rather than a scene. } for t := 1 to Num_Plot_Elements do begin Fast_Seek_Element[ 0 , t ] := SeekPlotElement( Adventure , Scope , T , GB ); end; { The parent of the mood should be the city it's installed in. This is what we want as } { our scope. } Scope := Scope^.Parent; end; for t := 1 to Num_Plot_Elements do begin { Check all the plot elements. Some of these may have been inherited from the SLOT. } if ( ElementID( Plot , T ) = 0 ) and EverythingOK then begin OkNow := FindElement( Scope , Plot , T , GB , MovePrefabs , Debug ); if AStringHasBString( SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ) , 'NEVERFAIL' ) and ( not OkNow ) then begin CreateElement( Adventure , Plot , T , GB ); if Debug or ( GearName( Plot ) = 'DEBUG' ) then begin DialogMsg( '...but NEVERFAIL has saved the day! ID=' + BStr( ElementID( Plot , T ) ) ); end; OkNow := ElementID( Plot , t ) <> 0; end; end else if EverythingOK then begin Fast_Seek_Element[ 1 , T ] := SeekPlotElement( Adventure , Plot , T , GB ); { If the element wasn't found, this will cause an error... unless, } { of course, we're dealing with a MetaScene or a new Quest Scene. } { These are the only element types that can not exist and still be valid. } E := SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ); if ( E <> '' ) and ( E[1] = 'Q' ) then begin OkNow := True; end else if ( E = '' ) or ( ( UpCase( E[1] ) <> 'S' ) and ( ElementID( Plot , t ) > 0 ) ) then begin OkNow := Fast_Seek_Element[ 1 , T ] <> Nil; end else begin OkNow := True; end; if Debug or ( GearName( Plot ) = 'DEBUG' ) then begin if not OKNow then DialogMsg( 'PLOT ERROR: ' + GearName( Plot ) + BStr( T ) + ' Predefined Element ' + BStr( ElementID( Plot , t ) ) + ' Not Found!' ) else DialogMsg( 'PLOT ELEMENT ' + BStr( T ) + ': ' + ElementName( Adventure , Plot , T , GB ) ); end; end; if Debug or ( GearName( Plot ) = 'DEBUG' ) then begin if ElementID( Plot , T ) <> 0 then DialogMsg( BStr( T ) + '=> ' + BStr( ElementID( Plot , T ) ) ); end; EverythingOK := EverythingOK and OKNow; end; if EverythingOK then begin { The plot has been successfully installed into the } { adventure. Initialize the stuff... rumor strings } { mostly. } { Quest content doesn't get initialized here- that gets done later. } if DoFullInit then InitPlot( Adventure , Plot , GB ); { Actually, quest content does get its treasures initialized here... } InitRandomLoot( Plot^.InvCom ); InitRandomLoot( Plot^.SubCom ); { ...and, now that I think about it, prefab moods should grab their elements now. } InitPrefabMoods( Slot , Plot ); { Also store the names of all known elements. They might come in handy later. } for t := 1 to Num_Plot_Elements do begin { Store the name of this element, which should still be stored in } { the FSE array. } if GB <> Nil then begin SetSAtt( Plot^.SA , 'NAME_' + BStr( T ) + ' <' + ElementName( Adventure , Plot , T , GB ) + '>' ); end else begin SetSAtt( Plot^.SA , 'NAME_' + BStr( T ) + ' <' + GearName( Fast_Seek_Element[ 1 , t ] ) + '>' ); end; end; end else begin { This plot won't fit in this adventure. Dispose of it. } { First get rid of any already-placed prefab elements. } for t := 1 to Num_Plot_Elements do begin E := SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ); if AStringHasBString( E , 'PREFAB' ) then begin PFE := SeekPlotElement( Adventure , Plot , T , GB ); if PFE <> Nil then begin if IsSubCom( PFE ) then begin RemoveGear( PFE^.Parent^.SubCom , PFE ); end else if IsInvCom( PFE ) then begin RemoveGear( PFE^.Parent^.InvCom , PFE ); end else if ( GB <> Nil ) and IsFoundAlongTrack( GB^.Meks , PFE ) then begin RemoveGear( GB^.Meks , PFE ); end; end; {if PFE <> Nil} end; end; RemoveGear( Plot^.Parent^.InvCom , Plot ); end; MatchPlotToAdventure := EverythingOK; end; Function InsertStory( Slot,Story: GearPtr; GB: GameBoardPtr ): Boolean; { Stick STORY into SLOT, selecting Actors and Locations } { as required. If everything is found, insert STORY as an InvCom } { of the SLOT. Otherwise, delete it. } begin InsertStory := MatchPlotToAdventure( FindRoot( Slot ) , Slot , Story , GB , True, True , False ); end; Function DoElementGrabbing( Scope,Slot,Plot: GearPtr ): Boolean; { Attempt to grab elements from the story to insert into the plot. } { Return TRUE if the elements were grabbed successfully, or FALSE } { if they could not be grabbed for whatever reason. } var EverythingOK: Boolean; T,N: Integer; Desc: String; P2: GearPtr; begin EverythingOK := True; if Scope^.G = GG_CityMood then Slot := Scope; if ( SLOT^.G = GG_Story ) or ( SLOT^.G = GG_CityMood ) then begin for t := 1 to Num_Plot_Elements do begin { If an element grab is requested, process that now. } desc := SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ); if ( desc <> '' ) and ( UpCase( desc[1] ) = 'G' ) then begin ExtractWord( desc ); N := ExtractValue( desc ); desc := SAttValue( Slot^.SA , 'ELEMENT' + BStr( N ) ); if Desc = '' then begin DialogMsg( 'ERROR: ' + GearName( Plot ) + ' tried to grab empty element ' + BStr( N ) + ' from ' + GearName( Slot ) ); EverythingOK := False; end else begin { Only copy over the first character of the element description, } { since that's all we need, and also because copying a PREFAB tag } { may result in story elements being unnessecarily deleted. } SetSAtt( Plot^.SA , 'ELEMENT' + BStr( T ) + ' <' + desc[1] + '>' ); SetNAtt( Plot^.NA , NAG_ElementID , T , ElementID( Slot , N ) ); { If this gear is a character, better see whether or not } { it is already involved in a plot. } if ( ElementID( Plot , T ) <> 0 ) and ( UpCase( Desc[1] ) = 'C' ) then begin { Clear the Plot's stat for now, to keep it from } { being returned by SeekPlotElement. } N := ElementID( Plot , T ); SetNAtt( Plot^.NA , NAG_ElementID , T , 0 ); P2 := FindPersonaPlot( FindRoot( Slot ) , N ); if P2 <> Nil then begin EverythingOK := False; end; SetNAtt( Plot^.NA , NAG_ElementID , T , N ); end; end; end; end; end; { If Slot^.G = GG_Story } DoElementGrabbing := EverythingOK; end; Function InsertSubPlot( Scope,Slot,SubPlot: GearPtr; GB: GameBoardPtr ): Boolean; { Stick SUBPLOT into SLOT, but better not initialize anything. } var InitOK: Boolean; begin InitOK := DoElementGrabbing( Scope , Slot , SubPlot ); InitOK := InitOK and MatchPlotToAdventure( Scope , Slot , SubPlot , GB , False , False , False ); InsertSubPlot := InitOK; end; Function InsertPlot( Scope,Slot,Plot: GearPtr; GB: GameBoardPtr; Threat: Integer ): Boolean; { Stick PLOT into SLOT, selecting Actors and Locations } { as required. If everything is found, insert PLOT as an InvCom } { of SLOT. Otherwise, delete it. } { All element searches will be restricted to descendants of SCOPE. } { If SLOT is a story, copy over grabbed elements and so on. } begin InsertPlot := InitMegaPlot( GB , Scope , Slot , Plot , Threat ) <> Nil; end; Function InsertMood( City,Mood: GearPtr; GB: GameBoardPtr ): Boolean; { This function will insert a mood into the adventure and move it to its correct place. } var AllOK: Boolean; TimeLimit: LongInt; Trigger: String; Dictionary: SAttPtr; begin AllOK := MatchPlotToAdventure( City , City , Mood , GB , False , False , False ); if AllOK then begin DelinkGear( City^.InvCom , Mood ); InsertSubCom( City , Mood ); { Set the time limit, if appropriate. } TimeLimit := NAttValue( Mood^.NA , NAG_MoodData , NAS_MoodTimeLimit ); if TimeLimit > 0 then SetNAtt( Mood^.NA , NAG_MoodData , NAS_MoodTimeLimit , TimeLimit + GB^.ComTime ); { Do string substitutions- %name1%..%name20%, %city% } Dictionary := Nil; SetSAtt( Dictionary , '%city% <' + GearName( City ) + '>' ); for TimeLimit := 1 to Num_Plot_Elements do begin SetSAtt( Dictionary , '%me_' + BStr( TimeLimit ) + '% <' + BStr( ElementID( Mood, TimeLimit ) ) + '>' ); SetSAtt( Dictionary , '%me_name' + BStr( TimeLimit ) + '% <' + SAttValue( Mood^.SA , 'NAME_' + BStr( TimeLimit ) ) + '>' ); end; ReplaceStrings( Mood , Dictionary ); DisposeSAtt( Dictionary ); { Run the mood's initialization code. } Trigger := 'UPDATE'; TriggerGearScript( GB , Mood , Trigger ); end; InsertMood := AllOK; end; Function InsertRSC( Source,Frag: GearPtr; GB: GameBoardPtr ): Boolean; { Insert random scene content, then save some information that will be } { needed later. } begin InsertRSC := MatchPlotToAdventure( FindRoot( Source ) , Source , Frag , GB , True , True , False );; end; Procedure EndPlot( GB: GameBoardPtr; Adv,Plot: GearPtr ); { This plot is over... } Procedure DelinkAndPutAway( var LList: GearPtr ); { Delink and put away everything you find that needs } { to be saved in this list. } var M,M2: GearPtr; begin M := LList; while M <> Nil do begin DelinkAndPutAway( M^.InvCom ); DelinkAndPutAway( M^.SubCom ); M2 := M^.Next; if NAttValue( M^.NA , NAG_ParaLocation , NAS_OriginalHome ) <> 0 then begin if StdPlot_Debug then DialogMsg( 'EndPlot: Putting away ' + GearName( M ) + '.' ); DelinkGear( LList , M ); PutAwayGlobal( GB , M ); end; M := M2; end; end; var P2,P3: GearPtr; begin { Deal with metascenes and other things that need to be cleaned up. } P2 := Plot^.SubCom; while ( P2 <> Nil ) do begin P3 := P2^.Next; { Deal with metascenes as well. } if ( P2^.G = GG_MetaScene ) then begin { This metascene has to be deleted. Remove everything from it. } if GB^.Scene = P2 then begin { If this scene is the current scene, just turn it into } { a dynamic scene and it'll be deleted automatically } { when the player exits. } DelinkGear( Plot^.SubCom , P2 ); if ( P2^.S >= 1 ) and ( P2^.S <= Num_Plot_Elements ) then P2^.S := ElementID( Plot , P2^.S ); InsertInvCom( FindRoot( Plot ) , P2 ); end else begin { Search for global gears in the invcom, storing them as needed. } DelinkAndPutAway( P2^.InvCom ); DelinkAndPutAway( P2^.SubCom ); DeleteFrozenLocation( GearName( P2 ) , GB^.Camp^.Maps ); end; end; P2 := P3; end; { Finally, set the PLOT's type to absolutely nothing, so it will } { be removed. } Plot^.G := GG_AbsolutelyNothing; end; Function AddRandomPlot( GB: GameBoardPtr; Slot: GearPtr; Context: String; EList: ElementTable; Threat: Integer ): Boolean; { Attempt to locate and add a plot with the requested context. } { EList is a list of element values with which to seed the plot. } { Return TRUE if the plot is successfully loaded, or FALSE otherwise. } var Adv,Scene,Part: GearPtr; T: Integer; msg: String; Shopping_List: NAttPtr; InitOK: Boolean; begin { Step One- Fill out the context, including element descriptions. } Adv := FindRoot( GB^.Scene ); Scene := FindRootScene( GB^.Scene ); for t := 1 to Num_Plot_Elements do begin { If this element represents a palette entry, add its info } { to the context. } if EList[ t ].EValue <> 0 then begin Part := SeekGearByElementDesc( Adv , EList[ t ].EType , EList[ t ].EValue , GB ); msg := BStr( T ); AddGearXRContext( GB , Adv , Part , Context , msg[1] ); end; end; { Step Two- Create a shopping list of contenders. } { If no appropriate plots are found, exit now. } Shopping_List := CreateComponentList( Standard_Plots , Context ); if Shopping_List = Nil then Exit( False ); { Step Three- Keep trying until the list is empty or a plot is loaded. } InitOK := False; repeat Part := SelectComponentFromList( Standard_Plots , Shopping_List ); if Part <> Nil then begin Part := CloneGear( Part ); { Copy over the parameters. } for t := 1 to Num_Plot_Elements do begin { If this element represents a palette entry, add its info } { to the context. } if EList[ t ].EValue <> 0 then begin SetNAtt( Part^.NA , NAG_ElementID , T , EList[ t ].EValue ); SetSAtt( Part^.SA , 'ELEMENT' + BStr( T ) + ' <' + EList[ t ].EType + '>' ); end; end; InitOK := InsertPlot( Adv , Slot , Part , GB , threat ); end else InitOK := False; until ( Shopping_List = Nil ) or InitOK; DisposeNAtt( Shopping_List ); AddRandomPlot := InitOK; end; Procedure PrepareNewComponent( Story: GearPtr; GB: GameBoardPtr ); { Load a new component for this story. } Procedure StoreXXRanHistory( C: GearPtr ); { Store the details of this component in the story. } var msg: String; T: Integer; begin AddSAtt( Story^.SA , 'XXRAN_SEQUENCE' , GearName( C ) ); msg := ''; for t := 1 to num_plot_elements do begin msg := msg + '[' + BStr( T ) + ':' + BStr( ElementID( Story , t ) ) + ']'; end; AddSAtt( Story^.SA , 'XXRAN_SSTATE' , msg ); msg := ''; for t := 1 to num_plot_elements do begin msg := msg + '[' + BStr( T ) + ':' + BStr( ElementID( C , t ) ) + ']'; end; AddSAtt( Story^.SA , 'XXRAN_PSTATE' , msg ); end; var C: GearPtr; Shopping_List: NAttPtr; plot_desc: String; MergeOK: Boolean; begin plot_desc := StoryContext( GB , Story ); Shopping_List := CreateComponentList( Standard_Plots , plot_desc ); { If xxran debug is on, print some extra information. } if XXRan_Debug then begin if NumNAtts( Shopping_List ) < 5 then begin DialogMsg( '[DEBUG] Only ' + BStr( NumNatts( Shopping_List ) ) + ' components for "' + plot_desc + '".' ); end; end; repeat if XXRan_Wizard and ( Shopping_List <> Nil ) then begin C := ComponentMenu( Standard_Plots , Shopping_List ); end else begin C := SelectComponentFromList( Standard_Plots , Shopping_List ); end; if C <> Nil then begin C := CloneGear( C ); MergeOK := InsertPlot( FindRoot( Story ) , Story , C , GB , NAttValue( Story^.NA , NAG_Narrative , NAS_DifficultyLevel ) ); end else MergeOK := False; until MergeOK or ( Shopping_List = Nil ); if MergeOK then begin { Assign a ComponentID to the new component. } SetNAtt( C^.NA , NAG_XXRan , NAS_ComponentID , NAttValue( Story^.NA , NAG_XXRan , NAS_ComponentID ) ); AddNAtt( Story^.NA , NAG_XXRan , NAS_ComponentID , 1 ); SetTrigger( GB , 'UPDATE' ); { Store the name of this component for reference. } StoreXXRanHistory( C ); end else begin DialogMsg( 'Plot deadend in ' + GearName( Story ) + ': ' + plot_desc ); DialogMsg( 'Send above information to "pyrrho12@yahoo.ca". Together, we can stomp out deadends.' ); end; DisposeNAtt( Shopping_List ); end; Function InsertArenaMission( Source,Mission: GearPtr; ThreatAtGeneration: Integer ): Boolean; { Insert an arena mission into the campaign. } var it: Boolean; P: GearPtr; T: Integer; EDesc: String; begin { Look through the elements. If KEY was requested, replace it with the } { core campaign enemy faction. } for t := 1 to Num_Plot_Elements do begin EDesc := UpCase( SAttValue( Mission^.SA , 'ELEMENT' + BStr( T ) ) ); if EDesc = 'KEY' then begin SetSAtt( Mission^.SA , 'ELEMENT' + BStr( T ) + ' ' ); SetNAtt( Mission^.NA , NAG_ElementID , T , NAttValue( Source^.NA , NAG_AHQData , NAS_CoreMissionEnemy ) ); end; end; { Attempt the plot insertion. } it := InsertPlot( Source , Source , Mission , Nil , ThreatAtGeneration ); { If the mission was successfully added, we need to do extra initialization. } if it then begin { Set correct PersonaIDs for all the personas involved. } P := Mission^.SubCom; while P <> Nil do begin if P^.G = GG_Persona then begin P^.S := ElementID( Mission , P^.S ); end; P := P^.Next; end; end; InsertArenaMission := it; end; Procedure UpdatePlots( GB: GameBoardPtr; Renown: Integer ); { It's time to update the plots. Check the city, and also its associated moods. } { For each one, check to see how many associated plots it has, then try to load } { a new plot if there's any room. } var Adv,City,Mood: GearPtr; Context: String; Function GetControllerID( Controller: GearPtr ): LongInt; { Get the Controller ID for this controller. If no Controller ID is found, } { assign a new one. } var CID: LongInt; begin CID := NAttValue( Controller^.NA , NAG_Narrative , NAS_ControllerID ); if CID = 0 then begin AddNAtt( Adv^.NA , NAG_Narrative , NAS_MaxControllerID , 1 ); CID := NAttValue( Adv^.NA , NAG_Narrative , NAS_MaxControllerID ); SetNAtt( Controller^.NA , NAG_Narrative , NAS_ControllerID , CID ); end; GetControllerID := CID; end; Function NumAttachedPlots( CID: LongInt ): Integer; { Return the total number of plots attached to this Controller ID. } var P: GearPtr; Total: Integer; begin P := Adv^.InvCom; Total := 0; while P <> Nil do begin if ( P^.G = GG_Plot ) and ( NAttValue( P^.NA , NAG_Narrative , NAS_ControllerID ) = CID ) then Inc( Total ); P := P^.Next; end; NumAttachedPlots := Total; end; Function NumAllowedPlots( Controller: GearPtr ): Integer; { Return the maximum number of plots that this controller can have attached. } begin if Controller^.G = GG_CityMood then begin NumAllowedPlots := Controller^.V; end else begin NumAllowedPlots := 10; end; end; Procedure AddAPlot( Controller: GearPtr ); { Select a legal plot for this controller and attempt to insert it into } { the adventure. } var plot_cmd: String; Plot: GearPtr; ShoppingList: NAttPtr; N: Integer; ItWorked: Boolean; begin { Determine the plot type being requested. If no explicit request is found, } { go with a *GENERAL plot. } plot_cmd := SAttValue( Controller^.SA , 'PLOT_TYPE' ); if plot_cmd = '' then plot_cmd := '*GENERAL'; plot_cmd := plot_cmd + ' ' + Context; { If the controller is a mood, add details of its first 9 elements. } if Controller^.G = GG_CityMood then begin for N := 1 to 9 do begin { If this element represents to a palette entry, add its info } { to the context. } if SAttValue( Controller^.SA , 'ELEMENT' + BStr( N ) ) <> '' then AddElementContext( GB , Controller , plot_cmd , BStr( N )[1] , N ); end; end; { Next, create a list of those plots which match the plot_cmd. } ShoppingList := Nil; Plot := Standard_Plots; N := 1; while Plot <> Nil do begin if ( Plot^.G = GG_Plot ) and ( StringMatchWeight( plot_cmd , SAttValue( Plot^.SA , 'REQUIRES' ) ) > 0 ) then begin SetNAtt( ShoppingList , N , N , 5 ); end; Plot := Plot^.Next; Inc( N ); end; { If we have some matches, select one at random and give it a whirl. } if ShoppingList <> Nil then begin Plot := CloneGear( SelectComponentFromList( Standard_Plots , ShoppingList ) ); { Mark this plot with our ComponentID, } { and store the plot stuff. } SetNAtt( Plot^.NA , NAG_Narrative , NAS_ControllerID , GetControllerID( Controller ) ); SetSAtt( Plot^.SA , 'SPCONTEXT <' + Context + '>' ); if StdPlot_Debug then DialogMsg( 'Attempting to insert ' + GearName( Plot ) ); { Attempt to add this plot to the adventure. } ItWorked := InsertPlot( Controller , Adv , Plot , GB , Renown ); if StdPlot_Debug then begin if ItWorked then DialogMsg( 'Plot insertion succeeded.' ) else DialogMsg( 'Plot insertion failed.' ); end; { Get rid of the shopping list. } DisposeNAtt( ShoppingList ); end; end; Procedure CheckAttachedPlots( Controller: GearPtr ); { Check to see how many plots are associated with this controller. } { If more plots are needed, add one. } var ControllerID: LongInt; Attached,Allowed: Integer; begin ControllerID := GetControllerID( Controller ); { Count how many plots are being used by the city and each of its moods. } Allowed := NumAllowedPlots( Controller ); {if XXRan_Debug and ( Controller^.G = GG_Scene ) then DialogMsg( GearName( Controller ) + ': ' + BStr( NumAttachedPlots( ControllerID ) ) + '/' + Bstr( Allowed ) );} if Allowed > 0 then begin Attached := NumAttachedPlots( ControllerID ); { If we have room for some more plots, try adding one. } if Attached < Allowed then begin AddAPlot( Controller ); end; end; end; begin { Locate the adventure and the city. These will be important. } Adv := FindRoot( GB^.Scene ); City := FindRootScene( GB^.Scene ); { If either the adventure or the city cannot be found, exit. } if ( Adv = Nil ) or ( Adv^.G <> GG_Adventure ) or ( City = Nil ) then Exit; { Determine the city context. This will be affected by moods. } Context := SceneContext( GB , City ) + DifficulcyContext( Renown ); Mood := City^.SubCom; while Mood <> Nil do begin if Mood^.G = GG_CityMood then begin AddTraits( Context , SAttValue( Mood^.SA , 'TYPE' ) ); end; Mood := Mood^.Next; end; Context := QuoteString( Context ); { Go through the city and the moods one by one. If there's a free space for a plot, } { attempt to load one. } CheckAttachedPlots( City ); Mood := City^.SubCom; while Mood <> Nil do begin if Mood^.G = GG_CityMood then CheckAttachedPlots( Mood ); Mood := Mood^.Next; end; end; Procedure UpdateMoods( GB: GameBoardPtr ); { Check through all the towns in the current world. Check the time limits on all } { moods found, removing those that have expired. If a town has no mood attached, } { consider attaching one. } Function SetNewMood( Scene: GearPtr ): Boolean; { Attempt to attach a new mood to this scene. Return TRUE if a mood was } { added successfully, or FALSE if it wasn't. } var scene_context: String; Mood: GearPtr; InitOK: Boolean; begin scene_context := SceneContext( GB , Scene ); Mood := FindNextComponent( Standard_Moods , scene_context ); InitOK := False; if Mood <> Nil then begin { We don't want to use the entry from the standard mood list; } { make a clone of it that we're free to whack around. } Mood := CloneGear( Mood ); SetNAtt( Mood^.NA , NAG_MoodData , NAS_MoodTimeLimit , 86400 + Random( 86400 ) + Random( 86400 ) ); InitOK := InsertMood( Scene , Mood , GB ); end; SetNewMood := InitOK; end; Procedure UpdateMoodsForScene( Scene: GearPtr ); { Check to see if this scene has any moods. Delete those moods which } { have outlived their usefulness. If no moods were found, consider } { adding one. } var Mood,M2: GearPtr; MoodFound: Boolean; TimeLimit: LongInt; begin { If Scene = Nil, we have a major problem. } if Scene = Nil then Exit; { No mood found yet- we haven't started searching! } MoodFound := False; { Look through all the moods in this scene and decide what to do with them. } Mood := Scene^.SubCom; while Mood <> Nil do begin M2 := Mood^.Next; if Mood^.G = GG_CityMood then begin { Even if this mood is getting deleted, we don't want to load } { a new one right away, so set MOODFOUND to TRUE... as long as } { it's a major mood. Otherwise, who cares about it? } if Mood^.S = GS_MajorMood then MoodFound := True; { Check the time limit now. } TimeLimit := NAttValue( Mood^.NA , NAG_MoodData , NAS_MoodTimeLimit ); if ( TimeLimit > 0 ) and ( TimeLimit < GB^.ComTime ) then begin RemoveGear( Scene^.SubCom , Mood ); end; end; Mood := M2; end; { If no moods were found, maybe add a new mood. } if ( not MoodFound ) and ( NAttValue( Scene^.NA , NAG_Narrative , NAS_MoodRecharge ) <= GB^.ComTime ) then begin { There's a chance of loading a new mood. } { NIEHH: Nothing Interesting Ever Happens Here. If the scene being examined is the } { town the PC is currently in, no new mood will be loaded. } if ( Random( 5 ) = 1 ) and ( Scene <> FindRootScene( GB^.Scene ) ) then begin { Try to set a mood. } { If setting the mood fails, set the recharge timer. } if not SetNewMood( Scene ) then SetNAtt( Scene^.NA , NAG_Narrative , NAS_MoodRecharge , GB^.ComTime + 7200 + Random( 43200 ) ); end else begin { No mood this time- try again in a day or so. } SetNAtt( Scene^.NA , NAG_Narrative , NAS_MoodRecharge , GB^.ComTime + 43200 + Random( 86400 ) ); end; end; end; var World, Scene: GearPtr; begin World := FindWorld( GB , GB^.Scene ); if World <> Nil then begin Scene := World^.SubCom; while Scene <> Nil do begin UpdateMoodsForScene( Scene ); Scene := Scene^.Next; end; end; end; Procedure CreateChoiceList( GB: GameBoardPtr; Story: GearPtr ); { It's time for the PC to make a dramatic choice. Create a list of } { legal choices, attempt to add them to the story, then mark the ones } { which load with a tag to indicate their nature. } var Context: String; LList,DC: GearPtr; DCRS,t,N: Integer; { Dramatic Choice Reward Seed, plus two counters } DCRList: Array [0..4] of Byte; { Dramatic Choice Reward List } begin { Step One: Determine the choice context. } Context := StoryContext( GB , Story ); { Determine which instant reward will be eligible for loading. } { The reward choices are numbered 11 to 20. 16 through 20 are second-tier } { copies of 11 through 15. } N := 0; { Only add a reward option if we are not yet at the conclusion. } if NAttValue( Story^.NA , NAG_Narrative , NAS_DifficultyLevel ) <= 80 then begin DCRS := NAttValue( Story^.NA , NAG_XXRan , NAS_DCRSeed ); if DCRS = 0 then begin DCRS := Random( 20000 ) + 1; SetNAtt( Story^.NA , NAG_XXRan , NAS_DCRSeed , DCRS ); end; DCRS := DCRS + NAttValue( Story^.NA , NAG_XXRan , NAS_EpisodeNumber ); for t := 11 to 15 do begin if NAttValue( Story^.NA , NAG_Completed_DC , T ) = 0 then begin DCRList[N] := T; Inc( N ); end; end; if N > 0 then begin N := DCRList[ DCRS mod N ]; end else begin { All of the first-tier rewards have been completed. Check the } { second tier. } N := 0; for t := 16 to 20 do begin if NAttValue( Story^.NA , NAG_Completed_DC , T ) = 0 then begin DCRList[N] := T; Inc( N ); end; end; if N > 0 then N := DCRList[ DCRS mod N ]; end; end; { Step Two: Go through the list of dramatic choices. Try to add all } { which apply in this situation. } LList := Dramatic_Choices; while LList <> Nil do begin { A choice can be added if: } { - It was the reward option selected and stored in N. } { or } { - Its REQUIRES field is satisfied by the context generated above. } { - It has not already been completed. } if ( LList^.V > 0 ) then begin if ( LList^.V = N ) or ( ( ( LList^.V < 11 ) or ( LList^.V > 20 ) ) and ( NAttValue( Story^.NA , NAG_Completed_DC , LList^.V ) = 0 ) and PartMatchesCriteria( Context , SAttValue( LList^.SA , 'REQUIRES' ) ) ) then begin DC := CloneGear( LList ); if InsertPlot( FindRoot( GB^.Scene ) , Story , DC , GB , NAttValue( Story^.NA , NAG_Narrative , NAS_DifficultyLevel ) ) then begin SetNAtt( DC^.NA , NAG_XXRan , NAS_IsDramaticChoicePlot , 1 ); end; end; end; LList := LList^.Next; end; end; Procedure ClearChoiceList( Story: GearPtr ); { The PC has apparently made a choice. Get rid of all the choice } { records from this story since we don't need them anymore. } var DC,DC2: GearPtr; begin DC := Story^.InvCom; while DC <> Nil do begin DC2 := DC^.Next; if NAttValue( DC^.NA , NAG_XXRan , NAS_IsDramaticChoicePlot ) <> 0 then begin RemoveGear( Story^.InvCom , DC ); end; DC := DC2; end; end; initialization persona_fragments := AggregatePattern( 'PFRAG_*.txt' , series_directory ); if persona_fragments = Nil then writeln( 'ERROR!!!' ); Standard_Plots := LoadRandomSceneContent( 'PLOT_*.txt' , series_directory ); Standard_Moods := AggregatePattern( 'MOOD_*.txt' , series_directory ); Dramatic_Choices := AggregatePattern( 'CHOICE_*.txt' , series_directory ); finalization DisposeGear( persona_fragments ); DisposeGear( Standard_Plots ); DisposeGear( Standard_Moods ); DisposeGear( Dramatic_Choices ); end. GH2/gh2arena.pp0000644000175000017500000022506511343146537012145 0ustar kaolkaolunit gh2arena; { Oct 23, 2006: } { This is it. I've been programming GH2 for a while now, and even though } { it's getting more and more playable all the time it'll still be a long } { time before it's really playable. So I started to think back to the humble } { beginnings of GH1. I decided that development of GH2 could be improved if } { I added arena mode; a simple, combat-focused use of the engine that would } { provide a fun game while the RPG campaign gets bulked up. I also got a few } { comments on the forum indicating that I should put more work into tactics } { mode. So here it is, the new humble beginning of GearHead arena, Mk2. } { 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} vidgfx; {$ELSE} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$ENDIF} const { ArenaMissionInfo moved to playwright.pp } NAG_MissionCoupon = 24; { Certain missions can only be taken a set number } { of times. } NumMissionCouponTypes = 2; NAS_SkillTrain_Coupon = 1; NAS_MechaFac_Coupon = 2; NAG_AHQSkillTrainer = 25; { Tells what skill trainers the PC has acquired. } NAG_AHQMechaSource = 26; { Tells what mecha factions the PC has acquired. } Procedure StartArenaCampaign; Procedure RestoreArenaCampaign( RDP: RedrawProcedureType ); implementation uses arenaplay,arenascript,interact,gearutil,narration,texutil,ghprop,rpgdice,ability, ghchars,ghweapon,movement,ui4gh,gearparser,playwright,randmaps,wmonster, pcaction,menugear,navigate,services,skilluse,training,backpack,chargen, description, {$IFDEF ASCII} vidinfo,vidmap,vidmenus; {$ELSE} {$IFDEF CUTE} cutemap,glmenus,glinfo; {$ELSE} glmap,glmenus,glinfo; {$ENDIF} {$ENDIF} Const GS_CharacterSet = 1; GS_CoreMissionSet = 2; NumArenaNPCs = 6; ANPC_FactionHead = 1; ANPC_Commander = 2; ANPC_Mechanic = 3; ANPC_Medic = 4; ANPC_Supply = 5; ANPC_Intel = 6; { Play Arena Mission selection types. } PAM_Regular = 0; PAM_Debug_Missions = 1; PAM_Debug_Core = 2; SaleFactor = 25; var ADR_Source: GearPtr; { Source gear for various redrawers. } ADR_SourceMenu: RPGMenuPtr; ADR_HQCamp: CampaignPtr; ADR_NumPilotsSelected,ADR_PilotsAllowed: Integer; ADR_PilotMenu,ADR_MechaMenu: RPGMenuPtr; ANPC_MasterPersona: GearPtr; Arena_Mission_Master_List,Core_Mission_Master_List: GearPtr; { *** REDRAW PROCEDURES *** } Procedure BasicArenaRedraw; { Just draw the basic setup for the arena mode menus. } begin SetupArenaDisplay; if ADR_PilotMenu <> Nil then DisplayMenu( ADR_PilotMenu , Nil ); if ADR_MechaMenu <> Nil then DisplayMenu( ADR_MechaMenu , Nil ); if ADR_HQCamp <> Nil then ArenaTeamInfo( ADR_HQCamp^.Source , ZONE_PCStatus ); end; Procedure HQMonologue( Adv: GearPtr; ArenaNPC: LongInt; Msg: String ); { NPC is about to deliver a line. } var NPC: GearPtr; A: Char; begin NPC := SeekGearByCID( Adv^.InvCom , ArenaNPC ); repeat BasicArenaRedraw; DoMonologueDisplay( Nil , NPC , Msg ); DoFlip; A := RPGKey; until IsMoreKey( A ); DialogMsg( '[' + GearName( NPC ) + ']: ' + Msg ); end; Procedure SelectAMissionRedraw; { Do the basic display, then draw the select forces dialog on top of that. } begin BasicArenaRedraw; SetupArenaMissionMenu; end; Procedure SelectAMForcesRedraw; { Do the basic display, then draw the select forces dialog on top of that. } begin BasicArenaRedraw; SetupMemoDisplay; CMessage( BStr( ADR_NumPilotsSelected ) + '/' + Bstr( ADR_PilotsAllowed ) + ' ' + MsgString( 'ARENA_SAMFRD_PilotsSelected' ) , ZONE_MemoMenu , InfoGreen ); end; Procedure ViewMechaRedraw; { The PC is viewing the mecha list. } var N: Integer; Part: GearPtr; begin SetupArenaDisplay; if ADR_PilotMenu <> Nil then DisplayMenu( ADR_PilotMenu , Nil ); if ADR_HQCamp <> Nil then ArenaTeamInfo( ADR_HQCamp^.Source , ZONE_PCStatus ); if ( ADR_SourceMenu <> Nil ) and ( ADR_Source <> Nil ) then begin N := CurrentMenuItemValue( ADR_SourceMenu ); if N > 0 then begin Part := RetrieveGearSib( ADR_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( Nil , Part , ZONE_ArenaInfo ); end; end; end; end; Procedure ViewPilotRedraw; { The PC is viewing the mecha list. } var N: Integer; Part: GearPtr; begin SetupArenaDisplay; if ADR_MechaMenu <> Nil then DisplayMenu( ADR_MechaMenu , Nil ); if ADR_HQCamp <> Nil then ArenaTeamInfo( ADR_HQCamp^.Source , ZONE_PCStatus ); if ( ADR_SourceMenu <> Nil ) and ( ADR_Source <> Nil ) then begin N := CurrentMenuItemValue( ADR_SourceMenu ); if N > 0 then begin Part := RetrieveGearSib( ADR_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( Nil , Part , ZONE_ArenaInfo ); end; end; end; end; Procedure ViewSourcePilotRedraw; { The PC is viewing a specific gear. } begin SetupArenaDisplay; if ADR_MechaMenu <> Nil then DisplayMenu( ADR_MechaMenu , Nil ); if ADR_HQCamp <> Nil then ArenaTeamInfo( ADR_HQCamp^.Source , ZONE_PCStatus ); if ADR_Source <> Nil then begin BrowserInterfaceInfo( Nil , ADR_Source , ZONE_ArenaInfo ); end; end; Procedure ViewSourceMechaRedraw; { The PC is viewing a specific gear. } begin SetupArenaDisplay; if ADR_PilotMenu <> Nil then DisplayMenu( ADR_PilotMenu , Nil ); if ADR_HQCamp <> Nil then ArenaTeamInfo( ADR_HQCamp^.Source , ZONE_PCStatus ); if ADR_Source <> Nil then begin BrowserInterfaceInfo( Nil , ADR_Source , ZONE_ArenaInfo ); end; end; Procedure AddPilotRedraw; { Draw the basic setup for the arena mode menus, then display character info } { for ADR_SOURCE. } begin BasicArenaRedraw; if ADR_Source <> Nil then CharacterDisplay( ADR_Source , Nil ); end; Procedure PurchaseHardwareRedraw; { The PC is buying hardware. Open the shopping display! } var N: Integer; Part: GearPtr; begin BasicArenaRedraw; SetupFHQDisplay; if ( ADR_SourceMenu <> Nil ) and ( ADR_Source <> Nil ) then begin N := CurrentMenuItemValue( ADR_SourceMenu ); if N > 0 then begin Part := RetrieveGearSib( ADR_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( Nil , Part , ZONE_ItemsInfo ); end; end; end; end; Procedure ViewListRedraw; { The PC is viewing either the pilots list or the mecha list. } var N: Integer; Part: GearPtr; begin BasicArenaRedraw; if ( ADR_SourceMenu <> Nil ) and ( ADR_Source <> Nil ) then begin N := CurrentMenuItemValue( ADR_SourceMenu ); if N > 0 then begin Part := RetrieveGearSib( ADR_Source , N ); if Part <> Nil then begin BrowserInterfaceInfo( Nil , Part , ZONE_ArenaInfo ); end; end; end; end; Procedure ViewSourceRedraw; { The PC is viewing a specific gear. } begin BasicArenaRedraw; if ADR_Source <> Nil then begin BrowserInterfaceInfo( Nil , ADR_Source , ZONE_ArenaInfo ); end; end; Procedure DoPurchaseRedraw; { The PC has browsed, and is now making a real purchase. } begin BasicArenaRedraw; SetupFHQDisplay; if ADR_Source <> Nil then begin BrowserInterfaceInfo( Nil , ADR_Source , ZONE_ItemsInfo ); end; end; { *** UTILITY FUNCTIONS *** } Function ArenaNPCMessage( Adv: GearPtr; ArenaNPC: LongInt; const Msg_Key: String ): String; { Try to find an appropriate message for the requested NPC to say. } var NPC: GearPtr; begin NPC := SeekGearByCID( Adv^.InvCom , ArenaNPC ); ArenaNPCMessage := NPCScriptMessage( Msg_Key , Nil , NPC , ANPC_MasterPersona ); end; Function FindMechasPilot( U , Mek: GearPtr ): GearPtr; { Search unit U to locate whatever pilot is assigned to mecha Mek. } { If no such pilot is found, clear Mek's PILOT attribute and } { return Nil. } var pc,mpc: GearPtr; name: String; begin { Begin by finding the pilot's name. } name := SAttValue( Mek^.SA , 'pilot' ); { Search through the unit's Sub looking for a character of } { this name. } pc := U^.SubCom; mpc := Nil; while ( pc <> Nil ) and ( mpc = Nil ) do begin if pc^.G = GG_Character then begin if GearName( PC ) = name then mpc := pc; end; pc := pc^.Next; end; { If the required pilot could not be found, } { delete this mecha's PILOT attribute. } if mpc = Nil then begin SetSAtt( Mek^.SA , 'pilot <>' ); end; FindMechasPilot := mpc; end; Function UnitSkill( HQCamp: CampaignPtr; Skill,SkStat: Integer ): Integer; { Return the "unit skill" value for the requested skill. Usually this } { will be the highest skill rank in the unit. } begin { Check through every mek on the board. } UnitSkill := SkillValue( HQCamp^.Source , Skill , SkStat ); end; Function ModifiedCost( HQCamp: CampaignPtr; BaseCost: LongInt; Skill: Integer ): LongInt; { The unit has to spend money on something, but this amount of money can be } { reduced based on a certain skill. When buying things the skill is shopping. } { When fixing things the skill is the appropriate repair skill. } begin { Determine the unit skill value. } if ( Skill >= 1 ) and ( Skill <= NumSkill ) then begin Skill := UnitSkill( HQCamp , Skill , STAT_Charm ); end else Skill := 0; { Each point of skill gives a 2% discount on the cost. } Skill := Skill * 2; { You can't get more than a 75% discount, no matter how you try. } if Skill > 75 then Skill := 75; { Return the modified cost. } ModifiedCost := BaseCost * ( 100 - Skill ) div 100; end; Function AHQRepairCost( HQCamp: CampaignPtr; Part: GearPtr ): LongInt; { Return the cash cost to repair this gear completely. } var Material: Integer; Total: LongInt; begin Total := 0; for Material := 0 to NumMaterial do begin Total := Total + ModifiedCost( HQCamp , RepairMasterCost( Part , Material ) , Repair_Skill_Needed[ Material ] ); end; AHQRepairCost := Total; end; Procedure DoFullRepair( HQCamp: CampaignPtr; Part: GearPtr ); { Do all the repair that this gear needs. } var Material: Integer; Cost,TRD: LongInt; begin for Material := 0 to NumMaterial do begin Cost := RepairMasterCost( Part , Material ); if Cost > 0 then begin TRD := TotalRepairableDamage( Part , Material ); ApplyRepairPoints( Part , Material , TRD , True ); AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , -Cost ); end; end; end; Function HQCash( HQCamp: CampaignPtr ): LongInt; { This is pretty much just a macro for returning the amount of } { cash this arena unit has. } begin HQCash := NAttValue( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits ); end; Function HQRenown( HQCamp: CampaignPtr ): LongInt; { This is pretty much just a macro for returning the amount of } { renown this arena unit has. } { This procedure will also check to make sure the renown doesn't } { drop below zero. } var it: LongInt; begin it := NAttValue( HQCamp^.Source^.NA , NAG_CharDescription , NAS_Renowned ); if it < 0 then begin it := 0; SetNAtt( HQCamp^.Source^.NA , NAG_CharDescription , NAS_Renowned , 0 ); end; HQRenown := it; end; Function HQFac( HQCamp: CampaignPtr ): Integer; { Return the faction of this arena unit. } begin HQFac := NAttValue( HQCamp^.Source^.NA , NAG_Personal , NAS_FactionID ); end; Function HQMaxMissions( HQCamp: CampaignPtr ): Integer; { Return the maximum number of missions this unit can have available. } { This number is based on the unit's conversation skill. } var C: Integer; begin C := ( UnitSkill( HQCamp , NAS_Conversation , STAT_Charm ) + 4 ) div 3; if C < 3 then C := 3; HQMaxMissions := C; end; Procedure ArenaReloadMaster( HQCamp: CampaignPtr; PC: GearPtr ); { Reload the mecha, and make the unit PAY!!! Money, that is. } { Bullets aren't free. } begin AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , -ReloadMasterCost( PC , Reload_All_Weapons ) ); DoReloadMaster( PC , Reload_All_Weapons ); end; Function ExpectedMissionReward( HQCamp: CampaignPtr; Scene: GearPtr ): LongInt; { Return the amount of cash the PC can expect if he completes this } { mission. } var TL,PayRate: LongInt; begin TL := HQRenown( HQCamp ); PayRate := NAttValue( Scene^.NA , NAG_ArenaMissionInfo , NAS_PayRate ); if PayRate = 0 then PayRate := 400; ExpectedMissionReward := Calculate_Reward_Value( Nil , TL , PaYRate ); end; Procedure PrepMission( HQCamp: CampaignPtr; Scene: GearPtr ); { Prepare the mission right before combat. Mostly, this just } { involves initializing the cash payout counter and NPCs. } var F,TL: LongInt; M: GearPtr; Desc: String; begin TL := HQRenown( HQCamp ); SetNAtt( Scene^.NA , NAG_ArenaMissionInfo , NAS_Pay , ExpectedMissionReward( HQCamp , Scene ) ); SetSAtt( Scene^.SA , 'name <>' ); Desc := SAttValue( Scene^.SA , 'TYPE' ) + ' ' + DifficulcyContext( TL ); SetSAtt( Scene^.SA , 'TYPE <' + Desc + '>' ); { Prep the NPCs to the correct level. } M := Scene^.InvCom; while M <> Nil do begin if M^.G = GG_Character then begin { Set the faction. } F := NAttValue( M^.NA , NAG_Personal , NAS_FactionID ); if F < 0 then SetNAtt( M^.NA , NAG_Personal , NAS_FactionID , ElementID( Scene , Abs( F ) ) ); SetSkillsAtLevel( M , TL ); end; M := M^.Next; end; end; Function NumMissions( HQCamp: CampaignPtr ): Integer; { Return the number of missions currently waiting for the PC. } var M: GearPtr; N: Integer; begin N := 0; M := HQCamp^.Source^.InvCom; while M <> Nil do begin if M^.G = GG_Scene then Inc( N ); M := M^.Next; end; NumMissions := N; end; Function GetMission( HQCamp: CampaignPtr; N: Integer ): GearPtr; { Retrieve mission N. } var it,M: GearPtr; begin M := HQCamp^.Source^.InvCom; it := Nil; while ( M <> Nil ) and ( it = Nil ) do begin if M^.G = GG_Scene then begin Dec( N ); if N = 0 then it := M; end; M := M^.Next; end; GetMission := it; end; Function HQContext( HQCamp: CampaignPtr ): String; { Return a context for this arena unit. } { The context is used to determine what missions can be } { loaded. } var Fac: GearPtr; HQC: String; Renown,T: Integer; begin { Start with the faction designation. } Fac := SeekCurrentLevelGear( HQCamp^.Source^.InvCom , GG_Faction , HQFac( HQCamp ) ); if Fac <> Nil then begin HQC := SAttValue( Fac^.SA , 'TYPE' ) + ' ' + SAttValue( Fac^.SA , 'DESIG' ); end else begin HQC := 'FDFOR MILITARY'; end; { Add the difficulcy level. } Renown := HQRenown( HQCamp ); HQC := HQC + ' ' + DifficulcyContext( Renown ); { Add tags for the skills and mecha factions that the PC hasn't earned yet. } for t := 1 to NumSkill do begin if NAttValue( HQCamp^.Source^.NA , NAG_AHQSkillTrainer , T ) = 0 then begin HQC := HQC + ' [s' + BStr( T ) + ']'; end; end; Fac := Factions_List; while Fac <> Nil do begin if NAttValue( HQCamp^.Source^.NA , NAG_AHQMechaSource , Fac^.S ) = 0 then begin HQC := HQC + ' [f' + BStr( Fac^.S ) + ']'; end; Fac := Fac^.Next; end; HQContext := HQC; end; Function HQCoupons( HQCamp: CampaignPtr ): STring; { Return the coupons this campaign has. } const Coupons_Per_Level: Array [1..NumMissionCouponTypes,1..5] of Byte = ( ( 2, 4, 6, 8, 10 ), { Skill Trainers } ( 1, 2, 3, 4, 5 ) { Mecha Factions } ); Coupon_Tag: Array [1..NumMissionCouponTypes] of String = ( 'SKILL_TRAIN_MISSION', 'MECHA_SOURCE_MISSION' ); Function CanAddCoupon( N: Integer ): Boolean; { You can add this coupon if no currently loaded mission is using it. } var M: GearPtr; CAC:Boolean; begin { Assume true until shown false. } CAC := True; M := HQCamp^.Source^.InvCom; while M <> Nil do begin if ( M^.G = GG_Scene ) and AStringHasBString( SAttValue( M^.SA , 'REQUIRES' ) , Coupon_Tag[ N ] ) then CAC := False; M := M^.Next; end; CanAddCoupon := CAC; end; var HQC: String; Renown,T: Integer; begin HQC := ''; { Add any mission coupons that haven't been spent yet. } { Determine the faction level. } Renown := HQRenown( HQCamp ); if Renown < 1 then Renown := 1; Renown := ( Renown + 19 ) div 20; { Check for coupons. } for t := 1 to NumMissionCouponTypes do begin if ( Coupons_Per_Level[ T , Renown ] - NAttValue( HQCamp^.Source^.NA , NAG_MissionCoupon , T ) ) > 0 then begin { We have a coupon left. Add a note. } if CanAddCoupon( T ) then begin HQC := HQC + ' ' + Coupon_Tag[ T ]; end; end; end; HQCoupons := HQC; end; Function AddCoreMission( HQCamp: CampaignPtr ): Boolean; { Add a core mission. If there's a mission stored in the core mission holder, } { and it hasn't been completed yet (check CoreMissionID against the CoreMissionStep) } { then use that one. Otherwise generate a new mission. } { Return TRUE if the core mission was generated successfully, or FALSE if the } { generation fails. This procedure should print an error message if the generation } { fails, since that's a pretty serious thing. } Function CoreCampaignContext( CMStep: Integer ): String; { Return the context of the core campaign. This includes the } { story state, the difficulcy level, the context of the player's faction (P:), } { and the context of the enemy faction (F:). } var Context: String; Fac: GearPtr; begin { Determine the campaign context. This is stored right in the adventure gear itself. } Context := SAttValue( HQCamp^.Source^.SA , 'CORE_CONTEXT' ); if Context = '' then begin Context := '+P--'; SetSAtt( HQCamp^.Source^.SA , 'CORE_CONTEXT <+P-->' ); end; { Locate the player faction and the element faction. Provide context for both. } Fac := SeekCurrentLevelGear( HQCamp^.Source^.InvCom , GG_Faction , HQFac( HQCamp ) ); AddGearXRContext( Nil , HQCamp^.Source , Fac , Context , 'P' ); Fac := SeekCurrentLevelGear( HQCamp^.Source^.InvCom , GG_Faction , NAttValue( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionEnemy ) ); AddGearXRContext( Nil , HQCamp^.Source , Fac , Context , 'F' ); { Add the difficulcy context. } Context := COntext + ' ' + DifficulcyContext( CMStep * 10 + 5 ); CoreCampaignContext := Context; end; Function NewCoreMissionPrototype( CMSet: GearPtr ): GearPtr; { Select a new core mission for the next step in the progress. } { Set its core mission ID, and return a pointer to it. } { If no appropriate mission can be found, print an error message and return Nil. } var CMStep: Integer; Context: String; CM,CMProto,CMTest: GearPtr; ShoppingList: NAttPtr; begin { Determine the threat level of this mission. } CMStep := NAttValue( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionStep ) + 1; { Determine the campaign context. } Context := CoreCampaignContext( CMStep ); { Create the shopping list of potential candidates. } ShoppingList := CreateComponentList( Core_Mission_Master_List , Context ); { As long as we still have candidates, look for one to select. } { Attempt to load it into the adventure- if loading succeeds, it's } { a good one. Delete the temporary copy and stick the clone in the set. } CMProto := Nil; while ( ShoppingList <> Nil ) and ( CMProto = Nil ) do begin CM := SelectComponentFromList( Core_Mission_Master_List , ShoppingList ); CMTest := CloneGear( CM ); if InsertArenaMission( HQCamp^.Source , CMTest , HQRenown( HQCamp ) ) then begin CMProto := CloneGear( CM ); InsertInvCom( CMSet , CMProto ); RemoveGear( HQCamp^.Source^.InvCom , CMTest ); end; end; { If no mission was found, print a debugging message along with the } { context so that future generations can learn from my mistakes. Or } { so I can add a new mission to fill in the gap. } if CMProto = Nil then begin Dialogmsg( 'ERROR: Core mission not found for context:' + Context ); end; { Dispose of the shopping list. } DisposeNAtt( ShoppingList ); { Return a pointer to the new mission. } NewCoreMissionPrototype := CMProto; end; var CMSet,CMProto,CM: GearPtr; AddOK: Boolean; begin AddOK := False; { Start by locating the core mission set, if it exists. } CMSet := SeekCurrentLevelGear( HQCamp^.Source^.InvCom , GG_Set , GS_CoreMissionSet ); if CMSet = Nil then begin { We don't have a Core Mission Set. Horrors! Better add one. } CMSet := NewGear( Nil ); CMSet^.G := GG_Set; CMSet^.S := GS_CoreMissionSet; InsertInvCom( HQCamp^.Source , CMSet ); end; { This set should contain a single gear- the core mission. If this mission } { doesn't exist, or if it has already been completed, better generate another } { one. } CMProto := CMSet^.InvCom; if CMProto = Nil then begin CMProto := NewCoreMissionPrototype( CMSet ); end else if NAttValue( CMProto^.NA , NAG_ArenaMissionInfo , NAS_IsCoreMission ) <= NAttValue( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionStep ) then begin RemoveGear( CMSet^.InvCom , CMProto ); CMProto := NewCoreMissionPrototype( CMSet ); end; { If the prototype was found, insert a clone. } if CMProto <> Nil then begin CM := CloneGear( CMProto ); AddOK := InsertArenaMission( HQCamp^.Source , CM , HQRenown( HQCamp ) ); if not AddOK then begin DialogMsg( 'ERROR: Insertion of core mission ' + GearName( CMProto ) + ' failed.' ); end; end; AddCoreMission := AddOK; end; Procedure AddMissions( HQCamp: CampaignPtr; N: Integer ); { Refresh the missions. Yay! Basically make sure there are some missions to } { choose between. } Procedure AddAMission( var ShoppingList: NAttPtr ); { Add a mission from the provided list. } var MissionOK: Boolean; M: GearPtr; begin MissionOK := False; while ( ShoppingList <> Nil ) and not MissionOK do begin M := CloneGear( SelectComponentFromList( Arena_Mission_Master_List , ShoppingList ) ); if InsertArenaMission( HQCamp^.Source , M , HQRenown( HQCamp ) ) then begin MissionOK := True; end; end; end; Function NoCoreMissionFound: Boolean; { Return TRUE if none of the pending missions belong to the core campaign, } { or FALSE if one of them does. } var M: GearPtr; NCMF: Boolean; begin { Assume true unless proven otherwise. } NCMF := True; M := HQCamp^.Source^.InvCom; while M <> Nil do begin if ( M^.G = GG_Scene ) and ( NAttValue( M^.NA , NAG_ArenaMissionInfo , NAS_IsCoreMission ) <> 0 ) then NCMF := False; M := M^.Next; end; NoCoreMissionFound := NCMF; end; Function CanAddCoreMission: Boolean; { Return TRUE if a core mission can currently be loaded, or FALSE if } { it can't be. It can be loaded if: } { A) the player hasn't already completed the core campaign } { B) the unit's renown is high enough to load the next step } { C) there isn't currently a core mission in the pending list } var CMS: Integer; begin { Determine the core mission step. } CMS := NAttValue( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionStep ); CanAddCoreMission := ( CMS < 8 ) and ( ( ( CMS + 1 ) * 10 ) <= HQRenown( HQCamp ) ) and NoCoreMissionFound; end; var Context: String; MissionList,RewardList: NAttPtr; begin { Start by determining the arena unit's context. This is determied by the } { current faction being fought for plus the arena unit's renown. } Context := HQContext( HQCamp ); { Next create the list of potential content to add. } { There are two content lists- regular content, and reward content. One reward mission } { should be loaded per five regular missions. A core campaign mission could also be selected, } { but this is handled differently. } MissionList := CreateComponentList( Arena_Mission_Master_List , '*MISSION ' + Context ); RewardList := Nil; while N > 0 do begin { Decrement the mission timers, and add special mission types as appropriate. } AddNAtt( HQCamp^.Source^.NA , NAG_AHQData , NAS_RewardMissionTimer , -1 ); if CanAddCoreMission then AddNAtt( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionTimer , -1 ); if ( NAttValue( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionTimer ) < 0 ) and CanAddCoreMission then begin if AddCoreMission( HQCamp ) then begin { Set the mission recharge timer to something large. } SetNAtt( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionTimer , 20 + Random( 10 ) + N ); end else begin { Adding the core mission failed for some reason, probably } { a plot deadend. } SetNAtt( HQCamp^.Source^.NA , NAG_AHQData , NAS_CoreMissionTimer , 10 + N ); Inc( N ); end; end else if ( NAttValue( HQCamp^.Source^.NA , NAG_AHQData , NAS_RewardMissionTimer ) < 0 ) then begin RewardList := CreateComponentList( Arena_Mission_Master_List , '*REWARD ' + Context + ' ' + HQCoupons( HQCamp ) ); if RewardList <> Nil then begin AddAMission( RewardList ); end else begin AddAMission( MissionList ); end; SetNAtt( HQCamp^.Source^.NA , NAG_AHQData , NAS_RewardMissionTimer , 4 ); DisposeNAtt( RewardList ); end else begin AddAMission( MissionList ); end; Dec( N ); end; DisposeNAtt( MissionList ); DisposeNAtt( RewardList ); end; Procedure UpdateMissions( HQCamp: CampaignPtr ); { Check to see if there are enough missions. Maybe delete some of them. } { Bring the total back to max. } var N: Integer; M: GearPtr; begin { Step one- delete some missions at random. } N := NumMissions( HQCamp ); Repeat if N > 1 then begin M := GetMission( HQCamp , Random( N ) + 1 ); if M <> Nil then RemoveGear( HQCamp^.Source^.InvCom , M ); end; Dec( N ); until ( N < 1 ) or ( Random( 3 ) <> 1 ); { Step one point five- remove the core campaign mission if the unit's } { renown has fallen beneath the critical threshold. } M := HQCamp^.Source^.InvCom; while M <> Nil do begin if ( M^.G = GG_Scene ) and ( NAttValue( M^.NA , NAG_ArenaMissionInfo , NAS_IsCoreMission ) <> 0 ) then begin if HQRenown( HQCamp ) < ( ( NAttValue( M^.NA , NAG_ArenaMissionInfo , NAS_IsCoreMission ) + 1 ) * 10 ) then begin RemoveGear( HQCamp^.Source^.InvCom , M ); Break; end; end; M := M^.Next; end; { Step two- make sure we have enough missions. } AddMissions( HQCamp , HQMaxMissions( HQCamp ) - NumMissions( HQCamp ) ); end; Procedure ClearWeaponRecharge( LList: GearPtr ); { To prevent strange bugs from happening, clear all weapon recharge times } { upon leaving combat. } begin while LList <> Nil do begin SetNAtt( LList^.NA , NAG_WeaponModifier , NAS_Recharge , 0 ); ClearWeaponRecharge( LList^.InvCom ); ClearWeaponRecharge( LList^.SubCom ); LList := LList^.Next; end; end; Procedure PostMissionCleanup( HQCamp: CampaignPtr; PCForces: GearPtr ); { After a battle is over, deal with the survivors. There are survivors? } { I must not have made the mission hard enough... } var PC: GearPtr; Cost: LongInt; begin { To start with, recharge/repair everyone in the list. } { We'll make three passes: First, "repair" all characters. } { Second, repair all mecha. Third, restock all mecha and characters. } { First pass- medical attention for characters. } PC := PCForces; while PC <> Nil do begin { Status effects get repaired for free- otherwise, a low-on-cash } { arena unit could enter the next battle with a mecha still on fire from their } { last battle, and that would suck. Also remove conditions now, since } { everybody needs that done. } StripNAtt( PC , NAG_StatusEffect ); StripNAtt( PC , NAG_Condition ); ClearWeaponRecharge( PC^.SubCom ); ClearWeaponRecharge( PC^.InvCom ); if PC^.G = GG_Character then begin Cost := AHQRepairCost( HQCamp , PC ); if ( Cost > 0 ) and ( HQCash( HQCamp ) >= Cost ) then begin DoFullRepair( HQCamp , PC ); end; end; PC := PC^.Next; end; { Second pass- repair all mecha. } PC := PCForces; while PC <> Nil do begin if PC^.G = GG_Mecha then begin Cost := AHQRepairCost( HQCamp , PC ); if ( Cost > 0 ) and ( HQCash( HQCamp ) >= Cost ) then begin DoFullRepair( HQCamp , PC ); end; end; PC := PC^.Next; end; { Third pass- reload all weapons. } PC := PCForces; while PC <> Nil do begin Cost := ReloadMasterCost( PC , Reload_All_Weapons ); if ( Cost > 0 ) and ( HQCash( HQCamp ) >= Cost ) then begin ArenaReloadMaster( HQCamp , PC ); end; PC := PC^.Next; end; { Once that's been taken care of, stick the PCs back in the unit. } InsertSubCom( HQCamp^.Source , PCForces ); { Finally update the missions. } UpdateMissions( HQCamp ); end; Procedure AddDamageReloadStatus( HQCamp: CampaignPtr; M: GearPtr; var msg: String ); { If this model needs repairs or reloading, indicate that here. } var POK: Integer; { Percent OK } begin if AHQRepairCost( HQCamp , M ) > 0 then begin POK := PercentDamaged( M ); if POK = 100 then POK := 99; msg := msg + ' (%' + BStr( POK ) + ')'; end; if ReloadMasterCost( M , Reload_All_Weapons ) > 0 then msg := msg + ' -ammo-'; end; Function AHQMechaName( HQCamp: CampaignPtr; Mek: GearPtr ): String; { Return the name of this mecha along with its pilot. } { If the mecha's pilot can't be found, clear the PILOT string attribute. } var name,pname: String; Pilot: GearPtr; begin name := FullGearName( Mek ); pname := SAttValue( Mek^.SA , 'pilot' ); if pname <> '' then begin Pilot := SeekGearByName( HQCamp^.Source^.SubCom , pname ); if Pilot = Nil then begin { Oops, no pilot. Must have died or been removed } { from the unit. Fix this mecha's data. } SetSatt( Mek^.SA , 'pilot <>' ); end else begin { Pilot has been found. Add to the name. } name := name + ' [' + pname + ']'; end; end; AddDamageReloadStatus( HQCamp , Mek , name ); AHQMechaName := name; end; Procedure UpdatePilotMechaMenus( HQCamp: CampaignPtr ); { Add the pilots and the mecha to their respective menus. } { If any mecha/pilot matchups are invalid, clear them. } { This is done via the above function, BTW. } var N,PMI,MMI: Integer; M: GearPtr; name: String; begin { If either of the menus currently exist, dispose of them. } if ADR_PilotMenu <> Nil then begin PMI := ADR_PilotMenu^.selectitem; DisposeRPGMenu( ADR_PilotMenu ); end else begin PMI := 1; end; if ADR_MechaMenu <> Nil then begin MMI := ADR_MechaMenu^.selectitem; DisposeRPGMenu( ADR_MechaMenu ); end else begin MMI := 1; end; { Allocate the menus. } ADR_PilotMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaPilotMenu ); ADR_MechaMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaMechaMenu ); { Go through the unit contents, adding to whichever menu is appropriate. } M := HQCamp^.Source^.SubCom; N := 1; while M <> Nil do begin if M^.G = GG_Character then begin name := GearName( M ); AddDamageReloadStatus( HQCamp , M , name ); AddRPGMenuItem( ADR_PilotMenu , name , N ); end else if M^.G = GG_Mecha then begin { For a mecha, add not just the mecha's name but also the } { pilot's name. If no pilot can be found, clean up that mess. } name := AHQMechaName( HQCamp , M ); AddRPGMenuItem( ADR_MechaMenu , name , N ); end else begin AddRPGMenuItem( ADR_MechaMenu , '*' + GearName( M ) , N ); end; Inc( N ); M := M^.Next; end; { Sort the menus. } RPMSortAlpha( ADR_PilotMenu ); RPMSortAlpha( ADR_MechaMenu ); SetItemByPosition( ADR_PilotMenu , PMI ); SetItemByPosition( ADR_MechaMenu , MMI ); end; Procedure StripAllMecha( var PC: GearPtr ); { We've been provided with a linked list. Remove everything } { that isn't a mecha. } var Mek,M2: GearPtr; Total: LongInt; begin Mek := PC; Total := 0; while Mek <> Nil do begin M2 := Mek^.Next; if Mek^.G <> GG_Character then begin Total := Total + GearCost( Mek ); RemoveGear( PC , Mek ); end; Mek := M2; end; if ( PC <> Nil ) and ( PC^.Next = Nil ) then AddNAtt( PC^.NA , NAG_Experience, NAS_Credits , Total ); end; Function HasSkillTrainers( HQCamp: CampaignPtr ): Boolean; { Return TRUE if this campaign has some skill trainers, or FALSE otherwise. } var HasTrainer: Boolean; T: Integer; begin HasTrainer := False; for T := 1 to NumSkill do begin if NAttValue( HQCamp^.Source^.NA , NAG_AHQSkillTrainer , T ) <> 0 then begin HasTrainer := True; Break; end; end; HasSkillTrainers := HasTrainer; end; { *** USER INTERFACE BITS *** } procedure AddPilotToUnit( HQCamp: CampaignPtr ); { Browse the disk for a character file. If one is selected, } { display the character's stats and ask whether or not to hire } { this character. If hired, add the character to the unit, } { save the game, then delete the character's individual file. } Function HiringPrice( PC: GearPtr ): LongInt; { Return the price of recruiting this character into the unit. } begin HiringPrice := GearValue( PC ); end; Function IChooseYou( PC: GearPtr ): Boolean; { Maybe add this character to the unit. This is going to cost } { money, so maybe not. } var YNMenu: RPGMenuPtr; cost: LongInt; ISaidYes: Boolean; begin { Create the menu. } YNMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); cost := HiringPrice( PC ); if HQCash( HQCamp ) >= cost then begin AddRPGMenuItem( YNMenu , MsgString( 'ARENA_APTU_Yes' ) + ' ($' + BStr( cost ) + ')' , 1 ); AddRPGMenuItem( YNMenu , MsgString( 'ARENA_APTU_No' ) , -1 ); end else begin AddRPGMenuItem( YNMenu , MsgString( 'ARENA_APTU_TooExpensive' ) , -1 ); end; ADR_Source := PC; if SelectMenu( YNMenu , @DoPurchaseRedraw ) = 1 then begin ISaidYes := True; end else begin ISaidYes := False; end; { Get rid of the Yes/No menu. } DisposeRPGMenu( YNMenu ); IChooseYou := ISaidYes; end; var PCList,PC: GearPtr; PCMenu: RPGMenuPtr; FList,FName: SAttPtr; F: Text; N: Integer; begin DialogMSG( MsgString( 'ARENA_APTU_Prompt' ) ); { Build the character list. Filter out any characters that can't be added } { to the unit. } FList := CreateFileList( Save_Character_Base + Default_Search_Pattern ); PCList := Nil; FName := FList; while FName <> Nil do begin Assign( F , Save_Game_Directory + FName^.info ); reset(F); PC := ReadCGears(F); Close(F); StripAllMecha( PC ); SetSAtt( PC^.SA , 'filename <' + FName^.Info + '>' ); AppendGear( PCList , PC ); FName := FName^.Next; end; DisposeSAtt( FList ); { Keep querying for characters until cancel is selected. } repeat { Create the PC menu. } PCMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); BuildSiblingMenu( PCMenu , PCList ); RPMSortAlpha( PCMenu ); AlphaKeyMenu( PCMenu ); AddRPGMenuItem( PCMenu , MsgString( 'Exit' ) , -1 ); { Select a file, then dispose of the menu. } { Don't need to worry about the menu being empty because } { of the EXIT item. } ADR_Source := PCList; ADR_SourceMenu := PCMenu; N := SelectMenu( PCMenu , @PurchaseHardwareRedraw ); DisposeRPGMenu( PCMenu ); { If a file was selected, load it and see if the player } { wants to keep it. } if N > 0 then begin PC := RetrieveGearSib( PCList , N ); DelinkGear( PCList , PC ); { Ask the player what to do with this character. } if IChooseYou( PC ) then begin { Add the character to the unit. } InsertSubCom( HQCamp^.Source , PC ); { Saving the game is done before deleting } { the character file so that if there's a } { problem in saving, at least the original } { character file will be intact. } PCSaveCampaign( HQCamp , Nil , False ); Assign( F , Save_Game_Directory + SAttValue( PC^.SA , 'filename' ) ); Erase(F); { Update the menus here. } UpdatePilotMechaMenus( HQCamp ); end else begin { Stick this character back in the list. } AppendGear( PCList , PC ); end; end; until N = -1; DisposeGear( PCList ); end; procedure PurchaseGear( HQCamp: CampaignPtr; Part: GearPtr ); { The unit may or may not want to buy PART. } { Show the price of this gear, and ask whether or not the } { player wants to make this purchase. } var YNMenu: RPGMenuPtr; Cost: LongInt; begin Cost := ModifiedCost( HQCamp , GearCost( Part ) , NAS_Shopping ); YNMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); AddRPGMenuItem( YNMenu , ReplaceHash( MsgString( 'ARENA_PurchaseYes' ) , GearName( Part ) ) + ' ($' + BStr( Cost ) + ')' , 1 ); AddRPGMenuItem( YNMenu , MsgSTring( 'ARENA_PurchaseNo' ) , -1 ); ADR_Source := Part; if SelectMenu( YNMenu , @DoPurchaseRedraw ) = 1 then begin if NAttValue( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits ) >= Cost then begin { Copy the gear, then stick it in inventory. } Part := CloneGear( Part ); InsertSubCom( HQCamp^.Source , Part ); { Reduce the buyer's cash by the cost of the gear. } AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , -Cost ); DialogMSG( ReplaceHash( MsgString( 'ARENA_PurchaseComplete' ) , GearName( Part ) ) ); end else begin { Not enough cash to buy... } DialogMSG( ReplaceHash( MsgString( 'ARENA_PurchaseFail' ) , GearName( Part ) ) ); end; end; DisposeRPGMenu( YNMenu ); end; procedure AHQShopping( HQCamp: CampaignPtr ); { Create a list of mecha which are within this unit's price } { range, then allow the user to browse the list and maybe } { purchase some. } var MekList: GearPtr; MekMenu: RPGMenuPtr; m1,mek: GearPtr; { The start of the mecha file, } { and the mek being considered for purchase. } N: Integer; Factions,DefaultColors: String; begin { Create the list of mecha that can be purchased. } MekList := AggregatePattern( '*.txt' , Design_Directory ); { Create the list of factions that mecha can be purchased from. } Factions := 'GENERAL'; mek := SeekCurrentLevelGear( HQCamp^.Source^.InvCom , GG_Faction , HQFac( HQCamp ) ); if mek <> Nil then begin Factions := Factions + ' ' + SAttValue( mek^.SA , 'DESIG' ); DefaultColors := 'SDL_COLORS <' + SAttValue( mek^.SA , 'mecha_colors' ) + '>'; end else begin DefaultColors := 'SDL_COLORS <66 121 179 210 215 80 205 25 0>'; end; { Add the faction designations for the earned mecha source rewards. } mek := Factions_List; while mek <> Nil do begin if NAttValue( HQCamp^.Source^.NA , NAG_AHQMechaSource , Mek^.S ) <> 0 then begin Factions := Factions + ' ' + SAttValue( mek^.SA , 'DESIG' ); end; mek := mek^.Next; end; { Remove non-mecha, expensive mecha, and extra-factional mecha. } { I don't think extra-factional is a word, but it's 1:30 at night and you know what I mean. } mek := MekList; while mek <> Nil do begin M1 := mek^.Next; { If it doesn't fit, remove it. } if ( Mek^.G <> GG_Mecha ) or ( ModifiedCost( HQCamp , GearCost( Mek ) , NAS_Shopping ) > HQCash( HQCamp ) ) or not MechaMatchesFaction( Mek , Factions ) then RemoveGear( MekList , Mek ) { If it does fit, paint it. } else SetSAtt( Mek^.SA , DefaultColors ); mek := M1; end; { Create the mecha menu. } MekMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); BuildSiblingMenu( MekMenu , MekList ); RPMSortAlpha( MekMenu ); AddRPGMenuItem( MekMenu , MsgString( 'Exit' ) , -1 ); repeat ADR_Source := MekList; ADR_SourceMenu := MekMenu; { Prompt the user for a file selection. } N := SelectMenu( MekMenu , @PurchaseHardwareRedraw ); if N > 0 then begin Mek := RetrieveGearSib( MekList , N ); PurchaseGear( HQCamp , Mek ); UpdatePilotMechaMenus( HQCamp ); end; until N = -1; { Get rid of dynamic resources. } DisposeRPGMenu( MekMenu ); DisposeGear( MekList ); end; Procedure ViewMecha( HQCamp: CampaignPtr; PC: GearPtr ); { Examine this mecha. Call up a menu with options related to this } { character. } Procedure SellMecha( SalePrice: LongInt ); { This mecha should be removed from the unit, and some cash gained. } begin DialogMsg( ReplaceHash( MsgString( 'ARENA_VMEK_SMI_SellItem' ) , GearName( PC ) ) ); RemoveGear( HQCamp^.Source^.SubCom , PC ); AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , SalePrice ); PCSaveCampaign( HQCamp , Nil , False ); end; Procedure AssignPilotForMecha; { Select a pilot for this mecha, then associate the two. } var Mek: GearPtr; N: Integer; begin DialogMSG( ReplaceHash( MsgString( 'ARENA_VMEK_APFM_SelectPilot' ) , GearName( PC ) ) ); ADR_Source := HQCamp^.Source^.SubCom; ADR_SourceMenu := ADR_PilotMenu; AddRPGMenuItem( ADR_PilotMenu , MsgString( 'CANCEL' ) , -1 ); N := SelectMenu( ADR_PilotMenu , @ViewPilotRedraw ); if ( N <> -1 ) then begin Mek := RetrieveGearSib( HQCamp^.Source^.SubCom , N ); if Mek <> Nil then begin AssociatePilotMek( HQCamp^.Source^.SubCom , Mek , PC ); end; end; { Update the display. } UpdatePilotMechaMenus( HQCamp ); end; Function InventoryValue: LongInt; { Return the value of all gears in this mecha's general } { inventory. } var I: GearPtr; Total: LongInt; begin I := PC^.InvCom; Total := 0; while I <> Nil do begin Total := Total + GearCost( I ); I := I^.Next; end; InventoryValue := Total div SaleFactor; end; Procedure SellMechaInventory; { This mecha's inventory should be deleted, and some cash gained. } var I: GearPtr; begin AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , InventoryValue ); while PC^.InvCom <> Nil do begin I := PC^.InvCom; DialogMsg( ReplaceHash( MsgString( 'ARENA_VMEK_SMI_SellItem' ) , GearName( I ) ) ); RemoveGear( PC^.InvCom , I ); end; PCSaveCampaign( HQCamp , Nil , False ); end; Procedure RenameMecha; { Rename this mecha. Very easy. } var name: String; begin name := GetStringFromUser( ReplaceHash( MsgString( 'FHQ_Rename_Prompt' ) , GearName( PC ) ) , @ViewSourceMechaRedraw ); if name <> '' then SetSAtt( PC^.SA , 'name <' + name + '>' ); end; var RPM: RPGMenuPtr; N: Integer; SalePrice,Cost: LongInt; begin SalePrice := GearCost( PC ) div SaleFactor; repeat ADR_Source := PC; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaMechaMenu ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VMEK_AssignPilot' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VMEK_ViewInventory' ) , 5 ); AddRPGMenuItem( RPM , MsgString( 'FHQ_Rename' ) , 7 ); if HasSkill( HQCamp^.Source , NAS_MechaEngineering ) then AddRPGMenuItem( RPM , MsgString( 'ARENA_VMEK_EditParts' ) , 6 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VMEK_SellMecha' ) + ' ($' + BStr( SalePrice ) + ')' , -2 ); if PC^.InvCom <> Nil then AddRPGMenuItem( RPM , MsgString( 'ARENA_VMEK_SellMechaInv' ) + ' ($' + BStr( InventoryValue ) + ')' , 4 ); Cost := AHQRepairCost( HQCamp , PC ); if ( Cost > 0 ) and ( Cost <= HQCash( HQCamp ) ) then begin AddRPGMenuItem( RPM , ReplaceHash( MsgSTring( 'ARENA_RepairUnit' ) , GearName( PC ) ) + ' ($' + BStr( Cost ) + ')' , 2 ); end; Cost := ReloadMasterCost( PC , Reload_All_Weapons ); if ( Cost > 0 ) and ( Cost <= HQCash( HQCamp ) ) then begin AddRPGMenuItem( RPM , ReplaceHash( MsgSTring( 'ARENA_ReloadUnit' ) , GearName( PC ) ) + ' ($' + BStr( Cost ) + ')' , 3 ); end; AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); N := SelectMenu( RPM , @ViewSourceMechaRedraw ); DisposeRPGMenu( RPM ); case N of 1: AssignPilotForMecha; 2: DoFullRepair( HQCamp , PC ); 3: ArenaReloadMaster( HQCamp , PC ); 4: begin SellMechaInventory; SalePrice := GearCost( PC ) div SaleFactor; end; 5: ArenaHQBackpack( HQCamp^.Source , PC , @BasicArenaRedraw ); 6: MechaPartEditor( Nil , HQCamp^.Source^.SubCom , HQCamp^.Source , PC , @BasicArenaRedraw ); 7: RenameMecha; -2: SellMecha( SalePrice ); end; until N < 0; end; Procedure ViewItem( HQCamp: CampaignPtr; Part: GearPtr ); { Examine this item. Call up a menu with options related to it. } Procedure SellItem( SalePrice: LongInt ); { This item should be removed from the unit, and some cash gained. } begin DialogMsg( ReplaceHash( MsgString( 'ARENA_VMEK_SMI_SellItem' ) , GearName( Part ) ) ); RemoveGear( HQCamp^.Source^.SubCom , Part ); AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , SalePrice ); PCSaveCampaign( HQCamp , Nil , False ); end; Procedure GiveItemToTeamMate; { Give this item to whoever can hold it. } var RPM: RPGMenuPtr; Mek: GearPtr; N: Integer; begin { Start by allocating the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaMechaMenu ); { Add all the whoevers that might be able to accept this item. } Mek := HQCamp^.Source^.SubCom; N := 1; while Mek <> Nil do begin if IsLegalInvCom( Mek , Part ) and ( Mek <> Part ) then begin AddRPGMenuItem( RPM , AHQMechaName( HQCamp , Mek ) , N ); end; Inc( N ); Mek := Mek^.Next; end; { Select an item from the menu. } if RPM^.NumItem > 0 then begin DialogMsg( ReplaceHash( MsgString( 'ARENA_VITEM_GiveItem_Prompt' ) , GearName( Part ) ) ); ADR_Source := HQCamp^.Source^.SubCom; ADR_SourceMenu := RPM; N := SelectMenu( RPM , @ViewMechaRedraw ); end else begin N := -1; end; DisposeRPGMenu( RPM ); if N > -1 then begin Mek := RetrieveGearSib( HQCamp^.Source^.SubCom , N ); if Mek <> Nil then begin DelinkGear( HQCamp^.Source^.SubCom , Part ); InsertInvCom( Mek , Part ); end; end; end; var RPM: RPGMenuPtr; N: Integer; SalePrice,Cost: LongInt; begin SalePrice := GearCost( Part ) div SaleFactor; repeat ADR_Source := Part; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaMechaMenu ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VItem_SellItem' ) + ' ($' + BStr( SalePrice ) + ')' , -2 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VItem_GiveItem' ) , -3 ); Cost := AHQRepairCost( HQCamp , Part ); if ( Cost > 0 ) and ( Cost <= HQCash( HQCamp ) ) then begin AddRPGMenuItem( RPM , ReplaceHash( MsgSTring( 'ARENA_RepairUnit' ) , GearName( Part ) ) + ' ($' + BStr( Cost ) + ')' , 2 ); end; Cost := ReloadMasterCost( Part , Reload_All_Weapons ); if ( Cost > 0 ) and ( Cost <= HQCash( HQCamp ) ) then begin AddRPGMenuItem( RPM , ReplaceHash( MsgSTring( 'ARENA_ReloadUnit' ) , GearName( Part ) ) + ' ($' + BStr( Cost ) + ')' , 3 ); end; AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); N := SelectMenu( RPM , @ViewSourceMechaRedraw ); DisposeRPGMenu( RPM ); case N of 2: DoFullRepair( HQCamp , Part ); 3: ArenaReloadMaster( HQCamp , Part ); -2: SellItem( SalePrice ); -3: GiveItemToTeamMate; end; until N < 0; end; procedure ExamineMecha( HQCamp: CampaignPtr ); { Examine the unit's mecha, and do any mecha-related things } { that need doing. } var N: Integer; Mek: GearPtr; begin repeat UpdatePilotMechaMenus( HQCamp ); AddRPGMenuItem( ADR_MechaMenu , MsgString( 'EXIT' ) , -1 ); ADR_SourceMenu := ADR_MechaMenu; ADR_Source := HQCamp^.Source^.SubCom; N := SelectMenu( ADR_MechaMenu , @ViewMechaRedraw ); if N > 0 then begin Mek := RetrieveGearSib( HQCamp^.Source^.SubCom , N ); if Mek^.G = GG_Mecha then begin ViewMecha( HQCamp , Mek ); end else begin ViewItem( HQCamp , Mek ); end; end; until N = -1; end; Procedure HQSchool( HQCamp: CampaignPtr; PC: GearPtr ); { Let the teaching commence! I was hoping to use the services.pp/OpenSchool procedure, } { but really this procedure is mostly a frontend for the DoleSkillExperience function } { and making it work in both cases would be more trouble than it's worth. } { The going rate for training is $100 = 1XP. } { I should probably share the constants between both procedures... heh. } const XPStep: Array [1..40] of Integer = ( 1,2,3,4,5, 6,7,8,9,10, 12,15,20,25,50, 75,100,150,200,250, 500,750,1000,1500,2000, 2500,3000,3500,4000,4500, 5000,6000,7000,8000,9000, 10000,12500,15000,20000,25000 ); Knowledge_First_Bonus = 14; Knowledge_First_Penalty = 8; CostFactor = 250; var SkillMenu,CostMenu: RPGMenuPtr; Skill,N: Integer; Cash: LongInt; DSLTemp: Boolean; begin ADR_Source := PC; { When using a school, can always learn directly. } DSLTemp := Direct_Skill_Learning; Direct_Skill_Learning := True; { Step One: Create the skills menu. } SkillMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaPilotMenu ); for N := 1 to NumSkill do begin if NAttValue( HQCamp^.Source^.NA , NAG_AHQSkillTrainer , N ) <> 0 then begin AddRPGMenuItem( SkillMenu , MsgString( 'SKILLNAME_' + BStr( N ) ) , N , SkillDescription( N ) ); end; end; RPMSortAlpha( SkillMenu ); AddRPGMenuItem( SkillMenu , MsgString( 'SCHOOL_Exit' ) , -1 ); repeat { Get a selection from the menu. } Skill := SelectMenu( SkillMenu , @ViewSourcePilotRedraw ); { If a skill was chosen, do the training. } if ( Skill >= 1 ) and ( Skill <= NumSkill ) then begin { Create the CostMenu, and see how much the } { player wants to spend. } CostMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaPilotMenu ); Cash := HQCash( HQCamp ); { Add menu entries for each of the cost values } { that the PC can afford. } for N := 1 to 40 do begin if XPStep[ N ] * CostFactor <= Cash then begin AddRPGMenuItem( CostMenu , '$' + BStr( XPStep[ N ] * CostFactor ) , N ); end; end; { Add the exit option, so that we'll never have } { an empty menu. } AddRPGMenuItem( CostMenu , MsgString( 'SCHOOL_ExitCostSelector' ) , -1 ); N := SelectMenu( CostMenu , @ViewSourcePilotRedraw ); DisposeRPGMenu( CostMenu ); { If CANCEL wasn't selected, take away the cash } { and give the PC some experience. } if N <> -1 then begin AddNAtt( HQCamp^.Source^.NA , NAG_Experience , NAS_Credits , -( XPStep[ N ] * CostFactor ) ); { Calculate the number of XPs earned. } Cash := XPStep[ N ]; { Add bonus for high Knowledge stat, } { or penalty for low Knowledge stat. } if CStat( PC , STAT_Knowledge ) >= Knowledge_First_Bonus then begin Cash := ( Cash * ( 100 + ( CStat( PC , STAT_Knowledge ) - Knowledge_First_Bonus + 1 ) * 5 ) ) div 100; end else if CStat( PC , STAT_Knowledge ) <= Knowledge_First_Penalty then begin Cash := ( Cash * ( 100 - ( Knowledge_First_Penalty - CStat( PC , STAT_Knowledge ) + 1 ) * 10 ) ) div 100; if Cash < 1 then Cash := 1; end; DialogMsg( ReplaceHash( MsgString( 'SCHOOL_STUDY' ) , MsgString( 'SKILLNAME_' + BStr( Skill ) ) ) ); if DoleSkillExperience( PC , Skill , Cash ) then begin DialogMsg( MsgString( 'SCHOOL_Learn' + BStr( Random( 5 ) + 1 ) ) ); end; end; end; until Skill = -1; { Restore the Direct_Skill_Learning setting. } Direct_Skill_Learning := DSLTemp; DisposeRPGMenu( SkillMenu ); end; Procedure ViewCharacter( HQCamp: CampaignPtr; PC: GearPtr ); { Examine this character. Call up a menu with options related to this } { character. } Procedure RemoveCharacter; { This character should be delinked from the unit and saved to disk. } begin DelinkGear( HQCamp^.Source^.SubCom , PC ); SaveChar( PC ); PCSaveCampaign( HQCamp , Nil , False ); DisposeGear( PC ); end; Procedure AssignMechaForPilot; { Select a mecha for this pilot, then associate the two. } var Mek: GearPtr; N: Integer; begin DialogMSG( ReplaceHash( MsgString( 'ARENA_VCHAR_AMFP_SelectMecha' ) , GearName( PC ) ) ); ADR_Source := HQCamp^.Source^.SubCom; ADR_SourceMenu := ADR_MechaMenu; AddRPGMenuItem( ADR_MechaMenu , MsgString( 'CANCEL' ) , -1 ); N := SelectMenu( ADR_MechaMenu , @ViewMechaRedraw ); if ( N <> -1 ) then begin Mek := RetrieveGearSib( HQCamp^.Source^.SubCom , N ); if Mek <> Nil then begin AssociatePilotMek( HQCamp^.Source^.SubCom , PC , Mek ); end; end; { Update the display. } UpdatePilotMechaMenus( HQCamp ); end; var RPM: RPGMenuPtr; N: Integer; Cost: LongInt; begin repeat ADR_Source := PC; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaPilotMenu ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VCHAR_DoTraining' ) , 4 ); if HasSkillTrainers( HQCamp ) then AddRPGMenuItem( RPM , MSgString( 'ARENA_OpenSchool' ) , 6 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VCHAR_ViewInventory' ) , 5 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VCHAR_AssignMecha' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_VCHAR_RemoveCharacter' ) , -2 ); Cost := AHQRepairCost( HQCamp , PC ); if ( Cost > 0 ) and ( Cost <= HQCash( HQCamp ) ) then begin AddRPGMenuItem( RPM , ReplaceHash( MsgSTring( 'ARENA_RepairUnit' ) , GearName( PC ) ) + ' ($' + BStr( Cost ) + ')' , 2 ); end; Cost := ReloadMasterCost( PC , Reload_All_Weapons ); if ( Cost > 0 ) and ( Cost <= HQCash( HQCamp ) ) then begin AddRPGMenuItem( RPM , ReplaceHash( MsgSTring( 'ARENA_ReloadUnit' ) , GearName( PC ) ) + ' ($' + BStr( Cost ) + ')' , 3 ); end; AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); N := SelectMenu( RPM , @ViewSourcePilotRedraw ); DisposeRPGMenu( RPM ); case N of 1: AssignMechaForPilot; 2: DoFullRepair( HQCamp , PC ); 3: ArenaReloadMaster( HQCamp , PC ); 4: DoTraining( Nil , PC , @BasicArenaRedraw ); 5: ArenaHQBackpack( HQCamp^.Source , PC , @BasicArenaRedraw ); 6: HQSchool( HQCamp , PC ); -2: RemoveCharacter; end; until N < 0; end; procedure ExamineCharacters( HQCamp: CampaignPtr ); { Take a look through this unit's characters. Maybe do stuff to them. } var N: Integer; begin repeat UpdatePilotMechaMenus( HQCamp ); AddRPGMenuItem( ADR_PilotMenu , MsgString( 'EXIT' ) , -1 ); ADR_SourceMenu := ADR_PilotMenu; ADR_Source := HQCamp^.Source^.SubCom; N := SelectMenu( ADR_PilotMenu , @ViewPilotRedraw ); if N > 0 then begin ViewCharacter( HQCamp , RetrieveGearSib( HQCamp^.Source^.SubCom , N ) ); end; until N = -1; end; Procedure DeliverMissionDebriefing( Adv,Scene: GearPtr ); { Deliver any pending news to the player. The big news will be which characters died, } { which ones were rescued by the Medicine skill, which mecha were destroyed, and which } { mecha were returned from the brink. } { If any characters died or mecha were lost, a small renown penalty will be applied to } { the team. } Procedure GiveTheNews( NPC: Integer; const Msg_Key: String; NameList: SAttPtr ); { The provided NPC will give the provided message about the provided names. } begin while NameList <> Nil do begin HQMonologue( Adv, NPC , ReplaceHash( ArenaNPCMessage( Adv , NPC , Msg_Key ) , NameList^.Info ) ); NameList := NameList^.Next; end; end; var Dead,Healed,Destroyed,Fixed,Captured,SList: SAttPtr; { The various message classes. } begin Dead := Nil; Healed := Nil; Destroyed := Nil; Fixed := Nil; Captured := Nil; { Step one: Look for matching messages. } SList := Scene^.SA; while SList <> Nil do begin if HeadMatchesString( ARENAREPORT_CharRecovered , SList^.Info ) then StoreSAtt( Healed , RetrieveAString( SList^.Info ) ) else if HeadMatchesString( ARENAREPORT_CharDied , SList^.Info ) then begin StoreSAtt( Dead , RetrieveAString( SList^.Info ) ); AddNAtt( Adv^.NA , NAG_CharDescription , NAS_Renowned , -2 ); end else if HeadMatchesString( ARENAREPORT_MechaRecovered , SList^.Info ) then StoreSAtt( Fixed , RetrieveAString( SList^.Info ) ) else if HeadMatchesString( ARENAREPORT_MechaDestroyed , SList^.Info ) then begin StoreSAtt( Destroyed , RetrieveAString( SList^.Info ) ); AddNAtt( Adv^.NA , NAG_CharDescription , NAS_Renowned , -1 ); end else if HeadMatchesString( ARENAREPORT_MechaObtained , SList^.Info ) then StoreSAtt( Captured , RetrieveAString( SList^.Info ) ) ; SList := SList^.Next; end; { Step two: Report the stuff we just found out. } GiveTheNews( ANPC_Medic , 'PCHealed' , Healed ); GiveTheNews( ANPC_Medic , 'PCDead' , Dead ); GiveTheNews( ANPC_Mechanic , 'MechaFixed' , Fixed ); GiveTheNews( ANPC_Mechanic , 'MechaDestroyed' , Destroyed ); GiveTheNews( ANPC_Supply , 'MechaObtained' , Captured ); DisposeSAtt( Dead ); DisposeSAtt( Healed ); DisposeSAtt( Destroyed ); DisposeSAtt( Fixed ); DisposeSAtt( Captured ); end; Procedure DeliverSalvageReport( Adv , PCList: GearPtr ); { Report on any salvage recovered from the battle. } begin while PCList <> Nil do begin if NAttValue( PCList^.NA , NAG_MissionReport , NAS_WasSalvaged ) <> 0 then begin HQMonologue( Adv, ANPC_Supply , ReplaceHash( ArenaNPCMessage( Adv , ANPC_Supply , 'SalvageReport' ) , FullGearName( PCList ) ) ); end; PCList := PCList^.Next; end; end; Procedure DeliverPersonalDebriefing( Adv , Scene: GearPtr ); { Deliver personal debriefing messages from the faction NPCs. } var SList,Messages: SAttPtr; NPC: LongInt; begin { Step one: Look for matching messages. } SList := Scene^.SA; Messages := Nil; while SList <> Nil do begin if HeadMatchesString( ARENAREPORT_Personal , SList^.Info ) then StoreSAtt( Messages , RetrieveAString( SList^.Info ) ); SList := SList^.Next; end; { Step Two: Deliver those messages. } SList := Messages; while SList <> Nil do begin NPC := ExtractValue( SList^.Info ); HQMonologue( Adv, NPC , SList^.Info ); SList := SList^.Next; end; { Step three- dispose of the messages. } DisposeSAtt( Messages ); end; Function MissionFrontEnd( HQCamp: CampaignPtr; Scene,PCForces: GearPtr ): Integer; { Play the mission, along with all the needed wrapper stuff. } Procedure ReportRenownGain( R0,R1: Integer ); { The team has gained some renown. If this causes a change in rank, } { the Intel officer will let the player know. } { R0 is initial renown, R1 is current renown. } var T,NewRank: Integer; begin NewRank := 0; for t := 1 to 4 do begin if ( R1 > ( t * 20 ) ) and ( R0 <= ( t * 20 ) ) then NewRank := T + 1; end; if NewRank <> 0 then HQMonologue( HQCamp^.Source , ANPC_Intel , ReplaceHash( ArenaNPCMessage( HQCamp^.Source , ANPC_Intel , 'GainPromotion' ) , MsgSTring( 'AHQRANK_' + BStr( NewRank ) ) ) ); end; Procedure ReportRenownLoss( R0,R1: Integer ); { Check for the team's rank dropping; if so, report it. } { R0 is initial renown, R1 is current renown. } var T,NewRank: Integer; begin NewRank := 0; for t := 1 to 4 do begin if ( R0 > ( t * 20 ) ) and ( R1 <= ( t * 20 ) ) then NewRank := T; end; if NewRank <> 0 then HQMonologue( HQCamp^.Source , ANPC_Intel , ReplaceHash( ArenaNPCMessage( HQCamp^.Source , ANPC_Intel , 'LosePromotion' ) , MsgSTring( 'AHQRANK_' + BStr( NewRank ) ) ) ); end; var N: Integer; C0,C1: LongInt; { Cash0, Cash1 } R0,R1: Integer; { Renown0, Renown1 } begin { Save the initial money and renown. } C0 := HQCash( HQCamp ); R0 := HQRenown( HQCamp ); N := ScenePlayer( HQCamp , Scene , PCForces ); { After the mission is over, deliver any reports. } if N <> 0 then begin { Deliver the member debriefings first. } DeliverPersonalDebriefing( HQCamp^.Source , Scene ); { The mission has ended properly; it wasn't quit. } { Do the debriefing here. } C1 := HQCash( HQCamp ); if C1 > C0 then begin HQMonologue( HQCamp^.Source , ANPC_Commander , ReplaceHash( ArenaNPCMessage( HQCamp^.Source , ANPC_Commander , 'ReportEarnings' ) , BStr( C1 - C0 ) ) ); end; DeliverMissionDebriefing( HQCamp^.Source , Scene ); DeliverSalvageReport( HQCamp^.Source , PCForces ); end; { After finishing the battle, get rid of the scene. } RemoveGear( HQCamp^.Source^.InvCom , Scene ); { Reinsert the surviving PCForces into the unit. } PostMissionCleanup( HQCamp , PCForces ); { See how much money the repairs/reload cost. } if N <> 0 then begin C0 := HQCash( HQCamp ); if C0 < C1 then HQMonologue( HQCamp^.Source , ANPC_Mechanic , ReplaceHash( ArenaNPCMessage( HQCamp^.Source , ANPC_Mechanic , 'ReportExpenses' ) , BStr( C1 - C0 ) ) ); R1 := HQRenown( HQCamp ); if R1 > R0 then ReportRenownGain( R0 , R1 ) else if R1 < R0 then ReportRenownLoss( R0 , R1 ); end; MissionFrontEnd := N; end; Function PlayArenaMission( HQCamp: CampaignPtr; SelectionMode: Byte ): Boolean; { Play an arena mission. Yahoo! } { Return TRUE if the mission was completed, or FALSE if the mission was } { quit in progress. } Function SelectAMission: GearPtr; { Select a mission. } var RPM: RPGMenuPtr; N: Integer; M: GearPtr; begin { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_SAMMenu ); AttachMenuDesc( RPM , ZONE_SAMText ); { Add all the missions to the menu. } N := 1; M := HQCamp^.Source^.InvCom; while M <> Nil do begin if M^.G = GG_Scene then begin AddRPGMenuItem( RPM , GearName( M ) , N , SAttValue( M^.SA , 'DESC' ) + ' ($' + BStr( ExpectedMissionReward( HQCamp , M ) ) + ')' ); Inc( N ); end; M := M^.Next; end; RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); AddRPGMenuItem( RPM , MsgString( 'CANCEL' ) , -2 ); N := SelectMenu( RPM , @SelectAMissionRedraw ); DisposeRPGMenu( RPM ); if N > -1 then begin M := GetMission( HQCamp , N ); end else begin M := Nil; end; SelectAMission := M; end; Function GetCustomMission( LList: GearPtr ): GearPtr; { Select one of the missions from LList. Initialize it, } { stick it in the adventure, and return a pointer to it. } var RPM: RPGMenuPtr; N: Integer; M: GearPtr; begin { Step one- select something from the list. This is going to require } { a menu. } { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_SAMMenu ); AttachMenuDesc( RPM , ZONE_SAMText ); { Add all the missions to the menu. } N := 1; M := LList; while M <> Nil do begin AddRPGMenuItem( RPM , GearName( M ) , N , SAttValue( M^.SA , 'DESC' ) ); Inc( N ); M := M^.Next; end; RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); AddRPGMenuItem( RPM , MsgString( 'CANCEL' ) , -2 ); N := SelectMenu( RPM , @SelectAMissionRedraw ); DisposeRPGMenu( RPM ); if N > -1 then begin { Clone the mission we want. Set its name to DEBUG. } M := CloneGear( RetrieveGearSib( LList , N ) ); SetSAtt( M^.SA , 'name ' ); { Attempt to place it in the adventure. } if not InsertArenaMission( HQCamp^.Source , M , HQRenown( HQCamp ) ) then M := Nil; end; GetCustomMission := M; end; Function SelectAMForces: GearPtr; { Select a number of pilots for this mission. Only pilots who have } { mecha will be considered. } var ECM: RPGMenuPtr; PCForces,Mek,Pilot: GearPtr; N: Integer; begin PCForces := Nil; ADR_NumPilotsSelected := 0; ADR_PilotsAllowed := 5; Repeat { Create the menu. } ECM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoText ); Mek := HQCamp^.Source^.SubCom; N := 1; while Mek <> Nil do begin if ( Mek^.G = GG_Mecha ) then begin Pilot := FindMechasPilot( HQCamp^.Source , Mek ); if Pilot <> Nil then AddRPGMenuItem( ECM , GearName( Pilot ) + ' [' + GearName( Mek ) + ']' , N ); end; Mek := Mek^.Next; Inc( N ); end; RPMSortAlpha( ECM ); AlphaKeyMenu( ECM ); AddRPGMenuItem( ECM , MsgString( 'ARENA_SAMF_StartMission' ) , -1 ); AddRPGMenuItem( ECM , MsgString( 'CANCEL' ) , -2 ); N := SelectMenu( ECM , @SelectAMForcesRedraw ); DisposeRPGMenu( ECM ); if N > -1 then begin Mek := RetrieveGearSib( HQCamp^.Source^.SubCom , N ); Pilot := FindMechasPilot( HQCamp^.Source , Mek ); DialogMsg( ReplaceHash( MsgString( 'ARENA_SAMF_AddPilot' ) , GearName( Pilot ) ) ); DelinkGear( HQCamp^.Source^.SubCom , Mek ); DelinkGear( HQCamp^.Source^.SubCom , Pilot ); AppendGear( PCForces , Mek ); AppendGear( PCForces , Pilot ); inc( ADR_NumPilotsSelected ); end; until ( N < 1 ) or ( ADR_NumPilotsSelected >= ADR_PilotsAllowed ); if N = -2 then begin InsertSubCom( HQCamp^.Source , PCForces ); PCForces := Nil; DialogMsg( MsgString( 'ARENA_SAMF_Cancel' ) ); end; SelectAMForces := PCForces; end; var PCForces,Scene: GearPtr; N: Integer; begin { Start by selecting the mission. } if NumMissions( HQCamp ) < 1 then AddMissions( HQCamp , HQMaxMissions( HQCamp ) ); if SelectionMode = PAM_Debug_Missions then begin { Select an insert a mission from the master list for } { debugging purposes. } Scene := GetCustomMission( Arena_Mission_Master_List ); end else if SelectionMode = PAM_Debug_Core then begin { Select and insert a mission from the core campaign } { list for debugging purposes. } Scene := GetCustomMission( Core_Mission_Master_List ); end else begin Scene := SelectAMission; end; if Scene = Nil then Exit( True ); { Start by selecting the PCForces. } PCForces := SelectAMForces; if PCForces <> Nil then begin { Prep the mission, and pass to the mission front end. } PrepMission( HQCamp , Scene ); N := MissionFrontEnd( HQCamp , Scene , PCForces ); end else begin { Selection was cancelled. } N := 1; end; PlayArenaMission := N <> 0; end; Procedure CreateNewPilot( Camp: CampaignPtr ); { Create a new pilot, and add it to the unit. } var Egg,PC,S: GearPtr; begin Egg := CharacterCreator( HQFac( Camp ) ); if Egg <> Nil then begin PC := Nil; while Egg^.SubCom <> Nil do begin S := Egg^.SubCom; DelinkGear( Egg^.SubCom , S ); AppendGear( PC , S ); end; DisposeGear( Egg ); StripAllMecha( PC ); InsertSubCom( Camp^.Source , PC ); end; end; Procedure CheckFactionsPresent( Adv: GearPtr ); { Check to make sure that all of the factions which currently exist are represented } { in this adventure. Update the alliegances as necessary. } Procedure ModifyFacRelations( NewFac: GearPtr ); { NewFac has just been added to the game. Check through the existing factions, and } { make sure they all have the appropriate reaction score for it. } var Fac,ProtoFac: GearPtr; begin Fac := Adv^.InvCom; while Fac <> Nil do begin if ( Fac^.G = GG_Faction ) and ( NewFac <> Fac ) then begin { We've found a faction, and it's not the new faction. How does } { this faction feel about the new faction? The answer can be found } { in the faction prototype. } ProtoFac := SeekCurrentLevelGear( Factions_List , GG_Faction , Fac^.S ); if ProtoFac <> Nil then begin SetNAtt( Fac^.NA , NAG_FactionScore , NewFac^.S , NAttValue( ProtoFac^.NA , NAG_FactionScore , NewFac^.S ) ); end; end; Fac := Fac^.Next; end; end; var FLF,InGameFac: GearPtr; begin { Start by looking through the Faction List Factions } FLF := Factions_List; while FLF <> Nil do begin InGameFac := SeekCurrentLevelGear( Adv^.InvCom , GG_Faction , FLF^.S ); if InGameFac = Nil then begin { We don't have this faction in the campaign. Horrors! In order to fix things, } { copy it over, then copy over all the faction relations. } InGameFac := CloneGear( FLF ); InsertInvCom( Adv , InGameFac ); ModifyFacRelations( InGameFac ); end; FLF := FLF^.Next; end; end; Procedure CheckFactionPersonalities( Adv: GearPtr ); { Check to make sure that the faction personalities are loaded, and that all } { positions are accounted for. } var NPCSet,NPCFile,FacNPCs,NewNPC: GearPtr; T: Integer; begin { Search for the NPC Set. } NPCSet := SeekCurrentLevelGear( Adv^.InvCom , GG_Set , GS_CharacterSet ); if NPCSet = Nil then begin { We don't have a NPCSet. Horrors! Better add one. } NPCSet := NewGear( Nil ); NPCSet^.G := GG_Set; NPCSet^.S := GS_CharacterSet; InsertInvCom( Adv , NPCSet ); { Load the NPC file from disk, and copy over the appropriate NPCs for the adventure faction. } NPCFile := LoadFile( 'ARENADATA_Personalities.txt' , Series_Directory ); FacNPCs := SeekCurrentLevelGear( NPCFile , GG_Set , NAttValue( Adv^.NA , NAG_Personal , NAS_FactionID ) ); if FacNPCs <> Nil then begin { We found a set containing this faction's members. Move them over to the NPCSet. } while FacNPCs^.InvCom <> Nil do begin NewNPC := FacNPCs^.InvCom; DelinkGear( FacNPCs^.InvCom , NewNPC ); InsertInvCom( NPCSet , NewNPC ); end; end else begin { We found nothing. Create a bunch of stand-in NPCs. } for t := 1 to NumArenaNPCs do begin NewNPC := LoadNewNPC( 'CITIZEN' , True ); SetNAtt( NewNPC^.NA , NAG_Personal , NAS_CID , T ); InsertInvCom( NPCSet , NewNPC ); end; end; DisposeGear( NPCFile ); end; end; Procedure WipeMissions( HQCamp: CampaignPtr ); { Delete all currently loaded missions, and regenerate the list. } var M,M2: GearPtr; begin { Also print the context, for debugging purposes. } DialogMsg( HQContext( HQCamp ) ); M := HQCamp^.Source^.InvCom; while M <> Nil do begin M2 := M^.Next; if M^.G = GG_Scene then begin RemoveGear( HQCamp^.Source^.InvCom , M ); end; M := M2; end; end; Procedure PlayArenaCampaign( Camp: CampaignPtr ); { Play this arena campaign. } var N: Integer; RPM: RPGMenuPtr; begin { Set the campaign pointer for redraw purposes. } ADR_HQCamp := Camp; { As soon as the campaign has been loaded, do some checks to make sure it has everything } { it needs. These checks are done here so that save files from previous versions will remain } { compatable with the current version. } CheckFactionsPresent( Camp^.Source ); CheckFactionPersonalities( Camp^.Source ); { If Camp^.GB exists, then the game was saved in the middle of a battle. } { Handle that battle before heading to the main menu. } if Camp^.GB <> Nil then begin N := MissionFrontEnd( Camp , Camp^.GB^.Scene , Nil ); { If a quit signal was recieved, just exit without going to } { the main menu below. } if N = 0 then Exit; end else begin N := 1; end; { Main Menu here } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaInfo ); AddRPGMenuItem( RPM , MsgSTring( 'ARENA_ExamineCharacters' ) , 5 ); AddRPGMenuItem( RPM , MsgSTring( 'ARENA_ExamineMecha' ) , 1 ); AddRPGMenuItem( RPM , MsgSTring( 'ARENA_PurchaseHardware' ) , 2 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_HireCharacter' ) , 3 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_CreateNewCharacter' ) , 4 ); AddRPGMenuItem( RPM , MsgString( 'ARENA_EnterCombat' ) , 6 ); if ArenaMode_Wizard then begin AddRPGMenuItem( RPM , 'Debug Missions' , 7 ); AddRPGMenuItem( RPM , 'Debug Core Campaign' , 8 ); AddRPGMenuItem( RPM , 'Wipe Missions' , 9 ); end; AddRPGMenuItem( RPM , MsgString( 'ARENA_ExitToMain' ) , 0 ); RPM^.mode := RPMNoCancel; repeat UpdatePilotMechaMenus( Camp ); N := SelectMenu( RPM , @BasicArenaRedraw ); Case N of 1: ExamineMecha( Camp ); 2: AHQShopping( Camp ); 3: AddPilotToUnit( Camp ); 4: CreateNewPilot( Camp ); 5: ExamineCharacters( Camp ); 6: if not PlayArenaMission( Camp , PAM_Regular ) then N := -1; 7: if not PlayArenaMission( Camp , PAM_Debug_Missions ) then N := -1; 8: if not PlayArenaMission( Camp , PAM_Debug_Core ) then N := -1; 9: WipeMissions( Camp ); end; until N < 1; { Save the campaign on the way out. } { Don't save the campaign if the game was saved in combat! } if N <> -1 then PCSaveCampaign( Camp , Nil , False ); { Dispose of dynamic resources. } DisposeRPGMenu( RPM ); { Clear the campaign pointer. } ADR_HQCamp := Nil; { Also get rid of the two menus. } DisposeRPGMenu( ADR_PilotMenu ); DisposeRPGMenu( ADR_MechaMenu ); end; Procedure StartArenaCampaign; { Initialize a new Arena campaign and start it. } Function SelectAFaction: Integer; { Select a faction for this arena unit. } var RPM: RPGMenuPtr; Fac: GearPtr; N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ArenaInfo ); AttachMenuDesc( RPM , ZONE_Dialog ); RPM^.mode := RPMNoCancel; Fac := Factions_List; while Fac <> Nil do begin if AStringHasBString( SAttValue( Fac^.SA , 'TYPE' ) , 'ARENAOK' ) then begin AddRPGMenuItem( RPM , GearName( Fac ) , Fac^.S , SAttValue( Fac^.SA , 'DESC' ) ); end; Fac := Fac^.Next; end; RPMSortALpha( RPM ); AlphaKeyMenu( RPM ); N := SelectMenu( RPM , @BasicArenaRedraw ); DisposeRPGMenu( RPM ); SelectAFaction := N; end; var Camp: CampaignPtr; Factions: GearPtr; name: String; begin { Create the campaign and the adventure. } Camp := NewCampaign; Camp^.Source := LoadFile( 'arenastub.txt' , Series_Directory ); { Insert the factions into the adventure. } Factions := AggregatePattern( 'FACTIONS_*.txt' , Series_Directory ); InsertInvCom( Camp^.Source , Factions ); { Select one faction for this unit. } SetNAtt( Camp^.Source^.NA , NAG_Personal , NAS_FactionID , SelectAFaction ); { Give the new arena unit a name. } name := GetStringFromUser( MsgString( 'ARENA_NewArenaName' ) , @BasicArenaRedraw ); if name <> '' then begin { Store the name. } SetSAtt( Camp^.Source^.SA , 'name <' + name + '>' ); { Actually play with the new campaign. } PlayArenaCampaign( Camp ); end; { Once we're finished, get rid of the campaign. } DisposeCampaign( Camp ); end; Procedure RestoreArenaCampaign( RDP: RedrawProcedureType ); { Load an arena campaign from disk and start it. } var RPM: RPGMenuPtr; rpgname: String; { Campaign Name } Camp: CampaignPtr; F: Text; { A File } begin { Create a menu listing all the units in the SaveGame directory. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu ); BuildFileMenu( RPM , Save_Unit_Base + Default_Search_Pattern ); { If any units are found, allow the player to load one. } if RPM^.NumItem > 0 then begin RPMSortAlpha( RPM ); DialogMSG('Select campaign file to load.'); rpgname := SelectFile( RPM , RDP ); if rpgname <> '' then begin Assign(F, Save_Game_Directory + rpgname ); reset(F); Camp := ReadCampaign(F); Close(F); PlayArenaCampaign( Camp ); DisposeCampaign( Camp ); end; end; DisposeRPGMenu( RPM ); end; initialization { Set all things to NIL to begin with. } ADR_PilotMenu := Nil; ADR_MechaMenu := Nil; ADR_HQCamp := Nil; ANPC_MasterPersona := LoadFile( 'ARENADATA_NPCMessages.txt' , Series_Directory ); Arena_Mission_Master_List := LoadRandomSceneContent( 'ARENAMISSION_*.txt' , Series_Directory ); Core_Mission_Master_List := LoadRandomSceneContent( 'ARENACORE_*.txt' , Series_Directory ); finalization DisposeGear( Arena_Mission_Master_List ); DisposeGear( Core_Mission_Master_List ); DisposeGear( ANPC_MasterPersona ); end. GH2/narration.pp0000644000175000017500000013512711374513724012452 0ustar kaolkaolunit narration; { This unit holds utilities for dealing with RPG campaigns and random plots. } { 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; { PLOT DEFINITION } { G = GG_Plot } { S = Quest Prototype Index } { V = Dramatic Choice ID } { MEME DEFINITION } { G = GG_Meme } { S = ID Number } { V = Undefined } { A Meme is only active when it's a subcom of a root scene. There is an ASL command } { for activating a meme. } { CITYMOOD DEFINITION } { G = GG_CityMood } { S = Major/Minor } { 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. } Const GS_MajorMood = 0; GS_MinorMood = 1; NAG_XXRan = -7; { Extra-Extra-Random Plot Generator Data } NAS_LoadNextComponent = 0; { if =0, load next component. } NAS_ComponentID = 5; { Each component is assigned an ID number. } { This number is only unique within the scope of the story. } NAS_EpisodeNumber = 6; { How many episodes have been completed? } NAS_LastEpisodeNoted = 7; { What was the last episode to get a history note? } NAS_DramaticChoice = 8; { What choice has the PC made for this episode? } NAS_IsDramaticChoicePlot = 9; { If nonzero, this plot is part of our DC menu. } NAS_DCRSeed = 10; { Random value for determining which dramatic choice } { reward to load for the upcoming episode. } NAS_DebriefingMsg = 11; NAS_XXChar_Motivation = 101; NAS_XXChar_Attitude = 102; NAG_Completed_DC = -25; { Lists the dramatic choices which have been completed. } { S = The dramatic choice being referred to } { V = The result; 1 = Success, -1 = Cancelled + may not be attempted again } NAG_ElementID = -9; { Used to store ID numbers for plot/story elements. } Num_Plot_Elements = 50; { Maximum number of plot elements } NAG_SubPlotLayerID = -19; { Most of the megaplot info is filed under } NAG_SubPlotPlotID = -21; { "Narrative" in gears.pp, but I figured I'd } { place this here since it's thematically identical } { to the SubQuestLID above. } NAG_MasterPlotElementIndex = -20; { When combining two plots, this NAtt stores } { the index number of element [S] in the master plot. } NAG_PlotStatus = -22; { S = Layer ID, V = State for this layer. Just like QuestStatus, below. } NAG_MemeData = -17; { Used to record certain things about memes. } NAS_MemeTimeLimit = 1; { Holds the time at which this meme should be deleted. } { If zero, this meme has no time limit. } NAS_NumViews = 0; { How many times the PC has seen this meme. } NAS_MaxMemeViews = 2; { How many times should this meme be viewed? Due to the peculiarities of } { the system, the default score (0) is equivalent to 1. } NAG_MoodData = -23; { Used to record certain things about moods. } NAS_MoodTimeLimit = 1; { The time at which this mood will be deleted. } { PLOT ELEMENTS } { A plot or a story can have up to 8 elements associated with it. The type of } { element is defined in the string attributes Element1..Element8, and the ID } { number of the element is held in the stats of the source gear. } { Only the first character in the Element# string attribute is needed to tell } { what kind of an element this is. The legal values are: } { "C" = Character, ID number is CID } { "S" = Scene, ID number is scene number } { "F" = Faction, ID number is faction number } { "I" = Item, ID number is NID } { All gears other than characters, scenes, metascenes, and factions are } { treated as items. } { Some additional types are defined for the purposes of element search. } { "M" = Metascene, ID number is metascene ID } { Once an entry point is selected the "M" tag is changed to "S". } { "P" = Prefab; element is defined within the plot/story. } { Once the plot is initialized an appropriate descriptive tag is added. } { "G" = Grab; element ID is taken from source story. } { The grabbed element gains the same descriptive tag as the original. } { These constants describe the standard palette entries for xxran stories. } XRP_EnemyChar = 1; { E: } XRP_EnemyFac = 2; { F: } XRP_TargetFac = 3; { P: } XRP_PlotStateNPC = 4; { N: } XRP_TargetNPC = 5; { T: } XRP_ComponentScene = 6; { S: } XRP_EpisodeScene = 7; { L: } XRP_TargetItem = 8; { I: } TRIGGER_StartGame = 'Start'; TRIGGER_EndGame = 'END'; { ArenaHQ Data. Not really narrative, but this is a good place } { to stick it. } NAG_AHQData = 27; NAS_RewardMissionTimer = 1; { Used for spacing out reward missions. } NAS_CoreMissionTimer = 2; { Used for spacing out core missions. } NAS_CoreMissionStep = 3; { Records the number of core missions completed. } NAS_CoreMissionEnemy = 4; { Enemy for the core story. } { The Attitude and Motivation constants, made public so the BeanCounter } { can examine them. } Num_XXR_Motivations = 8; Num_XXR_Attitudes = 14; XXR_Motivation: Array [0..Num_XXR_Motivations] of String[3] = ( '---', 'mer', 'pro', 'ggd', 'see', 'rev', 'cha', 'com', 'nih' ); XXR_Attitude: Array [0..Num_XXR_Attitudes] of String[3] = ( '---', 'jr_', 'sr_', 'sec', 'equ', 'env', 'pch', 'hat', 'mut', 'obs', 'tha', 'nme', 'ant', 'adm', 'dis' ); Function LancemateCanDevelop( NPC: GearPtr ): Boolean; Procedure AddXXCharContext( NPC: GearPtr; var Context: String; palette_entry_code: Char ); Function XNPCDesc( GB: GameBoardPtr; Adv,NPC: GearPtr ): String; Function FindSceneID( Part: GearPtr; GB: GameBoardPtr ): Integer; Function SeekFaction( Scene: GearPtr; ID: Integer ): GearPtr; Function GetFactionID( Part: GearPtr ): Integer; Function FactionIsInactive( Fac: GearPtr ): Boolean; Function ElementID( Plot: GearPtr; N: Integer ): LongInt; Function PlotElementID( Plot: GearPtr; Code: Char; ID: LongInt ): Integer; Function FindMetaPersona( Source: GearPtr; N: Integer ): GearPtr; Function FindPersonaPlot( Adventure: GearPtr; CID: Integer ): GearPtr; Function FindPersonaStory( Adventure: GearPtr; CID: Integer ): GearPtr; Function PersonaInUse( Adventure: GearPtr; ID: LongInt ): Boolean; Function PersonaUsedByQuest( Adv, Persona: GearPtr ): Boolean; Function SeekGearByElementDesc( Adventure: GearPtr; E_Desc: Char; E_ID: Integer; GB: GameBoardPtr ): GearPtr; Function SeekPlotElement( Adventure , Plot: GearPtr; N: Integer; GB: GameBoardPtr ): GearPtr; Function PlotUsedHere( Plot: GearPtr; GB: GameBoardPtr ): Boolean; Function SeekPersona( Scene: GearPtr; CID: LongInt ): GearPtr; Function SeekPersona( GB: GameBoardPtr; CID: LongInt ): GearPtr; function SeekGearByCID( LList: GearPtr; CID: LongInt ): GearPtr; Function FindItemPlot( Adventure: GearPtr; NID: LongInt ): GearPtr; Function FindItemQuest( City: GearPtr; NID: LongInt ): GearPtr; Function FindItemStory( Adventure: GearPtr; NID: LongInt ): GearPtr; Function FindMetascenePlot( Adventure: GearPtr; MSID: LongInt ): GearPtr; Function FindMetasceneStory( Adventure: GearPtr; MSID: LongInt ): GearPtr; Function MetaSceneNotInUse( Adventure: GearPtr; MSID: LongInt ): Boolean; Function FindMetascene( Adventure: GearPtr; MSID: LongInt ): GearPtr; Function FindSceneEntrance( Adventure: GearPtr; GB: GameBoardPtr; MSID: LongInt ): GearPtr; Function NewCID( Adventure: GearPtr ): LongInt; Function NewNID( Adventure: GearPtr ): LongInt; Function NewMetaSceneID( Adventure: GearPtr ): LongInt; Function NewSceneID( Adv: GearPtr ): Integer; Function ElementLocation( Adv,Plot: GearPtr; N: Integer; GB: GameBoardPtr ): Integer; Function ElementFaction( Adv,Plot: GearPtr; N: Integer; GB: GameBoardPtr ): Integer; Function ElementName( Adventure,Plot: GearPtr; N: Integer; GB: GameBoardPtr ): String; Procedure WriteCampaign( Camp: CampaignPtr; var F: Text ); Function ReadCampaign( var F: Text ): CampaignPtr; Function TeamDescription( Scene,Team: GearPtr ): String; Function CreateTeam( Scene: GearPtr; TDesc: String ): GearPtr; Procedure ChooseTeam( NPC , Scene: GearPtr ); Procedure PutAwayGlobal( GB: GameBoardPtr; var Item: GearPtr ); Function RealSceneID( Scene: GearPtr ): Integer; Function FindActualScene( Scene: GearPtr; SID: Integer ): GearPtr; Function FindActualScene( GB: GameBoardPtr; SID: Integer ): GearPtr; Function IsAScene( S: GearPtr ): Boolean; Function SceneIsTemp( S: GearPtr ): Boolean; Function FindRootScene( S: GearPtr ): GearPtr; Function FindWorld( GB: GameBoardPtr; S: GearPtr ): GearPtr; Procedure DelinkGearForMovement( GB: GameBoardPtr; GearToBeMoved: GearPtr ); Function KeepPlayingSC( GB: GameBoardPtr ): Boolean; Procedure RecordFatality( Camp: CampaignPtr; NPC: GearPtr ); Function HasMeritBadge( Adv: GearPtr; Badge: Integer ): Boolean; implementation uses texutil,rpgdice,ghchars,gearutil,ability,menugear,ghprop,ghweapon,interact, {$IFDEF ASCII} vidgfx; {$ELSE} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$ENDIF} const FROZEN_MAP_CONTINUE = 1; FROZEN_MAP_SENTINEL = -1; Function LancemateCanDevelop( NPC: GearPtr ): Boolean; { If this lancemate can learn new skills via the TrainNPC function, } { return TRUE. This is determined by the NAS_LancemateTraining_Total and } { NAS_LancemateTraining_Spent attributes. } const Points_Per_TrainNPC = 11; begin NPC := LocatePilot( NPC ); if NPC = Nil then Exit( False ); LancemateCanDevelop := ( ( NAttValue( NPC^.NA , NAG_Narrative , NAS_LancemateTraining_Spent ) + 1 ) * Points_Per_TrainNPC ) < NAttValue( NPC^.NA , NAG_Narrative , NAS_LancemateTraining_Total ); end; Procedure AddXXCharContext( NPC: GearPtr; var Context: String; palette_entry_code: Char ); { Add context descriptors for the attitude and motivation of this NPC. } var T: Integer; begin T := NAttValue( NPC^.NA , NAG_XXRan , NAS_XXChar_Motivation ); if ( T > 0 ) and ( T <= Num_XXR_Motivations ) then Context := Context + ' ' + palette_entry_code + ':M.' + XXR_Motivation[ t ] else Context := Context + ' ' + palette_entry_code + ':M.---'; T := NAttValue( NPC^.NA , NAG_XXRan , NAS_XXChar_Attitude ); if ( T > 0 ) and ( T <= Num_XXR_Attitudes ) then Context := Context + ' ' + palette_entry_code + ':A.' + XXR_Attitude[ t ] else Context := Context + ' ' + palette_entry_code + ':A.---'; { Lancemates may also get a TRAIN tag, if appropriate. } if ( NAttValue( NPC^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and LancemateCanDevelop( NPC ) then begin Context := Context + ' ' + palette_entry_code + ':TRAIN'; end; end; Function XNPCDesc( GB: GameBoardPtr; Adv,NPC: GearPtr ): String; { Extended NPC description. } { If GB = Nil, information about the NPC's plot recharge will not be included. } var it: String; Fac,Persona: GearPtr; CID,FID: LongInt; begin { Error check- make sure the adventure really is the adventure. } Adv := FindRoot( Adv ); it := NPCTraitDesc( NPC ); it := it + ' ' + SAttValue( NPC^.SA , 'JOB_DESIG' ); AddXXCharContext( NPC , it , '@' ); Case NAttValue( NPC^.NA , NAG_Relationship , 0 ) of NAV_ArchEnemy: it := it + ' NEMESIS'; NAV_Lover: it := it + ' LOVER'; NAV_Family: it := it + ' FAMILY'; NAV_Friend: it := it + ' FRIEND'; NAV_ArchAlly: it := it + ' LANCEMATE'; end; if IsArchEnemy( Adv, NPC ) then it := it + ' ARCHENEMY'; if IsArchAlly( Adv, NPC ) then it := it + ' ARCHALLY'; { If this NPC is a mission-giver, note that here. } if NAttValue( NPC^.NA , NAG_CharDescription , NAS_IsMissionGiver ) <> 0 then it := it + ' MISSION'; CID := NAttValue( NPC^.NA , NAG_Personal , NAS_CID ); Persona := SeekPersona( Adv , CID ); if ( Persona <> Nil ) and AStringHasBString( SAttValue( Persona^.SA , 'SPECIAL' ) , 'NOPLOTS' ) then it := it + ' INUSE' else if PersonaUsedByQuest( Adv , Persona ) then it := it + ' INUSE' else if PersonaInUse( Adv , CID ) then it := it + ' INUSE' else it := it + ' NOTUSED'; FID := NAttValue( NPC^.NA , NAG_Personal , NAS_FactionID ); Fac := SeekFaction( Adv , FID ); if Fac <> Nil then it := it + ' ' + SATtValue( Fac^.SA , 'DESIG' ) + ' ' + SATtValue( Fac^.SA , 'CONTEXT' ) else it := it + ' NOFAC'; if ( FID <> 0 ) and ( FID = NAttValue( Adv^.NA , NAG_Personal , NAS_FactionID ) ) then it := it + ' PCFAC'; if GB <> Nil then begin if GB^.ComTime >= NAttValue( NPC^.NA , NAG_Personal , NAS_PlotRecharge ) then begin it := it + ' RECHARGED'; end; end; it := QuoteString( it ); XNPCDesc := it; end; Function FindSceneID( Part: GearPtr; GB: GameBoardPtr ): Integer; { Find the scene number of this gear. Return 0 if no scene } { can be found which contains it. Return the metascene ID } { if the scene is a metascene. } var it: Integer; Scene: GearPtr; begin it := 0; if Part <> Nil then begin { Move upwards through the tree until either we } { find a scene gear or root level. } Scene := Part; while ( Scene <> Nil ) and not IsAScene( Scene ) do begin Scene := Scene^.Parent; end; { If we didn't find the scene, maybe this gear is on the gameboard. } if Scene = Nil then begin if IsFoundAlongTrack( GB^.Meks , FindRoot( Part ) ) then Scene := GB^.Scene; end; if Scene <> Nil then begin it := RealSceneID( Scene ); end; end; FindSceneID := it; end; Function SeekFaction( Scene: GearPtr; ID: Integer ): GearPtr; { Look for a faction corresponding to the provided ID number. } { Return NIL if no such faction is found. } var F: GearPtr; begin { Error check. } if ( Scene = Nil ) or ( ID = 0 ) then Exit( Nil ); { Find the root of SCENE, which should be the ADVENTURE. } { The faction should be located along the invcoms. } F := FindRoot( Scene )^.InvCom; while ( F <> Nil ) and (( F^.G <> GG_Faction ) or ( F^.S <> ID )) do F := F^.Next; { If the faction was not in the normal place, call the } { heavy-duty and cycle-wasteful search routine. } if F = Nil then F := SeekGear( FindRoot( Scene ) , GG_Faction , ID ); SeekFaction := F; end; Function GetFactionID( Part: GearPtr ): Integer; { This function will return the Faction ID associated with } { any given part, if appropriate. } { FOr a faction this will be it's "S" descriptor. } { For anything else, faction affiliation is stored as a NAtt. } begin if Part = Nil then begin GetFactionID := 0; end else if Part^.G = GG_Faction then begin GetFactionID := Part^.S; end else begin GetFactionID := NAttValue( Part^.NA , NAG_Personal , NAS_FactionID ); end; end; Function FactionIsInactive( Fac: GearPtr ): Boolean; { Return TRUE if this faction has an INACTIVE tag in its } { TYPE string attribute, or FALSE otherwise. } begin FactionIsInactive := AStringHasBString( SATtValue( Fac^.SA , 'TYPE' ) , 'INACTIVE' ); end; Function ElementID( Plot: GearPtr; N: Integer ): LongInt; { Return the stored ID number for the requested plot element. } begin ElementID := NAttValue( Plot^.NA , NAG_ElementID , N ); end; Function PlotElementID( Plot: GearPtr; Code: Char; ID: LongInt ): Integer; { Determine which plot element is referred to by the supplied data. } { CODE indicates what kind of gear we're looking for, while ID } { is the identification number that should be listed in the Plot's } { stats. } { If the supplied ID number cannot be found within this plot, } { return 0. } var t,N: Integer; EDesc: String; begin N := 0; Code := UpCase( Code ); for t := 1 to Num_Plot_Elements do begin if ElementID( Plot , T ) = ID then begin EDesc := SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ); DeleteWhiteSpace( EDesc ); if ( EDesc <> '' ) and ( UpCase( EDesc[1] ) = Code ) then begin N := T; end; end; end; PlotElementID := N; end; Function FindMetaPersona( Source: GearPtr; N: Integer ): GearPtr; { Locate the replacement persona from this PLOT or STORY. } begin FindMetaPersona := SeekCurrentLevelGear( Source^.SubCom , GG_Persona , N ); end; Function SeekPlotAlongPath( Part: GearPtr; Code: Char; ID: LongInt; SeekType: Integer; NeedsPersona: Boolean ): GearPtr; { Seek a gear which uses the specified element along the given } { path. If no such plot is found return Nil. Recursively search } { all active subcomponents. } var it: GearPtr; begin it := Nil; while ( Part <> Nil ) and ( it = Nil ) do begin if ( Part^.G = SeekType ) and ( PlotElementID( Part , Code , ID ) <> 0 ) then begin if NeedsPersona then begin if FindMetaPersona( Part , PlotElementID( Part , Code , ID ) ) <> Nil then begin it := Part; end; end else begin it := Part; end; end else if ( Part^.G = GG_Story ) or ( Part^.G = GG_Faction ) then begin it := SeekPlotALongPath( Part^.InvCom , Code , ID , SeekType , NeedsPersona ); end; Part := Part^.Next; end; SeekPlotAlongPath := it; end; Function FindPersonaPlot( Adventure: GearPtr; CID: Integer ): GearPtr; { Search all through ADVENTURE looking for a plot which } { involves PERSONA. If no such plot is found, return NIL. } begin { Plots should be located along Adventure/InvCom. Plots which } { are not located there are probably sub-plots, so they probably } { don't yet have actors assigned. } Adventure := FindRoot( Adventure ); if ( Adventure = Nil ) or ( Adventure^.G <> GG_Adventure ) then begin FindPersonaPlot := Nil; end else begin FindPersonaPlot := SeekPlotAlongPath( Adventure^.InvCom , 'C' , CID , GG_Plot , False ); end; end; Function FindPersonaStory( Adventure: GearPtr; CID: Integer ): GearPtr; { Search all through ADVENTURE looking for a story which } { involves PERSONA. If no such story is found, return NIL. } begin Adventure := FindRoot( Adventure ); if ( Adventure = Nil ) or ( Adventure^.G <> GG_Adventure ) then begin FindPersonaStory := Nil; end else begin FindPersonaStory := SeekPlotAlongPath( Adventure^.InvCom , 'C' , CID , GG_Story , True ); end; end; Function PersonaInUse( Adventure: GearPtr; ID: LongInt ): Boolean; { Seek a plot, story, or remnant which uses this Character ID. } { Quests aren't checked, making the title of this particular function misleading. } { I should change that sometime. } var it: Boolean; begin { Assume FALSE until we find this persona in use. } it := False; while ( Adventure <> Nil ) and ( not It ) do begin if (( Adventure^.G = GG_Plot ) or ( Adventure^.G = GG_Story )) and ( PlotElementID( Adventure , 'C' , ID ) <> 0 ) then begin it := True; end else if ( Adventure^.G = GG_Story ) or ( Adventure^.G = GG_Faction ) or ( Adventure^.G = GG_Adventure ) then begin it := PersonaInUse( Adventure^.InvCom , ID ); end; Adventure := Adventure^.Next; end; PersonaInUse := it; end; Function PersonaUsedByQuest( Adv, Persona: GearPtr ): Boolean; { Return TRUE if PERSONA is being used by a quest, and is still in use by } { that quest. } begin PersonaUsedByQuest := ( Adv <> Nil ) and ( Persona <> Nil ) and ( NAttValue( Persona^.NA , NAG_Narrative , NAS_PlotID ) < 0 ) and ( NAttValue( FindROot( Adv )^.NA , NAG_PlotStatus , NAttValue( Persona^.NA , NAG_Narrative , NAS_PlotID ) ) >= 0 ); end; Function FindItemPlot( Adventure: GearPtr; NID: LongInt ): GearPtr; { Locate the plot that uses this metascene. } begin FindItemPlot := SeekPlotAlongPath( Adventure^.InvCom , 'I' , NID , GG_Plot , False ); end; Function FindItemQuest( City: GearPtr; NID: LongInt ): GearPtr; { Locate the plot that uses this metascene. } begin FindItemQuest := SeekPlotAlongPath( City^.SubCom , 'I' , NID , GG_Plot , False ); end; Function FindItemStory( Adventure: GearPtr; NID: LongInt ): GearPtr; { Locate the story that uses this metascene. } begin FindItemStory := SeekPlotAlongPath( Adventure^.InvCom , 'I' , NID , GG_Story , False ); end; Function FindMetascenePlot( Adventure: GearPtr; MSID: LongInt ): GearPtr; { Locate the plot that uses this metascene. } begin FindMetascenePlot := SeekPlotAlongPath( Adventure^.InvCom , 'S' , MSID , GG_Plot , False ); end; Function FindMetasceneStory( Adventure: GearPtr; MSID: LongInt ): GearPtr; { Locate the story that uses this metascene. } begin FindMetasceneStory := SeekPlotAlongPath( Adventure^.InvCom , 'S' , MSID , GG_Story , False ); end; Function MetaSceneNotInUse( Adventure: GearPtr; MSID: LongInt ): Boolean; { Return TRUE if this metascene is not in use, or FALSE otherwise. } begin MetaSceneNotInUse := ( FindMetascenePlot( Adventure , MSID ) = Nil ) and ( FindMetasceneStory( Adventure , MSID ) = Nil ); end; Function FindMetascene( Adventure: GearPtr; MSID: LongInt ): GearPtr; { Attempt to locate the metascene referenced by MSID. If no such metascene is } { defined, return NIL, even if the metascene ID is currently assigned to a } { story or plot. } var Plot,MS,T: GearPtr; N: Integer; begin MS := Nil; { Find the plot that's using this meta-location ID. } Plot := SeekPlotAlongPath( Adventure^.InvCom , 'S' , MSID , GG_Plot , False ); { If we found a plot, search it for a MetaScene gear. } if Plot <> Nil then begin { This character is featured in a plot. The plot may } { well contain a persona for this character to use } { while the plot is in effect. } N := PlotElementID( Plot , 'S' , MSID ); T := Plot^.SubCom; while T <> Nil do begin if ( T^.G = GG_MetaScene ) and ( T^.S = N ) then MS := T; T := T^.Next; end; end; FindMetascene := MS; end; Function FindQuestscene( LList: GearPtr; QSID: LongInt ): GearPtr; { Attempt to locate the questscene referenced by MSID. Check LList and } { all of its subcoms. } var MS,T: GearPtr; N: Integer; begin MS := Nil; while ( LList <> Nil ) and ( MS = Nil ) do begin if LList^.G = GG_Plot then begin N := PlotElementID( LList , 'Q' , QSID ); if N > 0 then begin T := LList^.SubCom; while T <> Nil do begin if ( T^.G = GG_MetaScene ) and ( T^.S = N ) then MS := T; T := T^.Next; end; end; end; if MS = Nil then MS := FindQuestScene( LList^.SubCom , QSID ); if MS = Nil then MS := FindQuestScene( LList^.InvCom , QSID ); LList := LList^.Next; end; FindQuestscene := MS; end; Function FindSceneEntrance( Adventure: GearPtr; GB: GameBoardPtr; MSID: LongInt ): GearPtr; { Attempt to find an entrance for this metascene. } Function CheckAlongPath( P: GearPtr ): GearPtr; var it: GearPtr; begin it := Nil; while ( P <> Nil ) and ( it = Nil ) do begin if ( P^.G = GG_MetaTerrain ) and ( P^.Stat[ STAT_Destination ] = MSID ) then begin it := P; end; if it = Nil then it := CheckAlongPath( P^.SubCom ); if it = Nil then it := CheckAlongPath( P^.InvCom ); P := P^.Next; end; CheckAlongPath := it; end; var it: GearPtr; begin it := CheckAlongPath( Adventure ); if it = Nil then it := CheckAlongPath( GB^.Meks ); FindSceneEntrance := it; end; Function SeekGearByElementDesc( Adventure: GearPtr; E_Desc: Char; E_ID: Integer; GB: GameBoardPtr ): GearPtr; { Given the element description code and the element ID, locate the gear } { being referenced. } var Part: GearPtr; begin Adventure := FindRoot( Adventure ); if E_Desc = 'C' then begin { Find a character. } Part := SeekGearByCID( Adventure , E_ID ); if ( Part = Nil ) and ( GB <> Nil ) then Part := SeekGearByCID( GB^.Meks , E_ID ); end else if E_Desc = 'S' then begin { Find a scene. } if GB <> Nil then begin Part := FindActualScene( GB , E_ID ); end else begin Part := SeekGear( Adventure , GG_Scene , E_ID ); end; end else if E_Desc = 'Q' then begin { Find a quest scene- this scene probably hasn't been deployed yet. } Part := FindQuestScene( FindRoot( Adventure ) , E_ID ); end else if E_Desc = 'F' then begin { Find a faction. } Part := SeekGear( Adventure , GG_Faction , E_ID ); end else if ( E_Desc = 'I' ) and ( E_ID <> 0 ) then begin { Find an item. } Part := SeekGearByIDTag( Adventure , NAG_Narrative , NAS_NID , E_ID ); if ( Part = Nil ) and ( GB <> Nil ) then Part := SeekGearByIDTag( GB^.Meks , NAG_Narrative , NAS_NID , E_ID ); end else begin Part := Nil; end; SeekGearByElementDesc := Part; end; Function SeekPlotElement( Adventure , Plot: GearPtr; N: Integer; GB: GameBoardPtr ): GearPtr; { Find the gear referred to in the N'th element of PLOT. } { If no such element may be found return Nil. } var Desc: String; Part: GearPtr; begin { Start by locating the element description string. } Desc := UpCase( SAttValue( Plot^.SA , 'ELEMENT' + BStr( N ) ) ); { Look for the element in the sensible place, given the } { nature of the string. } if Desc = '' then begin Part := Nil; end else begin Part := SeekGearByElementDesc( Adventure , Desc[1] , ElementID( Plot , N ) , GB ); end; { Return the part that was found. } SeekPlotElement := Part; end; Function PlotUsedHere( Plot: GearPtr; GB: GameBoardPtr ): Boolean; { See whether or not any of the elements of PLOT are in use on } { this game board or in its associated scene. } var PUH,EH: Boolean; T: Integer; Desc: String; begin { Error check - Make sure both the plot and the game board are } { defined. } if ( Plot = Nil ) or ( GB = Nil ) then begin PlotUsedHere := False; end else begin { Assume FALSE, then look for any element that's being used } { on the game board. } PUH := False; { Search through all the elements. } for t := 1 to NumGearStats do begin { Start by locating the element description string. } Desc := UpCase( SAttValue( Plot^.SA , 'ELEMENT' + BStr( T ) ) ); { Look for the element in the sensible place, given the } { nature of the string. } if Desc = '' then begin EH := False; end else if Desc[1] = 'C' then begin { Find a character. } EH := SeekGearByCID( GB^.Meks , ElementID( Plot , T ) ) <> Nil; end else if Desc[1] = 'S' then begin { Find a scene. } if GB^.Scene <> Nil then EH := ElementID( Plot , T ) = GB^.Scene^.S else EH := False; end else if Desc[1] = 'I' then begin { Find an item. } EH := SeekGearByIDTag( GB^.Meks , NAG_Narrative , NAS_NID , ElementID( Plot , T ) ) <> Nil; end; PUH := PUH or EH; end; { Return whatever result was found. } PlotUsedHere := PUH; end; end; Function SeekPersona( Scene: GearPtr; CID: LongInt ): GearPtr; { Seek the closest persona gear with the provided Character ID. } { If this NPC is involved in a plot, use the persona gear from } { the plot if one is provided. Otherwise, seek the PERSONA } { in the GB/Scene gear. } var Plot,Persona: GearPtr; N: Integer; begin Persona := Nil; { Use the persona located in the character's PLOT, if appropriate. } Plot := FindPersonaPlot( FindRoot( Scene ) , CID ); if Plot <> Nil then begin { This character is featured in a plot. The plot may } { well contain a persona for this character to use } { while the plot is in effect. } N := PlotElementID( Plot , 'C' , CID ); Persona := FindMetaPersona( Plot , N ); end; { Use the persona from the character's STORY next. } if Persona = Nil then begin Plot := FindPersonaStory( FindRoot( Scene ) , CID ); if Plot <> Nil then begin N := PlotElementID( Plot , 'C' , CID ); Persona := FindMetaPersona( Plot , N ); end; end; { Next two places to look - The current scene, and the } { adventure itself. } if Persona = Nil then Persona := SeekGear( Scene , GG_Persona , CID ); if ( Persona = Nil ) and ( CID > Num_Plot_Elements ) then begin Scene := FindRootScene( Scene ); if Scene <> Nil then Persona := SeekGear( Scene , GG_Persona , CID , False ); end; SeekPersona := Persona; end; Function SeekPersona( GB: GameBoardPtr; CID: LongInt ): GearPtr; { Call the above procedure with the scene. } begin SeekPersona := SeekPersona( GB^.Scene , CID ); end; function SeekGearByCID( LList: GearPtr; CID: LongInt ): GearPtr; { Seek a gear with the provided ID. If no such gear is } { found, return NIL. } begin if CID = 0 then Exit( Nil ); SeekGearByCID := SeekGearByIDTag( LList , NAG_Personal , NAS_CID , CID ); end; Function NewCID( Adventure: GearPtr ): LongInt; { Determine a new, unique CID for a character being added to the } { campaign. In GH2, all CIDs come from this function, so we don't } { have to worry about searching for any pre-existing CIDs. } var it: LongInt; begin { Make sure the Adventure really is the Adventure. } Adventure := FindRoot( Adventure ); { To start with, find the highest ID being used by a character. } it := NAttValue( Adventure^.NA , NAG_Narrative , NAS_MaxCID ); if it = 0 then it := Num_Plot_Elements; { Return the highest value found, +1. } SetNAtt( Adventure^.NA , NAG_Narrative , NAS_MaxCID , it + 1 ); NewCID := it + 1; end; Function NewNID( Adventure: GearPtr ): LongInt; { Determine a new, unique NID for an item being added to the } { campaign. Again, all IDs come from this procedure, so don't } { worry about pre-existing values. } var it: LongInt; begin Adventure := FindRoot( Adventure ); it := NAttValue( Adventure^.NA , NAG_Narrative , NAS_MaxNID ); { Return the highest value found, +1. } SetNAtt( Adventure^.NA , NAG_Narrative , NAS_MaxNID , it + 1 ); NewNID := it + 1; end; Function NewMetaSceneID( Adventure: GearPtr ): LongInt; { Determine a new, unique ID for a metascene entrance point. } var it: LongInt; begin Adventure := FindRoot( Adventure ); it := NAttValue( Adventure^.NA , NAG_Narrative , NAS_MinMSID ) - 1; if it > -100 then it := -100; SetNAtt( Adventure^.NA , NAG_Narrative , NAS_MinMSID , it ); NewMetaSceneID := it; end; Function NewSceneID( Adv: GearPtr ): Integer; { The campaign initialization should have set the maximum scene ID currently } { in use. We'll use that to get a new ID. } begin Adv := FindRoot( Adv ); AddNAtt( Adv^.NA , NAG_Narrative , NAS_MaxSceneID , 1 ); NewSceneID := NAttValue( Adv^.NA , NAG_Narrative , NAS_MaxSceneID ); end; Function ElementLocation( Adv,Plot: GearPtr; N: Integer; GB: GameBoardPtr ): Integer; { Find the scene number where this element resides. If no such } { scene can be found, return 0. } var E: GearPtr; begin E := SeekPlotElement( Adv, Plot, N , GB ); ElementLocation := FindSceneID( E , GB ); end; Function ElementFaction( Adv,Plot: GearPtr; N: Integer; GB: GameBoardPtr ): Integer; { Find the scene number where this element resides. If no such } { scene can be found, return 0. } var E: GearPtr; begin E := SeekPlotElement( Adv, Plot, N , GB ); ElementFaction := GetFactionID( E ); end; Function ElementName( Adventure,Plot: GearPtr; N: Integer; GB: GameBoardPtr ): String; { Find the name of element N. Return an empty string if no such } { element can be found. } var Desc: String; Part: GearPtr; begin Desc := UpCase( SAttValue( Plot^.SA , 'ELEMENT' + BStr( N ) ) ); if Desc <> '' then begin if ( UpCase( Desc[1] ) = 'S' ) and ( ElementID( Plot , N ) < 0 ) then begin Part := FindSceneEntrance( Adventure , GB , ElementID( Plot , N ) ); end else begin Part := SeekPlotElement( Adventure, Plot, N , GB ); end; if Part <> Nil then begin { If this is a metascene, we don't really want the name of the scene } { itself since that's meaningless. Instead, what we really want is the } { name of the entrance to the scene. } ElementName := GearName( Part ); end else begin ElementName := '***ERROR***'; end; end else begin ElementName := '***NOT DEFINED***'; end; end; Procedure WriteCampaign( Camp: CampaignPtr; var F: Text ); { Output the supplied campaign and all appropriate data to disk. } var Frz: FrozenLocationPtr; begin { Output GameBoard. } if Camp^.GB <> Nil then begin writeln( F , Camp^.GB^.MAP_Width ); writeln( F , Camp^.GB^.MAP_Height ); writeln( F , Camp^.GB^.ComTime ); writeln( F , Camp^.GB^.Scale ); WriteMap( Camp^.GB^.Map , F ); { Can't output the scene gear directly, since it'll be outputted } { with the rest of SOURCE later on. Output its reference number. } writeln( F , FindGearIndex( Camp^.Source , Camp^.GB^.Scene ) ); { Output map contents. } WriteCGears( F , Camp^.GB^.Meks ); end else begin { To indicate that there's no gameboard, i.e. that this is } { an arena campaign saved at the HQ, output a 0. Since there's } { no such thing as a map with 0 width, this value will let the } { loader know that there's no gameboard. Also, by working things } { this way, I ensure that save files from previous versions of GH2 } { are still compatable. Am I brilliant or what? } writeln( F , '0' ); end; { Output frozen maps. } Frz := Camp^.Maps; while Frz <> Nil do begin writeln( F , FROZEN_MAP_CONTINUE ); writeln( F , Frz^.Name ); writeln( F , Frz^.MAP_Width ); writeln( F , Frz^.MAP_Height ); WriteMap( Frz^.Map , F ); Frz := Frz^.Next; end; { Output frozen map sentinel marker. } writeln( F , FROZEN_MAP_SENTINEL ); { Output SOURCE. } WriteCGears( F , Camp^.Source ); end; Function ReadCampaign( var F: Text ): CampaignPtr; { Input the campaign and all appropriate data from disk. } var Camp: CampaignPtr; SceneIndex: LongInt; N: Integer; Frz: FrozenLocationPtr; W,H: Integer; begin { Allocate the campaign and the gameboard. } Camp := NewCampaign; readln( F , W ); if W > 0 then begin readln( F , H ); Camp^.GB := NewMap( W , H ); Camp^.GB^.MAP_Width := W; Camp^.GB^.MAP_Height := H; readln( F , Camp^.GB^.ComTime ); Camp^.ComTime := Camp^.GB^.ComTime; readln( F , Camp^.GB^.Scale ); Camp^.GB^.Map := ReadMap( F , Camp^.GB^.MAP_Width , Camp^.GB^.MAP_Height ); { Read the index of this game board's SCENE gear, and } { remember to set it in the GB structure after SOURCE is loaded. } readln( F , SceneIndex ); { Read the list of map contents. } Camp^.GB^.Meks := ReadCGears( F ); end else begin { If W=0, this implies that we don't actually have a gameboard. } { Load the rest of the file anyways. } Camp^.GB := Nil; Camp^.ComTime := 0; end; { Read the frozen maps. } repeat ReadLn( F , N ); if N = FROZEN_MAP_CONTINUE then begin Frz := CreateFrozenLocation( Camp^.Maps ); ReadLn( F , Frz^.Name ); ReadLn( F , Frz^.MAP_Width ); ReadLn( F , Frz^.MAP_Height ); Frz^.Map := ReadMap( F , Frz^.MAP_Width , Frz^.MAP_Height ); end; until N = FROZEN_MAP_SENTINEL; { Read the source, and set the gameboard's scene. } Camp^.Source := ReadCGears( F ); { Only set the scene if we have a gameboard. It's possible that we don't. } if Camp^.GB <> Nil then Camp^.GB^.Scene := LocateGearByNumber( Camp^.Source , SceneIndex ); { Return the restored campaign structure. } ReadCampaign := Camp; end; Function TeamDescription( Scene,Team: GearPtr ): String; { Create a description for this team. This is to be used } { by the team location routines. } var it: String; begin { Start with an empty string. } it := ''; if Team <> Nil then begin if AreEnemies( Scene, Team^.S , NAV_DefPlayerTeam ) then begin it := it + ' enemy'; end else if AreAllies( Scene , Team^.S , NAV_DefPlayerTeam ) then begin it := it + ' ally'; end; it := it + ' ' + AI_Type_Label[ Team^.Stat[ STAT_TeamOrders ] ]; if Team^.Stat[ STAT_WanderMon ] > 0 then it := it + ' wmon'; end; TeamDescription := it; end; Function CreateTeam( Scene: GearPtr; TDesc: String ): GearPtr; { Make a new team corresponding to the description provided. } var Team: GearPtr; CMD: String; T: Integer; begin Team := NewGear( Nil ); Team^.G := GG_Team; Team^.S := NewTeamID( Scene ); InsertSubCom( Scene , Team ); { Set the new team's attributes based upon the remainder of } { the PLACE string. } TDesc := UpCase( TDesc ); while TDesc <> '' do begin cmd := ExtractWord( TDesc ); if cmd = 'ENEMY' then begin SetNAtt( Team^.NA , NAG_SideReaction , NAV_DefPlayerTeam , NAV_AreEnemies ); end else if cmd = 'ALLY' then begin SetNAtt( Team^.NA , NAG_SideReaction , NAV_DefPlayerTeam , NAV_AreAllies ); end else begin { This command may be an AIType. } for t := 0 to NumAITypes do begin if cmd = AI_Type_Label[ t ] then Team^.Stat[ STAT_TeamOrders ] := t; end; end; end; CreateTeam := Team; end; Function FindMatchingTeam( Scene: GearPtr; TDesc: String ): GearPtr; { Locate a team which matches the provided description. } { If no such team can be found, return Nil. If more than } { one team is found, return one of them, although there's } { no guarantee which one. } var T,it: GearPtr; begin { Teams are located as root subcomponents of the scene, } { so look there. } T := Scene^.SubCom; { Initialize our search bit to NIL. } it := Nil; while ( T <> Nil ) and ( it = Nil ) do begin if ( T^.G = GG_Team ) and ( T^.S <> NAV_DefPlayerTeam ) and PartMatchesCriteria( TeamDescription( Scene , T ) , TDesc ) then begin it := T; end; T := T^.Next; end; FindMatchingTeam := it; end; Procedure ChooseTeam( NPC , Scene: GearPtr ); { Find a team which matches the NPC's needs. } var TeamData: String; Team: GearPtr; begin TeamData := SAttValue( NPC^.SA , 'TEAMDATA' ); { If we have no teamdata, and the NPC is a prop, just set it to } { team zero. } if ( TeamData = '' ) and ( NPC^.G = GG_Prop ) then begin SetNAtt( NPC^.NA , NAG_Location , NAS_Team , 0 ); end else begin Team := FindMatchingTeam( Scene , TeamData ); { If no matching team was found, create a new team. } if Team = Nil then begin Team := CreateTeam( Scene , TeamData ); end; { Store the correct team number in the NPC. } SetNAtt( NPC^.NA , NAG_Location , NAS_Team , Team^.S ); end; end; Procedure PutAwayGlobal( GB: GameBoardPtr; var Item: GearPtr ); { ITEM is a global gear. It belongs somewhere other than it is. } { IMPORTANT: GB, GB^.SCene, and Item are all defined. } var SID: Integer; Scene: GearPtr; begin { Find this gear's original home scene. } SID := NAttValue( Item^.NA , NAG_ParaLocation , NAS_OriginalHome ); { Erase the original home data, since we're sending it home now. } { If the gear gets moved again its original home data should be } { reset. } SetNAtt( Item^.NA , NAG_ParaLocation , NAS_OriginalHome , 0 ); { Put it away there. } if SID > 0 then begin Scene := FindActualScene( GB , SID ); end else begin Scene := Nil; end; if Scene <> Nil then begin InsertInvCom( Scene , Item ); { If inserting a character, better choose a team. } if IsMasterGear( Item ) then begin ChooseTeam( Item , Scene ); end; end else if GB^.SCene <> Nil then begin InsertInvCom( FindRoot( GB^.Scene ) , Item ); end else begin DisposeGear( Item ); end; end; Function RealSceneID( Scene: GearPtr ): Integer; { Given SCENE, return its actual ID value. If this is a MetaScene, } { we have to check its plot. } var it: Integer; begin if ( Scene^.G = GG_MetaScene ) and ( Scene^.Parent <> Nil ) and ( Scene^.Parent^.G = GG_Plot ) and IsSubCom( Scene ) and ( Scene^.S >= 1 ) and ( Scene^.S <= Num_Plot_Elements ) then begin it := ElementID( Scene^.Parent , Scene^.S ); end else if ( Scene^.G = GG_MetaScene ) or IsSubCom( Scene ) then begin it := Scene^.S; end else begin it := 0; end; RealSceneID := it; end; Function FindActualScene( Scene: GearPtr; SID: Integer ): GearPtr; { Find the ACTUAL scene with the specified ID, as opposed to any } { temporary scenes which may have the same value. } function LookForScene( S: GearPtr ): GearPtr; { Look for the requested scene along this path, } { checking subcoms as well. } var it: GearPtr; begin it := Nil; while ( S <> Nil ) and ( it = Nil ) do begin if ( ( S^.G = GG_Scene ) or ( S^.G = GG_World ) ) and ( S^.S = SID ) then it := S; if it = Nil then it := LookForScene( S^.SubCom ); S := S^.Next; end; LookForScene := it; end; var Part: GearPtr; begin if Scene = Nil then Exit( Nil ); Scene := FindRoot( Scene ); Part := Nil; if SID > 0 then begin Part := LookForScene( Scene ); end else if SID < 0 then begin Part := FindMetascene( Scene , SID ); end; FindActualScene := Part; end; Function FindActualScene( GB: GameBoardPtr; SID: Integer ): GearPtr; { Find the ACTUAL scene with the specified ID, as opposed to any } { temporary scenes which may have the same value. } begin FindActualScene := FindActualScene( GB^.Scene , SID ); end; Function IsAScene( S: GearPtr ): Boolean; { Return TRUE if S is a scene or metascene, or FALSE otherwise. } begin IsAScene := ( S <> Nil ) and ( ( S^.G = GG_Scene ) or ( S^.G = GG_MetaScene ) ); end; Function SceneIsTemp( S: GearPtr ): Boolean; { Return TRUE if S is a temporary scene. These include: } { - Metascenes } { - Dynamic Scenes } begin SceneIsTemp := ( S = Nil ) or ( S^.G = GG_MetaScene ) or IsInvCom( S ); end; Function FindRootScene( S: GearPtr ): GearPtr; { Return the root scene of S. If no root is found, return Nil. } { S should be a scene or metascene connected to the adventure; otherwise, } { expect an error. } begin if S = Nil then Exit( Nil ); if ( S^.G = GG_MetaScene ) and ( NAttValue( S^.NA , NAG_Narrative , NAS_EntranceScene ) <> 0 ) then S := FindActualScene( FindRoot( S ) , NAttValue( S^.NA , NAG_Narrative , NAS_EntranceScene ) ) else if IsInvCom( S ) then S := FindActualScene( FindRoot( S ) , S^.S ); while ( S <> Nil ) and not ( ( S^.Parent <> Nil ) and ( S^.Parent^.G <> GG_Scene ) ) do begin S := S^.Parent; end; FindRootScene := S; end; Function FindWorld( GB: GameBoardPtr; S: GearPtr ): GearPtr; { Find the world this scene belongs to. } { If no world can be found, return Nil. } begin { First, find the city this scene belongs to. That should make things easier. } S := FindRootScene( S ); while ( S <> Nil ) and ( S^.G <> GG_World ) do begin S := S^.Parent; end; FindWorld := S; end; Procedure DelinkGearForMovement( GB: GameBoardPtr; GearToBeMoved: GearPtr ); { Delink the provided gear in preparation for movement to somewhere else. } var Scene,Master: GearPtr; SceneID,TID: Integer; begin if NAttValue( GearToBeMoved^.NA , NAG_ParaLocation , NAS_OriginalHome ) = 0 then begin SceneID := FindSceneID( GearToBeMoved , GB ); Scene := FindActualScene( GB , SceneID ); TID := NAttValue( GearToBeMoved^.NA , NAG_Location , NAS_Team ); if ( SceneID > 0 ) and ( SCene <> Nil ) then begin { Record team description. } SetSATt( GearToBeMoved^.SA , 'TEAMDATA <' + TeamDescription( Scene, LocateTeam( Scene , TID ) ) + '>' ); { Record the item's orginal home, if not already done. } SetNAtt( GearToBeMoved^.NA , NAG_ParaLocation , NAS_OriginalHome , Scene^.S ); end else begin SetNAtt( GearToBeMoved^.NA , NAG_ParaLocation , NAS_OriginalHome , -1 ); end; end; { Locate the root, if possible. } Master := FindRoot( GearToBeMoved ); { Delink the gear, if it can be found. } if IsSubCom( GearToBeMoved ) then begin DelinkGear( GearToBeMoved^.Parent^.SubCom , GearToBeMoved ); end else if IsInvCom( GearToBeMoved ) then begin DelinkGear( GearToBeMoved^.Parent^.InvCom , GearToBeMoved ); end else if IsFoundAlongTrack( GB^.Meks , GearToBeMoved) then begin DelinkGear( GB^.Meks , GearToBeMoved ); end; { If the root of the gear is on the map, is a mecha, and is temporary, } { get rid of that now. } if ( Master <> GearToBeMoved ) and ( GB <> Nil ) and IsFoundAlongTrack( GB^.Meks , Master ) and ( NAttValue( Master^.NA , NAG_EpisodeData , NAS_Temporary ) <> 0 ) then begin RemoveGear( GB^.Meks , Master ); end; { Remove attributes of the current area. } StripNAtt( GearToBeMoved , NAG_Location ); StripNAtt( GearToBeMoved , NAG_Damage ); StripNAtt( GearToBeMoved , NAG_WeaponModifier ); StripNAtt( GearToBeMoved , NAG_Condition ); StripNAtt( GearToBeMoved , NAG_StatusEffect ); end; Function KeepPlayingSC( GB: GameBoardPtr ): Boolean; { Check out this scenario and decide whether or not to keep } { playing. Right now, combat will continue as long as there } { is at least one active mek on each team. } var PTeam,ETeam: Integer; { Player Team , Enemy Team } begin { If this scenario is being controlled by a SCENE gear, } { control of when to quit will be handled by the event strings. } { Also, if we have received a QUIT order, stop playing. } if gb^.Scene <> Nil then KeepPlayingSC := Not gb^.QuitTheGame else if gb^.QuitTheGame then KeepPlayingSC := False else begin { Determine the number of player mecha and enemy mecha. } PTeam := NumActiveMasters( GB , NAV_DefPlayerTeam ); ETeam := NumActiveMasters( GB , NAV_DefEnemyTeam ); KeepPlayingSC := ( PTeam > 0 ) and ( ETeam > 0 ); end; end; Procedure RecordFatality( Camp: CampaignPtr; NPC: GearPtr ); { This character has died, and is about to be removed from the campaign. } { Record the death, and also record any special things about this NPC. } var Relationship: Integer; begin AddNAtt( Camp^.Source^.NA , NAG_Narrative , NAS_TotalFatalities , 1 ); Relationship := NAttValue( NPC^.NA , NAG_Relationship , 0 ); case Relationship of NAV_Family: AddNAtt( Camp^.Source^.NA , NAG_Narrative , NAS_FamilyFatalities , 1 ); NAV_Lover: AddNAtt( Camp^.Source^.NA , NAG_Narrative , NAS_LoverFatalities , 1 ); NAV_Friend: AddNAtt( Camp^.Source^.NA , NAG_Narrative , NAS_FriendFatalities , 1 ); end; Relationship := NAttValue( NPC^.NA , NAG_Location , NAS_Team ); if ( Relationship = NAV_DefPlayerTeam ) or ( Relationship = NAV_LancemateTeam ) then begin AddNAtt( Camp^.Source^.NA , NAG_Narrative , NAS_LancemateFatalities , 1 ); end; end; Function HasMeritBadge( Adv: GearPtr; Badge: Integer ): Boolean; { Return TRUE if the current PC has the requested merit badge or its } { equivalent ability, FALSE if not. } begin Adv := FindRoot( Adv ); HasMeritBadge := NAttValue( Adv^.NA , NAG_MeritBadge , Badge ) <> 0; end; end. GH2/pcaction.pp0000644000175000017500000032144611365256066012261 0ustar kaolkaolunit pcaction; { This unit specifically handles PC actions. } { 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 PCSaveCampaign( Camp: CampaignPtr; PC: gearPtr; PrintMsg: Boolean ); Procedure GetPlayerInput( Mek: GearPtr; Camp: CampaignPtr ); Function WorldMapMain( Camp: CampaignPtr ): Integer; implementation uses ability,action,aibrain,arenacfe,arenascript,backpack, effects,gearutil,targetui,ghchars,gearparser,robotics, ghprop,ghswag,ghweapon,interact,menugear,movement, playwright,rpgdice,skilluse,texutil,ui4gh,grabgear, narration,description,ghintrinsic,training,ghsensor, wmonster,infodisplay, {$IFDEF ASCII} vidgfx,vidmap,vidmenus,vidinfo; {$ELSE} {$IFDEF CUTE} cutegfx,cutemap,glmenus,glinfo,sdl; {$ELSE} glgfx,glmap,glmenus,glinfo,sdl; {$ENDIF} {$ENDIF} const { This array cross-references the RL direction key with } { the gearhead direction number it corresponds to. } Roguelike_D: Array [1..9] of Byte = ( 3, 2, 1, 4, 0, 0, 5, 6, 7 ); Reverse_RL_D: Array [0..7] of Byte = ( 6 , 3 , 2 , 1 , 4 , 7, 8 , 9 ); var PCACTIONRD_PC: GearPtr; PCACTIONRD_GB: GameBoardPtr; PCACTIONRD_Menu: RPGMenuPtr; { These two are for redrawing the FieldHQ } PCACTIONRD_Source: GearPtr; { with the new interface. } Procedure PCActionRedraw; { Redraw the map and the PC's info. } begin CombatDisplay( PCACTIONRD_GB ); end; Procedure PCMenuRedraw; { Redraw the map and the PC's info. } begin CombatDisplay( PCACTIONRD_GB ); InfoBox( ZONE_Menu ); {$IFDEF ASCII} ClockBorder; if Tactics_Turn_In_Progess then begin TacticsTimeInfo( PCACTIONRD_GB ); end else begin CMessage( TimeString( PCACTIONRD_GB^.ComTime ) , ZONE_Clock , StdWhite ); end; {$ENDIF} end; Procedure FieldHQRedraw; { Do a redraw for the Field HQ. } var Part: GearPtr; begin CombatDisplay( PCACTIONRD_GB ); SetupFHQDisplay; if ( PCACTIONRD_Menu <> Nil ) and ( PCACTIONRD_Source <> Nil ) then begin Part := RetrieveGearSib( PCACTIONRD_Source , CurrentMenuItemValue( PCACTIONRD_Menu ) ); if Part <> Nil then begin BrowserInterfaceInfo( PCACTIONRD_GB , Part , ZONE_ItemsInfo ); end; end; end; Procedure PCSRedraw; { Redraw the map and the PC's info. } begin CombatDisplay( PCACTIONRD_GB ); SetupMemoDisplay; end; Procedure ViewCharRedraw; { Redraw the view character screen. } begin CombatDisplay( PCACTIONRD_GB ); CharacterDisplay( PCACTIONRD_PC , PCACTIONRD_GB ); InfoBox( ZONE_Menu ); end; Procedure FHQ_Rename( GB: GameBoardPtr; NPC: GearPtr ); { Enter a new name for NPC. } var name: String; begin name := GetStringFromUser( ReplaceHash( MsgString( 'FHQ_Rename_Prompt' ) , GearName( NPC ) ) , @PCActionRedraw ); if name <> '' then SetSAtt( NPC^.SA , 'name <' + name + '>' ); end; Procedure FHQ_Rejoin( GB: GameBoardPtr; PC,NPC: GearPtr ); { NPC will rejoin the party if there's enough room. } var CanJoin: Boolean; begin { Depending on whether the NPC in question is a robot or a pet, different procedures } { must be called. } CanJoin := PetsPresent( GB ) < PartyPetSlots( PC ); if CanJoin then begin DialogMsg( ReplaceHash( MsgString( 'REJOIN_OK' ) , GearName( NPC ) ) ); AddLancemate( GB , NPC ); end else begin DialogMsg( ReplaceHash( MsgString( 'REJOIN_DontWant' ) , GearName( NPC ) ) ); end; end; Procedure AutoTraining( GB: GameBoardPtr; var NPC: GearPtr ); { The NPC in question is going to raise some skills. } var N,T: Integer; FXP: LongInt; TrainedSome: Boolean; M,M2: GearPtr; Gene: String; begin TrainedSome := False; repeat FXP := NAttValue( NPC^.NA , NAG_Experience , NAS_TotalXP ) - NAttValue( NPC^.NA , NAG_Experience , NAS_SpentXP ); { Determine how many skills or stats may be trained. } N := 0; for t := 1 to NumSkill do begin if ( NAttValue( NPC^.NA , NAG_SKill , T ) > 0 ) and ( SkillAdvCost( NPC , NAttValue( NPC^.NA , NAG_SKill , T ) ) <= FXP ) then begin Inc( N ); end; end; if N > 0 then begin N := Random( N ); for t := 1 to NumSkill do begin if ( NAttValue( NPC^.NA , NAG_SKill , T ) > 0 ) and ( SkillAdvCost( NPC , NAttValue( NPC^.NA , NAG_SKill , T ) ) <= FXP ) then begin if N = 0 then begin AddNAtt( NPC^.NA , NAG_Experience , NAS_SpentXP , SkillAdvCost( NPC , NAttValue( NPC^.NA , NAG_SKill , T ) ) ); AddNAtt( NPC^.NA , NAG_Skill , T , 1 ); dialogmsg( ReplaceHash( ReplaceHash( MsgString( 'AUTOTRAIN_LEARN' ) , GearName( NPC ) ) , MsgString( 'SKILLNAME_' + BStr( T ) ) ) ); TrainedSome := True; N := 5; end; Dec( N ); end; end; end; until N < 1; { Free XP now becomes Freaky XP... check for evolution. } FXP := NAttValue( NPC^.NA , NAG_GearOps , NAS_EvolveAt ); if ( FXP > 0 ) and ( NAttValue( NPC^.NA , NAG_Experience , NAS_TotalXP ) > FXP ) then begin { Search the monster list for another creature which is: 1) from the } { same genepool as our original, and 2) more powerful. } M := WMOnList; Gene := UpCase( SATtValue( NPC^.SA , 'GENEPOOL' ) ); N := 0; while M <> Nil do begin if ( UpCase( SAttValue( M^.SA , 'GENEPOOL' ) ) = Gene ) and ( M^.V > NPC^.V ) then Inc( N ); M := M^.Next; end; { If at least one such monster has been found, } { it's time to do the evolution! } if N > 0 then begin N := Random( N ); M2 := Nil; M := WMonList; while M <> Nil do begin if ( UpCase( SAttValue( M^.SA , 'GENEPOOL' ) ) = Gene ) and ( M^.V > NPC^.V ) then begin Dec( N ); if N = -1 then M2 := M; end; M := M^.Next; end; { We've selected a new body. Change over. } if ( M2 <> Nil ) and ( NPC^.Parent = Nil ) then begin { First, make the current monster drop everything } { it's carrying. } ShakeDown( GB , NPC , NAttValue( NPC^.NA , NAG_Location , NAS_X ) , NAttValue( NPC^.NA , NAG_Location , NAS_Y ) ); { Then copy the new body to the map. } M := CloneGear( M2 ); { Insert M into the map. } DeployGear( GB , M , True ); DialogMsg( ReplaceHash( ReplaceHash( MsgString( 'AUTOTRAIN_EVOLVE' ) , GearName( NPC ) ) , GearName( M ) ) ); { Copy over name, XP, team, location, and skills. } SetSAtt( M^.SA , 'name <' + GearName( NPC ) + '>' ); SetNAtt( M^.NA , NAG_Experience , NAS_SpentXP , NAttValue( NPC^.NA , NAG_Experience , NAS_SpentXP ) ); SetNAtt( M^.NA , NAG_Experience , NAS_TotalXP , NAttValue( NPC^.NA , NAG_Experience , NAS_TotalXP ) ); SetNAtt( M^.NA , NAG_Location , NAS_Team , NAttValue( NPC^.NA , NAG_Location , NAS_Team ) ); SetNAtt( M^.NA , NAG_Location , NAS_X , NAttValue( NPC^.NA , NAG_Location , NAS_X ) ); SetNAtt( M^.NA , NAG_Location , NAS_Y , NAttValue( NPC^.NA , NAG_Location , NAS_Y ) ); SetNAtt( M^.NA , NAG_Personal , NAS_CID , NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) ); SetNAtt( M^.NA , NAG_CharDescription , NAS_CharType , NAttValue( NPC^.NA , NAG_CharDescription , NAS_CharType ) ); GearUp( M ); for t := 1 to NumSkill do begin if NAttValue( NPC^.NA , NAG_Skill , T ) > NAttValue( M^.NA , NAG_Skill , T ) then SetNAtt( M^.NA , NAG_Skill , T , NAttValue( NPC^.NA , NAG_Skill , T ) ); end; TrainedSome := True; { Now, delete the original. } RemoveGear( GB^.Meks , NPC ); NPC := M; end; end; end; if not TrainedSome then DialogMsg( ReplaceHash( MsgString( 'AUTOTRAIN_FAIL' ) , GearName( NPC ) ) ); end; Procedure FHQ_Disassemble( GB: GameBoardPtr; PC,NPC: GearPtr ); { Robot NPC is no longer desired. Disassemble it into spare parts, delete the NPC, } { then give the parts to PC. } var M: Integer; begin { Error check- NPC must be on the gameboard. } if not IsFoundAlongTrack( GB^.Meks , NPC ) then Exit; { First, make the robot drop everything it's carrying. } ShakeDown( GB , NPC , NAttValue( NPC^.NA , NAG_Location , NAS_X ) , NAttValue( NPC^.NA , NAG_Location , NAS_Y ) ); { Print a message. } DialogMsg( ReplaceHash( MsgString( 'FHQ_DIS_Doing' ) , GearName( NPC ) ) ); { The size of the spare parts is to be determined by the weight of the robot. } M := GearMass( NPC ); { Delete the NPC. } RemoveGear( GB^.Meks , NPC ); { Get the spare parts. } NPC := LoadNewSTC( 'SPAREPARTS-1' ); NPC^.V := M * 5; InsertInvCom( PC , NPC ); end; Procedure FHQ_ThisLancemateWasSelected( GB: GameBoardPtr; PC,NPC: GearPtr ); { NPC was selected by the lancemate browser. Allow the PC to train, } { equip, or dismiss this character. } var RPM: RPGMenuPtr; N: Integer; begin PCACTIONRD_PC := NPC; PCACTIONRD_GB := GB; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); if IsSafeArea( GB ) or OnTheMap( GB, NPC ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_LMV_Equip' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'FHQ_LMV_Train' ) , 2 ); if ( NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) <> 0 ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_SelectMecha' ) , 4 ); if ( NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) = 0 ) or ( UpCase( SAttValue( NPC^.SA , 'JOB' ) ) = 'ROBOT' ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_Rename' ) , 5 ); if ( NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) = 0 ) and ( NAttValue( NPC^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_Rejoin' ) , 6 ); if ( GB <> Nil ) and ( GB^.Scene <> Nil ) and IsSubCom( GB^.Scene ) and IsSAfeArea( GB ) and OnTheMap( GB, NPC ) and ( NAttValue( NPC^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and ( NAttValue( NPC^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_TempLancemate ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_LMV_Dismiss' ) , 3 ); if ( NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) = 0 ) and ( UpCase( SAttValue( NPC^.SA , 'JOB' ) ) = 'ROBOT' ) then AddRPGMenuItem( RPM , MsgString( 'FHQ_Disassemble' ) , 7 ); AddRPGMenuItem( RPM , MsgString( 'FHQ_PartEditor' ) , 8 ); AddRPGMenuItem( RPM , MsgString( 'EXIT' ) , -1 ); repeat PCACTIONRD_PC := NPC; n := SelectMenu( RPM , @ViewCharRedraw ); case N of 1: begin PCActionRD_GB := GB; LancemateBackpack( GB , PC , NPC , @PCActionRedraw ); end; 2: if NAttValue( NPC^.NA , NAG_Personal , NAS_CID ) <> 0 then begin PCACTIONRD_GB := GB; DoTraining( GB , NPC , @PCActionRedraw ); end else AutoTraining( GB , NPC ); 3: begin RemoveLancemate( GB , NPC , True ); DialogMsg( ReplaceHash( MsgString( 'FHQ_LMV_Removed' ) , GearName( NPC ) ) ); N := -1; end; 4: FHQ_SelectMechaForPilot( GB , NPC ); 5: FHQ_Rename( GB , NPC ); 6: FHQ_Rejoin( GB , PC , NPC ); 7: begin FHQ_Disassemble( GB , PC , NPC ); N := -1; end; 8: MechaPartBrowser( NPC , @PCActionRedraw ); end; until N = -1; DisposeRPGMenu( RPM ); end; Procedure FieldHQ( GB: GameBoardPtr; PC: GearPtr ); { View the PC's lancemates. This menu should allow the PC to view, equip, } { train and dismiss these characters. } var RPM: RPGMenuPtr; N: Integer; M: GearPtr; begin { To start with, gather up all of the PC's crap from all over the city. } GatherFieldHQ( GB ); repeat { Create the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_FieldHQMenu ); M := GB^.Meks; N := 1; while M <> Nil do begin if ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) then begin AddRPGMenuItem( RPM , TeamMateName( M ) , N ); end else if ( NAttValue( M^.NA , NAG_CharDescription , NAS_CharType ) = NAV_CTLancemate ) and ( NAttValue( M^.NA , NAG_Personal , NAS_CID ) = 0 ) Then begin AddRPGMenuItem( RPM , TeamMateName( M ) , N ); end else if ( M^.G <> GG_Character ) and ( NAttValue( M^.NA , NAG_Location , NAS_Team ) = NAV_DefPlayerTeam ) then begin AddRPGMenuItem( RPM , TeamMateName( M ) , N ); end; M := M^.Next; Inc( N ); end; RPMSortAlpha( RPM ); AddRPGMenuItem( RPM , MSgString( 'EXIT' ) , -1 ); PCACTIONRD_Menu := RPM; PCACTIONRD_Source := GB^.Meks; { Get a selection from the menu. } n := SelectMenu( RPM , @FieldHQRedraw ); DisposeRPGMenu( RPM ); if N > 0 Then begin M := RetrieveGearSib( GB^.Meks , N ); if M^.G = GG_Character then begin FHQ_ThisLancemateWasSelected( GB , PC , M ); end else begin PCActionRD_GB := GB; FHQ_ThisWargearWasSelected( GB , GB^.Meks , PC , M , @PCActionRedraw ); end; end; until N = -1; end; Procedure CheckHiddenMetaterrain( GB: GameBoardPtr; Mek: GearPtr ); { Some metaterrain might be hidden. If any hidden metaterrain } { is located, reveal it and run its REVEAL trigger. } var MT: GearPtr; T: String; P: Point; begin { First record the PC's current position, for future reference. } P := GearCurrentLocation( Mek ); { Look through all the gears on the board, searching for metaterrain. } MT := GB^.Meks; while MT <> Nil do begin { If this terrain matches our basic criteria, } { we'll perform the next few tests. } if ( MT^.G = GG_MetaTerrain ) and ( MT^.Stat[ STAT_MetaVisibility ] > 0 ) and ( Range( MT , P.X , P.Y ) <= 1 ) then begin { Roll the PC's AWARENESS skill. If it beats } { the terrain's concealment score, reveal it. } if SkillRoll( GB , Mek , NAS_Awareness , STAT_Perception , MT^.Stat[ STAT_MetaVisibility ] , 0 , False , True ) > MT^.Stat[ STAT_MetaVisibility ] then begin MT^.Stat[ STAT_MetaVisibility ] := 0; T := 'REVEAL'; TriggerGearScript( GB , MT , T ); VisionCheck( GB , Mek ); end; end; MT := MT^.Next; end; end; Procedure PCSearch( GB: GameBoardPtr; PC: GearPtr ); { The PC will search for enemy units and hidden things. } { This action costs MENTAL. } var Mek: GearPtr; begin { Costs one point of MENTAL and an action. } AddMentalDown( PC , 1 ); WaitAMinute( GB , PC , ReactionTime( PC ) ); { Look through all the gears on the board, searching for ones } { that aren't visible yet. } { Note that by searching in this way, the PC will not be vunerable } { to being spotted himself. } Mek := GB^.Meks; while Mek <> Nil do begin if OnTheMap( GB, Mek ) and not MekCanSeeTarget( GB , PC , Mek ) then begin if IsMasterGear( Mek ) and CheckLOS( GB , PC , Mek ) then begin { The mek has just been spotted. } RevealMek( GB , Mek , PC ); end; end; Mek := Mek^.Next; end; CheckHiddenMetaTerrain( GB , PC ); end; Procedure MemoBrowser( GB: GameBoardPtr; PC: GearPtr ); { Find all the memos that the player has accumulated, then allow } { them to be browsed through, then restore the display afterwards. } const m_email = 1; m_memo = 2; m_rumor = 3; m_news = 4; m_Personadex = 5; var MainMenu: RPGMenuPtr; A: Integer; begin MainMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_MemoText ); if HasPCommCapability( PC , NAS_EMail ) then AddRPGMenuItem( MainMenu , MsgString( 'MEMO_ReadEMail' ) , m_EMail ); if HasPCommCapability( PC , NAS_Memo ) then begin AddRPGMenuItem( MainMenu , MsgString( 'MEMO_ReadMemo' ) , m_Memo ); AddRPGMenuItem( MainMenu , MsgString( 'MEMO_ReadRumors' ) , m_Rumor ); end; if HasPCommCapability( PC , NAS_News ) then AddRPGMenuItem( MainMenu , MsgString( 'MEMO_ReadNews' ) , m_News ); if HasPCommCapability( PC , NAS_Personadex ) then AddRPGMenuItem( MainMenu , MsgString( 'MEMO_Personadex' ) , m_Personadex ); if MainMenu^.NumItem < 1 then begin DialogMsg( MsgString( 'MEMO_NoBrowser' ) ); end else if MainMenu^.NumItem = 1 then begin case MainMenu^.FirstItem^.Value of m_Memo: BrowseMemoType( GB , 'MEMO' ); m_rumor: BrowseMemoType( GB , 'RUMEMO' ); m_News: BrowseMemoType( GB , 'NEWS' ); m_EMail: BrowseMemoType( GB , 'EMAIL' ); m_Personadex: View_Personadex( GB ); end; end else begin AlphaKeyMenu( MainMenu ); repeat A := SelectMenu( MainMenu , @PCSRedraw ); case A of m_Memo: BrowseMemoType( GB , 'MEMO' ); m_rumor: BrowseMemoType( GB , 'RUMEMO' ); m_News: BrowseMemoType( GB , 'NEWS' ); m_EMail: BrowseMemoType( GB , 'EMAIL' ); m_Personadex: View_Personadex( GB ); end; until ( A = -1 ) or not KeepPlayingSC( GB ); end; DisposeRPGMenu( MainMenu ); end; Function InterfaceType( GB: GameBoardPtr; Mek: GearPtr ): Integer; { Return the constant for the currently-being-used control type. } begin if GB^.Scale > 2 then begin InterfaceType := WorldMapMethod; end else if Mek^.G = GG_Character then begin InterfaceType := CharacterMethod; end else begin InterfaceType := ControlMethod; end; end; Procedure PCTalk( GB: GameBoardPtr; PC: GearPtr ); { PC wants to do some talking. Select an NPC, then let 'er rip. } begin DialogMsg( MsgSTring( 'TALKING_Prompt' ) ); if LookAround( GB , PC ) then begin DoTalkingWithNPC( GB , PC , LOOKER_Gear , False ); end else begin DialogMsg( MsgString( 'Cancelled' ) ); end; end; Procedure PCTelephone( GB: GameBoardPtr; PC: GearPtr ); { Make a telephone call, if the PC has a telephone. } var Name: String; NPC,RootScene: GearPtr; begin if HasPCommCapability( PC , PCC_Phone ) then begin DialogMsg( MsgString( 'PHONE_Prompt' ) ); Name := GetStringFromUser( MsgString( 'PHONE_GetName' ) , @PCActionRedraw ); if Name = '*' then Name := SAttValue( PC^.SA , 'REDIAL' ) else SetSAtt( PC^.SA , 'REDIAL <' + Name + '>' ); if Name <> '' then begin { First, seek the NPC on the gameboard... } NPC := SeekGearByName( GB^.Meks , Name ); if NPC = Nil then begin { Next, try the entire root scene... } RootScene := FindRootScene( GB^.Scene ); if RootScene <> Nil then begin NPC := SeekChildByName( RootScene , Name ); end; { Finally, try searching by keyword. } if NPC = Nil then NPC := FindLocalNPCByKeyword( GB , Name ); end; if CanContactByPhone( GB , NPC ) then begin DoTalkingWithNPC( GB , PC , NPC , True ); end else begin DialogMsg( ReplaceHash( MsgString( 'PHONE_NotFound' ) , Name ) ); end; end; end else begin DialogMsg( MsgString( 'PHONE_NoPhone' ) ); end; end; Procedure UsePropFrontEnd( GB: GameBoardPtr; PC , Prop: GearPtr; T: String ); { Do everything that needs to be done when a prop is used. } begin TriggerGearScript( GB , Prop , T ); VisionCheck( GB , PC ); WaitAMinute( GB , PC , ReactionTime( PC ) ); end; Function SelectOneVisibleUsableGear( GB: GameBoardPtr; X,Y: Integer; Trigger: String ): GearPtr; { Create a menu, then select one of the visible, usable gears } { from tile X,Y. } var RPM: RPGMenuPtr; it: GearPtr; N: Integer; begin { Create and fill the menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); N := NumVisibleUsableGearsXY( GB , X , Y , Trigger ); while N > 0 do begin it := FindVisibleUsableGearXY( GB , X , Y , N , Trigger ); AddRPGMenuItem( RPM , GearName( it ) , N ); Dec( N ); end; { Select an item. } N := SelectMenu( RPM , @PCMenuRedraw ); DisposeRPGMenu( RPM ); if N > 0 then begin SelectOneVisibleUsableGear := FindVisibleUsableGearXY( GB , X , Y , N , Trigger ); end else begin SelectOneVisibleUsableGear := Nil; end; end; Function ActivatePropAtSpot( GB: GameBoardPtr; PC: GearPtr; X,Y: Integer; Trigger: String ): Boolean; { Check spot X,Y. If there are any usable items there, use one. } { If there are multiple items in the spot, prompt for a selection. } { Return TRUE if a prop was activated, or FALSE otherwise. } var N: Integer; Prop: GearPtr; begin { First count how many usable items there are at the spot. } N := NumVisibleUsableGearsXY( GB , X , Y , Trigger ); { Next, choose the item which is to be used. } if N > 0 then begin if N = 1 then begin Prop := FindVisibleUsableGearXY( GB , X , Y , 1 , Trigger ); end else begin Prop := SelectOneVisibleUsableGear( GB , X , Y , Trigger ); end; if ( Prop <> Nil ) then begin UsePropFrontEnd( GB , PC , Prop , Trigger ); ActivatePropAtSpot := True; end else begin ActivatePropAtSpot := False; end; end else begin ActivatePropAtSpot := False; end; end; Procedure PCUseProp( GB: GameBoardPtr; PC: GearPtr ); { PC wants to do something with a prop. Select an item, then let 'er rip. } var D,PropD: Integer; P: Point; begin { See whether or not there's only one prop to use. } PropD := -1; P := GearCurrentLocation( PC ); for D := 0 to 7 do begin if NumVisibleUsableGearsXY( GB , P.X + AngDir[ D , 1 ] , P.Y + AngDir[ D , 2 ] , 'USE' ) > 0 then begin if PropD = -1 then PropD := D else PropD := -2; end; end; if PropD < 0 then begin DialogMsg( MsgString( 'PCUS_Prompt' ) ); PropD := DirKey( @PCActionRedraw ); end; if PropD > -1 then begin if not ActivatePropAtSpot( GB , PC , P.X + AngDir[ PropD , 1 ] , P.Y + AngDir[ PropD , 2 ] , 'USE' ) then DialogMsg( MsgString( 'PCUS_NotFound' ) ); end; end; Procedure PCEnter( GB: GameBoardPtr; PC: GearPtr ); { The PC is attempting to enter a place. } { Seek a usable gear in this tile, then try to activate it. } var P: Point; begin P := GearCurrentLocation( PC ); if not ActivatePropAtSpot( GB , PC , P.X , P.Y , 'USE' ) then DialogMsg( MsgString( 'PCUS_NotFound' ) );; end; Procedure PCUseSkillOnProp( GB: GameBoardPtr; PC: GearPtr; Skill: Integer ); { PC wants to do something with a prop. Select an item, then let 'er rip. } var PropD: Integer; P: Point; Trigger: String; begin P := GearCurrentLocation( PC ); DialogMsg( MsgString( 'PCUSOP_Prompt' ) ); PropD := DirKey( @PCActionRedraw ); Trigger := Skill_Use_Trigger[ Skill ]; if ( PropD = -1 ) and ( NumVisibleUsableGearsXY( GB , P.X , P.Y , Trigger ) > 0 ) then begin if not ActivatePropAtSpot( GB , PC , P.X , P.Y , Trigger ) then DialogMsg( MsgString( 'PCUS_NotFound' ) );; end else if ( PropD <> -1 ) and ( NumVisibleUsableGearsXY( GB , P.X + AngDir[ PropD , 1 ] , P.Y + AngDir[ PropD , 2 ] , Trigger ) > 0 ) then begin if not ActivatePropAtSpot( GB , PC , P.X + AngDir[ PropD , 1 ] , P.Y + AngDir[ PropD , 2 ] , Trigger ) then DialogMsg( MsgString( 'PCUS_NotFound' ) ); end else if GB^.Scene <> Nil then begin TriggerGearScript( GB , GB^.Scene , Trigger ); end; end; Procedure DoPCRepair( GB: GameBoardPtr; PC: GearPtr; Skill: Integer ); { The PC is going to use one of the repair skills. Call the } { standard procedure, then print output. } var D,Best: Integer; P: Point; Mek,Target: GearPtr; begin DialogMsg( MsgString( 'PCREPAIR_Prompt' ) ); D := DirKey( @PCActionRedraw ); P := GearCurrentLocation( PC ); if D <> -1 then begin P.X := P.X + AngDir[ D , 1 ]; P.Y := P.Y + AngDir[ D , 2 ]; end; Mek := GB^.Meks; Target := Nil; Best := 0; while Mek <> Nil do begin if ( not AreEnemies( GB , PC , Mek ) ) and ( RepairNeededBySkill( Mek , Skill ) > Best ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_X ) = P.X ) and ( NAttValue( Mek^.NA , NAG_Location , NAS_Y ) = P.Y ) then begin Target := Mek; Best := RepairNeededBySkill( Mek , Skill ); end; mek := mek^.Next; end; if Target <> Nil then begin DoFieldRepair( GB , PC , FindRoot( Target ) , Skill ); end else begin if not ActivatePropAtSpot( GB , PC , P.X , P.Y , Skill_Use_Trigger[ Skill ] ) then DialogMsg( MsgString( 'PCREPAIR_NoDamageDone' ) ); end; end; Procedure DominateAnimal( GB: GameBoardPtr; PC: GearPtr ); { The PC will attempt to dominate this animal. Make a skill roll and see } { if it's possible. If the skill roll fails, the animal may become enraged. } Function IsGoodTarget( M: GearPtr ): Boolean; { Return TRUE if M is a good target for domination, or FALSE otherwise. } begin if GearActive( M ) and AreEnemies( GB , M , PC ) and ( NAttValue( M^.NA , NAG_PErsonal , NAS_CID ) = 0 ) then begin IsGoodTarget := True; end else if GearActive( M ) and ( NAttValue( M^.NA , NAG_PErsonal , NAS_CID ) = 0 ) and ( NAttValue( M^.NA , NAG_CharDescription , NAS_CharType ) = NAV_CTLancemate ) and ( NAttValue( M^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then begin IsGoodTarget := True; end else begin IsGoodTarget := False; end; end; var D: Integer; M,Target: GearPtr; SkTarget,SkRoll: Integer; P,P2: Point; begin if CurrentMental( PC ) < 1 then begin DialogMsg( MsgString( 'DOMINATE_TOO_TIRED' ) ); Exit; end; DialogMsg( ReplaceHash( MsgString( 'DOMINATE_Announce' ) , PilotName( PC ) ) ); P := GearCurrentLocation( PC ); { Pass one - try to find a monster nearby. } M := GB^.Meks; Target := Nil; D := 0; while M <> Nil do begin { Two types of animal may be dominated: those which are hostile } { to the PC, and those which are already his pets. } P2 := GearCurrentLocation( M ); if ( Abs( P2.X - P.X ) <= 1 ) and ( Abs( P2.Y - P.Y ) <= 1 ) and IsGoodTarget( M ) then begin Target := M; Inc( D ); end; M := M^.Next; end; { If more than one monster was found, prompt for a direction. } if D > 1 then begin DialogMsg( MsgString( 'DOMINATE_Prompt' ) ); D := DirKey( @PCActionRedraw ); P.X := P.X + AngDir[ D , 1 ]; P.Y := P.Y + AngDir[ D , 2 ]; M := GB^.Meks; Target := Nil; while M <> Nil do begin { Two types of animal may be dominated: those which are hostile } { to the PC, and those which are already his pets. } P2 := GearCurrentLocation( M ); if ( P2.X = P.X ) and ( P2.Y = P.Y ) and IsGoodTarget( M ) then Target := M; M := M^.Next; end; end; if Target = Nil then begin DialogMsg( MsgString( 'DOMINATE_NotFound' ) ); Exit; end else if AreEnemies( GB , Target , PC ) then begin { Locate the target value for this animal. } { If it has no skill target, then either it can't be dominated or } { the PC has already tried and failed to dominate it. } SkTarget := NAttValue( Target^.NA , NAG_GearOps , NAS_DominationTarget ); { The PC only gets one attempt regardless... } SetNAtt( Target^.NA , NAG_GearOps , NAS_DominationTarget , 0 ); if SkTarget < 1 then begin DialogMsg( ReplaceHash( MsgString( 'DOMINATE_Fail' ) , GearName( Target ) ) ); end else begin SkRoll := SkillRoll( GB , PC , NAS_Survival , STAT_Ego , SkTarget , ToolBonus( PC , -NAS_DominateAnimal ) , False , True ); if ( SkRoll > SkTarget ) and ( PetsPresent( GB ) < PartyPetSlots( PC ) ) then begin DialogMsg( ReplaceHash( MsgString( 'DOMINATE_OK' ) , GearName( Target ) ) ); AddLancemate( GB , Target ); SetNAtt( Target^.NA , NAG_CharDescription , NAS_CharType , NAV_CTLancemate ); DoleExperience( Target , CStat( LocatePilot( PC ) , STAT_Knowledge ) * 50 ); DoleSkillExperience( PC , NAS_Survival , SkTarget * 2 ); DoleExperience( PC , Target , SkTarget ); end else if ( SkRoll < ( SkTarget div 3 ) ) then begin DialogMsg( ReplaceHash( MsgString( 'DOMINATE_Enraged' ) , GearName( Target ) ) ); for SkRoll := 6 to 10 do AddNAtt( Target^.NA , NAG_Skill , SkRoll , Random( 5 ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'DOMINATE_Fail' ) , GearName( Target ) ) ); if SkTarget > 0 then DoleSkillExperience( PC , 40 , Random( 5 ) + 1 ); end; end; end else begin { This animal is an ex-member of the party. It'll come back fairly } { peacefully, as long as there's room. } if PetsPresent( GB ) < PartyPetSlots( PC ) then begin DialogMsg( ReplaceHash( MsgString( 'DOMINATE_OK' ) , GearName( Target ) ) ); AddLancemate( GB , Target ); end else begin DialogMsg( ReplaceHash( MsgString( 'DOMINATE_DontWant' ) , GearName( Target ) ) ); end; end; { Dominating an animal costs MP and takes time. } { If no animal was chosen, the procedure already exited above... } AddMentalDown( PC , 5 ); WaitAMinute( GB , PC , ReactionTime( PC ) * 2 ); end; Procedure PickPockets( GB: GameBoardPtr; PC: GearPtr ); { The PC will attempt to steal from a nearby NPC. } Function IsGoodTarget( M: GearPtr ): Boolean; { Return TRUE if M is a good target for pick pockets, or FALSE otherwise. } { It's a good target if it's an NPC (with CID), not a lancemate or the PC } { himself, and alive. } var Team: Integer; begin if GearActive( M ) and ( M^.G = GG_Character ) and ( NAttValue( M^.NA , NAG_PErsonal , NAS_CID ) <> 0 ) then begin Team := NAttValue( M^.NA , NAG_Location , NAS_Team ); IsGoodTarget := ( Team <> NAV_DefPlayerTeam ) and ( Team <> NAV_LancemateTeam ); end else begin IsGoodTarget := False; end; end; var D: Integer; M,Target: GearPtr; SkTarget,SkRoll: Integer; P,P2: Point; Cash,NID: LongInt; begin if CurrentMental( PC ) < 1 then begin DialogMsg( MsgString( 'PICKPOCKET_TOO_TIRED' ) ); Exit; end; DialogMsg( ReplaceHash( MsgString( 'PICKPOCKET_Announce' ) , PilotName( PC ) ) ); P := GearCurrentLocation( PC ); { Pass one - try to find a target nearby. } M := GB^.Meks; Target := Nil; D := 0; while M <> Nil do begin P2 := GearCurrentLocation( M ); if ( Abs( P2.X - P.X ) <= 1 ) and ( Abs( P2.Y - P.Y ) <= 1 ) and IsGoodTarget( M ) then begin Target := M; Inc( D ); end; M := M^.Next; end; { If more than one monster was found, prompt for a direction. } if D > 1 then begin DialogMsg( MsgString( 'PICKPOCKET_Prompt' ) ); D := DirKey( @PCActionRedraw ); P.X := P.X + AngDir[ D , 1 ]; P.Y := P.Y + AngDir[ D , 2 ]; M := GB^.Meks; Target := Nil; while M <> Nil do begin P2 := GearCurrentLocation( M ); if ( P2.X = P.X ) and ( P2.Y = P.Y ) and IsGoodTarget( M ) then Target := M; M := M^.Next; end; end; { From here on, we want to deal with the actual PC. I don't think anyone will ever } { get the chance to pick pockets in a mecha, but better safe than sorry. } PC := LocatePilot( PC ); if Target = Nil then begin DialogMsg( MsgString( 'PICKPOCKET_NotFound' ) ); Exit; end else if ( NAttValue( Target^.NA , NAG_Personal , NAS_CashOnHandRestock ) > GB^.ComTime ) and ( Target^.InvCom = Nil ) then begin { If the victim has nothing to steal, then the PC can't very well steal it, } { can he? } DialogMsg( ReplaceHash( MsgString( 'PICKPOCKET_EMPTY' ) , GearName( Target ) ) ); Exit; end else begin { Time to start the actual stealing of stuff. } SkTarget := Target^.Stat[ STAT_Perception ] + 5; SkRoll := SkillRoll( GB , PC , NAS_Stealth , STAT_Craft , Target^.Stat[ STAT_Perception ] + 5 , ToolBonus( PC , -NAS_PickPockets ) , True , True ); if SkRoll > SkTarget then begin { The PC will now steal something. } { Roll the amount of money claimed. } Cash := Calculate_Threat_Points( NAttValue( Target^.NA , NAG_CharDescription , NAS_Renowned ) * 2 , Random( 5 ) + 1 ) div 20 + Random( 10 ); if Cash < 10 then Cash := Random( 8 ) + Random( 8 ) + 2; AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , Cash ); { Check for items... } M := SelectRandomGear( Target^.InvCom ); if M <> Nil then begin DelinkGear( Target^.InvCom , M ); { mark the item as stolen... } SetNAtt( M^.NA , NAG_Narrative , NAS_Stolen , 1 ); InsertInvCom( PC , M ); { Set the trigger for picking up an item, just in case } { there are any plots tied to this item. } NID := NAttValue( M^.NA , NAG_Narrative , NAS_NID ); if NID <> 0 then SetTrigger( GB , TRIGGER_GetItem + BStr( NID ) ); DialogMsg( ReplaceHash( ReplaceHash( MsgString( 'PICKPOCKET_CASH+ITEM' ) , BStr( Cash ) ) , GearName( M ) ) ); end else begin DialogMsg( ReplaceHash( MsgString( 'PICKPOCKET_CASH' ) , BStr( Cash ) ) ); end; DoleSkillExperience( PC , NAS_Stealth , SkTarget div 2 ); DoleExperience( PC , Target , SkTarget ); end else begin DialogMsg( MsgString( 'PICKPOCKET_FAIL' ) ); { If the failure was bad, the Guardians may notice... } DoleSkillExperience( PC , NAS_Stealth , 1 ); if SkRoll < ( SkTarget - 10 ) then begin SetTrigger( GB , 'THIEF!' ); AddReputation( PC , 6 , -1 ); AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( Target^.NA , NAG_PErsonal , NAS_CID ) , -20 ); end; end; { Picking pockets always has the consequences of Chaotic reputation } { and the target will like you less. Even if they don't know it's you } { stealing from them, it seems like every time they meet you they end } { up poorer...? } if SkRoll < ( SkTarget + 10 ) then AddNAtt( PC^.NA , NAG_ReactionScore , NAttValue( Target^.NA , NAG_PErsonal , NAS_CID ) , -( 5 + Random(10) ) ); AddReputation( PC , 2 , -2 ); { Also set the recharge time. } SetNAtt( Target^.NA , NAG_Personal , NAS_CashOnHandRestock , GB^.ComTime + 43200 + Random( 86400 ) ); end; { Stealing things takes concentration and time. } AddMentalDown( PC , 5 ); WaitAMinute( GB , PC , ReactionTime( PC ) * 2 ); end; Procedure PCActivateSkill( GB: GameBoardPtr; PC: GearPtr ); { Allow the PC to pick a known skill from his list, then apply } { that skill to either himself or a nearby object. } { There are two kinds of skills that can be activated by this } { command: repair skills and clue skills. Clue skills must be } { applied to an item. } var RPM: RPGMenuPtr; N,Usage: Integer; begin { Make sure we have the actual PC first. } PC := LocatePilot( PC ); if PC = Nil then Exit; { Make the skill menu. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); AttachMenuDesc( RPM , ZONE_Info ); RPM^.DTexColor := InfoGreen; { Add all usable skills to the list, as long as the PC knows them. } for N := 1 to NumSkill do begin if ( SkillMan[ N ].Usage = USAGE_Clue ) and ( TeamHasSkill( GB , NAV_DefPlayerTeam , N ) or TeamHasTalent( GB , NAV_DefPlayerTeam , NAS_JackOfAll ) ) then begin AddRPGMenuItem( RPM , MsgString( 'SKILLNAME_' + BStr( N ) ) , N , SkillDescription( N ) ); end else if ( SkillMan[ N ].Usage > 0 ) and HasSkill( PC , N ) then begin AddRPGMenuItem( RPM , MsgString( 'SKILLNAME_' + BStr( N ) ) , N , SkillDescription( N ) ); end; end; { Add all usable talents to the list too. } for N := 1 to NumTalent do begin if ( Talent_Usage[ N ] > 0 ) and HasTalent( PC , N ) then begin AddRPGMenuItem( RPM , MsgString( 'TALENT' + BStr( N ) ) , -N , MsgString( 'TALENTDESC' + BStr( N ) ) ); end; end; RPMSortAlpha( RPM ); AlphaKeyMenu( RPM ); AddRPGMenuItem( RPM , MsgString( 'PCAS_Cancel' ) , -1 ); DialogMSg( MsgString( 'PCAS_Prompt' ) ); N := SelectMenu( RPM , @PCMenuRedraw ); DisposeRPGMenu( RPM ); if ( N <> -1 ) then begin { Determine the usage. } if N > 0 then Usage := SkillMan[ N ].Usage else Usage := Talent_Usage[ Abs( N ) ]; if Usage = USAGE_Repair then begin DoPCRepair( GB , PC , N ); end else if Usage = USAGE_Clue then begin PCUseSkillOnProp( GB , PC , N ); end else if Usage = USAGE_Performance then begin StartPerforming( GB , PC ); end else if Usage = USAGE_Robotics then begin BuildRobot( GB , PC ); end else if Usage = USAGE_DominateAnimal then begin DominateAnimal( GB , PC ); end else if Usage = USAGE_PickPockets then begin PickPockets( GB , PC ); end; end else begin DialogMsg( MsgString( 'Cancelled' ) ); end; end; Procedure ForcePlot( GB: GameBoardPtr; PC,Scene: GearPtr ); { Debugging command - forcibly loads a plot into the adventure. } var RPM: RPGMenuPtr; PID: Integer; Plot: GearPtr; begin if ( scene = Nil ) or ( Scene^.Parent = Nil ) then exit; { Create a menu listing all the units in the SaveGame directory. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); BuildSiblingMenu( RPM , Standard_Plots ); if RPM^.NumItem > 0 then begin RPMSortAlpha( RPM ); DialogMSG('Select plot file to load.'); pID := SelectMenu( RPM , @PCMenuRedraw ); if ( PID <> -1 ) and ( RetrieveGearSib( Standard_Plots , PID )^.G = GG_Plot ) then begin Plot := CloneGear( RetrieveGearSib( Standard_Plots , PID ) ); SetSAtt( Plot^.SA , 'name ' ); if InsertPlot( FindRootScene( GB^.Scene ) , FindRoot( Scene ) , Plot , GB , NAttValue( PC^.NA , NAG_CharDescription , NAS_Renowned ) ) then begin DialogMsg( 'Plot successfully loaded.' ); end else begin DialogMsg( 'Plot rejected.' ); end; end; end; DisposeRPGMenu( RPM ); end; Procedure PCSaveCampaign( Camp: CampaignPtr; PC: gearPtr; PrintMsg: Boolean ); { Save the campaign and all associated info to disk. } var Name: String; F: Text; begin { Find the PC's name, open the file, and save. } if ( Camp^.Source <> Nil ) and ( Camp^.Source^.S = GS_ArenaCampaign ) then begin Name := Save_Unit_Base + GearName( Camp^.Source ) + Default_File_Ending; end else begin Name := Save_Campaign_Base + PilotName( PC ) + Default_File_Ending; end; Assign( F , Name ); Rewrite( F ); WriteCampaign( Camp , F ); Close( F ); { Let the player know that everything went fine. } if PrintMsg then Dialogmsg( MsgString( 'SAVEGAME_OK' ) ); end; Procedure DoSelectPCMek( GB: GameBoardPtr; PC: GearPtr ); { Select one of the team 1 mecha for the player to use. } begin FHQ_SelectMechaForPilot( GB , PC ); end; Procedure PCBackpackMenu( GB: GameBoardPtr; PC: GearPtr; StartWithInv: Boolean ); { This is a front-end for the BackpackMenu command; all it does is } { call that procedure, then redraw the map afterwards. } begin PCActionRD_GB := GB; BackpackMenu( GB , PC , StartWithInv , @PCActionRedraw ); end; Procedure PCFieldHQ( GB: GameBoardPtr; PC: GearPtr ); { This is a front-end for the BackpackMenu command; all it does is } { call that procedure, then redraw the map afterwards. } begin FieldHQ( GB , PC ); CombatDisplay( GB ); end; Procedure SetPlayOptions( GB: GameBoardPtr; Mek: GearPtr ); { Allow the player to set control type, default burst value settings, } { and whatever other stuff you think is appropriate. } var RPM: RPGMenuPtr; N: Integer; begin { The menu needs to be re-created with each iteration, since the } { data in it needs to be updated. } N := 1; repeat RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); AddRPGMenuItem( RPM , 'Mecha Control: '+ControlTypeName[ControlMethod] , 1 ); AddRPGMenuItem( RPM , 'Chara Control: '+ControlTypeName[CharacterMethod] , 5 ); AddRPGMenuItem( RPM , 'Explore Control: '+ControlTypeName[WorldMapMethod] , 6 ); AddRPGMenuItem( RPM , 'Ballistic Wpn BV: '+BVTypeName[DefBallisticBV] , 2 ); AddRPGMenuItem( RPM , 'Energy Wpn BV: '+BVTypeName[DefBeamGunBV] , 3 ); AddRPGMenuItem( RPM , 'Missile BV: '+BVTypeName[DefMissileBV] , 4 ); if ( Mek^.Scale = 0 ) then begin if NAttValue( Mek^.NA , NAG_Prefrences , NAS_UseNonLethalAttacks ) = 0 then begin AddRPGMenuItem( RPM , 'NonLethal Attacks: Off' , 10 ); end else begin AddRPGMenuItem( RPM , 'NonLethal Attacks: On' , 10 ); end; end; if Thorough_Redraw then begin AddRPGMenuItem( RPM , 'Enable Quick Redraw' , 7 ); end else begin AddRPGMenuItem( RPM , 'Disable Quick Redraw' , 7 ); end; if Display_Mini_Map then begin AddRPGMenuItem( RPM , 'Disable Mini-Map' , 8 ); end else begin AddRPGMenuItem( RPM , 'Enable Mini-Map' , 8 ); end; if Use_Tall_Walls then begin AddRPGMenuItem( RPM , 'Walls are Full Height' , 9 ); end else begin AddRPGMenuItem( RPM , 'Walls are Short' , 9 ); end; if Use_Isometric_Mode then begin AddRPGMenuItem( RPM , 'Switch to Perspective' , 11 ); end else begin AddRPGMenuItem( RPM , 'Switch to Isometric' , 11 ); end; AddRPGMenuItem( RPM , ' Exit Prefrences' , -1 ); SetItemByValue( RPM , N ); N := SelectMenu( RPM , @PCMenuRedraw ); DisposeRPGMenu( RPM ); if N = 1 then begin if ControlMethod = MenuBasedInput then ControlMethod := RLBasedInput else ControlMethod := MenuBasedInput; WaitAMinute( GB , Mek , 1 ); end else if N = 5 then begin if CharacterMethod = MenuBasedInput then CharacterMethod := RLBasedInput else CharacterMethod := MenuBasedInput; WaitAMinute( GB , Mek , 1 ); end else if N = 6 then begin if WorldMapMethod = MenuBasedInput then WorldMapMethod := RLBasedInput else WorldMapMethod := MenuBasedInput; WaitAMinute( GB , Mek , 1 ); end else if N = 2 then begin if DefBallisticBV = BV_Off then DefBallisticBV := BV_Max else DefBallisticBV := BV_Off; end else if N = 3 then begin if DefBeamGunBV = BV_Off then DefBeamGunBV := BV_Max else DefBeamGunBV := BV_Off; end else if N = 4 then begin DefMissileBV := DefMissileBV + 1; if DefMissileBV > BV_Max then DefMissileBV := BV_Off; end else if N = 7 then begin Thorough_Redraw := Not Thorough_Redraw; end else if N = 8 then begin { Toggle the Mini-Map. } Display_Mini_Map := Not Display_Mini_Map; end else if N = 9 then begin { Toggle the tall walls. } Use_Tall_Walls := not Use_Tall_Walls; end else if N = 10 then begin { Toggle NonLethal attacks. } if NAttValue( Mek^.NA , NAG_Prefrences , NAS_UseNonLethalAttacks ) = 0 then begin SetNAtt( Mek^.NA , NAG_Prefrences , NAS_UseNonLethalAttacks , 1 ); end else begin SetNAtt( Mek^.NA , NAG_Prefrences , NAS_UseNonLethalAttacks , 0 ); end; end else if N = 11 then begin Use_Isometric_Mode := Not Use_Isometric_Mode; end; until N = -1; end; Procedure BrowsePersonalHistory( GB: GameBoardPtr; PC: GearPtr ); { As the PC advances throughout the campaign, she will likely } { accumulate a number of history messages. This procedure will } { allow those messages to be browsed. } var HList,SA: SAttPtr; Adv: GearPtr; begin HList := Nil; Adv := FindRoot( GB^.Scene ); if Adv <> Nil then begin SA := Adv^.SA; while SA <> Nil do begin if UpCase( Copy( SA^.Info , 1 , 7 ) ) = 'HISTORY' then begin StoreSAtt( HList , RetrieveAString( SA^.Info ) ); end; SA := SA^.Next; end; if HList <> Nil then begin MoreText( HList , 1 ); DisposeSAtt( HList ); end; end; end; Procedure PCViewChar( GB: GameBoardPtr; PC: GearPtr ); { This procedure is supposed to allow the PC to see his/her } { stats, edit mecha, access the training and option screens, } { and otherwise provide a nice all-in-one command for a } { bunch of different play options. } var RPM: RPGMenuPtr; N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); AddRPGMenuItem( RPM , MsgString( 'PCVIEW_BackPack' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'PCVIEW_Injuries' ) , 3 ); AddRPGMenuItem( RPM , MsgString( 'PCVIEW_Training' ) , 2 ); AddRPGMenuItem( RPM , MsgString( 'PCVIEW_FieldHQ' ) , 4 ); AddRPGMenuItem( RPM , MsgString( 'PCVIEW_SetOptions' ) , 5 ); AddRPGMenuItem( RPM , MsgString( 'HELP_PersonalHistory' ) , 6 ); {$IFNDEF ASCII} AddRPGMenuItem( RPM , MsgString( 'PCVIEW_SetColor' ) , 7 ); if PC^.G = GG_Character then AddRPGMenuItem( RPM , MsgString( 'PCVIEW_SetSprite' ) , 8 ); {$ENDIF} AddRPGMenuItem( RPM , MsgString( 'PCVIEW_Exit' ) , -1 ); repeat PCACTIONRD_PC := PC; N := SelectMenu( RPM , @ViewCharRedraw ); case N of 1: PCBackPackMenu( GB , PC , True ); 2: begin PCACTIONRD_GB := GB; DoTraining( GB , PC , @PCActionRedraw ); end; 3: InjuryViewer( PC , @PCActionRedraw ); 4: FieldHQ( GB , PC ); 5: SetPlayOptions( GB , PC ); 6: BrowsePersonalHistory( GB , PC ); {$IFNDEF ASCII} 7: SelectColors( PC , @ViewCharRedraw ); 8: SelectSprite( PC , @ViewCharRedraw ); {$ENDIF} end; until N = -1; {$IFNDEF ASCII} {$IFDEF CUTE} CleanSpriteList; {$ELSE} CleanTexList; {$ENDIF} {$ENDIF} DisposeRPGMenu( RPM ); CombatDisplay( GB ); end; Procedure WaitOnRecharge( GB: GameBoardPtr; Mek: GearPtr ); { Set the mek's CALLTIME to whenever the next weapon is supposed to } { be recharged. } var CT: LongInt; procedure SeekWeapon( Part: GearPtr ); { Seek the weapon which will recharge soonest. } var RT: LongInt; begin while ( Part <> Nil ) do begin if NotDestroyed( Part ) then begin if ( Part^.G = GG_Module ) or ( Part^.G = GG_Weapon ) then begin RT := NAttValue( Part^.NA , NAG_WeaponModifier , NAS_Recharge); { Set the Call Time to this weapon's recharge time if it recharges quicker } { than any other seen so far, and if it is currently recharging. } if ( RT < CT ) and ( RT > GB^.ComTime ) then CT := RT; end; if ( Part^.SubCom <> Nil ) then SeekWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekWeapon( Part^.InvCom ); end; Part := Part^.Next; end; end; begin { Set a default waiting period of a single round. If no weapon will } { recharge before this time, return control to the player anyhow. } CT := GB^.ComTime + ClicksPerRound + 1; { Check through all weapons to find which one recharges soonest. } SeekWeapon( Mek^.SubCom ); { Set the call time to whatever time was found. } SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , CT ); end; Procedure RLSmartAttack( GB: GameBoardPtr; Mek: GearPtr ); { Turn and then fire upon the PC's target. } var Enemy,Weapon: GearPtr; CD,MoveAction,T,TX,TY,N,AtOp: Integer; begin { Find out the mek's current target. } T := NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Target ); Enemy := LocateMekByUID( GB , T ); TX := NAttValue( Mek^.NA , NAG_Location , NAS_SmartX ); TY := NAttValue( Mek^.NA , NAG_Location , NAS_SmartY ); { Error check- if the smart attack isn't executed within five moves, } { forget about it. } AddNAtt( Mek^.NA , NAG_Location , NAS_SmartCount , 1 ); if NAttValue( Mek^.NA , NAG_Location , NAS_SmartCount ) > 5 then begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , 0 ); DialogMsg( MsgString( 'PCATTACK_OutOfArc' ) ); Exit; end; { Find the weapon being used in the attack. } Weapon := LocateGearByNumber( Mek , NAttValue( Mek^.NA , NAG_Location , NAS_SmartWeapon ) ); if ( T = -1 ) and OnTheMap( GB, TX , TY ) then begin { If T=-1, the PC is firing at a spot instead of a } { specific enemy. } if WeaponArcCheck( GB , Mek , Weapon , TX , TY ) then begin { Whatever else happens, the smartattack is over. } SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , 0 ); { The enemy is within the proper arc. Fire away! } if RangeArcCheck( GB , Mek , Weapon , TX , TY , TerrMan[ TileTerrain( GB , TX , TY ) ].Altitude ) then begin { Everything is okay. Call the attack procedure. } AttackerFrontEnd( GB , Mek , Weapon , TX , TY , TerrMan[ TileTerrain( GB , TX , TY ) ].Altitude , DefaultAtOp( Weapon ) ); end else begin DialogMSG( MsgString( 'PCATTACK_OutOfRange' ) ); end; end else begin { Turn to face the target. } CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); MoveAction := NAV_TurnRight; { Arcs CD and CD+7mod8 don't need to be checked, since } { those are covered by the current F90 arc. } for t := 1 to 3 do begin if CheckArc( Mek , TX , TY , ( CD + T ) mod 8 ) then MoveAction := NAV_TurnRight else if CheckArc( Mek , TX , TY , ( CD + 7 - T ) mod 8 ) then MoveAction := NAV_TurnLeft; end; PrepAction( GB , Mek , MoveAction ); end; end else if ( Enemy = Nil ) or ( Not GearOperational( Enemy ) ) or ( not MekVisible( GB , Enemy ) ) or ( Weapon = Nil ) or ( not ReadyToFire( GB , Mek , Weapon ) ) then begin { This mecha is no longer a good target, or the weapon } { selected is no longer valid. Cancel the SmartAttack. } SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , 0 ); end else if WeaponArcCheck( GB , Mek , Weapon , Enemy ) then begin { Whatever else happens, the smartattack is over. } SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , 0 ); { See if we're aiming for the main body or a subcom. } N := NAttValue( Mek^.NA , NAG_Location , NAS_SmartTarget ); if N > 0 then begin Enemy := LocateGearByNumber( Enemy , N ); AtOp := 0; end else begin AtOp := DefaultAtOp( Weapon ); end; { The enemy is within the proper arc. Fire away! } if RangeArcCheck( GB , Mek , Weapon , Enemy ) then begin { Everything is okay. Call the attack procedure. } AttackerFrontEnd( GB , Mek , Weapon , Enemy , AtOp ); end else begin DialogMSG( MsgString( 'PCATTACK_OutOfRange' ) ); end; end else begin { Turn to face the target. } CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); MoveAction := NAV_TurnRight; { Arcs CD and CD+7mod8 don't need to be checked, since } { those are covered by the current F90 arc. } for t := 1 to 3 do begin if CheckArc( Mek , Enemy , ( CD + T ) mod 8 ) then MoveAction := NAV_TurnRight else if CheckArc( Mek , Enemy , ( CD + 7 - T ) mod 8 ) then MoveAction := NAV_TurnLeft; end; PrepAction( GB , Mek , MoveAction ); end; end; Procedure AimThatAttack( Mek,Weapon: GearPtr; CallShot: Boolean; GB: GameBoardPtr ); { A weapon has been selected; now, select a target. } var WPM: RPGMenuPtr; N,AtOp: Integer; begin if not ReadyToFire( GB , Mek , Weapon ) then begin DialogMsg( ReplaceHash( MsgString( 'ATA_NotReady' ) , GearName( Weapon ) ) ); Exit; end; AtOp := DefaultAtOp( Weapon ); if SelectTarget( GB , Mek , Weapon , CallShot , AtOp ) then begin { Check to make sure the target is within maximum range, } { and that it falls within the correct arc. } AtOp := DefaultAtOp( Weapon ); if ( LOOKER_Gear = Nil ) and RangeArcCheck( GB , Mek , Weapon , LOOKER_X , LOOKER_Y , TerrMan[ TileTerrain( GB , LOOKER_X , LOOKER_Y ) ].Altitude ) then begin AttackerFrontEnd( GB , Mek , Weapon , LOOKER_X , LOOKER_Y , TerrMan[ TileTerrain( GB , LOOKER_X , LOOKER_Y ) ].Altitude , DefaultAtOp( Weapon ) ); end else if LOOKER_Gear = Nil then begin if ( Range( Mek , LOOKER_X , LOOKER_Y ) > WeaponRange( GB , Weapon , RANGE_Long ) ) and ( Range( Mek , LOOKER_X , LOOKER_Y ) > ThrowingRange( GB , Mek , Weapon ) ) then begin DialogMSG( MsgString( 'PCATTACK_OutOfRange' ) ); end else if InterfaceType( GB , Mek ) = MenuBasedInput then begin DialogMSG( MsgString( 'PCATTACK_OutOfArc' ) ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , NAV_SmartAttack ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartCount , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , FindGearIndex( Mek , Weapon ) ); SetNAtt( Mek^.NA , NAG_EPisodeData , NAS_Target , -1 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartX , LOOKER_X ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartY , LOOKER_Y ); RLSmartAttack( GB , Mek ); end; end else if ( FindRoot( LOOKER_Gear ) = FindRoot( Mek ) ) then begin DialogMSG( 'Attack cancelled.' ); end else if RangeArcCheck( GB , Mek , Weapon , LOOKER_Gear ) then begin { Call the Attack procedure with the info we've gained. } { If a called shot was requested, create the menu here. } { Note that called shots cannot be made using burst firing. } if CallShot and ( LOOKER_Gear^.SubCom <> Nil ) then begin { Create a menu, fill it with bits. } WPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); BuildGearMenu( WPM , LOOKER_Gear , GG_Module , False ); { If you have target analysis software, can make a } { called shot at movement systems and weapons too! } if ( LOOKER_Gear^.G = GG_Mecha ) and ( SeekSoftware( Mek , S_Information , SInfo_MechaDex , True ) <> Nil ) then begin BuildGearMenu( WPM , LOOKER_Gear , GG_Weapon , False ); BuildGearMenu( WPM , LOOKER_Gear , GG_MoveSys , False ); end; AlphaKeyMenu( WPM ); N := SelectMenu( WPM , @PCMenuRedraw ); if N <> -1 then begin LOOKER_Gear := LocateGearByNumber( LOOKER_Gear , N ); end; DisposeRPGMenu( WPM ); AtOp := 0; end; AttackerFrontEnd( GB , Mek , Weapon , LOOKER_Gear , AtOp ); end else begin if WeaponArcCheck( GB , Mek , Weapon , LOOKER_Gear ) then begin DialogMSG( MsgString( 'PCATTACK_OutOfRange' ) ); end else if InterfaceType( GB , Mek ) = MenuBasedInput then begin DialogMSG( MsgString( 'PCATTACK_OutOfArc' ) ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , NAV_SmartAttack ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartCount , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , FindGearIndex( Mek , Weapon ) ); if CallShot and ( LOOKER_Gear^.SubCom <> Nil ) then begin { Create a menu, fill it with bits. } WPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); BuildGearMenu( WPM , LOOKER_Gear , GG_Module , False ); { If you have target analysis software, can make a } { called shot at movement systems and weapons too! } if ( LOOKER_Gear^.G = GG_Mecha ) and ( SeekSoftware( Mek , S_Information , SInfo_MechaDex , True ) <> Nil ) then begin BuildGearMenu( WPM , LOOKER_Gear , GG_Weapon , False ); BuildGearMenu( WPM , LOOKER_Gear , GG_MoveSys , False ); end; AlphaKeyMenu( WPM ); N := SelectMenu( WPM , @PCMenuRedraw ); if N <> -1 then begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartTarget , N ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartTarget , 0 ); end; DisposeRPGMenu( WPM ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartTarget , 0 ); end; RLSmartAttack( GB , Mek ); end; end; end; end; Procedure DoPlayerAttack( Mek: GearPtr; GB: GameBoardPtr ); { The player has accessed the weapons menu. Select an active } { weapon, then select a target. If the target is within range, } { process the attack and report upon it to the user. } const CalledShotOff = ' Called Shot: Off [/]'; CalledShotOn = ' Called Shot: On [/]'; var WPM: RPGMenuPtr; { The Weapons Menu } MI,MI2: RPGMenuItemPtr; { For checking all the weapons. } Weapon: GearPtr; { Also for checking all the weapons. } N: Integer; CallShot: Boolean; begin { Error check - make sure that MEK is a valid, active master gear. } if not IsMasterGear( Mek ) then exit; { *** START MENU BUILDER *** } { Create the weapons menu. } { Travel through the mek structure in the standard pattern } { looking for things which may be attacked with. } { WEAPONS - may be attacked with. Duh. } { MODULES - Arms enable punching, Legs enable kicking, tails enable tail whipping. } { AMMO - Missiles with Range=0 in the general inventory may be thrown. } WPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( WPM , ZONE_Menu1 ); WPM^.DTexColor := InfoGreen; BuildGearMenu( WPM , Mek , GG_Weapon ); BuildGearMenu( WPM , Mek , GG_Module ); BuildGearMenu( WPM , Mek , GG_Ammo ); AlphaKeyMenu( WPM ); { Next, filter the generated list so that only weapons which are ready } { to attack may be attacked with. } MI := WPM^.FirstItem; while MI <> Nil do begin MI2 := MI^.Next; Weapon := LocateGearByNumber( Mek , MI^.Value ); if not ReadyToFire( GB , Mek , Weapon ) then begin { This weapon isn't ready to fire. Remove it from the menu. } RemoveRPGMenuItem( WPM , MI ); end else begin { This weapon _is_ ready to fire. Give it a spiffy } { description. } MI^.desc := GearName( Weapon ) + ' ' + WeaponDescription( GB , Weapon ); end; MI := MI2; end; { Add the firing options. Save the address of the called shot entry. } MI := AddRPGMenuItem( WPM , CalledShotOff , -4 ); AddRPGMenuKey( WPM , '/' , -4 ); CallShot := False; AddRPGMenuItem( WPM , ' Wait for recharge [.]' , -3 ); AddRPGMenuKey( WPM , '.' , -3 ); AddRPGMenuItem( WPM , ' Options [?]' , -2 ); AddRPGMenuKey( WPM , '?' , -2 ); AddRPGMenuItem( WPM , ' Cancel [ESC]' , -1 ); { *** END MENU BUILDER *** } { Actually get a selection from the menu. } { A loop is needed so that if the player wants to set options, the game } { will return directly to the weapons menu afterwards. } repeat { Need to clear the entire menu zone, just to make sure the } { display looks right. } N := SelectMenu( WPM , @PCMenuRedraw ); if N = -2 then SetPlayOptions( GB , Mek ) else if N = -4 then begin CallShot := Not CallShot; if CallShot then MI^.msg := CalledShotOn else MI^.msg := CalledShotOff; end; until ( N <> -2 ) and ( N <> -4 ); { Get rid of the menu. We don't need it any more. } DisposeRPGMenu( WPM ); { If the selection wasn't cancelled, proceed with the attack. } if N > -1 then begin { A weapon has been selected. Now, select a target. } Weapon := LocateGearByNumber( Mek , N ); { Call the LOOKER procedure to select a target. } AimThatAttack( Mek , Weapon , CallShot , GB ); end else if N = -3 then begin { Wait on Recharge was selected from the menu. } WaitOnRecharge( GB , Mek ); end; end; Procedure DoEjection( GB: GameBoardPtr; Mek: GearPtr ); { The player wants to eject from this mecha. First prompt to } { make sure that the PC is serious about this, then do the } { ejection itself. } var RPM: RPGMenuPtr; Pilot: GearPtr; P: Point; begin { Error check - One cannot eject from oneself. } { The PC must be in a mecha to use this command. } if ( Mek = Nil ) or ( Mek^.G <> GG_Mecha ) then Exit; { Make sure that the player is really serious about this. } DialogMsg( MsgString( 'EJECT_Prompt' ) ); RPM := CreateRPGMenu( PlayerBlue , StdWhite , ZONE_Menu2 ); AddRPGMenuItem( RPM , MsgString( 'EJECT_Yes' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'EJECT_No' ) , -1 ); SetItemByPosition( RPM , 2 ); if SelectMenu( RPM , @PCMenuRedraw ) <> -1 then 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 ) ) ); P := GearCurrentLocation( Mek ); repeat Pilot := ExtractPilot( Mek ); if Pilot <> Nil then begin DialogMsg( GearName( Pilot ) + MsgString( 'EJECT_Message' ) + GearName( Mek ) + '.' ); { In a safe area, deploy the pilot in the same tile as the mecha. } if IsSafeArea( GB ) and not MovementBlocked( Pilot , GB , P.X ,P.Y , P.X , P.Y ) then begin SetNAtt( Pilot^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( Pilot^.NA , NAG_Location , NAS_Y , P.Y ); DeployGear( GB , Pilot , True ); end else begin DeployGear( GB , Pilot , False ); end; end; until Pilot = Nil; if IsSAfeArea( GB ) then begin SetSAtt( Mek^.SA , 'PILOT <>' ); end else begin { Since this mecha is being abandoned in a combat zone, set the team } { value to 0. Otherwise the PC could just use ejection } { as an easy out to any combat without risking losing a } { mecha. If the player team wins and gets salvage, they } { should get this mek back anyhow. } SetNAtt( Mek^.NA , NAG_Location , NAS_Team , 0 ); end; end; DisposeRPGMenu( RPM ); end; Procedure DoRest( GB: GameBoardPtr; Mek: GearPtr ); { The PC wants to rest, probably because he's out of stamina. Take a break for } { an hour or so of game time. } begin if ( NAttValue( LocatePilot( Mek )^.NA , NAG_Condition , NAS_Hunger ) > HUNGER_PENALTY_STARTS ) then begin DialogMsg( MsgString( 'REST_TooHungry' ) ); end else if IsSafeArea( GB ) then begin DialogMsg( MsgString( 'REST_OK' ) ); QuickTime( GB , 3600 ); WaitAMinute( GB , Mek , 1 ); end else begin DialogMsg( MsgString( 'REST_NotHere' ) ); end; end; Procedure KeyMapDisplay; { Display the game commands and their associated keystrokes. } var RPM: RPGMenuPtr; RPI: RPGMenuItemPtr; T: Integer; begin DialogMSG( MSgString( 'HELP_Prompt' ) ); RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( RPM , ZONE_Menu1 ); for t := 1 to NumMappedKeys do begin AddRPGMenuItem( RPM , KeyMap[T].CmdName , T , KeyMap[T].CmdDesc ); end; {$IFNDEF ASCII} AddRPGMenuItem( RPM , 'Zoom In' , -1 , 'Zoom the camera in.' ); AddRPGMenuItem( RPM , 'Zoom Out' , -2 , 'Zoom the camera out.' ); AddRPGMenuItem( RPM , 'Rotate Left' , -3 , 'Rotate the camera left.' ); AddRPGMenuItem( RPM , 'Rotate Right' , -4 , 'Rotate the camera right.' ); {$ENDIF} RPMSortAlpha( RPM ); RPI := RPM^.FirstItem; while RPI <> Nil do begin case RPI^.Value of -1: RPI^.msg := 'PageUp - ' + RPI^.msg; -2: RPI^.msg := 'PageDown - ' + RPI^.msg; -3: RPI^.msg := 'Insert - ' + RPI^.msg; -4: RPI^.msg := 'Delete - ' + RPI^.msg; else RPI^.msg := KeyMap[RPI^.Value].KCode + ' - ' + RPI^.msg; end; RPI := RPI^.Next; end; SelectMenu( RPM , @PCMenuRedraw ); DisposeRPGMenu( RPM ); end; Procedure BrowseTextFile( GB: GameBoardPtr; FName: String ); { Load and display a text file, then clean up afterwards. } var txt: SAttPtr; begin txt := LoadStringList( FName ); if txt <> Nil then begin MoreText( txt , 1 ); DisposeSAtt( txt ); CombatDisplay( GB ); end; end; Procedure PCRLHelp( GB: GameBoardPtr ); { Show help information for all the commands available to the } { RogueLike interface. } var RPM: RPGMenuPtr; A: Integer; begin DialogMSG( MSgString( 'HELP_Prompt' ) ); RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); AddRPGMenuItem( RPM , MsgString( 'HELP_KeyMap' ) , 1 ); AddRPGMenuItem( RPM , MsgString( 'HELP_Chara' ) , 2 ); AddRPGMenuItem( RPM , MsgString( 'HELP_Mecha' ) , 3 ); AddRPGMenuItem( RPM , MsgString( 'HELP_FieldHQ' ) , 4 ); AddRPGMenuItem( RPM , MsgString( 'HELP_Exit' ) , -1 ); repeat A := SelectMenu( RPM , @PCMenuRedraw ); case A of 1: KeyMapDisplay; 2: BrowseTextFile( GB , Chara_Help_File ); 3: BrowseTextFile( GB , Mecha_Help_File ); 4: BrowseTextFile( GB , FieldHQ_Help_File ); end; until A = -1; DisposeRPGMenu( RPM ); end; Procedure RLQuickAttack( GB: GameBoardPtr; PC: GearPtr ); { Try to attack. If no weapon is ready, wait for recharge. } var Weapon: GearPtr; procedure SeekWeapon( Part: GearPtr ); { Look for a weapon which is ready to fire, select } { based on range. } var WR1,WR2: Integer; begin while ( Part <> Nil ) do begin if ReadyToFire( GB , PC , Part ) then begin WR1 := WeaponRange( GB , Weapon , RANGE_Long ); if ThrowingRange( GB , PC , Weapon ) > WR1 then WR1 := ThrowingRange( GB , PC , Weapon ); WR2 := WeaponRange( GB , Part , RANGE_Long ); if ThrowingRange( GB , PC , Part ) > WR2 then WR2 := ThrowingRange( GB , PC , Part ); if Weapon = Nil then Weapon := Part else if WR2 > WR1 then Weapon := Part else if ( WR2 = WR1 ) and ( WeaponDC(Part) > WeaponDC(Weapon) ) then Weapon := Part; end; if ( Part^.SubCom <> Nil ) then SeekWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekWeapon( Part^.InvCom ); Part := Part^.Next; end; end; var Target: GearPtr; begin { Start by looking for a weapon to use. } { If the PC already has a target in mind, pick the weapon best suited to that target. } Target := LocateMekByUID( GB , NAttValue( PC^.NA , NAG_EpisodeData , NAS_Target ) ); if ( Target <> Nil ) and OnTheMap( GB , Target ) and MekCanSeeTarget( gb , PC , Target ) and GearOperational( Target ) and ( NAttValue( Target^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) <> NAV_NowSurrendered ) then begin Weapon := SelectBestWeapon( GB , PC , Target ); if Weapon = Nil then begin SeekWeapon( PC^.SubCom ); SeekWeapon( PC^.InvCom ); end; end else begin Weapon := Nil; SeekWeapon( PC^.SubCom ); SeekWeapon( PC^.InvCom ); end; if Weapon = Nil then begin DialogMsg( 'You don''t have a weapon ready!' ); WaitOnRecharge( GB , PC ); end else begin AimThatAttack( PC , Weapon , False , GB ); end; end; Function CoreBumpAttack( GB: GameBoardPtr; PC,Target: GearPtr ): Boolean; { Try to attack TARGET. If no weapon is ready, wait for recharge. } { If an attack takes place, clear the location var SmartAction. } var Weapon: GearPtr; function NewWeaponBetter( W1,W2: GearPtr ): Boolean; { Return TRUE if W2 is better than W1 for the purposes } { of smartbump attacking, or FALSE otherwise. } { A better weapon is a short range one with the best damage. } var R1,R2: Integer; begin R1 := WeaponRange( Nil , W1 , RANGE_Long ); R2 := WeaponRange( Nil , W2 , RANGE_Long ); if ( R2 < 3 ) and ( R1 > 2 ) then begin NewWeaponBetter := True; end else if ( ( R1 div 3 ) = ( R2 div 3 ) ) and ( WeaponDC( W2 ) > WeaponDC( W1 ) ) then begin NewWeaponBetter := True; end else begin NewWeaponBetter := False; end; end; procedure SeekWeapon( Part: GearPtr ); { Seek a weapon which is capable of hitting target. } { Preference is given to short-range weapons. } begin while ( Part <> Nil ) do begin if ( Part^.G = GG_Module ) or ( Part^.G = GG_Weapon ) then begin if ReadyToFire( GB , PC , Part ) and RangeArcCheck( GB , PC , Part , Target ) then begin if Weapon = Nil then Weapon := Part else begin if NewWeaponBetter( Weapon , Part ) then begin Weapon := Part; end; end; end; end; if ( Part^.SubCom <> Nil ) then SeekWeapon( Part^.SubCom ); if ( Part^.InvCom <> Nil ) then SeekWeapon( Part^.InvCom ); Part := Part^.Next; end; end; begin { Start by looking for a weapon to use. } Weapon := Nil; SeekWeapon( PC^.SubCom ); if Weapon = Nil then begin CoreBumpAttack := False; end else begin { Note that BumpAttacks are always done at AtOp = 0. } AttackerFrontEnd( GB , PC , Weapon , Target , 0 ); SetNAtt( PC^.NA , NAG_Location , NAS_SmartAction , 0 ); CoreBumpAttack := True; end; end; Procedure RLBumpAttack( GB: GameBoardPtr; PC,Target: GearPtr ); { Call the core bumpattack procedure, cancelling the action if it fails. } begin if not CoreBumpAttack( GB , PC , Target ) then begin DialogMsg( 'You don''t have a weapon ready!' ); WaitOnRecharge( GB , PC ); SetNAtt( PC^.NA , NAG_Location , NAS_SmartAction , 0 ); end; end; Procedure ContinuousSkillUse( GB: GameBoardPtr; Mek: GearPtr ); { The PC is using a skill. } var Skill: Integer; begin Skill := NAttValue( Mek^.NA , NAG_Location , NAS_SmartSkill ); if ( Skill >= 1 ) and ( Skill <= NumSkill ) then begin Case Skill of NAS_Performance: PCDoPerformance( GB , Mek ); end; { Decrease the usage count by one. } { If it drops to zero, end this action. } { Otherwise add a delay for the next action. } AddNAtt( Mek^.NA , NAG_Location , NAS_SmartCount , -1 ); if NAttValue( Mek^.NA , NAG_Location , NAS_SmartCount ) < 1 then begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartSkill , 0 ); end else WaitAMinute( GB , Mek , ReactionTime( Mek ) ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartSkill , 0 ); end; end; Procedure RLSmartGo( GB: GameBoardPtr; Mek: GearPtr ); { The PC is going somewhere. March him in the right direction. } var DX,DY: Integer; begin DX := NAttValue( Mek^.NA , NAG_Location , NAS_SmartX ); DY := NAttValue( Mek^.NA , NAG_Location , NAS_SmartY ); AddNAtt( Mek^.NA , NAG_Location , NAS_SmartCount , -1 ); IF NAttValue( Mek^.NA , NAG_Location , NAS_SmartCount ) < 1 then begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end else if not MOVE_MODEL_TOWARDS_SPOT( Mek , GB , DX , DY ) then begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end; end; Procedure RLSmartAction( GB: GameBoardPtr; Mek: GearPtr ); { Do the smart bump! What is smart bump? Well, in most games of } { this sort bumping into another model will cause you to attack it. } { In this game I wanted the player's model to react semi-intelligently } { to stuff it bumps into. If it's an enemy, attack it. If it's a } { friend, talk to it. If it's a wall, just look at it... } var CD,SD: Integer; { Current Direction, Smart Direction } T,MoveAction: Integer; P,P0,P2: Point; M2: GearPtr; begin CD := NAttValue( Mek^.NA , NAG_Location , NAS_D ); SD := NAttValue( Mek^.NA , NAG_Location , NAS_SmartAction ); { First, check to see if we have chosen a continuous action other } { than simply walking. } if SD = NAV_SmartAttack then begin RLSmartAttack( GB , Mek ); end else if SD = NAV_UseSkill then begin ContinuousSkillUse( GB , Mek ); end else if SD = NAV_SmartGo then begin RLSmartGo( GB , Mek ); end else if CD <> Roguelike_D[ SD ] then begin { Turn to face the required direction. } P := GearCurrentLocation( Mek ); P.X := P.X + AngDir[ Roguelike_D[ SD ] , 1 ]; P.Y := P.Y + AngDir[ Roguelike_D[ SD ] , 2 ]; M2 := FindVisibleBlockerAtSpot( GB , P.X , P.Y ); if ( M2 <> Nil ) and GearOperational( M2 ) and AreEnemies( GB , Mek , M2 ) and ( NAttValue( M2^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) <> NAV_NowSurrendered ) and CoreBumpAttack( GB , Mek , M2 ) then begin { If the attack was performed, cancel the SmartAction. } SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end else begin MoveAction := NAV_TurnRight; for t := 1 to 4 do begin if (( CD + T ) mod 8 ) = Roguelike_D[ SD ] then MoveAction := NAV_TurnRight else if (( CD + 8 - T ) mod 8 ) = Roguelike_D[ SD ] then MoveAction := NAV_TurnLeft; end; PrepAction( GB , Mek , MoveAction ); end; end else begin { We are already looking in the correct direction. } { Do something. } P0 := GearCurrentLocation( Mek ); P.X := P0.X + AngDir[ CD , 1 ]; P.Y := P0.Y + AngDir[ CD , 2 ]; if not MovementBlocked( Mek , GB , P0.X , P0.Y , P.X , P.Y ) then begin { We can move in this direction. Do so. } if ( NAttValue( Mek^.NA , NAG_Location , NAS_SmartSpeed ) = NAV_FullSpeed ) and ( CurrentStamina( Mek ) > 0 ) then begin PrepAction( GB , Mek , NAV_FullSpeed ); end else begin PrepAction( GB , Mek , NAV_NormSpeed ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartSpeed , NAV_NormSpeed ); end; { CLear the SmartBump counter. } SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end else begin { There's something in the way of our movement. Deal with it. } M2 := FindVisibleBlockerAtSpot( GB , P.X , P.Y ); if M2 = Nil then begin DialogMsg( MsgString( 'SMARTACTION_Blocked' ) ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end else if AreEnemies( GB , Mek , M2 ) and ( NAttValue( M2^.NA , NAG_EpisodeData , NAS_SurrenderStatus ) <> NAV_NowSurrendered ) then begin { M2 is an enemy! Thwack it! Thwack it now!!! } RLBumpAttack( GB , Mek , M2 ); end else if ( NAttValue( M2^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam ) and not IsBlockingTerrain( GB , Mek , TileTerrain( GB , P.X , P.Y ) ) then begin { M2 is a lancemate. Try changing places with it. } { This will happen outside of the normal movement code... I hope that } { it won't be exploitable... } P := GearCurrentLocation( Mek ); P2 := GearCurrentLocation( M2 ); SetNAtt( Mek^.NA , NAG_Location , NAS_X , P2.X ); SetNAtt( Mek^.NA , NAG_Location , NAS_Y , P2.Y ); SetNAtt( M2^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( M2^.NA , NAG_Location , NAS_Y , P.Y ); WaitAMinute( GB , Mek , CPHMoveRate( GB^.Scene , Mek , GB^.Scale ) ); WaitAMinute( GB , M2 , CPHMoveRate( GB^.Scene , M2 , GB^.Scale ) ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end else if ( M2^.G = GG_Prop ) or not IsMasterGear( M2 ) then begin { M2 is an object of some type. Try using it. } { CLear the SmartBump counter. } SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); PrepAction( GB , Mek , NAV_Stop ); UsePropFrontEnd( GB , Mek , M2 , 'USE' ); end else begin { M2 isn't an enemy... try talking to it. } DoTalkingWithNPC( GB , Mek , M2 , False ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , 0 ); end; end; end; end; Procedure RLWalker( GB: GameBoardPtr; Mek: GearPtr; D: Integer; RunNow: Boolean ); { The player has pressed a direction key and is preparing to } { walk in that direction... or, alternatively, to attack } { an enemy in that tile, or to speak with an ally in that tile. } begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , D ); if RunNow then begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartSpeed , NAV_FullSpeed ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartSpeed , NAV_NormSpeed ); end; RLSmartAction( GB , Mek ); end; Procedure GameOptionMenu( Mek: GEarPtr; GB: GameBoardPtr ); { Let the user set options from this menu. } { -> Combat Settings } { -> Quit Game } var N: Integer; RPM: RPGMenuPtr; begin RPM := CreateRPGMenu( PlayerBlue , StdWhite , ZONE_Menu ); AddRPGMenuItem( RPM , 'Inventory' , 2 ); AddRPGMenuItem( RPM , 'Get Item' , 3 ); AddRPGMenuItem( RPM , 'Enter Location' , 4 ); AddRPGMEnuItem( RPM , 'Apply Skill' , 5 ); AddRPGMenuItem( RPM , 'Combat Settings' , 1 ); AddRPGMEnuItem( RPM , 'Eject from Mecha' , -6 ); AddRPGMenuItem( RPM , 'Character Info' , 6 ); AddRPGMenuItem( RPM , 'Quit Game' , -2 ); AddRPGMenuItem( RPM , 'Return to Main' , -1 ); DialogMsg('Advanced options menu.'); repeat N := SelectMenu( RPM , @PCMenuRedraw ); if N = 1 then SetPlayOptions( GB , Mek ) else if N = 2 then PCBackpackMenu( GB , Mek , True ) else if N = 3 then PCGetItem( GB , Mek ) else if N = 4 then PCEnter( GB , Mek ) else if N = 5 then PCActivateSkill( GB , Mek ) else if N = 6 then PCViewChar( GB , Mek ) else if N = -6 then DoEjection( GB , Mek ) else if N = -2 then GB^.QuitTheGame := True; until ( N < 0 ) or ( NAttValue( Mek^.NA , NAG_Action , NAS_CallTime ) > GB^.ComTime ); DisposeRPGMenu( RPM ); end; Procedure InfoMenu( Mek: GEarPtr; GB: GameBoardPtr ); { This menu contains various information utilities. } { -> Examine Map } { -> Pilot Stats } var N: Integer; RPM: RPGMenuPtr; begin RPM := CreateRPGMenu( PlayerBlue , StdWhite , ZONE_Menu ); AddRPGMenuItem( RPM , 'Examine Map' , 1 ); AddRPGMenuItem( RPM , 'Mecha Browser' , 3 ); AddRPGMenuItem( RPM , 'Return to Main' , -1 ); DialogMsg('Information Menu.'); repeat N := SelectMenu( RPM , @PCMenuRedraw ); if N = 1 then LookAround( GB , Mek ) else if N = 3 then MechaPartBrowser( Mek , @PCActionRedraw ); until N < 0; DisposeRPGMenu( RPM ); end; Procedure ShowRep( PC: GearPtr ); { Display all of the PC's reputations. } { This is a debugging command. } var T,N: Integer; begin PC := LocatePilot( PC ); if PC <> Nil then begin for t := 1 to 7 do begin N := NAttValue( PC^.NA , NAG_CharDescription , -T ); if N <> 0 then DialogMsg( PersonalityTraitDesc( T , N ) + ' (' + BStr( Abs( N ) ) + ')' ); end; end; end; Procedure DirectScript( GB: GameBoardPtr ); { CHEAT COMMAND! Directly invoke an ASL script. } var event: String; begin event := GetStringFromUser( 'DEBUG CODE 45123' , @PCActionRedraw ); if event <> '' then begin CombatDisplay( GB ); InvokeEvent( event , GB , GB^.Scene , event ); end else begin CombatDisplay( GB ); end; end; Procedure PCRunToggle; { If the PC is running, switch to walking. If he's walking, switch to running. } begin PC_SHOULD_RUN := not PC_SHOULD_RUN; if PC_SHOULD_RUN then begin DialogMsg( MsgString( 'RUN_ON' ) ); end else begin DialogMsg( MsgString( 'RUN_OFF' ) ); end; end; Procedure DoQuickFire( GB: GameBoardPtr; Mek: GearPtr ); { QUICKFIRE: The central function! } { Enacts a QuickFire action. If the player has chosen a weapon to QuickFire with, } { they will aim for the nearest enemy in range, and attack with that weapon. } { Excellent for guns, and works okay with thrown/melee weapons, since it only } { considers attack range, not throw range, and thus doesn't throw needlessly. } { QuickFire tries to stick with the player's target, but will automatically } { retarget if necessary. } { GB: The game board upon which the Mek will QuickFire } { Mek: The entity that will attack the nearest enemy in range } var QFWpn: GearPtr; { The QuickFire weapon } T: GearPtr; { Enemy target } AtOp: Integer; { Default attack options } begin QFWpn := FindQuickFireWeapon( GB , Mek ); if ( QFWpn = Nil ) or ( FindMaster( QFWpn ) <> FindRoot( QFWpn ) ) then begin { Couldn't find a suitable QuickFire weapon } DialogMsg( MsgString( 'QUICKFIRE_NoWeapon' ) ); Exit; end; { Check that weapon is ready } if not ReadyToFire( GB, Mek, QFWpn ) then begin DialogMsg( ReplaceHash( MsgString( 'ATA_NotReady' ) , GearName( QFWpn ) ) ); Exit; end; { Obtain target } T := LocateMekByUID( GB , NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Target ) ); AtOp := DefaultAtOp( QFWpn ); if ( T = Nil ) or not GearActive( T ) then T := SeekTarget( GB, Mek ); { Big targeting conditional. We fight our target, or we retarget and fight that instead. } if ( T <> Nil ) and ( Range( GB, Mek, T ) <= WeaponRange( GB, QFWpn , RANGE_Long ) ) and GearActive( T ) and MekCanSeeTarget( GB, Mek, T ) then begin { We can fire at our last target } if RangeArcCheck( GB, Mek, QFWpn, T ) then begin AttackerFrontEnd( GB, Mek, QFWpn, T, AtOp ); end else begin SetNAtt( Mek^.NA , NAG_Location , NAS_SmartCount , -5 ); SetNAtt( Mek^.NA , NAG_EpisodeData , NAS_Target , NAttValue( T^.NA , NAG_EpisodeData , NAS_UID ) ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartAction , NAV_SmartAttack ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartWeapon , FindGearIndex( Mek , QFWpn ) ); SetNAtt( Mek^.NA , NAG_Location , NAS_SmartTarget , 0 ); RLSmartAttack( GB , Mek ); end; end else begin DialogMsg( MsgString( 'QUICKFIRE_NoEnemies' ) ); Exit; end; end; Procedure SwitchPartyMode( GB: GameBoardPtr ); { Switch the party mode currently being used: either Clock or Tactics. } { If the current mode is Tactics, you can't switch back to Clock unless this is } { a safe area. If switching to tactics mode, be sure to set the TacticsTurnStart } { attribute. } var mode: Integer; begin { Error check- can only switch modes when we have a scene. } if ( GB = Nil ) or ( GB^.Scene = Nil ) then Exit; mode := NAttValue( GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod ); if mode = NAV_ClockMode then begin { Switch to tactics mode. } DialogMsg( MsgString( 'SwitchPartyMode_GoTactics' ) ); SetNAtt( GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_TacticsMode ); SetNAtt( GB^.Scene^.NA , NAG_SceneData , NAS_TacticsTurnStart , GB^.ComTime ); end else begin { Set the mode to clock mode. } if IsSafeArea( GB ) then begin { It's safe to perform the switch. } DialogMsg( MsgString( 'SwitchPartyMode_GoClock' ) ); SetNAtt( GB^.Scene^.NA , NAG_SceneData , NAS_PartyControlMethod , NAV_ClockMode ); end else begin DialogMsg( MsgString( 'SwitchPartyMode_Fail' ) ); end; end; end; Procedure ShowSkillXP( PC: GearPtr ); { Show how much skill experience this PC has. } var T,XP: LongInt; begin PC := LocatePilot( PC ); if PC <> Nil then begin for t := 1 to NumSkill do begin XP := NAttValue( PC^.NA , NAG_Experience , NAS_Skill_XP_Base + T ); if XP > 0 then begin DialogMsg( MsgString( 'SkillName_' + BStr( T ) ) + ': ' + BStr( XP ) + '/' + BStr( SkillAdvCost( PC , NAttValue( PC^.NA , NAG_Skill , T ) ) ) ); end; end; end; end; Procedure SpitContents( M: GearPtr ); { Just list all the siblings in this list.} begin while M <> Nil do begin dialogmsg( GearName( M ) + ' ' + BStr( NAttValue( M^.NA , NAG_Location , NAS_X ) ) + ',' + BStr( NAttValue( M^.NA , NAG_Location , NAS_Y ) ) ); M := M^.Next; end; end; Function PCA_CommandProcessor( Mek: GearPtr; Camp: CampaignPtr; KCode: Integer; IsPC: Boolean ): Boolean; { Process this command. KCode is one of the standard key codes; it may have been } { modified by the calling procedure, i.e. holding shift turns "walk" into "run". } { Branch to the appropriate procedure; return TRUE if the PC has acted, or FALSE } { if the PC hasn't. } { IsPC will be true if MEK is a member of the player team, and false if MEK is a } { lancemate or other such indirectly controlled model. If the requested action isn't } { legal for a lancemate then an error message will be printed here. } var GotMove: Boolean; begin GotMove := False; {$IFNDEF ASCII} if KCode = KMC_SouthWest then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 3 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_South then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 2 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_SouthEast then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 1 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_West then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 4 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_East then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 0 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_NorthWest then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 5 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_North then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 6 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; end else if KCode = KMC_NorthEast then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 7 ) ] , PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) ); GotMove := True; {$ELSE} if KCode = KMC_SouthWest then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 3 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_South then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 2 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_SouthEast then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 1 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_West then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 4 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_East then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 0 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_NorthWest then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 5 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_North then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 6 ) ] , PC_Should_Run ); GotMove := True; end else if KCode = KMC_NorthEast then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ KeyboardDirToMapDir( 7 ) ] , PC_Should_Run ); GotMove := True; {$ENDIF} end else if KCode = KMC_NormSpeed then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ NAttValue( Mek^.NA , NAG_Location , NAS_D ) ] , False ); GotMove := True; end else if KCode = KMC_TurnLeft then begin PrepAction( Camp^.GB , Mek , NAV_TurnLeft ); end else if KCode = KMC_TurnRight then begin PrepAction( Camp^.GB , Mek , NAV_TurnRight ); end else if KCode = KMC_FullSpeed then begin RLWalker( Camp^.GB , Mek , Reverse_RL_D[ NAttValue( Mek^.NA , NAG_Location , NAS_D ) ] , True ); GotMove := True; end else if ( KCode = KMC_Reverse ) and MoveLegal( Camp^.GB^.Scene , Mek , NAV_Reverse , Camp^.GB^.ComTime ) then begin PrepAction( Camp^.GB , Mek , NAV_Reverse ); end else if KCode = KMC_Stop then begin PrepAction( Camp^.GB , Mek , NAV_Stop ); end else if KCode = KMC_ShiftGears then begin ShiftGears( Camp^.GB , Mek , TRUE ); end else if KCode = KMC_ExamineMap then begin LookAround( Camp^.GB , Mek ); end else if KCode = KMC_AttackMenu then begin DoPlayerAttack( Mek , Camp^.GB ); end else if KCode = KMC_Attack then begin RLQuickAttack( Camp^.GB , Mek ); end else if KCode = KMC_QuitGame then begin Camp^.GB^.QuitTheGame := True; end else if KCode = KMC_Talk then begin PCTalk( Camp^.GB , Mek ); end else if KCode = KMC_Telephone then begin PCTelephone( Camp^.GB , Mek ); end else if KCode = KMC_Help then begin PCRLHelp( Camp^.GB ); end else if KCode = KMC_Get then begin PCGetItem( Camp^.GB , Mek ); end else if KCode = KMC_Inventory then begin PCBackpackMenu( Camp^.GB , Mek , True ); end else if KCode = KMC_Equipment then begin PCBackpackMenu( Camp^.GB , Mek , False ); end else if ( KCode = KMC_Enter ) or ( KCode = KMC_Enter2 ) then begin PCEnter( Camp^.GB , Mek ); end else if KCode = KMC_PartBrowser then begin MechaPartBrowser( Mek , @PCActionRedraw ); end else if KCode = KMC_LearnSkills then begin PCACTIONRD_GB := Camp^.GB; DoTraining( Camp^.GB , Mek , @PCActionRedraw ); end else if KCode = KMC_SelectMecha then begin DoSelectPCMek( Camp^.GB , Mek ); end else if KCode = KMC_SaveGame then begin PCSaveCampaign( Camp , Mek , True ); end else if KCode = KMC_CharInfo then begin PCViewChar( Camp^.GB , Mek ); end else if KCode = KMC_ApplySkill then begin PCActivateSkill( Camp^.GB , Mek ); end else if KCode = KMC_Eject then begin DoEjection( Camp^.GB , Mek ); end else if KCode = KMC_Rest then begin DoRest( Camp^.GB , Mek ); end else if KCode = KMC_History then begin DisplayConsoleHistory( Camp^.GB ); end else if KCode = KMC_FieldHQ then begin PCFieldHQ( Camp^.GB , Mek ); end else if KCode = KMC_ViewMemo then begin MemoBrowser( Camp^.GB , Mek ); end else if KCode = KMC_UseProp then begin PCUseProp( Camp^.GB , Mek ); end else if KCode = KMC_Search then begin PCSearch( Camp^.GB , Mek ); end else if KCode = KMC_RunToggle then begin PCRunToggle; end else if KCode = KMC_QuickFire then begin DoQuickFire( Camp^.GB, Mek ); end else if KCode = KMC_RollHistory then begin MoreText( Skill_Roll_History , MoreHighFirstLine( Skill_Roll_History ) ); end else if KCode = KMC_PartyMode then begin SwitchPartyMode( Camp^.GB ); GotMove := True; end else if KCode = KMC_UseSystem then begin UsableGearMenu( Camp^.GB , Mek ); {$IFNDEF ASCII} end else if KCode = KMC_WallToggle then begin Use_Tall_Walls := not Use_Tall_Walls; {$ENDIF} end; PCA_CommandProcessor := GotMove; end; Procedure MenuPlayerInput( Mek: GearPtr; GB: GameBoardPtr ); { This mek belongs to the player. Get input. } var MoveMode, T , S: Integer; RPM: RPGMenuPtr; begin { Create the action menu. } RPM := CreateRPGMenu( PlayerBlue , StdWhite , ZONE_Menu ); { Add movement options - Cruise, Full, Turn-L, Turn-R } { - if it's appropriate to do so. Check to make sure } { that the mek is capable of moving first. } MoveMode := NAttValue( Mek^.NA , NAG_Action , NAS_MoveMode ); if CPHMoveRate( GB^.Scene , Mek , gb^.Scale ) > 0 then begin if MoveMode = MM_Walk then begin AddRPGMenuItem( RPM , 'Walk' , NAV_NormSpeed ); AddRPGMenuItem( RPM , 'Run' , NAV_FullSpeed ); end else begin AddRPGMenuItem( RPM , 'Cruise Speed' , NAV_NormSpeed ); if MoveLegal( GB^.Scene , Mek , NAV_FullSpeed , GB^.ComTime ) then AddRPGMenuItem( RPM , 'Full Speed' , NAV_FullSpeed ); end; if MoveLegal( GB^.Scene , Mek , NAV_TurnLeft , GB^.ComTime ) then AddRPGMenuItem( RPM , '<<< Turn Left', NAV_TurnLeft ); if MoveLegal( GB^.Scene , Mek , NAV_TurnRight , GB^.ComTime ) then AddRPGMenuItem( RPM , ' Turn Right >>>', NAV_TurnRight); if MoveLegal( GB^.Scene , Mek , NAV_Reverse , GB^.ComTime ) then AddRPGMenuItem( RPM , ' Reverse', NAV_Reverse); end; { Add movemode switching options, if applicable. } { Check to see what movemodes the mek has } { available. } for t := NumMoveMode downto 1 do begin { We won't add a switch option for the move mode currently } { being used. } if T <> MoveMode then begin if ( BaseMoveRate( GB^.Scene , Mek , T ) > 0 ) and MoveLegal( GB^.Scene , Mek , T , NAV_NormSpeed , GB^.ComTime ) then begin if T = MM_Fly then begin if JumpTime( GB^.Scene , Mek ) > 0 then begin AddRPGMenuItem( RPM , 'Jump' , 100+T ); end else begin AddRPGMenuItem( RPM , MsgString( 'MoveModeName_' + BStr(T) ) , 100+T ); end; end else begin AddRPGMenuItem( RPM , MsgString( 'MoveModeName_' + BStr(T) ) , 100+T ); end; end; end; end; { Add the Stop/Wait option. For meks which have } { had their movement systems disabled, this will } { be the only option. } if NAttValue( Mek^.NA , NAG_Action , NAS_MoveAction ) = NAV_Stop then begin AddRPGMenuItem( RPM , 'Wait', -1 ); end else begin AddRPGMenuItem( RPM , 'Stop', -1 ); end; AddRPGMenuItem( RPM , 'Weapons Menu', -3 ); AddRPGMenuItem( RPM , 'Info Menu', -2 ); AddRPGMenuItem( RPM , 'Options Menu', -5 ); AddRPGMenuItem( RPM , 'Search' , -6 ); { Set the SelectItem field of the menu to the } { item which matches the mek's last menu action. } SetItemByValue( RPM , NAttValue( Mek^.NA , NAG_Location , NAS_LastMenuItem ) ); RPM^.Mode := RPMNoCleanup; { Keep processing input from the mek until we get } { an input which changes the CallTime. } while (NAttValue( Mek^.NA , NAG_Action , NAS_CallTime) <= GB^.ComTime ) and (not GB^.QuitTheGame) and GearActive( Mek ) do begin { Indicate the mek to get the action for, } { and prepare the display. } { Input the action. } S := SelectMenu( RPM , @PCMenuRedraw ); { Set ETA, movement stats, whatever. } if ( S > -1 ) and ( S < 100 ) then begin { Some basic movement command. } PrepAction( GB , Mek , S ); end else if ( S div 100 ) = 1 then begin { A movemode switch has been selected. } SetMoveMode( GB , Mek , S mod 100 ); end else if S = -1 then begin { WAIT or STOP, depending... } PrepAction( GB , Mek , NAV_Stop ); end else if S = -2 then begin InfoMenu( Mek , GB ); end else if S = -3 then begin DoPlayerAttack( Mek , GB ); end else if S = -5 then begin GameOptionMenu( Mek , GB ); end else if S = -6 then begin PCSearch( GB , Mek ); end; {if} end; {While} { Record the last used menu action. } SetNAtt( Mek^.NA , NAG_Location , NAS_LastMenuItem , S ); { De-allocate the menu. } DisposeRPGMenu( RPM ); end; Function KeyToKeyCode( KP: Char ): Integer; { Return the key code represented by this key press. } var t,kcode: Integer; begin kcode := 0; for t := 1 to NumMappedKeys do begin if KeyMap[t].IsACommand and ( KeyMap[t].kcode = KP ) then begin kcode := t; Break; end; end; KeyToKeyCode := kcode; end; {$IFNDEF ASCII} Procedure GraphicsTest( GB: GameBoardPtr ); var Time0: QWord; T: Integer; begin Time0 := SDL_GetTicks; DialogMsg( '*** Graphics Test ***' ); for t := 1 to 100 do begin CombatDisplay( GB ); DoFlip; end; DialogMsg( 'Time: ' + BStr( SDL_GetTicks - Time0 ) ); end; {$ENDIF} Procedure GodMode( GB: GameBoardPtr; PC: GearPtr ); { CHEAT COMMAND! Make PC ready for instant adventuring from beginning } { to ending. } var T: Integer; Item: GearPtr; begin DialogMsg( '*** GOD MODE ACTIVATED ***' ); for t := 1 to NumSkill do begin if NAttValue( PC^.NA , NAG_Skill , T ) > 0 then SetNAtt( PC^.NA , NAG_Skill , T , 20 ); end; SetNAtt( PC^.NA , NAG_Skill , NAS_Vitality , 30 ); SetNAtt( PC^.NA , NAG_Skill , NAS_Awareness , 30 ); SetNAtt( PC^.NA , NAG_Experience , NAS_Credits , 10000000 ); AddSAtt( FindRoot( GB^.Scene )^.SA , 'HISTORY' , 'God mode was activated.' ); SelectEquipmentForNPC( GB, PC, 120 ); Item := LoadNewItem( 'Network Phone' ); if Item <> Nil then InsertInvCom( PC , Item ); end; Procedure SpitSocialNetwork( Camp: CampaignPtr ); { Search the campaign, then report on NPCs with a nonstandard attitude } { towards the PC. } Procedure SeekAlongTrack( LList: GearPtr ); { Seek NPCs along this track. Print the details of any you find. } var msg: String; R: Integer; begin while LList <> Nil do begin if ( LList^.G = GG_Character ) and ( ( NAttValue( LList^.NA , NAG_XXRan , NAS_XXChar_Attitude ) <> 0 ) or ( NAttValue( LList^.NA , NAG_Relationship , 0 ) <> 0 ) ) then begin msg := GearName( LList ) + ' (' + SAttValue( LList^.SA , 'JOB_DESIG' ) + ') '; r := NAttValue( LList^.NA , NAG_Relationship , 0 ); if R <> 0 then msg := msg + ' ' + MsgString( 'RELATIONSHIP_' + BStr( R ) ); AddXXCharContext( LList , msg , '@' ); DialogMsg( msg ); end; SeekAlongTrack( LList^.SubCom ); SeekAlongTrack( LList^.InvCom ); LList := LList^.Next; end; end; begin SeekAlongTrack( Camp^.Source ); if Camp^.GB <> Nil then SeekAlongTrack( Camp^.GB^.meks ); end; Procedure RLPlayerInput( Mek: GearPtr; Camp: CampaignPtr ); { Allow the PC to control the action as per normal in a RL } { game- move using the arrow keys, use other keys for everything } { else. } var KP: Char; { Key Pressed } KCode: Integer; GotMove: Boolean; Mobile: Boolean; begin { Record where the mek currently is. } Mobile := CurrentMoveRate( Camp^.GB^.Scene , Mek ) > 0; FocusOn( Mek ); if ( NAttValue( Mek^.NA , NAG_Location , NAS_SmartAction ) <> 0 ) and Mobile then begin { The player is smartbumping. Call the appropriate procedure. } RLSmartAction( Camp^.GB , Mek ); end else begin GotMove := False; { Start the input loop. } while (NAttValue( Mek^.NA , NAG_Action , NAS_CallTime) <= Camp^.GB^.ComTime) and (not GotMove) and (not Camp^.GB^.QuitTheGame) and GearActive( Mek ) do begin CombatDisplay( Camp^.GB ); DoFlip; { Input the action. } KP := RPGKey; KCode := KeyToKeyCode( KP ); {$IFNDEF ASCII} {$IFNDEF CUTE} if ( kcode = KMC_North ) and not Use_Isometric_Mode then begin if PC_Should_Run or ( RK_KeyState[ SDLK_RSHIFT ] = 1 ) or ( RK_KeyState[ SDLK_LSHIFT ] = 1 ) then begin KCode := KMC_FullSpeed; end else begin kcode := KMC_NormSpeed; end; end else if ( KP = KeyMap[ KMC_West ].KCode ) and not Use_Isometric_Mode then begin KCode := KMC_TurnLeft; end else if ( KP = KeyMap[ KMC_East ].KCode ) and not Use_Isometric_Mode then begin KCode := KMC_TurnRight; end; {$ENDIF} {$ENDIF} if KCode > 0 then begin { We got a command. Process it. } GotMove := PCA_CommandProcessor( Mek, Camp, KCode, True ); {$IFNDEF ASCII} end else if ( KP = RPK_RightButton ) and Mouse_Active then begin GameOptionMenu( Mek , Camp^.GB ); {$ENDIF} end else if KP = '}' then begin ForcePlot( Camp^.GB , Mek , Camp^.GB^.Scene ); end else if ( KP = '!' ) and ( Camp^.GB^.Scene <> Nil ) then begin BrowseDesignFile( FindRoot( Camp^.GB^.Scene )^.InvCom , @PCActionRedraw ); end else if ( KP = '$' ) and ( Camp^.GB^.Scene <> Nil ) then begin BrowseDesignFile( FindRoot( Camp^.GB^.Scene )^.SubCom , @PCActionRedraw ); end else if ( KP = '^' ) and ( Camp^.GB^.Scene <> Nil ) then begin SpitContents( Camp^.GB^.meks ); end else if KP = '*' then begin BrowseDesignFile( Camp^.GB^.Meks , @PCActionRedraw ); end else if KP = '@' then begin ShowRep( Mek ); end else if KP = '~' then begin ShowSkillXP( Mek ); end else if KP = '#' then begin DirectScript( Camp^.GB ); {$IFNDEF ASCII} end else if KP = '"' then begin GraphicsTest( Camp^.GB ); {$ENDIF} end else if xxran_debug and ( KP = '|' ) then begin GodMode( Camp^.GB , LocatePilot( Mek ) ); end else if xxran_debug and ( KP = '`' ) then begin SpitSocialNetwork( Camp ); end; {if} end; {While} end; {IF} end; Function PCisMarooned( GB: GameBoardPtr; Mek: GearPtr ): Boolean; { Check the PC's available move modes, return TRUE if the PC is SOL. } var P: Point; Terr,MM: Integer; IsMarooned: Boolean; begin { Assume TRUE unless shown otherwise. } IsMarooned := True; { Determine the terrain of the tile the mek is standing in. The shifted movemode } { must be legal there. } P := GearCurrentLocation( Mek ); Terr := TileTerrain( GB , P.X , P.Y ); { Check all the movemodes in turn. } for MM := 1 to NumMoveMode do begin { If the PC has this movemode, see if it can be used here. } if BaseMoveRate( GB^.Scene , Mek , MM ) > 0 then begin if not IsBlockingTerrainForMM( GB , Mek , Terr , MM ) then IsMarooned := False; end; end; PCisMarooned := IsMarooned; end; Procedure GetPlayerInput( Mek: GearPtr; Camp: CampaignPtr ); { Branch to either the MENU based input routine or the RL one. } var IT: Integer; TL: LongInt; begin PCACTIONRD_PC := Mek; PCACTIONRD_GB := Camp^.GB; { Check the player for jumping. } TL := NAttValue( Mek^.NA , NAG_Action , NAS_TimeLimit ); if ( TL > 0 ) then begin DialogMsg( BStr( Abs( TL - Camp^.GB^.ComTime ) ) + ' seconds jump time left.' ); end; { Check the player for valid movemode. This is needed } { for jumping mecha. I think that the way jumping is } { currently handled in the game is a bit messy- lots of } { bits here and there trying to make it look right. } { Someday I'll try to clean up action.pp and make everything } { more elegant, but for right now as long as everything works } { and is vaguely understandable I can't complain. } if not MoveLegal( Camp^.GB^.Scene , Mek , NAV_NormSpeed , Camp^.GB^.ComTime ) then begin GearUp( Mek ); end; { Check to see if the PC is marooned. If so, load a recovery scenario. } { The PC only counts as marooned if outside of a mecha; if inside a mecha, } { he'll have to eject. Recovery can also only take place in a safe scene. } if ( Mek^.G = GG_Character ) and IsSafeArea( Camp^.GB ) then begin { See if the Mek can move in the terrain currently being stood upon. } if PCisMarooned( Camp^.GB , Mek ) then begin { Looks like the PC is marooned. Load a rescue scenario. } StartRescueScenario( Camp^.GB , Mek , '*PICKUP' ); end; end; { Find out what kind of interface to use. } IT := InterfaceType( Camp^.GB , Mek ); if IT = MenuBasedInput then begin MenuPlayerInput( Mek , Camp^.GB ); end else begin RLPlayerInput( Mek , Camp ); end; { At the end of any action, do a search for metaterrain. } if GearActive( Mek ) then CheckHiddenMetaterrain( Camp^.GB , Mek ); end; Procedure SetPartyWorldPos( GB: GameBoardPtr; X , Y: Integer ); { Set the position of the party on the world map. } { All PC and Lancemate gears should have their location set to X,Y. } var M: GearPtr; T: Integer; begin M := GB^.Meks; while M <> Nil do begin T := NAttValue( M^.NA , NAG_Location , NAS_Team ); if ( T = NAV_DefPlayerTeam ) or ( T = NAV_LancemateTeam ) then begin SetNAtt( M^.NA , NAG_Location , NAS_X , X ); SetNAtt( M^.NA , NAG_Location , NAS_Y , Y ); end; M := M^.Next; end; end; Function MoveOnWorld( GB: GameBoardPtr; Mek: GearPtr; var X,Y: Integer; D: Integer ): Integer; { Attempt to move on the world map. This function returns the travel time, } { or 0 if travel is impossible. } Function WorldMapMoveRate( Mek: GearPtr; MM: Integer ): Integer; { Return a scaled rate for moving on the world map. } var it,T: Integer; begin it := BaseMoveRate( GB^.Scene , Mek , MM ); if it > 0 then begin for t := 1 to Mek^.Scale do it := it * 2; it := GB^.Scale * 3600 div it; if it < 1 then it := 1; end else begin it := 0; end; WorldMapMoveRate := it; end; var X2,Y2,TopSpeed,terr,T,spd: Integer; begin { Locate the new position. } X2 := X + AngDir[ D , 1 ]; Y2 := Y + AngDir[ D , 2 ]; FixWorldCoords( GB^.Scene , X2 , Y2 ); { If this new position is on the map, maybe the PC can move there. } if OnTheMap( GB , X2 , Y2 ) then begin terr := TileTerrain( GB , X2 , Y2 ); if Mek <> Nil then begin { Determine the top speed at which the PC can cross this terrain. } TopSpeed := 0; for t := 1 to NumMoveMode do begin if TerrMan[ terr ].MMPass[ T ] then begin spd := WorldMapMoveRate( Mek , T ); if spd > TopSpeed then TopSpeed := spd; end; end; if TopSpeed > 0 then begin X := X2; Y := Y2; SetPartyWorldPos( GB , X , Y ); MoveOnWorld := TopSpeed; end else begin MoveOnWorld := 0; end; end else begin { No PC... Better set the Quit flag. } GB^.QuitTheGame := True; MoveOnWorld := 0; end; end else begin MoveOnWorld := 0; end; end; Function WorldMapMain( Camp: CampaignPtr ): Integer; { Explore the world map. Maybe enter a location. } var TravelTime,T: Integer; A: Char; PCX,PCY: Integer; { The party's location on the map. } PC: GearPtr; update_trigger: String; begin { Set the gameboard's pointer to the campaign. } Camp^.GB^.Camp := Camp; PC := GG_LocatePC( Camp^.GB ); PCACTIONRD_PC := PC; PCACTIONRD_GB := Camp^.GB; if PC <> Nil then begin PCX := NAttValue( PC^.NA , NAG_Location , NAS_X ); PCY := NAttValue( PC^.NA , NAG_Location , NAS_Y ); end else begin PCX := Random( Camp^.GB^.Map_Width ) + 1; PCY := Random( Camp^.GB^.Map_Height ) + 1; end; SetPartyWorldPos( Camp^.GB , PCX , PCY ); { Set the STARTGAME trigger, and update all props. } SetTrigger( Camp^.GB , TRIGGER_StartGame ); update_trigger := 'UPDATE'; CheckTriggerAlongPath( update_trigger , Camp^.GB , Camp^.GB^.Meks , True ); { Start world map exploration loop here. } { Basically, the PC will have the chance to move around. } while KeepPlayingSC( Camp^.GB ) do begin CombatDisplay( Camp^.GB ); DoFlip; { Player input loop. } TravelTime := 0; repeat A := RPGKey; if A = KeyMap[ KMC_North ].KCode then begin TravelTime := MoveOnWorld( Camp^.GB , PC , PCX , PCY , 6 ); end else if A = KeyMap[ KMC_South ].KCode then begin TravelTime := MoveOnWorld( Camp^.GB , PC , PCX , PCY , 2 ); end else if A = KeyMap[ KMC_West ].KCode then begin TravelTime := MoveOnWorld( Camp^.GB , PC , PCX , PCY , 4 ); end else if A = KeyMap[ KMC_East ].KCode then begin TravelTime := MoveOnWorld( Camp^.GB , PC , PCX , PCY , 0 ); end else if A = KeyMap[ KMC_QuitGame ].KCode then begin Camp^.GB^.QuitTheGame := True; end else if A = KeyMap[ KMC_Help ].KCode then begin PCRLHelp( Camp^.GB ); end else if A = KeyMap[ KMC_Inventory ].KCode then begin PCBackpackMenu( Camp^.GB , PC , True ); end else if A = KeyMap[ KMC_Equipment ].KCode then begin PCBackpackMenu( Camp^.GB , PC , False ); end else if ( A = KeyMap[ KMC_Enter ].KCode ) or ( A = KeyMap[ KMC_Enter2 ].KCode ) then begin PCEnter( Camp^.GB , PC ); end else if A = KeyMap[ KMC_PartBrowser ].KCode then begin MechaPartBrowser( PC , @PCActionRedraw ); end else if A = KeyMap[ KMC_LearnSkills ].KCode then begin PCACTIONRD_GB := Camp^.GB; DoTraining( Camp^.GB , PC , @PCActionRedraw ); end else if A = KeyMap[ KMC_SelectMecha ].KCode then begin DoSelectPCMek( Camp^.GB , PC ); end else if A = KeyMap[ KMC_SaveGame ].KCode then begin PCSaveCampaign( Camp , PC , True ); end else if A = KeyMap[ KMC_CharInfo ].KCode then begin PCViewChar( Camp^.GB , PC ); end else if A = KeyMap[ KMC_ApplySkill ].KCode then begin PCActivateSkill( Camp^.GB , PC ); end else if A = KeyMap[ KMC_History ].KCode then begin DisplayConsoleHistory( Camp^.GB ); end else if A = KeyMap[ KMC_FieldHQ ].KCode then begin PCFieldHQ( Camp^.GB , PC ); end else if A = KeyMap[ KMC_ViewMemo ].KCode then begin MemoBrowser( Camp^.GB , PC ); end else if A = KeyMap[ KMC_UseProp ].KCode then begin PCUseProp( Camp^.GB , PC ); end else if A = KeyMap[ KMC_Search ].KCode then begin PCSearch( Camp^.GB , PC ); end else if A = '#' then begin DirectScript( Camp^.GB ); end else if A = '$' then begin MoreText( Skill_Roll_History , MoreHighFirstLine( Skill_Roll_History ) ); end; CombatDisplay( Camp^.GB ); DoFlip; until ( travelTime > 0 ) or not KeepPlayingSC( Camp^.GB ); { Advance the game clock. } for t := 1 to TravelTime do AdvanceGameClock( Camp^.GB , True , True ); HandleTriggers( Camp^.GB ); {end world map exploration loop.} end; { Handle the last pending triggers. } SetTrigger( Camp^.GB , TRIGGER_EndGame ); HandleTriggers( Camp^.GB ); { Return the outcome code. } WorldMapMain := ( Camp^.GB^.ReturnCode ); end; end. GH2/effects.pp0000644000175000017500000033310211401151246012051 0ustar kaolkaolunit 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. GH2/ghsupport.pp0000644000175000017500000001656411347637673012525 0ustar kaolkaolunit ghsupport; { This unit handles support gears - various things needed for a } { mecha's operation which don't fit in elsewhere. } { It also handles cockpits. } { And usables. } { *** RULES *** } { - Support gears may only be installed in the BODY module. } { - Every mecha needs an engine. } { 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,gears,ui4gh; { *** SUPPORT FORMAT *** } { G = GG_Support } { S = System Type } { V = System Rating } { Stat[1] = Armor Rating } { Stat[2] = Engine Subtype } const NumSupportType = 2; GS_Gyro = 1; GS_Engine = 2; STAT_EngineSubType = 2; EST_HighOutput = 1; { Increased energy point supply } EST_HighPerformance = 2; { Improved MV/TR, reduced energy points, may go boom } { G = GG_Cockpit } { S = Undefined } { V = Cockpit Style } { STAT 1 = Armor Rating } { STAT 2 = Passenger Scale } { G = GG_Usable } { S = Usable Type } Num_Usable_System_Types = 4; GS_Transformation = 1; GS_ForceField = 2; GS_Holograms = 3; GS_LongRangeScanner = 4; { V = Power Level/Parameter } Function CockpitBaseMass( CPit: GearPtr ): Integer; Procedure CheckCPitRange( CPit: GearPtr ); Function IsLegalCPitSub( Part, Equip: GearPtr ): Boolean; Function UsableBaseMass( Part: GearPtr ): Integer; Function UsableComplexity( Part: GearPtr ): Integer; Procedure CheckUsableRange( Part: GearPtr ); Function UsableValue( Part: GearPtr ): LongInt; Function SupportBaseDamage( Part: GearPtr ): Integer; Function SupportName( Part: GearPtr ): String; Function SupportBaseMass( Part: GearPtr ): Integer; Function SupportValue( Part: GearPtr ): LongInt; Procedure CheckSupportRange( Part: GearPtr ); implementation uses ghmecha; Function CockpitBaseMass( CPit: GearPtr ): Integer; {Cockpits usually have no weight... unless they're} {armored. In that case, the weight of the cockpit} {equals the armor rating assigned to it.} begin CockpitBaseMass := CPit^.Stat[1]; end; Procedure CheckCPitRange( CPit: GearPtr ); { Examine the values of all the cockpit's stats and make sure } { everything is nice and legal. } var T: Integer; begin { Check S - Currently Undefined } CPit^.S := 0; { Check V - Cockpit Type; Currently Undefined } CPit^.V := 0; { Check Stats } { Stat 1 - Armor } if CPit^.Stat[1] < 0 then CPit^.Stat[1] := 0 else if CPit^.Stat[1] > 2 then CPit^.Stat[1] := 2; { Stat 2 - Pilot Scale } { The scale of the piot must be less than or equal to the scale of } { the cockpit. } if CPit^.Stat[2] >= CPit^.Scale then CPit^.Stat[2] := CPit^.Scale - 1; for t := 3 to NumGearStats do CPit^.Stat[ T ] := 0; end; Function IsLegalCPitSub( Part, Equip: GearPtr ): Boolean; { Return TRUE if the specified EQUIP may be legally installed } { in PART, FALSE if it can't be. } begin if ( Equip^.G = GG_Character ) and ( Equip^.Scale = Part^.Stat[2] ) then IsLegalCPitSub := True else IsLegalCPitSub := False; end; Function UsableBaseMass( Part: GearPtr ): Integer; { Return the basic mass of this part. } begin if Part^.S = GS_Transformation then begin UsableBaseMass := 1; end else if Part^.S = GS_LongRangeScanner then begin UsableBaseMass := Part^.V; end else begin UsableBaseMass := Part^.V * 2; end; end; Function UsableComplexity( Part: GearPtr ): Integer; { Return the basic complexity of this part- the number of } { slots it takes when installed in a mecha. } begin if Part^.S = GS_Transformation then begin UsableComplexity := 2; end else if Part^.S = GS_ForceField then begin UsableComplexity := Part^.V * 2; end else begin UsableComplexity := Part^.V; end; end; Procedure CheckUsableRange( Part: GearPtr ); { Check the range of this usable gear to make sure that it's all } { nice and legal. If anything is found out of the ordinary, } { change it. } begin { Check S- Usable Type } if Part^.S < 1 then Part^.S := 1 else if Part^.S > Num_Usable_System_Types then Part^.S := Num_Usable_System_Types; { Check V- Power/Parameter } if Part^.S = GS_Transformation then begin if Part^.V < 0 then Part^.V := 0 else if Part^.V > NumForm then Part^.V := NumForm; end else begin if Part^.V < 1 then Part^.V := 1 else if Part^.V > 10 then Part^.V := 10; end; end; Function UsableValue( Part: GearPtr ): LongInt; { Return the basic cost of this part. } begin case Part^.S of GS_Transformation: UsableValue := 1000; GS_ForceField: UsableValue := Part^.V * Part^.V * Part^.V * 25 + Part^.V * Part^.V * 50 + Part^.V * 175; GS_Holograms: UsableValue := Part^.V * Part^.V * Part^.V * 25 + Part^.V * 75 + 750; GS_LongRangeScanner: UsableValue := Part^.V * Part^.V * 50 + Part^.V * 100 + 500; end; end; Function SupportBaseDamage( Part: GearPtr ): Integer; { Return the amount of damage this system can withstand. } begin if Part^.S = GS_Engine then begin SupportBaseDamage := 3; end else begin SupportBaseDamage := 1; end; end; Function SupportName( Part: GearPtr ): String; { Return a name for this particular sensor. } begin SupportName := MsgString( 'SUPPORTGEARNAME_' + BStr( Part^.S ) ); end; Function SupportBaseMass( Part: GearPtr ): Integer; { Return the mass of this system. } begin { The mass is equal to the armor value } SupportBaseMass := Part^.Stat[1]; end; Function SupportValue( Part: GearPtr ): LongInt; { Calculate the base cost of this sensor type. } var it: LongInt; begin if Part^.S = GS_Gyro then begin it := 50; end else if Part^.S = GS_Engine then begin case Part^.Stat[ STAT_EngineSubType ] of EST_HighOutput: it := Part^.V * 200; EST_HighPerformance: it := Part^.V * 200; else it := Part^.V * 45; end; end else it := Part^.V * 45; { Add the armor cost. } it := it + Part^.Stat[1] * 25; SupportValue := it; end; Procedure CheckSupportRange( Part: GearPtr ); { Examine this support system to make sure everything is legal. } begin { Check S - System Type } if Part^.S < 1 then Part^.S := 1 else if Part^.S > NumSupportType then Part^.S := 1; { Check V - System Rating. Must be in the range from 1 to 10. } if ( Part^.S = GS_Engine ) and ( Part^.Parent <> Nil ) then begin if Part^.V <> Part^.Parent^.V then Part^.V := Part^.Parent^.V; if Part^.Scale <> Part^.Parent^.Scale then Part^.Scale := Part^.Parent^.Scale; end else begin { GH2 - Gyros no longer have ratings. } Part^.V := 1; end; { Check Stats - Stat 1 is armor. } if Part^.Stat[1] < 0 then Part^.Stat[1] := 0 else if Part^.Stat[1] > 2 then Part^.Stat[2] := 2; end; end. GH2/cutemap.pp0000644000175000017500000015152211401151246012074 0ustar kaolkaolunit cutemap; { 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 locale,sdl,math,gears,texutil,cutegfx; const LoAlt = -3; HiAlt = 5; Num_Prop_Meshes = 13; var tile_x,tile_y,tile_z: LongInt; { Tile where the mouse pointer is pointing. } origin_x,origin_y: Integer; { Tile which the camera is pointing at. } origin_d: Integer; { The camera angle; one of four. } Overlays,Underlays: Array [1..MaxMapWidth,1..MaxMapWidth,LoAlt..HiAlt] of Integer; Model_Map: Array [1..MaxMapWidth,1..MaxMapWidth,LoAlt..( HiAlt + 1 )] of GearPtr; Strong_Hit_Sprite,Weak_Hit_Sprite,Parry_Sprite,Miss_Sprite,Encounter_Sprite: SensibleSpritePtr; Terrain_Sprite,Extras_Sprite,Shadow_Sprite,Building_Sprite,Current_Backdrop: SensibleSpritePtr; Off_Map_Model_Sprite: SensibleSpritePtr; Focused_On_Mek: GearPtr; Function ScreenDirToMapDir( D: Integer ): Integer; Function KeyboardDirToMapDir( D: Integer ): Integer; Function SpriteColor( GB: GameBoardPtr; M: GearPtr ): String; Procedure Render_Off_Map_Models; Procedure RenderMap( GB: GameBoardPtr ); Procedure FocusOn( Mek: GearPtr ); Procedure DisplayMiniMap( GB: GameBoardPtr ); Procedure IndicateTile( GB: GameBoardPtr; X , Y , Z: Integer ); Procedure ScrollMap( GB: GameBoardPtr ); Procedure ClearOverlays; Function ProcessShotAnimation( GB: GameBoardPtr; var AnimList,AnimOb: GearPtr ): Boolean; Function ProcessPointAnimation( GB: GameBoardPtr; var AnimList,AnimOb: GearPtr ): Boolean; Procedure RenderWorldMap( GB: GameBoardPtr; PC: GearPtr; X0,Y0: Integer ); Procedure InitGraphicsForScene( GB: GameBoardPtr ); implementation uses ghmecha,ghchars,gearutil,ability,ghprop,effects,narration,ui4gh,colormenu; type Cute_Map_Cel_Description = Record Sprite: SensibleSpritePtr; F: Integer; { The frame to be displayed. } end; Cute_Map_Cel_Toggle = Array [ 1..MaxMapWidth, 1..MaxMapWidth, LoAlt..HiAlt ] of Boolean; const Strong_Hit_Sprite_Name = 'blast64.png'; Weak_Hit_Sprite_Name = 'nodamage64.png'; Parry_Sprite_Name = 'misc_parry.png'; Miss_Sprite_Name = 'misc_miss.png'; Default_Prop_Sprite_Name = 'c_cha_m_citizen.png'; Items_Sprite_Name = 'c_default_items.png'; Default_Wreckage = 1; Default_Dead_Thing = 2; WallBrown: TSDL_Color = ( R: 76; G: 64; B: 51 ); DoorBlue: TSDL_Color = ( R: 0; G: 128; B: 128 ); WallGray: TSDL_Color = ( R: 70; G: 70; B: 55 ); { For the low wall. } SmokeGray: TSDL_Color = ( R: 155; G: 150; B: 150 ); ToxicGreen: TSDL_Color = ( R: 50; G: 170; B: 15 ); TT_OpenGround = 1; TT_Tree = 2; TT_Mountain = 3; TT_ForestFloor = 4; TT_Swamp = 5; TT_Pavement = 6; TT_Rubble = 7; TT_RoughGround = 8; TT_GenericWall = 9; TT_GenericFloor = 10; TT_Threshold = 11; TT_Carpet = 12; TT_WoodenFloor = 13; TT_WoodenWall = 14; TT_TileFloor = 15; TT_WreckageWall = 16; TT_GlassWall = 17; TT_Elevator = 18; TT_StairsDown = 19; TT_TrapDoor = 20; TT_Door = 21; { Terrain texture 22 is open for expansion } TT_Water = 23; NumCMCelLayers = 9; { Total number of cel layers. } NumBasicCelLayers = 8; { Number of cel layers set by RenderMap. } CMC_Terrain = 1; CMC_Shadow = 2; CMC_MetaTerrain = 3; CMC_MShadow = 4; CMC_Destroyed = 5; CMC_Items = 6; CMC_Master = 7; CMC_Toupee = 8; CMC_Effects = 9; Num_Rotation_Angles = 4; Backdrop_Size = 512; { Width/Height in pixels of the backdrop image. } SI_Ally = 3; SI_Neutral = 2; SI_Enemy = 1; NumOMM = 32; OM_North = 1; OM_East = 2; OM_South = 4; OM_West = 3; var Mini_Map_Sprite,World_Terrain,Items_Sprite: SensibleSpritePtr; CM_Cels: Array [ 1..MaxMapWidth, 1..MaxMapWidth, LoAlt..HiAlt, 0..NumCMCelLayers ] of Cute_Map_Cel_Description; CM_Cel_IsOn: Array [0..NumCMCelLayers] of Cute_Map_Cel_Toggle; CM_Cel_OMIcon: Array [ 1..MaxMapWidth, 1..MaxMapWidth ] of Byte; CM_ModelNames: Array [ 1..MaxMapWidth, 1..MaxMapWidth, LoAlt..HiAlt ] of String; OFF_MAP_MODELS: Array [1..4,0..NumOMM] of Integer; Function ScreenDirToMapDir( D: Integer ): Integer; { Convert the requested screen direction to a map direction. } begin if Use_Isometric_Mode then begin ScreenDirToMapDir := ( D + Origin_D * 2 + 7 ) mod 8; end else begin ScreenDirToMapDir := ( D + Origin_D * 2 ) mod 8; end; end; Function KeyboardDirToMapDir( D: Integer ): Integer; { Given the press of a key on the keyboard, return the map direction it } { corresponds to. Normally this will be the same as the screen dir, } { unless using isometric mode and ISO_DIR_OFFSET is nonzero. } var it: Integer; begin it := ScreenDirToMapDir( D ); if Use_Isometric_Mode then begin it := ( it + Iso_Dir_Offset ) mod 8; end; KeyboardDirToMapDir := it; end; Procedure ClearCMCelLayer( L: Integer ); { Clear sprite descriptions from the provided overlay layer. } var X,Y,Z: Integer; begin FillChar( CM_Cel_IsOn[ L ] , SizeOf( CM_Cel_IsOn[ L ] ) , False ); end; Procedure AddCMCel( GB: GameBoardPtr; X,Y,Z,L: Integer; SS: SensibleSpritePtr; Frame: Integer ); { Add an overlay image safely to the display. } begin if not OnTheMap( GB , X , Y ) then Exit; if ( Z < LoAlt ) or ( Z > HiAlt ) then Exit; if ( L < 0 ) or ( L > NumCMCelLayers ) then Exit; CM_Cels[ X , Y , Z , L ].Sprite := SS; CM_Cels[ X , Y , Z , L ].F := Frame; CM_Cel_IsOn[ L ][ X , Y , Z ] := SS <> Nil; end; Procedure ClearCMCel( GB: GameBoardPtr; X,Y,Z,L: Integer ); { Remove the overlay image from this tile. } begin if not OnTheMap( GB , X , Y ) then Exit; if ( Z < LoAlt ) or ( Z > HiAlt ) then Exit; if ( L < 0 ) or ( L > NumCMCelLayers ) then Exit; CM_Cel_IsOn[ L ][ X , Y , Z ] := False; end; Function SpriteName( GB: GameBoardPtr; M: GearPtr ): String; { Locate the sprite name for this gear. If no sprite name is defined, } { set the default sprite name for the gear type & store it as a string } { attribute so we won't need to do this calculation later. } const FORM_DEFAULT: Array [1..NumForm] of String = ( 'btr_buruburu.png','zoa_scylla.png','ghu_ultari.png', 'ara_kojedo.png', 'aer_wraith.png', 'orn_wasp.png', 'ger_harpy.png', 'aer_bluebird.png', 'gca_rover.png' ); DefaultMaleSpriteName = 'cha_m_citizen.png'; DefaultFemaleSpriteName = 'cha_f_citizen.png'; DefaultMaleSpriteHead = 'cha_m_'; DefaultFemaleSpriteHead = 'cha_f_'; mini_sprite = 'cha_pilot.png'; Unknown_Sprite = 'prop_unknown.png'; var it,fname: String; FList: SAttPtr; begin { If this model is an out-of-scale character, return the mini-sprite. } if ( M^.G = GG_Character ) and ( GB <> Nil ) and ( M^.Scale < GB^.Scale ) then Exit( mini_sprite ); it := SAttValue( M^.SA , 'SDL_SPRITE' ); if it = '' then begin if M^.G = GG_Character then begin if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin it := DefaultMaleSpriteHead; end else begin it := DefaultFemaleSpriteHead; end; fname := it + SAttValue( M^.SA , 'JOB' ) + '.*'; FList := CreateFileList( Graphics_Directory + fname ); if FList <> Nil then begin it := SelectRandomSAtt( FList )^.Info; DisposeSAtt( FList ); end else begin fname := it + LowerCase( SAttValue( M^.SA , 'JOB_DESIG' ) ) + '.*'; FList := CreateFileList( Graphics_Directory + fname ); if FList <> Nil then begin it := SelectRandomSAtt( FList )^.Info; DisposeSAtt( FList ); end else begin if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin it := DefaultMaleSpriteName; end else begin it := DefaultFemaleSpriteName; end; end; end; end else if ( M^.G = GG_Mecha ) and ( M^.S >= 0 ) and ( M^.S < NumForm ) then begin it := FORM_DEFAULT[ M^.S + 1 ]; end else begin it := Unknown_Sprite; end; SetSAtt( M^.SA , 'SDL_SPRITE <' + it + '>' ); end; SpriteName := it; end; Function CuteSpriteName( M: GearPtr ): String; { Locate the sprite name for this gear. If no sprite name is defined, } { set the default sprite name for the gear type & store it as a string } { attribute so we won't need to do this calculation later. } const FORM_DEFAULT: Array [1..NumForm] of String = ( 'c_btr_buruburu.png','c_btr_buruburu.png','c_btr_buruburu.png', 'c_btr_buruburu.png', 'c_btr_buruburu.png', 'c_btr_buruburu.png', 'c_btr_buruburu.png', 'c_btr_buruburu.png', 'c_btr_buruburu.png' ); DefaultMaleSpriteName = 'c_cha_m_citizen.png'; DefaultFemaleSpriteName = 'c_cha_f_citizen.png'; DefaultMaleSpriteHead = 'c_cha_m_'; DefaultFemaleSpriteHead = 'c_cha_f_'; var it,fname: String; FList: SAttPtr; begin it := SAttValue( M^.SA , 'CUTE_SPRITE' ); if it = '' then begin if M^.G = GG_Character then begin if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin it := DefaultMaleSpriteHead; end else begin it := DefaultFemaleSpriteHead; end; fname := it + SAttValue( M^.SA , 'JOB' ) + '.*'; FList := CreateFileList( Graphics_Directory + fname ); if FList <> Nil then begin it := SelectRandomSAtt( FList )^.Info; DisposeSAtt( FList ); end else begin fname := it + SAttValue( M^.SA , 'JOB_DESIG' ) + '.*'; FList := CreateFileList( Graphics_Directory + fname ); if FList <> Nil then begin it := SelectRandomSAtt( FList )^.Info; DisposeSAtt( FList ); end else begin if NAttValue( M^.NA , NAG_CharDescription , NAS_Gender ) = NAV_Male then begin it := DefaultMaleSpriteName; end else begin it := DefaultFemaleSpriteName; end; end; end; end else if ( M^.G = GG_Mecha ) and ( M^.S >= 0 ) and ( M^.S < NumForm ) then begin it := FORM_DEFAULT[ M^.S + 1 ]; end else if M^.G = GG_Prop then begin it := Default_Prop_Sprite_Name; end else begin it := Items_Sprite_Name; end; SetSAtt( M^.SA , 'CUTE_SPRITE <' + it + '>' ); end; CuteSpriteName := it; end; Function SpriteColor( GB: GameBoardPtr; M: GearPtr ): String; { Determine the color string for this model. } const neutral_clothing_color = '140 130 120'; var it: String; T: Integer; Team,Faction: GearPtr; begin it := SAttValue( M^.SA , 'SDL_COLORS' ); { Props usually but not always have their own palette, so if no } { color has been stored in SDL_COLORS assume no color is needed. } if ( it = '' ) and ( M^.G <> GG_Prop ) and ( M^.G <> GG_MetaTerrain ) and ( GB = Nil ) then begin if M^.G = GG_Character then begin it := neutral_clothing_color; it := it + ' ' + RandomColorString( CS_Skin ) + ' ' + RandomColorString( CS_Hair ); end else begin it := '175 175 171 100 100 120 0 200 200'; end; SetSAtt( M^.SA , 'SDL_COLORS <' + it + '>' ); end else if ( it = '' ) and ( M^.G <> GG_Prop ) and ( M^.G <> GG_MetaTerrain ) then begin T := NAttValue( M^.NA , NAG_Location , NAS_Team ); Team := LocateTeam( GB , T ); if Team <> Nil then it := SAttValue( Team^.SA , 'SDL_COLORS' ); if it = '' then begin if Team <> Nil then Faction := SeekFaction( GB^.Scene , NAttValue( Team^.NA , NAG_Personal , NAS_FactionID ) ) else Faction := Nil; if Faction = Nil then Faction := SeekFaction( GB^.Scene , NAttValue( M^.NA , NAG_Personal , NAS_FactionID ) ); if M^.G = GG_Character then begin if Faction <> Nil then it := SAttValue( Faction^.SA , 'chara_colors' ); if it = '' then begin if T = NAV_DefPlayerTeam then begin it := '66 121 179'; end else if AreEnemies( GB , T , NAV_DefPlayerTeam ) then begin it := '180 10 120'; end else if AreAllies( GB , T , NAV_DefPlayerTeam ) then begin it := '150 150 150'; end else begin it := neutral_clothing_color; end; end; it := it + ' ' + RandomColorString( CS_Skin ) + ' ' + RandomColorString( CS_Hair ); end else begin if Faction <> Nil then it := SAttValue( Faction^.SA , 'mecha_colors' ); if it = '' then begin if T = NAV_DefPlayerTeam then begin it := '66 121 179 210 215 80 205 25 0'; end else if AreEnemies( GB , T , NAV_DefPlayerTeam ) then begin it := '103 3 45 166 47 32 244 216 28'; end else if AreAllies( GB , T , NAV_DefPlayerTeam ) then begin it := '66 121 119 190 190 190 0 205 0'; end else begin it := '175 175 171 100 100 120 0 200 200'; end; end; end; end; SetSAtt( M^.SA , 'SDL_COLORS <' + it + '>' ); end; SpriteColor := it; end; Function AlmostSeen( GB: GameBoardPtr; X1 , Y1: Integer ): Boolean; { Tell whether or not to show the edge of visibility symbol here. We'll } { show it if this tile is unseen, and is adjacent to a seen tile that's not a wall. } var IsAlmostSeen: Boolean; D,X2,Y2: Integer; begin IsAlmostSeen := False; For D := 0 to 7 do begin X2 := X1 + AngDir[ D , 1 ]; Y2 := Y1 + AngDir[ D , 2 ]; if OnTheMap( GB , X2 , Y2 ) and TileVisible( GB , X2 , Y2 ) and ( TerrMan[ TileTerrain( GB , X2 , Y2 ) ].Altitude < 6 ) then begin IsAlmostSeen := True; Break; end; end; AlmostSeen := IsAlmostSeen; end; Procedure DrawBackdrop; { If Current_Backdrop exists, fill the screen with it. } var X,Y: Integer; MyDest: TSDL_Rect; begin if Current_Backdrop <> Nil then begin for x := 0 to ( ScreenWidth div Backdrop_Size ) do begin for y := 0 to ( ScreenHeight div Backdrop_Size ) do begin MyDest.X := x * Backdrop_Size; MyDest.Y := y * Backdrop_Size; DrawSprite( Current_Backdrop , MyDest , 0 ); end; end; end; end; Procedure Render_Off_Map_Models; { Draw the off-map models, as stored in the Off_Map_Models array. } Procedure DrawOffMap( Quad,N: Integer ); { Draw an off-map model as indicated by the OFF_MAP_MODEL array. } var MyDest: TSDL_Rect; begin if Quad = OM_East then begin MyDest.Y := ( ScreenHeight * ( N + 1 ) ) div ( NumOMM + 2 ); MyDest.X := ScreenWidth - 16; end else if Quad = OM_West then begin MyDest.Y := ( ScreenHeight * ( N + 1 ) ) div ( NumOMM + 2 ); MyDest.X := 1; end else if Quad = OM_South then begin MyDest.X := ( ( ScreenWidth * ( N + 1 ) ) div ( NumOMM + 2 ) ); MyDest.Y := 1; end else begin MyDest.X := ( ( ScreenWidth * ( N + 1 ) ) div ( NumOMM + 2 ) ); MyDest.Y := ScreenHeight - 12; end; DrawSprite( Off_Map_Model_Sprite , MyDest , OFF_MAP_MODELS[ Quad , N ] - 1 + ( Animation_Phase div 5 mod 2 ) * 3 ); end; var T: Integer; begin { Once everything else has been rendered, draw the off-map icons. } for t := 0 to NumOMM do begin if Off_Map_Models[ OM_North , T ] > 0 then DrawOffMap( OM_North , T ); if Off_Map_Models[ OM_South , T ] > 0 then DrawOffMap( OM_South , T ); if Off_Map_Models[ OM_West , T ] > 0 then DrawOffMap( OM_West , T ); if Off_Map_Models[ OM_East , T ] > 0 then DrawOffMap( OM_East , T ); end; end; Procedure Render_Cute( GB: GameBoardPtr ); { Render the location stored in G_Map, along with all items and characters on it. } { Also save the position of the mouse pointer, in world coordinates. } { I'm going to use the GH1 method for doing this- create a list of cels first containing all the } { terrain, mecha, and effects to be displayed. Then, render them. There's something I don't like } { about this method but I don't remember what, and it seems to be more efficient than searching } { through the list of models once per tile once per elevation level. } Procedure AddShadow( X,Y,Z: Integer ); { For this shadow, we're only concerned about three blocks- the one directly to the left (which } { I'll label #1), the one to the left and above (#2), and the one directly above (#3). You can } { find the right shadow frame by adding +1 if #1 is a wall, +2 if #2 is a wall, and +4 if #3 is } { a wall. The case where #1 and #3 are occupied is the same as if all three tiles were occupied. } { Here's a picture: } { 2 3 } { 1 x <-- X is the target tile. } const { Because of the variable camera angle, which totally sucks when you're trying to } { do something like this QUICKLY, use the following constants to find the relative } { locations of tiles 1, 2, and 3. If I had the time I could probably find a clever } { way to do this, but didn't some famous programmer once say that cleverness is the } { root of all evil? } Camera_Angle_X_Adjustment: Array [0..3,1..3] of SmallInt = ( ( -1 , -1 , 0 ), ( 0 , -1 , -1 ), ( 1 , 1 , 0 ), ( 0 , 1 , 1 ) ); Camera_Angle_Y_Adjustment: Array [0..3,1..3] of SmallInt = ( ( 0 , -1 , -1 ), ( 1 , 1 , 0 ), ( 0 , 1 , 1 ), ( -1 , -1 , 0 ) ); Function IsHigher( X2,Y2: Integer ): Boolean; var Terr,H2: Integer; begin if OnTheMap( GB , X2 , Y2 ) then begin terr := TileTerrain( GB , X2 , Y2 ); if ( terr <> TERRAIN_LowBuilding ) and ( terr <> TERRAIN_MediumBuilding ) and ( terr <> TERRAIN_HighBuilding ) then begin H2 := TerrMan[ terr ].Altitude; IsHigher := H2 > Z; end else begin IsHigher := False; end; end else begin IsHigher := False; end; end; var Total: Integer; begin Total := 0; if IsHigher( X + Camera_Angle_X_Adjustment[ origin_d , 1 ] , Y + Camera_Angle_Y_Adjustment[ origin_d , 1 ] ) then Total := Total + 1; if IsHigher( X + Camera_Angle_X_Adjustment[ origin_d , 2 ] , Y + Camera_Angle_Y_Adjustment[ origin_d , 2 ] ) then Total := Total + 2; if IsHigher( X + Camera_Angle_X_Adjustment[ origin_d , 3 ] , Y + Camera_Angle_Y_Adjustment[ origin_d , 3 ] ) then Total := Total + 4; if Total = 7 then Total := 5; if Total > 0 then AddCMCel( GB , X , Y , Z , CMC_Shadow , Shadow_Sprite , Total - 1 ); end; Procedure AddBasicTerrainCel( X,Y,F: Integer ); { Add a basic terrain cel. This is just a plain cel from the terrain sprite } { with an undersea column beneath. } begin AddCMCel( GB , X , Y , LoAlt , CMC_Terrain , Terrain_Sprite , 11 ); AddCMCel( GB , X , Y , 0 , CMC_Terrain , Terrain_Sprite , F ); AddShadow( X,Y,0 ); end; Procedure AddBasicWallCel( X,Y,F: Integer ); { Add a basic wall cel using F. } begin AddCMCel( GB , X , Y , LoAlt , CMC_Terrain , Terrain_Sprite , 11 ); AddCMCel( GB , X , Y , 0 , CMC_Terrain , Terrain_Sprite , F ); AddCMCel( GB , X , Y , 1 , CMC_Terrain , Terrain_Sprite , F ); end; Procedure AddBasicHillCel( X,Y,F,H: Integer ); { Add a basic hill cel. Actually, this is a whole pile of cels. } var T: Integer; begin AddCMCel( GB , X , Y , LoAlt , CMC_Terrain , Terrain_Sprite , 11 ); for t := 0 to H do AddCMCel( GB , X , Y , T , CMC_Terrain , Terrain_Sprite , F ); AddShadow( X,Y,H ); end; Function DoorSprite( X,Y: Integer ): Integer; { Return the appropriate door sprite for this tile: use either the vertical } { door or the horizontal door. } begin { Calculate the location of the tile directly above this one. } X := X + AngDir[ ScreenDirToMapDir( 6 ) , 1 ]; Y := Y + AngDir[ ScreenDirToMapDir( 6 ) , 2 ]; if OnTheMap( GB , X , Y ) and ( TerrMan[ TileTerrain( GB , X , Y ) ].Altitude > 5 ) then DoorSprite := 16 else DoorSprite := 14; end; const Row_D: Array [0..3,0..1] of Integer = ( (0,1),(-1,0),(0,-1),(1,0) ); Column_D: Array [0..3,0..1] of Integer = ( (1,0),(0,1),(-1,0),(0,-1) ); Layer_Height = 18; var X,Y,Z,X0,Y0,MaxR,MaxC,Row,Column,Terr: Integer; SX,SY,H: LongInt; Frame: Integer; M: GearPtr; MyDest,TexDest: TSDL_Rect; Spr: SensibleSpritePtr; begin { How to find out the proper mouse location- while drawing each sprite, do a check with the } { map coordinates. If we get a second match later on, that supercedes the previous match obviously, } { since we're overwriting something anyways. Brilliance! } { Clear the OFF_MAP_MODELS. } { NOTE: X and Y do not refer to X and Y!!! Coordinates, that is... } { here they're being used as the map border and the icon position. } for X := 1 to 4 do begin for Y := 0 to NumOMM do begin OFF_MAP_MODELS[ X , Y ] := 0; end; end; for X := 1 to GB^.Map_Width do begin for Y := 1 to GB^.Map_Height do begin if TileVisible( GB , X , Y ) then begin Terr := TileTerrain( GB , X , Y ); case Terr of TERRAIN_OpenGround: AddBasicTerrainCel( X , Y , 0 ); TERRAIN_Pavement: AddBasicTerrainCel( X , Y , 2 ); TERRAIN_Swamp: AddBasicTerrainCel( X , Y , 3 ); TERRAIN_L1_Hill: AddBasicHillCel( X , Y , 0 , 1 ); TERRAIN_L2_Hill: AddBasicHillCel( X , Y , 0 , 2 ); TERRAIN_L3_Hill: AddBasicHillCel( X , Y , 0 , 3 ); TERRAIN_RoughGround: AddBasicTerrainCel( X , Y , 4 ); TERRAIN_LowWall: AddBasicHillCel( X , Y , 5 , 1 ); TERRAIN_Wall: AddBasicWallCel( X , Y , 5 ); TERRAIN_Floor: AddBasicTerrainCel( X , Y , 12 ); TERRAIN_Threshold: AddBasicTerrainCel( X , Y , 12 ); TERRAIN_Carpet: AddBasicTerrainCel( X , Y , 6 ); TERRAIN_WoodenFloor: AddBasicTerrainCel( X , Y , 7 ); TERRAIN_WoodenWall: AddBasicWallCel( X , Y , 8 ); TERRAIN_TileFloor: AddBasicTerrainCel( X , Y , 12 ); TERRAIN_GlassWall: AddBasicWallCel( X , Y , 9 ); else AddBasicTerrainCel( X , Y , 0 ); end; end else begin if AlmostSeen( GB , X , Y ) then AddCMCel( GB , X , Y , 0 , CMC_Terrain , Terrain_Sprite , 13 ); end; { Clear the model map here. } for z := LoAlt to ( HiAlt + 1 ) do begin model_map[ X , Y , z ] := Nil; if Names_Above_Heads then CM_ModelNames[ X , Y , Z ] := ''; end; { And the off-map icon. } CM_Cel_OMIcon[ X , Y ] := 0; end; end; { Next add the characters, mecha, and items to the list. } M := GB^.Meks; while M <> Nil do begin if OnTheMap( GB , M ) and MekVisible( GB , M ) then begin X := NAttValue( M^.NA , NAG_Location , NAS_X ); Y := NAttValue( M^.NA , NAG_Location , NAS_Y ); Z := MekAltitude( GB , M ); if Destroyed( M ) then begin { Insert wreckage-drawing code here. } if M^.G = GG_Character then begin AddCMCel( GB , X , Y , Z , CMC_Destroyed , Items_Sprite , Default_Dead_Thing ); end else begin AddCMCel( GB , X , Y , Z , CMC_Destroyed , Items_Sprite , Default_Wreckage ); end; end else if IsMasterGear( M ) then begin { Insert sprite-drawing code here. } AddCMCel( GB , X , Y , Z , CMC_Master , LocateSprite( CuteSpriteName( M ) , SpriteColor( GB , M ) , 50 , 120 ) , ( Animation_Phase div 5 ) mod 2 ); AddCMCel( GB , X , Y , Z , CMC_MShadow , Shadow_Sprite , 6 ); if OnTheMap( GB , X , Y ) and ( Z >= LoAlt ) and ( Z <= HiAlt ) then begin model_map[ X , Y , Z ] := M; if Names_Above_Heads then CM_ModelNames[ X , Y , Z ] := PilotName( M ); end; end else if M^.G = GG_MetaTerrain then begin { Insert MetaTerrain-drawing code here. } case M^.S of GS_MetaDoor: if M^.Stat[ STAT_Pass ] = -100 then AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Terrain_Sprite , DoorSprite( X , Y ) ) else AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Terrain_Sprite , DoorSprite( X , Y ) + 1 ); GS_MetaStairsUp: ; GS_MetaStairsDown: ; GS_MetaTrapdoor: ; GS_MetaElevator: ; GS_MetaBuilding: ; GS_MetaEncounter: ; GS_MetaCloud: ; GS_MetaFire: ; else ; end; end else begin { Draw the yellow-striped box. } AddCMCel( GB , X , Y , Z , CMC_Items , Items_Sprite , 0 ); end; end; M := M^.Next; end; if Focused_On_Mek <> Nil then begin end; { Go through the rows and columns of our screen display. } for Row := -7 to 8 do begin { Draw the terrain for this row. } for Column := -8 to 8 do begin { Calculate the map position for this row,column combo. } X := origin_x + Row_D[ origin_d , 0 ] * Row + Column_D[ origin_d , 0 ] * Column; Y := origin_y + Row_D[ origin_d , 1 ] * Row + Column_D[ origin_d , 1 ] * Column; if OnTheMap( GB , X , Y ) then begin MyDest.X := 380 + Column * 50; MyDest.Y := 200 + Row * 40 - 20 * LoAlt; for Z := LoAlt to HiAlt do begin For Frame := 1 to NumCMCelLayers do begin if CM_Cel_IsOn[ Frame ][ X , Y , Z ] then begin DrawSprite( CM_Cels[ X , Y , Z , Frame ].Sprite , MyDest , CM_Cels[ X , Y , Z , Frame ].F ); end; end; if Names_Above_Heads and ( CM_ModelNames[ X , Y , Z ] <> '' ) then begin TexDest := MyDest; TexDest.X := MyDest.X + 25; TexDest.Y := MyDest.Y + 15; QuickTextC( CM_ModelNames[ X , Y , Z ] , TexDest , StdWhite , Small_Font ); end; MyDest.Y := MyDest.Y - Layer_Height; end; end; end; end; { Do the tactics cursor. } if Tactics_Turn_In_Progess and ( Focused_On_Mek <> Nil ) then begin X := NAttValue( Focused_On_Mek^.NA , NAG_Location , NAS_X ); Y := NAttValue( Focused_On_Mek^.NA , NAG_Location , NAS_Y ); end; end; Procedure Render_Isometric( GB: GameBoardPtr ); { Render the isometric 2D map. } const Altitude_Height = 20; { Pixel height of each altitude layer. } HalfTileWidth = 32; HalfTileHeight = 16; { Terrain cel constants. } TCEL_OpenGround = 0; TCEL_Void = 1; TCEL_Pavement = 2; TCEL_DarkGround = 3; TCEL_RoughGround = 4; TCEL_Wall = 5; TCEL_Carpet = 6; TCEL_WoodenFloor = 7; TCEL_WoodenWall = 8; TCEL_GlassWall = 9; TCEL_Floor = 10; TCEL_LightForest_A = 11; TCEL_HeavyForest_A = 12; TCEL_LightForest_B = 13; TCEL_HeavyForest_B = 14; TCEL_ShortWall = 15; TCEL_Door = 16; TCEL_Threshold = 17; TCEL_Elevator = 18; TCEL_ShortDoor = 19; TCEL_LowHill = 20; TCEL_MediumHill = 21; TCEL_HighHill = 22; { Extras cel constants. } ECEL_Unknown = 0; ECEL_Trapdoor = 1; ECEL_Indicator = 2; ECEL_Item = 3; ECEL_Dead_Thing = 4; ECEL_Wreckage = 5; ECEL_StairsUp = 6; ECEL_StairsDown = 7; ECEL_Fire = 20; ECEL_Smoke = 21; function MapDirToScreenDir( D: Integer ): Integer; { Given an in-game map dir, convert this to a screen dir which } { can be used to render sprites. } begin MapDirToScreenDir := ( D + 9 - Origin_D * 2 ) mod 8; end; Procedure AddShadow( X,Y,Z: Integer ); { For this shadow, we're only concerned about three blocks- the one directly to the left (which } { I'll label #1), the one to the left and above (#2), and the one directly above (#3). You can } { find the right shadow frame by adding +1 if #1 is a wall, +2 if #2 is a wall, and +4 if #3 is } { a wall. The case where #1 and #3 are occupied is the same as if all three tiles were occupied. } { Here's a picture: } { 2 3 } { 1 x <-- X is the target tile. } Function IsHigher( X2,Y2: Integer ): Boolean; var Terr,H2: Integer; begin if OnTheMap( GB , X2 , Y2 ) then begin terr := TileTerrain( GB , X2 , Y2 ); if ( terr <> TERRAIN_LowBuilding ) and ( terr <> TERRAIN_MediumBuilding ) and ( terr <> TERRAIN_HighBuilding ) then begin H2 := TerrMan[ terr ].Altitude; IsHigher := H2 > Z; end else begin IsHigher := False; end; end else begin IsHigher := False; end; end; var Total: Integer; begin Total := 0; if IsHigher( X + AngDir[ ScreenDirToMapDir( 3 ) , 1 ] , Y + AngDir[ ScreenDirToMapDir( 3 ) , 2 ] ) then Total := Total + 1; if IsHigher( X + AngDir[ ScreenDirToMapDir( 4 ) , 1 ] , Y + AngDir[ ScreenDirToMapDir( 4 ) , 2 ] ) then Total := Total + 2; if IsHigher( X + AngDir[ ScreenDirToMapDir( 5 ) , 1 ] , Y + AngDir[ ScreenDirToMapDir( 5 ) , 2 ] ) then Total := Total + 4; if Total = 7 then Total := 5; if Total > 0 then AddCMCel( GB , X , Y , Z , CMC_Shadow , Shadow_Sprite , Total - 1 ); end; Procedure AddBasicFloorCel( X,Y,F: Integer ); { Add a basic terrain cel plus a shadow. } begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , F ); AddShadow( X,Y,0 ); end; Procedure AddBasicTerrainCel( X,Y,F: Integer ); { Add a basic terrain cel without a shadow. } begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , F ); end; Procedure AddBuilding( X,Y,F: Integer ); { Add a basic terrain cel. } begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , TCEL_OpenGround ); AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , building_sprite , F ); end; Procedure AddBasicWallCel( X,Y,F: Integer ); { Add a basic wall cel using F. } begin if Use_Tall_Walls then begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , F ); end else begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , TCEL_ShortWall ); end; end; Procedure AddBasicDoorCel( X,Y,F: Integer ); { Add a door or elevator cel using F. } begin if Use_Tall_Walls then begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , F ); end else begin AddCMCel( GB , X , Y , 0 , CMC_Terrain , terrain_sprite , TCEL_ShortDoor ); end; ClearCMCel( GB , X , Y , 0 , CMC_Shadow ); end; Function DoorSprite( X,Y: Integer ): Integer; { Return the appropriate door sprite for this tile: use either the vertical } { door or the horizontal door. } begin { Calculate the location of the tile directly above this one. } X := X + AngDir[ ScreenDirToMapDir( 6 ) , 1 ]; Y := Y + AngDir[ ScreenDirToMapDir( 6 ) , 2 ]; if OnTheMap( GB , X , Y ) and ( TerrMan[ TileTerrain( GB , X , Y ) ].Altitude > 5 ) then DoorSprite := 16 else DoorSprite := 14; end; Function MapX( VX,VY: Integer ): Integer; { Given virtual point VX,VY return which actual map tile we're } { talking about. } begin if origin_d = 0 then MapX := VX else if origin_d = 1 then MapX := ( GB^.Map_Width - VY + 1 ) else if origin_d = 2 then MapX := ( GB^.Map_Width - VX + 1 ) else MapX := VY; end; Function MapY( VX,VY: Integer ): Integer; { Given virtual point VX,VY return which actual map tile we're } { talking about. } begin if origin_d = 0 then MapY := VY else if origin_d = 1 then MapY := VX else if origin_d = 2 then MapY := ( GB^.Map_Height - VY + 1 ) else MapY := ( GB^.Map_Height - VX + 1 ); end; Function VirtX( MX,MY: Integer ): Integer; { Given map point MX,MY return the virtual screen tile to which } { it will be rendered. } begin if origin_d = 0 then VirtX := MX else if origin_d = 1 then VirtX := MY else if origin_d = 2 then VirtX := ( GB^.Map_Width - MX + 1 ) else VirtX := ( GB^.Map_Height - MY + 1 ); end; Function VirtY( MX,MY: Integer ): Integer; { Given map point MX,MY return the virtual screen tile to which } { it will be rendered. } begin if origin_d = 0 then VirtY := MY else if origin_d = 1 then VirtY := ( GB^.Map_Width - MX + 1 ) else if origin_d = 2 then VirtY := ( GB^.Map_Height - MY + 1 ) else VirtY := MX; end; Function RelativeX( X,Y: Integer ): LongInt; { Return the relative position of tile X,Y. The UpLeft corner } { of tile [1,1] is the origin of our display. } begin RelativeX := ( (X-1) * HalfTileWidth ) - ( (Y-1) * HalfTileWidth ); end; Function RelativeY( X,Y: Integer ): LongInt; { Return the relative position of tile X,Y. The UpLeft corner } { of tile [1,1] is the origin of our display. } begin RelativeY := ( (Y-1) * HalfTileHeight ) + ( (X-1) * HalfTileHeight ); end; Function ScreenX( X,Y: Integer ): LongInt; { Return the screen coordinates of map column X. } begin ScreenX := RelativeX( X - VirtX( Origin_X , Origin_Y ) , Y - VirtY( Origin_X , Origin_Y ) ) + ( ScreenWidth div 2 ); end; Function ScreenY( X,Y: Integer ): Integer; { Return the screen coordinates of map row Y. } begin ScreenY := RelativeY( X - VirtX( Origin_X , Origin_Y ) , Y - VirtY( Origin_X , Origin_Y ) ) + ( ScreenHeight div 2 ); end; Function OnTheScreen( X , Y: Integer ): Boolean; { This function returns TRUE if the specified point is visible } { on screen, FALSE if it isn't. } var SX,SY: LongInt; { Find Screen X and Screen Y and see if it's in the map area. } begin SX := ScreenX( X , Y ); SY := ScreenY( X , Y ); if ( SX >= ( -64 ) ) and ( SX <= ( ScreenWidth ) ) and ( SY >= -64 ) and ( SY <= ( ScreenHeight ) ) then begin OnTheScreen := True; end else begin OnTheScreen := False; end; end; var VX,VY,VX_Max,VY_Max,X,Y,Z,T,Row,Column,Terr,Quad: Integer; M: GearPtr; MyDest,TexDest: TSDL_Rect; Spr: SensibleSpritePtr; begin { Clear the OFF_MAP_MODELS. } { NOTE: X and Y do not refer to X and Y!!! Coordinates, that is... } { here they're being used as the map border and the icon position. } for X := 1 to 4 do begin for Y := 0 to NumOMM do begin OFF_MAP_MODELS[ X , Y ] := 0; end; end; { Fill out the basic terrain cels, and while we're here clear the model map. } for X := 1 to GB^.Map_Width do begin for Y := 1 to GB^.Map_Height do begin if TileVisible( GB , X , Y ) then begin Terr := TileTerrain( GB , X , Y ); case Terr of TERRAIN_OpenGround: AddBasicFloorCel( X , Y , TCEL_OpenGround ); TERRAIN_LightForest: begin AddBasicTerrainCel( X , Y , TCEL_LightForest_A ); AddCMCel( GB , X , Y , 0 , CMC_Toupee , Terrain_Sprite , TCEL_LightForest_B ); end; TERRAIN_HeavyForest: begin AddBasicTerrainCel( X , Y , TCEL_HeavyForest_A ); AddCMCel( GB , X , Y , 0 , CMC_Toupee , Terrain_Sprite , TCEL_HeavyForest_B ); end; TERRAIN_Rubble: AddBasicFloorCel( X , Y , TCEL_RoughGround ); TERRAIN_Pavement: AddBasicFloorCel( X , Y , TCEL_Pavement ); TERRAIN_Swamp: AddBasicTerrainCel( X , Y , TCEL_DarkGround ); TERRAIN_L1_Hill: AddBasicTerrainCel( X , Y , TCEL_LowHill ); TERRAIN_L2_Hill: AddBasicTerrainCel( X , Y , TCEL_MediumHill ); TERRAIN_L3_Hill: AddBasicTerrainCel( X , Y , TCEL_HighHill ); TERRAIN_RoughGround: AddBasicTerrainCel( X , Y , TCEL_RoughGround ); TERRAIN_LowWall: AddBasicWallCel( X , Y , TCEL_Wall ); TERRAIN_Wall: AddBasicWallCel( X , Y , TCEL_Wall ); TERRAIN_Floor: AddBasicFloorCel( X , Y , TCEL_Floor ); TERRAIN_Threshold: AddBasicFloorCel( X , Y , TCEL_Threshold ); TERRAIN_Carpet: AddBasicFloorCel( X , Y , TCEL_Carpet ); TERRAIN_WoodenFloor: AddBasicFloorCel( X , Y , TCEL_WoodenFloor ); TERRAIN_WoodenWall: AddBasicWallCel( X , Y , TCEL_WoodenWall ); TERRAIN_TileFloor: AddBasicFloorCel( X , Y , TCEL_Floor ); TERRAIN_Space: AddCMCel( GB , X , Y , 0 , CMC_Terrain , Terrain_Sprite , TCEL_Void ); TERRAIN_MediumBuilding: AddBuilding( X , Y , ( ( X * 17 ) + ( Y * 71 ) ) mod 4 + 1 ); TERRAIN_HighBuilding: AddBuilding( X , Y , ( ( X * 17 ) + ( Y * 71 ) ) mod 4 + 5 ); TERRAIN_GlassWall: AddBasicWallCel( X , Y , TCEL_GlassWall ); TERRAIN_LowBuilding: AddBuilding( X , Y , ( ( X * 17 ) + ( Y * 71 ) ) mod 4 + 15 ); else AddBasicTerrainCel( X , Y , TCEL_Void ); end; end else begin if AlmostSeen( GB , X , Y ) then AddCMCel( GB , X , Y , 0 , CMC_Terrain , Extras_Sprite , ECEL_Unknown ); end; { Clear the model map here. } for z := LoAlt to ( HiAlt + 1 ) do begin model_map[ X , Y , z ] := Nil; if Names_Above_Heads then CM_ModelNames[ X , Y , Z ] := ''; end; { And the off-map icon. } CM_Cel_OMIcon[ X , Y ] := 0; end; end; { Next add the characters, mecha, and items to the list. } M := GB^.Meks; while M <> Nil do begin if OnTheMap( GB , M ) and MekVisible( GB , M ) then begin X := NAttValue( M^.NA , NAG_Location , NAS_X ); Y := NAttValue( M^.NA , NAG_Location , NAS_Y ); Z := MekAltitude( GB , M ); if Destroyed( M ) then begin { Insert wreckage-drawing code here. } if M^.G = GG_Character then begin AddCMCel( GB , X , Y , Z , CMC_Destroyed , Extras_Sprite , ECEL_Dead_Thing ); end else begin AddCMCel( GB , X , Y , Z , CMC_Destroyed , Extras_Sprite , ECEL_Wreckage ); end; end else if IsMasterGear( M ) then begin { Insert sprite-drawing code here. } AddCMCel( GB , X , Y , Z , CMC_Master , LocateSprite( SpriteName( GB , M ) , SpriteColor( GB , M ) , 64 , 64 ), MapDirToScreenDir( NAttValue( M^.NA , NAG_Location , NAS_D ) ) ); { Also add a shadow. } AddCMCel( GB , X , Y , TerrMan[ TileTerrain( gb , X , Y ) ].Altitude , CMC_MShadow , Shadow_Sprite , 6 ); { If appropriate, save the model map and model name. } if OnTheMap( GB , X , Y ) and ( Z >= LoAlt ) and ( Z <= HiAlt ) then begin model_map[ X , Y , Z ] := M; if Names_Above_Heads and ( M^.G <> GG_Prop ) then CM_ModelNames[ X , Y , Z ] := PilotName( M ); { Also record the off-map icon. } if AreAllies( GB , NAV_DefPlayerTeam , NAttValue( M^.NA , NAG_Location , NAS_Team ) ) then begin cm_cel_OMIcon[ X , Y ] := SI_Ally; { Mini_Map[ X , Y ] := 5;} end else if AreEnemies( GB , NAV_DefPlayerTeam , NAttValue( M^.NA , NAG_Location , NAS_Team ) ) then begin cm_cel_OMIcon[ X , Y ] := SI_Enemy; { Mini_Map[ X , Y ] := 1;} end else begin cm_cel_OMIcon[ X , Y ] := SI_Neutral; { Mini_Map[ X , Y ] := 3;} end; end; end else if M^.G = GG_MetaTerrain then begin { Insert MetaTerrain-drawing code here. } case M^.S of GS_MetaDoor: if M^.Stat[ STAT_Pass ] = -100 then AddBasicDoorCel( X , Y , TCEL_Door ); GS_MetaStairsUp: AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Extras_Sprite , ECEL_StairsUp ); GS_MetaStairsDown: AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Extras_Sprite , ECEL_StairsDown ); GS_MetaTrapdoor: AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Extras_Sprite , ECEL_Trapdoor ); GS_MetaElevator: AddBasicDoorCel( X , Y , TCEL_Elevator ); GS_MetaBuilding: begin AddBuilding( X , Y , NAttValue( M^.NA , NAG_MTAppearance , NAS_BuildingMesh ) ); if OnTheMap( GB , X , Y ) and Names_Above_Heads then CM_ModelNames[ X , Y , 1 ] := GearName( M ); end; GS_MetaEncounter: begin AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Encounter_Sprite , ( M^.Stat[ STAT_EncounterType ] mod 3 ) * 3 + ( ( Animation_Phase div 5 ) mod 2 ) ); if OnTheMap( GB , X , Y ) and Names_Above_Heads then CM_ModelNames[ X , Y , 0 ] := GearName( M ); end; GS_MetaCloud: AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Extras_Sprite , ECEL_Smoke ); GS_MetaFire: AddCMCel( GB , X , Y , 0 , CMC_MetaTerrain , Extras_Sprite , ECEL_Fire ); else AddCMCel( GB , X , Y , Z , CMC_MetaTerrain , LocateSprite( SpriteName( GB , M ) , SpriteColor( GB , M ) , 64 , 64 ) , MapDirToScreenDir( NAttValue( M^.NA , NAG_Location , NAS_D ) ) ); end; end else begin { Draw the yellow-striped box. } AddCMCel( GB , X , Y , Z , CMC_Items , Extras_Sprite , ECEL_Item ); end; end; M := M^.Next; end; { Go through each tile on the map, displaying terrain and } { other contents. } DrawBackdrop; TexDest.W := 64; TexDest.H := 15; { We need to calculate the virtual X and Y maximums. } { If origin_d is even it's the X axis running down the right from the } { top tile and the Y axis runs to the left. If origin_d is odd, then } { the opposite is true. } if ( ( origin_d mod 2 ) = 0 ) then begin VX_Max := GB^.Map_Width; VY_Max := GB^.Map_Height; end else begin VX_Max := GB^.Map_Height; VY_Max := GB^.Map_Width; end; for VX := 1 to VX_Max do begin for VY := 1 to VY_Max do begin { Determine the map X,Y coordinates that this virtual } { point is pointing to. } X := MapX( VX , VY ); Y := MapY( VX , VY ); if OnTheScreen( VX , VY ) then begin for Z := LoAlt to HiAlt do begin for t := 0 to NumCMCelLayers do begin if CM_Cel_IsOn[ T ][ X , Y , Z ] then begin MyDest.X := ScreenX( VX , VY ); MyDest.Y := ScreenY( VX , VY ) - Altitude_Height * Z; if CM_Cels[ X ,Y , Z , T ].Sprite^.H > 64 then MyDest.Y := MyDest.Y - 32; DrawSprite( CM_Cels[ X ,Y , Z , T ].Sprite , MyDest , CM_Cels[ X ,Y , Z , T ].F ); end; end; if Names_Above_Heads and ( CM_ModelNames[ X , Y , Z ] <> '' ) then begin TexDest.X := ScreenX( VX , VY ); TexDest.Y := ScreenY( VX , VY ) - Altitude_Height * Z; QuickTextC( CM_ModelNames[ X , Y , Z ] , TexDest , StdWhite , Small_Font ); end; end; { For Z... } end else if OnTheMap( GB , X , Y ) and ( CM_Cel_OMIcon[ X , Y ] >= 1 ) and ( CM_Cel_OMIcon[ X , Y ] <= 3 ) then begin { This image is off the map, but has a substitute image } { so it should be indicated on the edge. } { Add a note to the OFF_MAP_MODELS array. } { Figure out its relative coordinates, with the center of the map } { as the origin. } { ***NOTE*** Lifted directly from GH1, don't fully understand } { everything going on here, let's hope it works. } MyDest.X := ScreenX( VX , VY ) - ( ScreenWidth div 2 ); MyDest.Y := ScreenY( VX , VY ) - ( ScreenHeight div 2 ); { Use W to save the segment total length, and H to store the } { relative length. } Quad := 1; if MyDest.Y <= MyDest.X then Quad := Quad + 1; if MyDest.Y <= -MyDest.X then Quad := Quad + 2; if ( Quad = 1 ) or ( Quad = 4 ) then begin MyDest.W := Abs( MyDest.Y ) * 2; MyDest.H := MyDest.X + Abs( MyDest.Y ); end else begin MyDest.W := Abs( MyDest.X ) * 2; MyDest.H := MyDest.Y + Abs( MyDest.X ); end; OFF_MAP_MODELS[ Quad , ( MyDest.H * NumOMM ) div MyDest.W ] := CM_Cel_OMIcon[ X , Y ]; end; { if OnTheScreen... } end; end; { We don't draw the off-map models yet, because they're getting stuck on top of } { everything else later on. } end; Procedure RenderMap( GB: GameBoardPtr ); { Render the location stored in G_Map, along with all items and characters on it. } { Also save the position of the mouse pointer, in world coordinates. } { I'm going to use the GH1 method for doing this- create a list of cels first containing all the } { terrain, mecha, and effects to be displayed. Then, render them. There's something I don't like } { about this method but I don't remember what, and it seems to be more efficient than searching } { through the list of models once per tile once per elevation level. } var X,Y,Z: Integer; M: GearPtr; begin { How to find out the proper mouse location- while drawing each sprite, do a check with the } { map coordinates. If we get a second match later on, that supercedes the previous match obviously, } { since we're overwriting something anyways. Brilliance! } ClrScreen; { Clear the basic cels- the ones that the map renderer has access to. There will be additional } { layers which the map renderer shouldn't touch. } for X := 1 to NumBasicCelLayers do ClearCMCelLayer( X ); if Use_Isometric_Mode then begin Render_Isometric( GB ); end else begin Render_Cute( GB ); end; end; Procedure FocusOn( Mek: GearPtr ); { Focus on the provided mecha. } begin if Mek <> Nil then begin ClearCMCelLayer( CMC_Effects ); origin_x := NAttValue( Mek^.NA , NAG_Location , NAS_X ); origin_y := NAttValue( Mek^.NA , NAG_Location , NAS_Y ); end; Focused_On_Mek := Mek; end; Procedure IndicateTile( GB: GameBoardPtr; X , Y , Z: Integer ); { Indicate the requested tile. } begin ClearCMCelLayer( CMC_Effects ); if OnTheMap( GB , X , Y ) then begin origin_x := x; origin_y := y; if ( Z >= LoAlt ) and ( Z <= HiAlt ) then AddCMCel( GB , X , Y , Z , CMC_Effects , Extras_Sprite , 2 ); end; end; Procedure DisplayMiniMap( GB: GameBoardPtr ); { Draw the mini-map. } const ZONE_MiniMap: TSDL_Rect = ( X:15; Y: 15; W: 300; H: 300 ); var MyDest: TSDL_Rect; X,Y: Integer; M: GearPtr; begin ZONE_MiniMap.W := GB^.MAP_Width * 3; ZONE_MiniMap.H := GB^.MAP_Height * 3; SDL_FillRect( game_screen , @ZONE_MiniMap , SDL_MapRGBA( Game_Screen^.Format , 0 , 0 , 255 , 150 ) ); for x := 1 to GB^.MAP_Width do begin for y := 1 to GB^.MAP_Height do begin MyDest.X := ZONE_MiniMap.X - 3 + X*3; MyDest.Y := ZONE_MiniMap.Y - 3 + Y*3; DrawSprite( Mini_Map_Sprite , MyDest , TileTerrain( GB , X , Y ) + 10 ); end; end; M := GB^.Meks; while M <> Nil do begin if IsMasterGear( M ) and MekVisible( GB , M ) and GearActive( M ) then begin X := NAttValue( M^.NA , NAG_Location , NAS_X ); Y := NAttValue( M^.NA , NAG_Location , NAS_Y ); MyDest.X := ZONE_MiniMap.X - 3 + X*3; MyDest.Y := ZONE_MiniMap.Y - 3 + Y*3; if AreAllies( GB , NAV_DefPlayerTeam , NAttValue( M^.NA , NAG_Location , NAS_Team ) ) then begin DrawSprite( Mini_Map_Sprite , MyDest , 5 + ( Animation_Phase div 5 mod 2 ) ); end else if AreEnemies( GB , NAV_DefPlayerTeam , NAttValue( M^.NA , NAG_Location , NAS_Team ) ) then begin DrawSprite( Mini_Map_Sprite , MyDest , 1 + ( Animation_Phase div 5 mod 2 ) ); end else begin DrawSprite( Mini_Map_Sprite , MyDest , 3 + ( Animation_Phase div 5 mod 2 ) ); end; DrawSprite( Mini_Map_Sprite , MyDest , 1 + ( Animation_Phase div 5 mod 2 ) ); end; M := M^.Next; end; end; Procedure ScrollMap( GB: GameBoardPtr ); { Asjust the position of the map origin. } begin if ( RK_KeyState[ SDLK_Delete ] = 1 ) then begin origin_d := ( origin_d + 1 ) mod Num_Rotation_Angles; end else if ( RK_KeyState[ SDLK_Insert ] = 1 ) then begin origin_d := ( origin_d + Num_Rotation_Angles - 1 ) mod Num_Rotation_Angles; end; end; Procedure ClearOverlays; { Erase all overlays currently on the screen. } begin ClearCMCelLayer( CMC_Effects ); end; Procedure AddOverlay( GB: GameBoardPtr; OL_Sprite: SensibleSpritePtr; X , Y , Z, F: Integer ); { Add an overlay to the screen. } begin AddCMCel( GB , X , Y , Z , CMC_Effects , OL_Sprite , F ); end; Function ProcessShotAnimation( GB: GameBoardPtr; var AnimList,AnimOb: GearPtr ): Boolean; { Process this shot. Return TRUE if the missile } { is visible on the screen, FALSE otherwise. } { V = Timer } { Stat 1 , 2 , 3 -> X1 , Y1 , Z1 } { Stat 4 , 5 , 6 -> X2 , Y2 , Z2 } const X1 = 1; Y1 = 2; Z1 = 3; X2 = 4; Y2 = 5; Z2 = 6; var P: Point; begin { Increase the counter, and find the next spot. } Inc( AnimOb^.V ); P := SolveLine( AnimOb^.Stat[ X1 ] , AnimOb^.Stat[ Y1 ] , AnimOb^.Stat[ Z1 ] , AnimOb^.Stat[ X2 ] , AnimOb^.Stat[ Y2 ] , AnimOb^.Stat[ Z2 ] , AnimOb^.V ); { If this is the destination point, then we're done. } if ( P.X = AnimOb^.Stat[ X2 ] ) and ( P.Y = AnimOb^.Stat[ Y2 ] ) then begin RemoveGear( AnimList , ANimOb ); P.X := 0; { If this is not the destination point, draw the missile. } end else begin {Display bullet...} AddOverlay( GB , Strong_Hit_Sprite , P.X , P.Y , P.Z , 0 ); end; ProcessShotAnimation := True; end; Function ProcessPointAnimation( GB: GameBoardPtr; var AnimList,AnimOb: GearPtr ): Boolean; { Process this effect. Return TRUE if the blast } { is visible on the screen, FALSE otherwise. } { V = Timer } { Stat 1 , 2 , 3 -> X , Y , Z } const X = 1; Y = 2; Z = 3; var it: Boolean; begin if AnimOb^.V < 10 then begin case AnimOb^.S of GS_DamagingHit: begin AddOverlay( GB , Strong_Hit_Sprite , AnimOb^.Stat[ X ] , AnimOb^.Stat[ Y ] , AnimOb^.Stat[ Z ] , AnimOb^.V ); end; GS_ArmorDefHit: begin AddOverlay( GB , Weak_Hit_Sprite , AnimOb^.Stat[ X ] , AnimOb^.Stat[ Y ] , AnimOb^.Stat[ Z ] , AnimOb^.V ); end; GS_Parry,GS_Block,GS_Intercept,GS_Resist: begin AddOverlay( GB , Parry_Sprite , AnimOb^.Stat[ X ] , AnimOb^.Stat[ Y ] , AnimOb^.Stat[ Z ] , AnimOb^.V ); Inc( AnimOb^.V ); end; GS_Dodge,GS_ECMDef: begin AddOverlay( GB , Miss_Sprite , AnimOb^.Stat[ X ] , AnimOb^.Stat[ Y ] , AnimOb^.Stat[ Z ] , AnimOb^.V ); Inc( AnimOb^.V ); end; GS_Backlash: begin AddOverlay( GB , Strong_Hit_Sprite , AnimOb^.Stat[ X ] , AnimOb^.Stat[ Y ] , AnimOb^.Stat[ Z ] , AnimOb^.V ); end; GS_AreaAttack: begin AddOverlay( GB , Strong_Hit_Sprite , AnimOb^.Stat[ X ] , AnimOb^.Stat[ Y ] , AnimOb^.Stat[ Z ] , AnimOb^.V ); end; end; { Increment the counter. } Inc( AnimOb^.V ); it := True; end else begin RemoveGear( AnimList , AnimOb ); it := False; end; ProcessPointAnimation := it; end; Procedure RenderWorldMap( GB: GameBoardPtr; PC: GearPtr; X0,Y0: Integer ); { Render the world map. X0,Y0 is the center tile. } var DX,DY,X,Y: Integer; MyDest: TSDL_Rect; MySprite: SensibleSpritePtr; M: GearPtr; begin ClrZone( ZONE_WorldMap ); MyDest.W := 64; MyDest.H := 64; { First, render the terrain. } for DX := -2 to 2 do begin for DY := -2 to 2 do begin X := X0 + DX; Y := Y0 + DY; FixWorldCoords( GB^.Scene , X , Y ); MyDest.X := ZONE_WorldMap.X + ( DX + 2 ) * 64; MyDest.Y := ZONE_WorldMap.Y + ( DY + 2 ) * 64; if OnTheMap( GB , X , Y ) then begin DrawSprite( World_Terrain , MyDest , TileTerrain( GB , X , Y ) - 1 ); end; end; end; { Next, draw any metaterrain that may be on the map. } M := GB^.Meks; while M <> Nil do begin if ( M^.G = GG_MetaTerrain ) and ( M^.Stat[ STAT_MetaVisibility ] = 0 ) and OnTheMap( GB , M ) then begin DX := NAttValue( M^.NA , NAG_Location , NAS_X ) - X0; if WorldWrapsX( GB^.Scene ) and ( DX < -2 ) then DX := DX + GB^.Map_Width; DY := NAttValue( M^.NA , NAG_Location , NAS_Y ) - Y0; if WorldWrapsY( GB^.Scene ) and ( DY < -2 ) then DY := DY + GB^.Map_Height; if ( DX >= -2 ) and ( DX <= 2 ) and ( DY >= -2 ) and ( DY <= 2 ) then begin MyDest.X := ZONE_WorldMap.X + ( DX + 2 ) * 64; MyDest.Y := ZONE_WorldMap.Y + ( DY + 2 ) * 64; MySprite := LocateSprite( SpriteName( Nil , M ) , SpriteColor( GB , M ) , 64 , 64 ); DrawSprite( MySprite , MyDest , NAttValue( M^.NA , NAG_Display , NAS_PrimaryFrame ) ); end; end; M := M^.Next; end; { Finally, draw the little crosshair in the middle to indicate the party poistion. } if PC <> Nil then begin MyDest.X := ZONE_WorldMap.X + 128; MyDest.Y := ZONE_WorldMap.Y + 128; MySprite := LocateSprite( SpriteName( Nil , PC ) , SpriteColor( GB , PC ) , 64 , 64 ); DrawSprite( MySprite , MyDest , 1 ); end; end; Procedure InitGraphicsForScene( GB: GameBoardPtr ); { Initialize the graphics for this scene. Make sure the correct tilesets are loaded. } const NumBackdrops = 1; Backdrop_FName: Array [1..NumBackdrops] of String = ( 'bg_space.png' ); iso_tileset_fname: Array [0..NumTileSet] of String = ( 'iso_terrain_default.png', 'iso_terrain_rocky.png','iso_terrain_default.png','iso_terrain_industrial.png','iso_terrain_default.png' ); var TileSet,BDNum: Integer; begin if Terrain_Sprite <> Nil then RemoveSprite( Terrain_Sprite ); if Shadow_Sprite <> Nil then RemoveSprite( Shadow_Sprite ); if Building_Sprite <> Nil then RemoveSprite( Building_Sprite ); if Extras_Sprite <> Nil then RemoveSprite( Extras_Sprite ); if Use_Isometric_Mode then begin if GB^.Scene <> Nil then TileSet := NAttValue( GB^.Scene^.NA , NAG_SceneData , NAS_TileSet ) else TileSet := NAV_DefaultTiles; if ( TileSet > NumTileSet ) or ( TileSet < 0 ) then TileSet := NAV_DefaultTiles; Terrain_Sprite := LocateSprite( iso_tileset_fname[ TileSet ] , 64 , 96 ); Shadow_Sprite := LocateSprite( 'iso_shadows_noalpha.png' , 64 , 96 ); Building_Sprite := LocateSprite( 'iso_buildings.png' , 64, 96 ); Extras_Sprite := LocateSprite( 'iso_extras.png' , 64, 96 ); end else begin Terrain_Sprite := LocateSprite( 'cute_terrain.png' , 50 , 120 ); Shadow_Sprite := LocateSprite( 'c_shadows_noalpha.png' , 50 , 120 ); Building_Sprite := LocateSprite( 'iso_buildings.png' , 64, 96 ); end; { Also set the backdrop. } if Current_Backdrop <> Nil then RemoveSprite( Current_Backdrop ); if GB^.Scene <> Nil then begin BDNum := NAttValue( GB^.Scene^.NA , NAG_SceneData , NAS_Backdrop ); if ( BDNum > 0 ) and ( BDNum <= NumBackdrops ) then begin Current_Backdrop := LocateSprite( Backdrop_FName[ BDNum ] , Backdrop_Size , Backdrop_Size ); end; end; { Also clear the cel layers, to prevent crashage. } For BDNum := 1 to NumCMCelLayers do ClearCMCelLayer( BDNum ); end; initialization RPGKey; SDL_PumpEvents; SDL_GetMouseState( Mouse_X , Mouse_Y ); tile_x := 1; tile_y := 1; origin_d := 0; Mini_Map_Sprite := LocateSprite( 'minimap.png' , 3 , 3 ); World_Terrain := LocateSprite( 'world_terrain.png' , 64 , 64 ); Terrain_Sprite := Nil; Shadow_Sprite := Nil; Building_Sprite := Nil; Current_Backdrop := Nil; Extras_Sprite := Nil; Items_Sprite := LocateSprite( Items_Sprite_Name , 50 , 120 ); Off_Map_Model_Sprite := LocateSprite( 'off_map.png' , 16 , 16 ); Strong_Hit_Sprite := LocateSprite( Strong_Hit_Sprite_Name , 64, 64 ); Weak_Hit_Sprite := LocateSprite( Weak_Hit_Sprite_Name , 64, 64 ); Parry_Sprite := LocateSprite( Parry_Sprite_Name , 64, 64 ); Miss_Sprite := LocateSprite( Miss_Sprite_Name , 64, 64 ); Encounter_Sprite := LocateSprite( 'encounter_64.png' , 64, 64 ); ClearOverlays; Focused_On_Mek := Nil; end. GH2/ghprop.pp0000644000175000017500000001643311326004556011745 0ustar kaolkaolunit ghprop; { What do props do? Well, not much by themselves... But they } { can be used to make buildings, safes, machinery, or whatever } { else you can think to do with them. } { Metaterrain acts basically like terrain- it can hinder movement or block line } { of sight. As a gear, it can have scripts associated with it. } { 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,gears,ui4gh; { PROP DEFINITION } { G => GG_Prop } { S => Prop Behavior } { V => Prop Size; translates to mass and damage. } { METATERRAIN DEFINITION } { G => GG_MetaTerrain } { S => Specific Type, 0 = Generic } { V => Terrain Size; translates to armor and damage. } { if MetaTerrain V = 0, cannot be destroyed. } const { Please note that a metaterrain gear does not need to have } { its "S" value within the 1..NumBasicMetaTerrain range, } { but those which do lie within this range will be initialized } { with the default scripts. } NumBasicMetaTerrain = 11; GS_MetaDoor = 1; GS_MetaCloud = 2; GS_MetaStairsUp = 3; GS_MetaStairsDown = 4; GS_MetaElevator = 5; GS_MetaTrapDoor = 6; GS_MetaRubble = 7; GS_MetaSign = 8; GS_MetaFire = 9; GS_MetaBuilding = 10; GS_MetaEncounter = 11; STAT_Altitude = 1; STAT_Obscurement = 2; STAT_Pass = 3; STAT_Destination = 4; STAT_MetaVisibility = 5; { If nonzero, this terrain can't be seen. } STAT_Lock = 6; STAT_CloudDuration = 7; { Used only by METACLOUD gears. } STAT_EncounterMove = 7; { Used only be ENCOUNTER gears; % chance it will move. } STAT_EncounterType = 8; { Determines the color of the encounter blip. } ENCOUNTER_Hostile = 0; ENCOUNTER_Defense = 1; ENCOUNTER_NonCombat = 2; { Prop Stats } STAT_PropMesh = 5; { Does this prop use a custom mesh? } { STAT_Lock is the same as above- used by treasure chests. } MESH_Bunker = 1; MESH_Pillbox = 2; MESH_VideoGame = 3; MESH_BigBox = 4; MESH_ShipBody = 5; MESH_ShipWedge = 6; MESH_ShipCurve = 7; MESH_ShipTower = 8; MESH_ShipEngine = 9; MESH_ShopShelf = 10; MESH_Crate = 11; MESH_EndTable = 12; MESH_Bed = 13; NAG_MTAppearance = 19; { Holds some appearance info for metaterrain } NAS_BuildingMesh = 1; { What mesh to use for building? } NAV_Default = 0; { Default building mesh. } NAV_Spaceport = 9; { Spaceport mesh } GS_BasicProp = 0; { Doesn't do anything. } GS_CombatProp = 1; { Will fire weapons at enemies, as appropriate. } { *** MAP FEATURE DEFINITION *** } { G = GG_MapFeature } { S = Feature Type } { V = Feature Value } GS_Building = -1; STAT_XPos = 1; STAT_YPos = 2; STAT_MFWidth = 3; STAT_MFHeight = 4; STAT_MFFloor = 5; STAT_MFMarble = 6; STAT_MFBorder = 7; STAT_MFSpecial = 8; MapFeatureMaxWidth = 25; MapFeatureMaxHeight = 15; MapFeatureMinDimension = 5; var { This array holds the scripts. } Meta_Terrain_Scripts: Array [1..NumBasicMetaTerrain] of SAttPtr; Procedure CheckPropRange( Part: GearPtr ); Procedure InitMetaTerrain( Part: GearPtr ); Procedure InitMapFeature( Part: GearPtr ); Function RandomBuildingName( B: GearPtr ): String; implementation Procedure CheckPropRange( Part: GearPtr ); { Examine the various bits of this gear to make sure everything } { is all nice and legal. } begin { Check V - Size Category } if Part^.V < 1 then Part^.V := 1 else if Part^.V > 100 then Part^.V := 100; end; Procedure InitMetaTerrain( Part: GearPtr ); { Initialize this metaterrain gear for a nice default example of } { the terrain type it's supposed to represent. } begin { If this is a part for which we have a standard script, } { install that script now. } if ( Part^.S >= 1 ) and ( Part^.S <= NumBasicMetaTerrain ) then begin SetSAtt( Part^.SA , 'ROGUECHAR <' + SAttValue( Meta_Terrain_Scripts[ Part^.S ] , 'roguechar' ) + '>' ); SetSAtt( Part^.SA , 'NAME <' + SAttValue( Meta_Terrain_Scripts[ Part^.S ] , 'NAME' ) + '>' ); end; { Do part-specific initializations here. } if Part^.S = GS_MetaDoor then begin { Begin with the stats for a closed door. } Part^.Stat[ STAT_Pass ] := -100; Part^.Stat[ STAT_Altitude ] := 6; end else if Part^.S = GS_MetaStairsUp then begin Part^.Stat[ STAT_Pass ] := -100; Part^.Stat[ STAT_Altitude ] := 1; Part^.Stat[ STAT_Obscurement ] := 1; end else if Part^.S = GS_MetaElevator then begin Part^.Stat[ STAT_Pass ] := -100; Part^.Stat[ STAT_Altitude ] := 6; end else if Part^.S = GS_MetaBuilding then begin Part^.Stat[ STAT_Pass ] := -100; end else if ( Part^.S = GS_MetaRubble ) or ( Part^.S = GS_MetaSign ) then begin Part^.Stat[ STAT_Pass ] := -100; Part^.Stat[ STAT_Altitude ] := 1; Part^.Stat[ STAT_Obscurement ] := 1; end; end; Procedure InitMapFeature( Part: GearPtr ); { This procedure does only one thing- if the part has a minimap defined, } { make sure it's at least a 5x5 area. } begin if ( part <> Nil ) and ( SAttValue( Part^.SA , 'MINIMAP' ) <> '' ) then begin if Part^.Stat[ STAT_MFWidth ] < 5 then Part^.Stat[ STAT_MFWidth ] := 5; if Part^.Stat[ STAT_MFHeight ] < 5 then Part^.Stat[ STAT_MFHeight ] := 5; end; end; Procedure LoadMetaScripts; { Load the metascripts from disk. } var T: Integer; begin for t := 1 to NumBasicMetaTerrain do begin Meta_Terrain_Scripts[ t ] := LoadStringList( MetaTerrain_File_Base + BStr( T ) + Default_File_Ending ); end; end; Procedure ClearMetaScripts; { Free the metascripts from memory. } var T: Integer; begin for t := 1 to NumBasicMetaTerrain do begin DisposeSAtt( Meta_Terrain_Scripts[ t ] ); end; end; Function RandomBuildingName( B: GearPtr ): String; { Create a random name for the provided building. } { Replace %b with the basic building name. } { Replace %a with an adjective. } { Replace %n with an ordinal number. } const NumNameForms = 3; NumAdjectives = 5; NumOridinals = 5; var it: String; begin it := MSgString( 'GHPROP_RBN_FORM_' + BStr( Random( NumNameForms ) + 1 ) ); if B = Nil then begin ReplacePat( it , '%b' , 'NoBuildingError' ); end else begin ReplacePat( it , '%b' , SAttValue( B^.SA , 'NAME' ) ); end; ReplacePat( it , '%a' , MSgString( 'GHPROP_RBN_Adjective_' + BStr( Random( NumAdjectives ) + 1 ) ) ); ReplacePat( it , '%n' , MSgString( 'GHPROP_RBN_Ordinal_' + BStr( Random( NumOridinals ) + 1 ) ) ); RandomBuildingName := it; end; initialization LoadMetaScripts; finalization ClearMetaScripts; end. GH2/description.pp0000644000175000017500000005060611365256066013001 0ustar kaolkaolunit 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 ' + LowerCase( MsgString( 'GenderName_' + BStr( NAttValue( NPC^.NA , NAG_CharDescription , NAS_Gender ) ) ) ); 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. GH2/randmaps.pp0000644000175000017500000033454011374513724012262 0ustar kaolkaolunit RandMaps; { ******************************* } { *** NEW SPECIFICATIONS *** } { ******************************* } { Every feature, both the SCENE gear and the MAP FEATURES, } { needs three SAtts defined: PARAM describes the rendering } { parameters to be sent to the actual drawing routine, while } { SELECTOR holds the parameters to be sent to the sub-area } { selection routine. GAPFILL describes how to plug empty spaces. } { If these strings are not defined in the scene/feature gear, } { a default value is obtained from the GameData/randmaps.txt file } { based upon the scene/feature's listed style. } { All map features are to be recursive. Bottom level features } { fit into the SCENE gear. } { 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,playwright; const DEFAULT_FLOOR_TYPE = 20; DEFAULT_WALL_TYPE = 23; SPECIAL_ShowAll = 'SHOWALL'; SPECIAL_ConvertDoors = 'CELL'; SPECIAL_Unchartable = 'UNCHARTABLE'; DW_Horizontal = 1; DW_Vertical = 2; { NAtts for components. } NAG_ComponentDesc = -8; NAS_CompUID = 1; NAS_ELementID = 2; { Used by the minimap to identify components. } { CELL FORMAT: X Y W H D } { Direction 0 means that the minimap will have the same orientation as } { when drawn out in a design file; dirs 1 through 3 rotate 90 degrees clockwise. } { Predrawn maps come with gears that describe their cells. } STAT_PDMC_X = 1; STAT_PDMC_Y = 2; STAT_PDMC_W = 3; STAT_PDMC_H = 4; STAT_PDMC_D = 5; var High_Component_ID: Integer; Function LoadPredrawnMap( const FName: String ): GameBoardPtr; Procedure SavePredrawnMap( GB: GameBoardPtr; const FName: String ); Function NewSubZone( MF: GearPtr ): GearPtr; Procedure DrawMiniMap( GB: GameBoardPtr; MF: GearPtr; MapDesc: String; D: Integer ); { WARNING: AddContent is for internal use only!!! } Function AddContent( CType: String; GB: GameboardPtr; Source,Zone: GearPtr; P: String; var Cells: SAttPtr; SCheck,STerr: Integer ): Boolean; function RandomMap( Scene: GearPtr ): GameBoardPtr; implementation uses gearutil,ghprop,rpgdice,texutil,gearparser,narration,ui4gh,arenascript,ghchars,sysutils,ability, {$IFDEF ASCII} vidgfx; {$ELSE} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$ENDIF} var Standard_Param_List: SAttPtr; random_scene_content,super_prop_list: GearPtr; UniqueZoneNum: Integer; Function LoadPredrawnMap( const FName: String ): GameBoardPtr; { Load the requested map feature from disk. If no such map can be found, } { return a blank map instead. } var F: Text; W,H: Integer; it: GameBoardPtr; begin if FileExists( Series_Directory + FName ) then begin Assign( F , Series_Directory + FName ); Reset( F ); ReadLn( F , W ); ReadLn( F , H ); it := NewMap( W , H ); it^.Map := ReadMap( F , W , H ); it^.Meks := ReadCGears( F ); Close( F ); end else begin it := NewMap( 50 , 50 ); end; LoadPredrawnMap := it; end; Procedure SavePredrawnMap( GB: GameBoardPtr; const FName: String ); { Save this map to disk using the provided filename. } var F: Text; begin Assign( F , Series_Directory + FName ); Rewrite( F ); { Width and height get written first. } writeln( F , GB^.Map_Width ); writeln( F , GB^.Map_Height ); { Write the map data. This part should be easy. } WriteMap( GB^.Map , F ); { Write the cell data. This part may take a bit more work. } WriteCGears( F , GB^.Meks ); Close( F ); end; Function IsLegalTerrain( T: Integer ): Boolean; { Return TRUE if T is a legal terrain type, or FALSE otherwise. } begin IsLegalTerrain := ( T >= 1 ) and ( T <= NumTerr ); end; Function GetSpecial( MF: GearPtr ): String; { Retrieve the special string for this map feature, } { convert it to uppercase and return it. } begin GetSpecial := UpCase( SAttValue( MF^.SA , 'SPECIAL' ) ); end; Function RectPointOverlap( X1,Y1,X2,Y2,PX,PY: Integer ): Boolean; { Return TRUE if point PX,PY is located inside the provided } { rectangle, FALSE if it isn't. } begin RectPointOverlap := ( PX >= X1 ) and ( PX <= X2 ) and ( PY >= Y1 ) and ( PY <= Y2 ); end; Function RectRectOverlap( X1,Y1,W1,H1,X2,Y2,W2,H2: Integer ): Boolean; { Return TRUE if the two rectangles described by X,Y,Width,Height } { overlap, FALSE if they don't. } var OL: Boolean; XB,YB: Integer; begin OL := False; { Check all points of the first rectangle against the second. } for XB := X1 to (X1 + W1 - 1 ) do begin for YB := Y1 to (Y1 + H1 - 1 ) do begin Ol := OL or RectPointOverlap( X2 , Y2 , X2 + W2 - 1 , Y2 + H2 - 1 , XB , YB ); end; end; RectRectOverlap := OL; end; Function RegionClear( GB: GameBoardPtr; SCheck,STerr,X,Y,W,H: Integer ): Boolean; { Return TRUE if the specified region counts as clear for the purpose } { of sticking a map feature there, FALSE if it doesn't. } Function InclusiveRegionClear: Boolean; { Return TRUE if this region contains at least one } { tile of STERR, false otherwise. } var IsClear: Boolean; TX,TY: Integer; begin IsClear := False; for TX := ( X - 1 ) to ( X + W ) do begin for TY := ( Y - 1 ) to ( Y + H ) do begin if OnTheMap( GB , TX , TY ) then begin if TileTerrain( GB , TX , TY ) = STerr then begin IsClear := True; end; end; end; end; InclusiveRegionClear := IsClear; end; Function ExclusiveRegionClear: Boolean; { Return TRUE if this region is free from tiles } { of type STERR, false otherwise. } var IsClear: Boolean; TX,TY: Integer; begin IsClear := True; for TX := ( X - 1 ) to ( X + W ) do begin for TY := ( Y - 1 ) to ( Y + H ) do begin if OnTheMap( GB , TX , TY ) then begin if TileTerrain( GB , TX,TY ) = STerr then begin IsClear := False; end; end; end; end; ExclusiveRegionClear := IsClear; end; begin { Call the appropriate checking routine based upon what kind of } { map generator we're dealing with. } if SCheck > 0 then begin RegionClear := InclusiveRegionClear; end else if SCheck < 0 then begin RegionClear := ExclusiveRegionClear; end else begin RegionClear := True; end; end; Function RandomPointWithinBounds( Container: GearPtr; W,H: Integer ): Point; { Select a placement point within the bounds of this container. } var P: Point; begin if ( Container = Nil ) or IsAScene( Container ) then begin P.X := Random( Container^.STAT[ STAT_MAPWIDTH ] - 3 - W ) + 3; P.Y := Random( Container^.STAT[ STAT_MAPHEIGHT ] - 3 - H ) + 3; end else begin P.X := Container^.Stat[ STAT_XPos ] + 1; if W < ( Container^.Stat[ STAT_MFWidth ] - 3 ) then P.X := P.X + Random( Container^.Stat[ STAT_MFWidth ] - W - 4 ); P.Y := Container^.Stat[ STAT_YPos ] + 1; if H < ( Container^.Stat[ STAT_MFHeight ] - 3 ) then P.Y := P.Y + Random( Container^.Stat[ STAT_MFHeight ] - H - 4 ); end; RandomPointWithinBounds := P; end; Function PlacementPointIsGood( GB: GameBoardPtr; Container: GearPtr; SCheck,STerr,X0,Y0,W,H: Integer ): Boolean; { Return TRUE if the specified area is free for adding a new } { map feature, or FALSE otherwise. } var BadPosition: Boolean; MF2: GearPtr; begin { Assume it isn't a bad position until shown otherwise. } BadPosition := False; { Check One - see if this position intersects with any } { other map feature at this same depth. } { Only those map features which have } { already been placed need be checked. } if Container <> Nil then begin MF2 := Container^.SubCom; while MF2 <> Nil do begin if ( MF2^.G = GG_MapFeature ) and OnTheMap( GB , MF2^.Stat[ STAT_XPos ] , MF2^.Stat[ STAT_YPos ] ) then begin BadPosition := BadPosition or RectRectOverlap( X0 - 1 , Y0 - 1 , W + 2 , H + 2 , MF2^.Stat[ STAT_XPos ] , MF2^.Stat[ STAT_YPos ] , MF2^.Stat[ STAT_MFWidth ] , MF2^.Stat[ STAT_MFHeight ] ); end; MF2 := MF2^.Next; end; end; { Check Two - see if this position is in a "clear" area } { of the map. } if not BadPosition then BadPosition := not RegionClear( GB , SCheck , STerr , X0 , Y0 , W , H ); { So, the placement point is good if X,Y isn't a bad position. } PlacementPointIsGood := not BadPosition; end; Function SelectPlacementPoint( GB: GameBoardPtr; Container,MF: GearPtr; var Cells: SAttPtr; SCheck,STerr: Integer ): Boolean; { Attempt to find a decent place to put map feature MF. } { - It should not intersect with any other map feature } { currently placed. } { - It should be at least one tile from the edge of the container } { on all sides. } { - It should be placed in an area that is considered "clear", } { depending upon the SCheck, STerr values. } const MaxTries = 10000; var Tries: Integer; P: Point; TheCell: SAttPtr; begin { If we have been provided with a list of cells, then in theory } { or work here has already been done for us. Pick one of the cells } { at random and return that. On the other hand, if we have no } { cells, we'll need to search for a free spot ourselves. } if not OnTheMap( GB , MF^.Stat[ STAT_XPos ] , MF^.Stat[ STAT_YPos ] ) then begin if Cells <> Nil then begin { Select a cell at random. } TheCell := SelectRandomSAtt( Cells ); { Extract the needed info from this cell. } MF^.Stat[ STAT_XPos ] := ExtractValue( TheCell^.Info ); MF^.Stat[ STAT_YPos ] := ExtractValue( TheCell^.Info ); MF^.Stat[ STAT_MFWidth ] := ExtractValue( TheCell^.Info ); MF^.Stat[ STAT_MFHeight ] := ExtractValue( TheCell^.Info ); SetNAtt( MF^.NA , NAG_Location , NAS_D , ExtractValue( TheCell^.Info ) ); { Delete this cell, to prevent it from being chosen again. } RemoveSAtt( Cells , TheCell ); SelectPlacementPoint := True; end else begin Tries := 0; repeat P := RandomPointWithinBounds( Container , MF^.Stat[ STAT_MFWidth ] , MF^.Stat[ STAT_MFHeight ] ); Inc( Tries ); { If we've been trying and trying with no success, } { get rid of the terrain check condition and just go } { on nonintersection. } if Tries > 9000 then SCheck := 0; until ( Tries > MaxTries ) or PlacementPointIsGood( GB , Container , SCheck , STerr , P.X , P.Y , MF^.Stat[ STAT_MFWidth ] , MF^.Stat[ STAT_MFHeight ] ); MF^.Stat[ STAT_XPos ] := P.X; MF^.Stat[ STAT_YPos ] := P.Y; SelectPlacementPoint := Tries <= MaxTries; end; end; end; Function DecideTerrainType( MF: GearPtr; var Cmd: String; D: Integer ): Integer; { Given the default provided by the instruction string and the } { value stored in the map feature gear, decide what terrain type } { to use for the current operation. } var it: Integer; begin it := ExtractValue( CMD ); if ( MF <> Nil ) and ( D >= 5 ) and ( D <= NumGearStats ) and IsLegalTerrain( MF^.Stat[ D ] ) then begin it := MF^.Stat[ D ]; end; DecideTerrainType := it; end; Procedure DrawTerrain( GB: GameBoardPtr; X,Y,T1,T2: Integer ); { Draw a terrain type into the designated tile. If two terrain } { types have been provided, pick one of them randomly. } begin if OnTheMap( GB , X , Y ) then begin if ( Random( 3 ) = 1 ) and ( T2 <> 0 ) then SetTerrain( GB , X,Y , T2 ) else SetTerrain( GB , X,Y , T1 ); end; end; Procedure RectFill( GB: GameBoardPtr; T1,T2,X0,Y0,W,H: Integer ); { Fill a rectangular area with the specified terrain. } { This is needed by several of the commands, so here it is } { as a separate procedure. } var X,Y: Integer; begin for X := X0 to ( X0 + W - 1 ) do begin for Y := Y0 to ( Y0 + H - 1 ) do begin if OnTheMap( GB , X , Y ) then begin DrawTerrain( GB , X , Y , T1 , T2 ); end; end; end; end; Procedure ProcessFill( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ); { Just fill this region with a terrain type. } var T1,T2: Integer; begin T1 := DecideTerrainType( MF , Cmd , STAT_MFFloor ); T2 := DecideTerrainType( MF , Cmd , STAT_MFMarble ); { Fill in the building area with the floor terrain. } RectFill( GB , T1 , T2 , X0 , Y0 , W , H ); end; Procedure InstallDoor( GB: GameBoardPtr; MF: GearPtr; X,Y,LockVal,HideVal: Integer ); { Add a standard door to the map at the specified location with the specified options. } function LocalWall: Integer; { Take a look at the four neighboring squares to locate } { a wall. } var D,T: Integer; begin D := 0; while D <= 8 do begin T := TileTerrain( GB , X + AngDir[ D , 1 ] , Y + AngDir[ D , 2 ] ); D := D + 2; if TerrMan[ T ].Pass < -99 then D := 10; end; LocalWall := T; end; var NewDoor,DoorPrototype: GearPtr; begin if MF <> Nil then DoorPrototype := SeekCurrentLevelGear( MF^.SubCom , GG_MetaTerrain , GS_MetaDoor ) else DoorPrototype := Nil; if DoorPrototype <> Nil then begin NewDoor := CloneGear( DoorPrototype ); end else begin NewDoor := NewGear( Nil ); NewDoor^.G := GG_MetaTerrain; NewDoor^.S := GS_MetaDoor; NewDoor^.V := 5; InitGear( NewDoor ); end; SetNAtt( NewDoor^.NA , NAG_Location , NAS_X , X ); SetNAtt( NewDoor^.NA , NAG_Location , NAS_Y , Y ); { if MF <> Nil then begin Name := SAttValue( MF^.SA , 'NAME' ); if Name <> '' then begin SetSAtt( NewDoor^.SA , 'NAME <' + MsgString( 'RANDMAPS_DoorSign' ) + Name + '>' ); end; end;} NewDoor^.Stat[ STAT_Lock ] := LockVal; if HideVal > 0 then begin DrawTerrain( GB , X , Y , LocalWall , 0 ); NewDoor^.Stat[ STAT_MetaVisibility ] := HideVal; end; InsertInvCom( GB^.Scene , NewDoor ); end; Procedure AddDoor( GB: GameBoardPtr; MF: GearPtr; X,Y: Integer ); { Add a standard door to the map at the specified location. } var Roll,Chance,LockVal,HideVal: Integer; begin LockVal := 0; HideVal := 0; if MF <> Nil then begin { Possibly make the door either LOCKED or SECRET, } { depending on the random chances stored in the MF. } Chance := NAttValue( MF^.NA , NAG_Narrative , NAS_LockedDoorChance ); if Chance > 0 then begin Roll := Random( 100 ); if Roll < Chance then begin LockVal := ( Roll div 10 ) + 3; end; end; Chance := NAttValue( MF^.NA , NAG_Narrative , NAS_SecretDoorChance ); if Chance > 0 then begin Roll := Random( 100 ); if Roll < Chance then begin HideVal := ( Roll div 8 ) + 2; end; end; end; InstallDoor( GB , MF , X , Y , LockVal , HideVal ); end; Procedure AddHiddenEntrance( GB: GameBoardPtr; X,Y,D: Integer ); { Add a hidden entrance back to the parent map. This is used generally } { to control where the PC will enter a scene. } var Entry,OS: GearPtr; { Entrance, and Originating SCene } begin Entry := LoadNewSTC( 'HIDDEN_ENTRANCE' ); { The destination in the hidden entrance should have the same value as the } { scene being entered from. However, if this is a metascene or dynamic scene, } { it's gonna take some work to find out what that is. } if GB^.Scene^.G = GG_MetaScene then begin OS := FindSceneEntrance( FindRoot( GB^.Scene ) , GB , RealSceneID( GB^.Scene ) ); if OS <> Nil then Entry^.Stat[ STAT_Destination ] := FindSceneID( OS , GB ); end else if IsInvCom( GB^.Scene ) then begin Entry^.Stat[ STAT_Destination ] := GB^.Scene^.S; end else begin Entry^.Stat[ STAT_Destination ] := GB^.Scene^.Parent^.S; end; SetNAtt( Entry^.NA , NAG_Location , NAS_X , X ); SetNAtt( Entry^.NA , NAG_Location , NAS_Y , Y ); { Store the entry direction for this entrance. } SetNAtt( GB^.Scene^.NA , NAG_EntryDirections , Entry^.Stat[ STAT_Destination ] , D + 1 ); InsertInvCom( GB^.Scene , Entry ); end; Procedure ConvertDoors( GB: GameBoardPtr; DoorPrototype: GearPtr; X0,Y0,W,H: Integer ); { Convert any doors within the specified range to the door } { prototype requested. } var map: Array[ 1..MaxMapWidth , 1..MaxMapWidth ] of Boolean; M,M2,D2: GearPtr; X,Y: Integer; begin { For this procedure to work, we must have the scene and } { a prototype door. } if ( GB = Nil ) or ( GB^.Scene = Nil ) or ( DoorPrototype = Nil ) then Exit; { Clear our replacement map. } { Set each tile to TRUE; change to FALSE once the door at this } { spot has been replaced. This should keep us from repeatedly } { replacing the same door over and over in an endless loop. } for x := 1 to MaxMapWidth do begin for y := 1 to MaxMapWidth do begin map[ X,Y] := True; end; end; M := GB^.Scene^.InvCom; while M <> Nil do begin M2 := M^.Next; if ( M^.G = GG_MetaTerrain ) and ( M^.S = GS_MetaDoor ) then begin { This is a door. Check it out. } X := NAttValue( M^.NA , NAG_Location , NAS_X ); Y := NAttValue( M^.NA , NAG_Location , NAS_Y ); if OnTheMap( GB , X , Y ) and Map[ X , Y ] and RectPointOverlap( X0 - 1 , Y0 - 1 , X0 + W , Y0 + H , X , Y ) then begin D2 := CloneGear( DoorPrototype ); RemoveGear( GB^.Scene^.InvCom , M ); SetNAtt( D2^.NA , NAG_Location , NAS_X , X ); SetNAtt( D2^.NA , NAG_Location , NAS_Y , Y ); InsertInvCom( GB^.Scene , D2 ); Map[ X , Y ] := False; end; end; M := M2; end; end; Procedure DrawWall( GB: GameBoardPtr; MF: GearPtr; Terrain,X0,Y0,W,H: Integer; AddGaps,AddDoors: Boolean ); { Do the grunt work of drawing the wall. } var DX,DY: Integer; { Door Position } X,Y: Integer; Procedure DrawWallNow; begin if OnTheMap( GB , X , Y ) then begin { If this is the door position, deal with that. } if AddGaps and ( MF <> Nil ) and ( X = DX ) and ( Y = DY ) then begin if AddDoors then begin SetTerrain( GB , X , Y , TERRAIN_Threshold ); AddDoor( GB , MF , X , Y ); end else begin SetTerrain( GB , X , Y , TERRAIN_OpenGround ); end; { If this isn't the door position, draw a wall. } end else begin SetTerrain( GB , X , Y , Terrain ); end; end; end; begin { Top wall. } DX := Random( W - 2 ) + X0 + 1; Y := Y0; DY := Y0; for X := X0 to ( X0 + W - 1 ) do begin DrawWallNow; end; { Bottom wall. } DX := Random( W - 2 ) + X0 + 1; Y := Y0 + H - 1; DY := Y; for X := X0 to ( X0 + W - 1 ) do begin DrawWallNow; end; { Right wall. } DY := Random( H - 2 ) + Y0 + 1; X := X0 + W - 1; DX := X; for Y := Y0 to ( Y0 + H - 1 ) do begin DrawWallNow; end; { Left wall. } DY := Random( H - 2 ) + Y0 + 1; X := X0; DX := X; for Y := Y0 to ( Y0 + H - 1 ) do begin DrawWallNow; end; end; Procedure ProcessWall( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer; AddGaps,AddDoors: Boolean ); { Draw a wall around this map feature. Use the MFBORDER terrain, if } { appropriate. } var terrain: Integer; begin { Decide on what terrain to use for the walls. } Terrain := DecideTerrainType( MF , Cmd , STAT_MFBorder ); { Call the wall-drawer. } DrawWall( GB, MF, Terrain,X0,Y0,W,H, AddGaps,AddDoors ); { Maybe add the exit, if one was requested. } if ASTringHasBString( GetSpecial( MF ) , 'ADDEXIT' ) then begin if ( MF^.G = GG_Scene ) or ( MF^.G = GG_MetaScene ) then begin Terrain := MF^.Stat[ STAT_MFFloor ]; if ( Terrain < 1 ) or ( Terrain > NumTerr ) then Terrain := TERRAIN_Floor; SetTerrain( GB , X0 , Y0 + H div 2 + 1 , TERRAIN ); SetTerrain( GB , X0 , Y0 + H div 2 , TERRAIN ); SetTerrain( GB , X0 , Y0 + H div 2 - 1 , TERRAIN ); AddHiddenEntrance( GB , X0 , Y0 + H div 2 , 0 ); end else begin DrawTerrain( GB , X0 , Y0 + H div 2 , TERRAIN_Threshold , 0 ); AddDoor( GB , MF , X0 , Y0 + H div 2 ); end; end; end; Function WallCoverage( GB: GameBoardPtr; X0,Y0,W,H,WallType: Integer ): Integer; { Return the percentage of the map covered in walls. } var X,Y,Walls,Tiles: Integer; begin Walls := 0; Tiles := 0; for X := X0 to ( X0 + W - 1 ) do begin for Y := Y0 to ( Y0 + H - 1 ) do begin if TileTerrain( GB,X,Y ) = WallType then Inc( Walls ); Inc( Tiles ); end; end; WallCoverage := ( Walls * 100 ) div Tiles; end; Procedure DrawTerrainWithWobblyPen( GB: GameBoardPtr; X,Y,Floor1,Floor2: Integer ); { Draw terrain in this spot, and possibly also in the four adjacent spots. } const cardinal_dir: Array [0..3,1..2] of SmallInt = ( (1,0),(0,1),(-1,0),(0,-1) ); var t: Integer; begin DrawTerrain( GB , X , Y , Floor1 , Floor2 ); for t := 0 to 3 do begin if ( Random( 3 ) <> 1 ) then begin DrawTerrain( GB , X + cardinal_dir[ t , 1 ] , Y + cardinal_dir[ t , 2 ] , Floor1 , Floor2 ); end; end; end; Function ProcessCavern( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SAttPtr; { Construct a cavern with several chambers. } begin { Step One: Place the chambers. } { Step Two: Connect the rooms in order. } end; Procedure DebugMap( GB: GameBoardPtr; X0,Y0,Wall,Floor1,Floor2: Integer ); { Spit out a debugging map of the gameboard. } var X,Y,Terr: Integer; msg: String; F: Text; begin Assign( F , 'debugmap.txt' ); Rewrite( F ); for Y := 1 to GB^.Map_Height do begin msg := ''; for X := 1 to GB^.Map_Width do begin if ( X = X0 ) and ( Y = Y0 ) then begin msg := msg + '@'; end else begin Terr := TileTerrain( GB , X , Y ); if Terr = Wall then msg := msg + '#' else if Terr = Floor1 then msg := msg + '.' else if Terr = Floor2 then msg := msg + ',' else msg := msg + '?'; end; end; writeln( F , msg ); end; Close( F ); end; Procedure ProcessCarve( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ); { This should draw a cave using the 'L' method. } var Floor1,Floor2,Wall: Integer; P: Point; Procedure DrawAnL( X , Y: Integer ); var X1,X2,Y1,Y2,XT,YT: Integer; begin { Determine X0,X1,Y0,Y1 points. } if Random( 2 ) = 1 then begin X1 := X - 5 - Random( 16 ); X2 := X; end else begin X1 := X; X2 := X + 5 + Random( 16 ); end; if Random( 2 ) = 1 then begin Y1 := Y - 5 - Random( 16 ); Y2 := Y; end else begin Y1 := Y; Y2 := Y + 5 + Random( 16 ); end; for XT := X1 to X2 do begin DrawTerrainWithWobblyPen( GB , XT , Y , Floor1 , Floor2 ); end; for YT := Y1 to Y2 do begin DrawTerrainWithWobblyPen( GB , X , YT , Floor1 , Floor2 ); end; end; Procedure AddWayOut; { Far out! Add an exit to this map feature. } { If MF is a scene, add a hidden gate to the parent map. } { Otherwise add a door. } Function IsGoodEntrance( X, Y, D: Integer; var P: Point ): Boolean; { Check this point and direction to make sure } { that it links up to a part of the maze. } var XYFTerr: Integer; FoundFloor: Boolean; MiniMap: String; begin { The entrance must start at a wall; I don't want } { any double doors on the same tile. } if TileTerrain( GB , X , Y ) <> Wall then Exit( False ); { If we're starting at a wall, check to make sure } { this entrance will connect to the maze. } FoundFloor := False; { Keep searching until we find a floor tile or exit } { the bounding box. } repeat X := X + AngDir[ D , 1 ]; Y := Y + AngDir[ D , 2 ]; XYFTerr := TileTerrain( GB , X , Y ); FoundFloor := ( XYFTerr = Floor1 ) or ( XYFTerr = Floor2 ); until FoundFloor or not RectPointOverlap( X0 , Y0 , X0 + W - 1 , Y0 + H - 1 , X , Y ); if FoundFloor then begin P.X := X; P.Y := Y; end; IsGoodEntrance := FoundFloor; end; Procedure DrawBlock( X1,Y1: Integer ); { Draw a 3x3 block centered on X,Y. } var X,Y: Integer; begin for X := ( X1 - 1 ) to ( X1 + 1 ) do begin for Y := ( Y1 - 1 ) to ( Y1 + 1 ) do begin DrawTerrain( GB , X , Y , Floor1 , Floor2 ); end; end; end; Procedure RenderEntrance( E_X0 , E_Y0 , D: Integer; EndPoint: Point ); { Render the entrance passageway. } var X,Y: Integer; begin { Add the hallway. } X := E_X0; Y := E_Y0; repeat X := X + AngDir[ D , 1 ]; Y := Y + AngDir[ D , 2 ]; DrawBlock( X , Y ); until (( X = EndPoint.X ) and ( Y = EndPoint.Y )) or not RectPointOverlap( X0 , Y0 , X0 + W - 1 , Y0 + H - 1 , X , Y ); { Add the door. } if ( MF^.G = GG_Scene ) or ( MF^.G = GG_MetaScene ) then begin AddHiddenEntrance( GB , E_X0 , E_Y0 , 0 ); end else begin DrawTerrain( GB , E_X0 , E_Y0 , TERRAIN_Threshold , 0 ); if ( D mod 4 ) = 0 then begin DrawTerrain( GB , E_X0 , E_Y0 + 1 , Wall , 0 ); DrawTerrain( GB , E_X0 , E_Y0 - 1 , Wall , 0 ); end else begin DrawTerrain( GB , E_X0 + 1 , E_Y0 , Wall , 0 ); DrawTerrain( GB , E_X0 - 1 , E_Y0 , Wall , 0 ); end; AddDoor( GB , MF , E_X0 , E_Y0 ); end; end; var Tries,DX,DY: Integer; P: Point; begin { This may take several attempts to get a good entrance... } Tries := 50; while Tries > 0 do begin { Decide on a random direction and entry point. } Case Random( 4 ) of 0: begin DX := X0 + Random( W - 6 ) + 3; DY := Y0; if IsGoodEntrance( DX , DY , 2 , P ) then begin RenderEntrance( DX , DY , 2 , P ); Tries := -1; end; end; 1: begin DX := X0 + Random( W - 6 ) + 3; DY := Y0 + H - 1; if IsGoodEntrance( DX , DY , 6 , P ) then begin RenderEntrance( DX , DY , 6 , P ); Tries := -1; end; end; 2: begin DX := X0; DY := Y0 + Random( H - 6 ) + 3; if IsGoodEntrance( DX , DY , 0 , P ) then begin RenderEntrance( DX , DY , 0 , P ); Tries := -1; end; end; else begin DX := X0 + W - 1; DY := Y0 + Random( H - 6 ) + 3; if IsGoodEntrance( DX , DY , 4 , P ) then begin RenderEntrance( DX , DY , 4 , P ); Tries := -1; end; end; end; Dec( Tries ); end; end; begin Floor1 := DecideTerrainType( MF , Cmd , STAT_MFFloor ); Floor2 := DecideTerrainType( MF , Cmd , STAT_MFMarble ); Wall := DecideTerrainType( MF , Cmd , STAT_MFBorder ); { Fill in entire area with rocks. } RectFill( GB , Wall , 0 , X0 , Y0 , W , H ); { Draw L's until the map is sufficiently perforated. } DrawAnL( X0 + ( W div 2 ) , Y0 + ( H div 2 ) ); while WallCoverage( GB , X0 , Y0 , W , H , Wall ) > 50 do begin P.X := X0 + Random( W - 2 ) + 1; P.Y := Y0 + Random( H - 2 ) + 1; if OnTheMap( GB , P.X , P.Y ) and ( TileTerrain( GB,P.X,P.Y ) <> Wall ) then DrawAnL( P.X , P.Y ); end; { Seal off the edges. } DrawWall( GB, MF, Wall, X0, Y0, W, H, False, False ); { At the end, if a way out was requested, draw it. } if AStringHasBSTring( GetSpecial( MF ) , 'ADDEXIT' ) then AddWayOut; end; Procedure ProcessScatter( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ); { Do a scattering of terrain. Useful for forests, hills, etc. } var T1,T2,T3: Integer; N,T: LongInt; X,Y: Integer; begin { Begin by reading the terrain definitions. } T1 := DecideTerrainType( MF , Cmd , STAT_MFFloor ); T2 := DecideTerrainType( MF , Cmd , STAT_MFMarble ); T3 := DecideTerrainType( MF , Cmd , STAT_MFSpecial ); { Calculate how many iterations to do. } N := W * H div 2; for t := 1 to N do begin { Pick a random point within the bounds. } X := X0 + ( W div 2 ) + Random( ( W + 1 ) div 2 ) - Random( ( W + 1 ) div 2 ); Y := Y0 + ( H div 2 ) + Random( ( H + 1 ) div 2 ) - Random( ( H + 1 ) div 2 ); if OnTheMap( GB , X , Y ) then begin { Check the terrain at this spot, then move up to the next terrain. } if ( TileTerrain( GB , X , Y ) = T1 ) and IsLegalTerrain( T2 ) then begin SetTerrain( GB , X , Y , T2 ) end else if ( TileTerrain( GB , X , Y ) = T2 ) and IsLegalTerrain( T3 ) then begin SetTerrain( GB , X , Y , T3 ) end else if ( TileTerrain( GB , X , Y ) <> T3 ) and IsLegalTerrain( T1 ) then begin SetTerrain( GB , X , Y , T1 ) end; end; end; end; Procedure ProcessEllipse( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ); { Do a vaguely ellipsoid area of terrain. } var T1,T2,T3: Integer; X,Y,MX,MY,SX,SY,SR: Integer; begin { Begin by reading the terrain definitions. } T1 := DecideTerrainType( MF , Cmd , STAT_MFFloor ); T2 := DecideTerrainType( MF , Cmd , STAT_MFMarble ); T3 := DecideTerrainType( MF , Cmd , STAT_MFSpecial ); MX := X0 + ( ( W - 1 ) div 2 ); MY := Y0 + ( ( H - 1 ) div 2 ); for X := X0 to ( X0 + W - 1 ) do begin for Y := Y0 to ( Y0 + H - 1 ) do begin if OnTheMap( GB , X , Y ) then begin { Calculate scaled X and Y values. } { Scale things for a circle of radius 100. } SX := Abs( X - MX ) * 100 div W; SY := Abs( Y - MY ) * 100 div H; { Calculate the radius to this spot. } SR := Range( 0 , 0 , SX , SY ); if ( SR <= 17 ) and IsLegalTerrain( T3 ) then begin SetTerrain( GB , X , Y , T3 ); end else if ( SR <= 33 ) and IsLegalTerrain( T2 ) then begin SetTerrain( GB , X , Y , T2 ); end else if ( SR <= 50 ) and IsLegalTerrain( T1 ) then begin SetTerrain( GB , X , Y , T1 ); end; end; end; end; end; Function SeekRoom( MF: GearPtr; Desig: String ): GearPtr; { Seek a sub-mapfeature with the provided deignation. } { Return NIL if no such map feature could be found. } begin Desig := UpCase( Desig ); MF := MF^.SubCom; while ( MF <> Nil ) and ( UpCase( SAttValue( MF^.SA , 'DESIG' ) ) <> Desig ) do MF := MF^.Next; SeekRoom := MF; end; Function ProcessLattice( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SAttPtr; { Draw a grid of lines on the map. } { GH2- The lattice will be of 5x5 cells separated by corridors of width 3. } var LineTerr,FieldTerr,MarbleTerr,X,Y,CX,CY,L_W,L_H,X_Offset,Y_Offset: Integer; CornerCell: GearPtr; Cells: SAttPtr; begin FieldTerr := DecideTerrainType( MF , Cmd , STAT_MFFloor ); MarbleTerr := DecideTerrainType( MF , Cmd , STAT_MFMarble ); LineTerr := DecideTerrainType( MF , Cmd , STAT_MFSpecial ); Cells := Nil; { Fill in entire area with the corridor terrain type. } RectFill( GB , LineTerr , 0 , X0 , Y0 , W , H ); { Start drawing boxes. } { The first cell needs 7 tiles, plus 8 tiles for each additional cell. } L_W := ( W + 1 ) div 8; L_H := ( H + 1 ) div 8; { There should be at least one tile's worth of corridor surrounding the cells, and depending } { on the map size there may be as many as 4. Determine the offset to the first cell. } X_Offset := ( W - ( L_W * 8 - 3 ) ) div 2 + X0; Y_Offset := ( H - ( L_H * 8 - 3 ) ) div 2 + Y0; { I think we're ready to start generating. } for CX := 1 to L_W do begin for CY := 1 to L_H do begin { Calculate the coordinates of our next cell. } X := ( CX - 1 ) * 8 + X_Offset; Y := ( CY - 1 ) * 8 + Y_Offset; { If this is one of the four corners, seek the corner cell. } if ( CX = 1 ) and ( CY = 1 ) then begin CornerCell := SeekRoom( MF , 'NW' ); end else if ( CX = L_W ) and ( CY = 1 ) then begin CornerCell := SeekRoom( MF , 'NE' ); end else if ( CX = 1 ) and ( CY = L_H ) then begin CornerCell := SeekRoom( MF , 'SW' ); end else if ( CX = L_W ) and ( CY = L_H ) then begin CornerCell := SeekRoom( MF , 'SE' ); end else CornerCell := Nil; { Draw the basic terrain. } RectFill( GB , FieldTerr , MarbleTerr , X , Y , 5 , 5 ); { Record the cell position. } if CornerCell <> Nil then begin { We have a cell that wants to belong here. Store the info. } CornerCell^.Stat[ STAT_XPos ] := X; CornerCell^.Stat[ STAT_YPos ] := Y; CornerCell^.Stat[ STAT_MFWidth ] := 5; CornerCell^.Stat[ STAT_MFHeight ] := 5; end else begin { Record a new cell. } StoreSAtt( Cells , BStr( X ) + ' ' + BStr( Y ) + ' 5 5 ' + BStr( Random( 4 ) ) ); end; end; end; { Return the list of cells. } ProcessLattice := Cells; end; Procedure ProcessCity( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ); { Draw a grid of lines on the map. } { PARAM1: Field Terrain } { PARAM2: Street Terrain } { PARAM3,4,5: Percent chance of small, medium, large buildings } var LineTerr,FieldTerr,X,Y,AChance,BChance,CChance,Roll: Integer; P: Point; WideX: Boolean; begin FieldTerr := DecideTerrainType( MF , Cmd , STAT_MFFloor ); LineTerr := DecideTerrainType( MF , Cmd , STAT_MFSpecial ); AChance := ExtractValue( Cmd ); BChance := ExtractValue( Cmd ); CChance := ExtractValue( Cmd ); WideX := Random( 2 ) = 1; { Fill in entire area with the field terrain type. } RectFill( GB , FieldTerr , 0 , X0 , Y0 , W , H ); { Draw the vertical lines. } P.X := X0 + 1; while P.X <= ( X0 + W ) do begin for x := P.X to ( P.X + 1 ) do begin if X < ( X0 + W ) then begin for Y := Y0 to ( Y0 + H - 1 ) do begin SetTerrain( GB , X , Y , LineTerr ); end; end; end; if WideX then begin P.X := P.X + 4 + Random( 5 ); end else begin P.X := P.X + 4; end; end; { Draw the horizontal lines. } P.Y := Y0 + 1; while P.Y <= ( Y0 + H + 1 ) do begin for Y := P.Y to ( P.Y + 1 ) do begin if Y < ( Y0 + H ) then begin for X := X0 to ( X0 + W - 1 ) do begin SetTerrain( GB , X , Y , LineTerr ); end; end; end; if WideX then begin P.Y := P.Y + 4; end else begin P.Y := P.Y + 4 + Random( 5 ); end; end; { The far line must be composed of LineTerr, otherwise there might be } { some buildings which are inaccessible. } if WideX then begin For X := X0 to ( X0 + W - 1 ) do begin SetTerrain( GB , X , Y0 + H - 1 , LineTerr ); end; end else begin For Y := Y0 to ( Y0 + H - 1 ) do begin SetTerrain( GB , X0 + W - 1 , Y , LineTerr ); end; end; { Add buildings. } for X := X0 to ( X0 + W - 1 ) do begin for Y := Y0 to ( Y0 + H - 1 ) do begin if TileTerrain( GB , X , Y ) = FieldTerr then begin Roll := Random( 100 ) - AChance; if Roll < 0 then begin SetTerrain( GB , X , Y , TERRAIN_LowBuilding ); end else if ( Roll - BChance ) < 0 then begin SetTerrain( GB , X , Y , TERRAIN_MediumBuilding ); end else if ( Roll - ( BChance + CChance ) ) < 0 then begin SetTerrain( GB , X , Y , TERRAIN_HighBuilding ); end; end; end; end; end; Procedure DrawWall( GB: GameBoardPtr; MF: GearPtr; X, Y, L, Style, WallType: Integer ); { Draw a wall starting at X0,Y0 and continuing for L tiles in } { the direction indicated by Style. Use WallType as the wall } { terrain, and DoorPrototype for the door. } var DL: Integer; { Door longitude. The tile at which to add the door. } T: Integer; begin { Select the door point now. } DL := Random( L - 2 ) + 2; for t := 1 to L do begin { If our point is on the map, do drawing here. } if OnTheMap( GB , X , Y ) then begin { If this is our door point, do that now. } if T = DL then begin SetTerrain( GB , X , Y , TERRAIN_Threshold ); AddDoor( GB , MF , X , Y ); { Otherwise draw the wall terrain. } end else begin SetTerrain( GB , X , Y , WallType ); end; if Style = DW_Horizontal then begin Inc( X ); end else begin Inc( Y ); end; end; end; end; Function ProcessMitose( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SAttPtr; { The requested area will split in two. A wall will be drawn } { between the two halves, and a door placed in the wall. } { This division will continue until we have a bunch of little } { rooms. } var Cells: SAttPtr; FloorType,WallType: Integer; Function IsGoodWallAnchor( AX,AY: Integer ): Boolean; { This spot is a good anchor point for a wall as long as } { there isn't a door. } begin IsGoodWallAnchor := OnTheMap( GB , AX , AY ) and ( TileTerrain( GB , AX , AY ) <> TERRAIN_Threshold ) and ( TileTerrain( GB , AX , AY ) <> FloorType ); end; Procedure DivideArea( CX0 , CY0 , CW , CH: Integer ); { If this area is large enough, divide it into two } { smaller areas, then recurse for each of the } { sub-areas. If it is not large enough, then just } { add its coordinates to the CELLS list. } Procedure VerticalDivison; { Attempt to divide this area with a horizontal wall. } var MaybeD,D,Tries: Integer; begin tries := 0; D := 0; repeat MaybeD := Random( CH - 10 ) + 5; if IsGoodWallAnchor( CX0 - 1 , CY0 + MaybeD ) and IsGoodWallAnchor( CX0 + CW , CY0 + MaybeD ) then D := MaybeD; Inc( Tries ); until ( D <> 0 ) or ( Tries > 15 ); { Check to make sure it's a good place. } if D <> 0 then begin { Draw the wall. } DrawWall( GB, MF , CX0, CY0 + D , CW, DW_Horizontal, WallType ); { Recurse to the two sub-areas. } DivideArea( CX0 , CY0 , CW , D ); DivideArea( CX0 , CY0 + D + 1 , CW , CH - D - 1 ); end else begin { No room for further divisions. Just store this cell. } StoreSAtt( Cells , BStr( CX0 ) + ' ' + BStr( CY0 ) + ' ' + BStr( CW ) + ' ' + BStr( CH ) + ' ' + BStr( Random( 4 ) )); end; end; Procedure HorizontalDivison; { Attempt to divide this area with a vertical wall. } var MaybeD,D,Tries: Integer; begin tries := 0; D := 0; repeat MaybeD := Random( CW - 10 ) + 5; if IsGoodWallAnchor( CX0 + MaybeD , CY0 - 1 ) and IsGoodWallAnchor( CX0 + MaybeD , CY0 + CH ) then D := MaybeD; Inc( Tries ); until ( D <> 0 ) or ( Tries > 15 ); { Check to make sure it's a good place. } if D <> 0 then begin { Draw the wall. } DrawWall( GB, MF , CX0 + D , CY0 , CH, DW_Vertical, WallType ); { Recurse to the two sub-areas. } DivideArea( CX0 , CY0 , D , CH ); DivideArea( CX0 + D + 1 , CY0 , CW - D - 1 , CH ); end else begin { No room for further divisions. Just store this cell. } StoreSAtt( Cells , BStr( CX0 ) + ' ' + BStr( CY0 ) + ' ' + BStr( CW ) + ' ' + BStr( CH ) + ' ' + BStr( Random( 4 ) ) ); end; end; begin if ( CW > 12 ) and ( CH > 5 ) and ( Random( 2 ) = 1 ) then begin HorizontalDivison; end else if ( CH > 12 ) and ( CW > 5 ) and ( Random( 2 ) = 1 ) then begin VerticalDivison; end else if ( CW > CH ) and ( CW > 12 ) and ( CH > 4 ) then begin HorizontalDivison; end else if ( CH > 12 ) and ( CW > 4 ) then begin VerticalDivison; end else begin { No room for further divisions. Just store this cell. } StoreSAtt( Cells , BStr( CX0 ) + ' ' + BStr( CY0 ) + ' ' + BStr( CW ) + ' ' + BStr( CH ) + ' ' + BStr( Random( 4 ) ) ); end; end; begin { Initialize values. } Cells := Nil; FloorType := DecideTerrainType( MF , Cmd , STAT_MFFloor ); WallType := DecideTerrainType( MF , Cmd , STAT_MFBorder ); RectFill( GB , FloorType , 0 , X0 + 1 , Y0 + 1 , W - 2 , H - 2 ); DivideArea( X0 + 1 , Y0 + 1 , W - 2 , H - 2 ); ProcessMitose := Cells; end; Function ProcessMall( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SAttPtr; { Create a bunch of 5x5 rooms. } var Cells: SAttPtr; FloorType,WallType: Integer; EntranceGrid: GearPtr; Extra,CY,DY,Phase,NumColumns,NumRows,RowWidth,T,tt: Integer; begin { Initialize values. } Cells := Nil; FloorType := DecideTerrainType( MF , Cmd , STAT_MFFloor ); WallType := DecideTerrainType( MF , Cmd , STAT_MFBorder ); EntranceGrid := SeekRoom( MF , 'EntranceGrid' ); { Start with a box. } RectFill( GB , WallType , 0 , X0 , Y0 , W , H ); RectFill( GB , FloorType , 0 , X0 + 1 , Y0 + 1 , W - 2 , H - 2 ); { Maybe add the exit, if one was requested. } if ASTringHasBString( GetSpecial( MF ) , 'ADDEXIT' ) then begin if ( MF^.G = GG_Scene ) or ( MF^.G = GG_MetaScene ) then begin SetTerrain( GB , X0 , Y0 + H div 2 + 1 , FloorType ); SetTerrain( GB , X0 , Y0 + H div 2 , FloorType ); SetTerrain( GB , X0 , Y0 + H div 2 - 1 , FloorType ); AddHiddenEntrance( GB , X0 , Y0 + H div 2 , 0 ); end else begin DrawTerrain( GB , X0 , Y0 + H div 2 , TERRAIN_Threshold , 0 ); AddDoor( GB , MF , X0 , Y0 + H div 2 ); end; end; NumColumns := ( W - 1 ) div 6; if NumColumns > 1 then Dec( NumColumns ); NumRows := ( H - 1 ) div 7; RowWidth := NumColumns * 6 + 1; Phase := 0; CY := Y0; DY := 8 + ( H - NumRows * 7 ) div ( ( NumRows + 1 ) div 2 ); Extra := ( H - NumRows * 7 + 1 ) mod ( ( NumRows + 1 ) div 2 ); for t := 0 to ( NumRows - 1 ) do begin RectFill( GB , WallType , 0 , X0 + W - RowWidth , CY , RowWidth , 6 ); for tt := 0 to ( NumColumns - 1 ) do begin StoreSAtt( Cells , BStr( X0 + W - RowWidth + tt * 6 + 1 ) + ' ' + BStr( CY + 1 - Phase ) + ' 5 5 ' + BStr( Phase * 2 ) ); end; if Phase = 0 then begin CY := CY + DY; if Extra > 0 then begin Inc( CY ); Dec( Extra ); end; end else CY := CY + 5; Phase := 1 - Phase; end; if EntranceGrid <> Nil then begin EntranceGrid^.Stat[ STAT_XPos ] := 2; EntranceGrid^.Stat[ STAT_YPos ] := 2; EntranceGrid^.Stat[ STAT_MFWidth ] := W - RowWidth - 1; EntranceGrid^.Stat[ STAT_MFHeight ] := H - 2; end; ProcessMall := Cells; end; Function ProcessCellbox( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SattPtr; { Fill this region with a terrain type, then fill the interior with 5x5 cells. } var T1,T2,X,Y: Integer; Cells: SAttPtr; begin T1 := DecideTerrainType( MF , Cmd , STAT_MFFloor ); T2 := DecideTerrainType( MF , Cmd , STAT_MFMarble ); { Fill in the building area with the floor terrain. } RectFill( GB , T1 , T2 , X0 , Y0 , W , H ); { Next, calculate the locations of all cells. } Cells := Nil; for X := 1 to (( W - 2 ) div 5 ) do begin for Y := 1 to (( H - 2 ) div 5 ) do begin StoreSAtt( Cells , BStr( X0 + ( X - 1 ) * 5 + 1 ) + ' ' + BStr( Y0 + ( Y - 1 ) * 5 + 1 ) + ' 5 5 ' + BStr( Random( 4 ) ) ); end; end; ProcessCellbox := Cells; end; Function ProcessClub( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SAttPtr; { Make a dance club, coffee house, or whatever other kind of sociable meeting place } { might exist in the GearHead universe. This is pretty much like the mall above but } { should feature a whole lot more empty space, with some cells to act as private booths. } var Cells: SAttPtr; FloorType,WallType: Integer; EntranceGrid: GearPtr; T,tt,ExitD: Integer; CellWalls: Array [0..3] of Boolean; egx,egy,egw,egh: Integer; { The width, height, and position of the entrance grid. } { On a club map, the entrance grid serves as the dance floor. } begin { Initialize values. } Cells := Nil; FloorType := DecideTerrainType( MF , Cmd , STAT_MFFloor ); WallType := DecideTerrainType( MF , Cmd , STAT_MFBorder ); EntranceGrid := SeekRoom( MF , 'EntranceGrid' ); for t := 0 to 3 do CellWalls[t] := False; egx := 2; egy := 2; egw := W - 2; egh := H - 2; ExitD := -1; { Start with a box. } RectFill( GB , WallType , 0 , X0 , Y0 , W , H ); RectFill( GB , FloorType , 0 , X0 + 1 , Y0 + 1 , W - 2 , H - 2 ); { Maybe add the exit, if one was requested. } if ASTringHasBString( GetSpecial( MF ) , 'ADDEXIT' ) then begin if ( MF^.G = GG_Scene ) or ( MF^.G = GG_MetaScene ) then begin SetTerrain( GB , X0 , Y0 + H div 2 + 1 , FloorType ); SetTerrain( GB , X0 , Y0 + H div 2 , FloorType ); SetTerrain( GB , X0 , Y0 + H div 2 - 1 , FloorType ); AddHiddenEntrance( GB , X0 , Y0 + H div 2 , 0 ); end else begin DrawTerrain( GB , X0 , Y0 + H div 2 , TERRAIN_Threshold , 0 ); AddDoor( GB , MF , X0 , Y0 + H div 2 ); end; ExitD := 3; end; { Maybe add walls with private rooms, but not to the wall where the exit is. } for t := 0 to 3 do begin if T <> ExitD then begin { Things will be slightly different whether we're dealing with a } { horizontal edge or a vertical edge. } if ( t mod 2 ) = 0 then begin if ( egh > 12 ) then begin egh := egh - 6; if t = 0 then egy := egy + 5; CellWalls[t] := True; end; end else begin if ( egw > 12 ) then begin egw := egw - 6; if t = 3 then egx := egx + 5; CellWalls[t] := True; end; end; end; end; for t := 0 to 3 do begin if CellWalls[t] then begin { Draw the wall, add the cells. } case T of 0: begin RectFill( GB , WallType , 0 , X0 , Y0 , W , 6 ); for tt := 1 to ( ( W - 12 ) div 6 ) do StoreSAtt( Cells , BStr( X0 + tt * 6 + 1 ) + ' ' + BStr( Y0 + 1 ) + ' 5 5 0' ); end; 1: begin RectFill( GB , WallType , 0 , X0 + W - 6 , Y0 , 6 , H ); for tt := 1 to ( ( H - 12 ) div 6 ) do StoreSAtt( Cells , BStr( X0 + W - 6 ) + ' ' + BStr( Y0 + tt * 6 + 1 ) + ' 5 5 1' ); end; 2: begin RectFill( GB , WallType , 0 , X0 , Y0 + H - 6 , W , 6 ); for tt := 1 to ( ( W - 12 ) div 6 ) do StoreSAtt( Cells , BStr( X0 + tt * 6 + 1 ) + ' ' + BStr( Y0 + H - 6 ) + ' 5 5 2' ); end; 3: begin RectFill( GB , WallType , 0 , X0 , Y0 , 6 , H ); for tt := 1 to ( ( H - 12 ) div 6 ) do StoreSAtt( Cells , BStr( X0 + 1 ) + ' ' + BStr( Y0 + tt * 6 + 1 ) + ' 5 5 3' ); end; end; end; end; if EntranceGrid <> Nil then begin EntranceGrid^.Stat[ STAT_XPos ] := egx; EntranceGrid^.Stat[ STAT_YPos ] := egy; EntranceGrid^.Stat[ STAT_MFWidth ] := egw; EntranceGrid^.Stat[ STAT_MFHeight ] := egh; end; ProcessClub := Cells; end; Function ProcessPredrawn( GB: GameBoardPtr ): SAttPtr; { This is a predrawn map. There's no actual rendering to do, but the } { cells to be used are currently stored as the map content. Remove them } { from the map and parse them. } var CellList: SAttPtr; Cell: GearPtr; begin CellList := Nil; while GB^.Meks <> Nil do begin Cell := GB^.Meks; DelinkGear( GB^.Meks , Cell ); { This gear should be a cell description. Parse it. Parse it real good. } StoreSAtt( CellList , BStr( Cell^.Stat[ STAT_PDMC_X ] ) + ' ' + BStr( Cell^.Stat[ STAT_PDMC_Y ] ) + ' ' + BStr( Cell^.Stat[ STAT_PDMC_W ] ) + ' ' + BStr( Cell^.Stat[ STAT_PDMC_H ] ) + ' ' + BStr( Cell^.Stat[ STAT_PDMC_D ] ) ); DisposeGear( Cell ); end; ProcessPredrawn := CellList; end; Function ProcessMonkeyMaze( GB: GameBoardPtr; MF: GearPtr; var Cmd: String; X0,Y0,W,H: Integer ): SAttPtr; { Draw a maze as featured in my non-hit game, Dungeon Monkey. } { This procedure is lifted straight from that game's random map } { generator, actually... It's about time GearHead got a new map } { type, isn't it? } { GHv2: MonkeyMaze altered to generate 7x7 cells instead of the traditional 5x5. } var Cells: SAttPtr; FloorType,WallType: Integer; NXMax,NYMax: Integer; const VecDir: Array [1..9,1..2] of Integer = ( (-1, 1),( 0, 1),( 1, 1), (-1, 0),( 0, 0),( 1, 0), (-1,-1),( 0,-1),( 1,-1) ); Function NodeX( ND: Integer ): Integer; { Convert node coordinate ND to an actual map coordinate. } begin NodeX := ND * 7 - 4 + X0; end; Function NodeY( ND: Integer ): Integer; { Convert node coordinate ND to an actual map coordinate. } begin NodeY := ND * 7 - 4 + Y0; end; Procedure DrawBlock( X1,Y1: Integer ); { Draw a 3x3 block centered on X,Y. } var X,Y: Integer; begin for X := ( X1 - 1 ) to ( X1 + 1 ) do begin for Y := ( Y1 - 1 ) to ( Y1 + 1 ) do begin DrawTerrain( GB , X , Y , FloorType , 0 ); end; end; end; Function AllNodesConnected: Boolean; { Return TRUE if all the nodes have been connected, or } { FALSE if some of them haven't been. } var FoundAWall: Boolean; NX,NY: Integer; begin { At the beginning, we haven't found any walls yet. } FoundAWall := False; for NX := 1 to NXMax do begin for NY := 1 to NYMax do begin if TileTerrain( GB , NodeX( NX ) , NodeY( NY ) ) = WallType then FoundAWall := True; end; end; AllNodesConnected := Not FoundAWall; end; Procedure DrawAnL( NX , NY: Integer ); { Draw an L-Shaped corridor on the map, centered on } { node point NX, NY. } { This will be the first structure in the monkeymap. } var X1,X2,Y1,Y2,XT,YT: Integer; begin { Determine X0,X1,Y0,Y1 points. } if ( NX > 1 ) and ( ( Random( 2 ) = 1 ) or ( NX = NXMax ) ) then begin X1 := NodeX( NX ) - ( 7 * ( Random( NX - 1 ) + 1 ) ); X2 := NodeX( NX ); end else begin X1 := NodeX( NX ); X2 := NodeX( NX ) + ( 7 * ( Random( NXMax - NX ) + 1 ) ); end; if ( NY > 1 ) and ( ( Random( 2 ) = 1 ) or ( NY = NYMax ) ) then begin Y1 := NodeY( NY ) - ( 7 * ( Random( NY - 1 ) + 1 ) ); Y2 := NodeY( NY ); end else begin Y1 := NodeY( NY ); Y2 := NodeY( NY ) + ( 7 * ( Random( NYMax - NY ) + 1 ) ); end; for XT := X1 to X2 do begin DrawBlock( XT , NodeY( NY ) ); end; for YT := Y1 to Y2 do begin DrawBlock( NodeX( NX ) , YT ); end; end; {DrawAnL} Procedure DrawOneLine( NX , NY: Integer ); { Draw an regular corridor on the map, centered on } { node point NX, NY. } var X1,X2,Y1,Y2,XT,YT: Integer; begin { Determine X0,X1,Y0,Y1 points. } if Random( 2 ) = 1 then begin if ( NX > 1 ) and ( ( Random( 2 ) = 1 ) or ( NX = NXMax ) ) then begin X1 := NodeX( NX ) - 7; X2 := NodeX( NX ); end else begin X1 := NodeX( NX ); X2 := NodeX( NX ) + 7; end; for XT := X1 to X2 do begin DrawBlock( XT , NodeY( NY ) ); end; end else begin if ( NY > 1 ) and ( ( Random( 2 ) = 1 ) or ( NY = NYMax ) ) then begin Y1 := NodeY( NY ) - 7; Y2 := NodeY( NY ); end else begin Y1 := NodeY( NY ); Y2 := NodeY( NY ) + 7; end; for YT := Y1 to Y2 do begin DrawBlock( NodeX( NX ) , YT ); end; end; end; {DrawOneLine} Procedure CarveALine( NX , NY: Integer ); { Starting at the indicated node, attempt to carve a line from } { NX,NY to another floor tile. } var D: Integer; X,Y,XF,YF: Integer; begin { Select one of the four cardinal directions at random. } D := Random( 4 ) * 2 + 2; { We'll use the VecDir array to direct the line. } X := NodeX( NX ); Y := NodeY( NY ); { Keep traveling until we either find a floor or go off the map. } repeat X := X + VecDir[ D , 1 ]; Y := Y + VecDir[ D , 2 ]; until ( TileTerrain( GB , X , Y ) = FloorType ) or ( Not RectPointOverlap( X0 , Y0 , X0 + W - 1 , Y0 + H - 1 , X , Y ) ); { If we didn't go off the map, we must have hit a floor. Bonus! } { Start carving the line in the randomly prescribed direction. } if RectPointOverlap( X0 , Y0 , X0 + W - 1 , Y0 + H - 1 , X , Y ) then begin XF := X; YF := Y; X := NodeX( NX ); Y := NodeY( NY ); repeat DrawBlock( X , Y ); X := X + VecDir[ D , 1 ]; Y := Y + VecDir[ D , 2 ]; until ( ( X = XF ) and ( Y = YF ) ) or ( Not OnTheMap( GB , X , Y ) ); end; end; {CarveALine} Procedure DrawTheMaze; { First step, generate a decent maze. } var NX,NY,Tries: Integer; begin { Start with a random point, then draw an "L" there. } NX := Random( NXMax - 2 ) + 2; NY := Random( NYMax - 2 ) + 2; DrawAnL( NX , NY ); Tries := 500; { Randomly expand upon this maze until all the nodes have } { been connected to one another. } repeat for NX := 1 to NXMax do begin for NY := 1 to NYMax do begin if TileTerrain( GB , NodeX( NX ) , NodeY( NY ) ) = FloorType then begin if Random( 12 ) = 1 then begin DrawOneLine( NX , NY ); end; end else begin CarveALine( NX , NY ); end; end; end; Dec( Tries ); until AllNodesConnected or ( Tries < 1 ); end; {DrawTheMaze} Procedure FillDungeon; { Fill the dungeon with rooms. } var NodeClear: Array [1..MaxMapWidth div 7,1..MaxMapWidth div 7] of Boolean; { Lists nodes which have not yet been developed. } X,Y,NumRooms,TRoom: Integer; Function SelectClearNode: Point; { Select a node which is free for development. } var P: Point; Tries: Integer; begin Tries := 500; repeat P.X := Random( NXMax ) + 1; P.Y := Random( NYMax ) + 1; Dec( Tries ); until NodeClear[ P.X , P.Y ] or ( Tries < 1 ); SelectClearNode := P; end; Procedure RoomRenderer( NX,NY,NW,NH: Integer ); { Render a room, adding doors at whimsy. } Procedure MaybeAddDoor( DX,DY: Integer; HorizontalWall: Boolean ); { Maybe add a door to this spot, or maybe not. } begin if Random( 4 ) <> 1 then begin if HorizontalWall then begin DrawTerrain( GB , DX - 1 , DY , WallType , 0 ); DrawTerrain( GB , DX + 1 , DY , WallType , 0 ); end else begin DrawTerrain( GB , DX , DY - 1 , WallType , 0 ); DrawTerrain( GB , DX , DY + 1 , WallType , 0 ); end; DrawTerrain( GB , DX , DY , TERRAIN_Threshold , 0 ); AddDoor( GB , MF , DX , DY ); end; end; var X1,Y1,W,H,RX,RY: Integer; begin X1 := NodeX( NX ) - 2; Y1 := NodeY( NY ) - 2; W := NW * 7 - 2; H := NH * 7 - 2; RectFill( GB , FloorType , 0 , X1 , Y1 , W , H ); for RX := NX to ( NX + NW - 1 ) do begin if TileTerrain( GB , NodeX( RX ) , Y1 - 1 ) = FloorType then MaybeAddDoor( NodeX( RX ) , Y1 - 1 , True ); if TileTerrain( GB , NodeX( RX ) , Y1 + H ) = FloorType then MaybeAddDoor( NodeX( RX ) , Y1 + H , True ); end; for RY := NY to ( NY + NH - 1 ) do begin if TileTerrain( GB , X1 - 1 , NodeY( RY ) ) = FloorType then MaybeAddDoor( X1 - 1 , NodeY( RY ) , False ); if TileTerrain( GB , X1 + W , NodeY( RY ) ) = FloorType then MaybeAddDoor( X1 + W , NodeY( RY ) , False ); end; { Record this cell in the list. } StoreSAtt( Cells , BStr( X1 ) + ' ' + BStr( Y1 ) + ' ' + BStr( W ) + ' ' + BStr( H ) + ' ' + BStr( Random( 4 ) ) ); end; Procedure DrawRoom( NX , NY: Integer ); { Draw a small square room centered on this node. } var NW,NH,TX,TY: Integer; begin NW := 1; NH := 1; { Maybe expand this room, if space permits. } if ( NX < ( NXMax - 1 ) ) and ( NY < ( NYMax - 1 ) ) and ( Random( 5 ) = 1 ) then begin if NodeClear[ NX + 1 , NY ] and NodeClear[ NX + 1 , NY + 1 ] and NodeClear[ NX , NY + 1 ] then begin NW := NW + 1; NH := NH + 1; end; end else if ( NX < ( NXMax - 1 ) ) and ( Random( 4 ) = 1 ) then begin TX := Random( 3 ) + 1; while ( TX > 0 ) and ( ( NX + NW ) <= NXMax ) do begin if NodeClear[ NX + NW , NY ] then begin Inc( NW ); Dec( TX ); end else begin TX := 0; end; end; end else if ( NX < ( NXMax - 1 ) ) and ( Random( 4 ) = 1 ) then begin TY := Random( 3 ) + 1; while ( TY > 0 ) and ( ( NY + NH ) <= NYMax ) do begin if NodeClear[ NX , NY + NH ] then begin Inc( NH ); Dec( TY ); end else begin TY := 0; end; end; end; { Call the renderer, block out the used nodes. } RoomRenderer( NX , NY , NW , NH ); for TX := NX to ( NX + NW - 1 ) do begin for TY := NY to ( NY + NH - 1 ) do begin NodeCLear[ TX , TY ] := False; end; end; end; Procedure AddRandomRoom; { Add a randomly placed room to the map. } var P: Point; begin P := SelectClearNode; if NodeClear[ P.X , P.Y ] then DrawRoom( P.X , P.Y ); end; begin { First, prepare the nodemap. } { Each node is ready for development if there's a floor tile there. } { It's possible (though unlikely) that the maze generator didn't } { fill the entire map, so check the status of each node tile to see } { whether or not it's okay to build there. } for X := 1 to NXMax do begin for Y := 1 to NYMax do begin NodeClear[ X , Y ] := TileTerrain( GB , NodeX( X ) , NodeY( Y ) ) = FloorType; end; end; NumRooms := ( NXMax * NYMax ) div 3; if NumRooms < 1 then NumRooms := 1; for TRoom := 1 to NumRooms do begin AddRandomRoom; end; end; Procedure AddWayOut; { Far out! Add an exit to this map feature. } { If MF is a scene, add a hidden gate to the parent map. } { Otherwise add a door. } Function IsGoodEntrance( X, Y, D: Integer; var P: Point ): Boolean; { Check this point and direction to make sure } { that it links up to a part of the maze. } var FoundFloor: Boolean; begin { The entrance must start at a wall; I don't want } { any double doors on the same tile. } if TileTerrain( GB , X , Y ) <> WallType then Exit( False ); { If we're starting at a wall, check to make sure } { this entrance will connect to the maze. } FoundFloor := False; { Keep searching until we find a floor tile or exit } { the bounding box. } repeat X := X + AngDir[ D , 1 ]; Y := Y + AngDir[ D , 2 ]; FoundFloor := TileTerrain( GB , X , Y ) = FloorType; until FoundFloor or not RectPointOverlap( X0 , Y0 , X0 + W - 1 , Y0 + H - 1 , X , Y ); if FoundFloor then begin P.X := X; P.Y := Y; end; IsGoodEntrance := FoundFloor; end; Procedure RenderEntrance( RE_X0 , RE_Y0 , D: Integer; EndPoint: Point ); { Render the maze as per above. } var X,Y: Integer; begin { Add the hallway. } X := RE_X0; Y := RE_Y0; repeat X := X + AngDir[ D , 1 ]; Y := Y + AngDir[ D , 2 ]; DrawBlock( X , Y ); until (( X = EndPoint.X ) and ( Y = EndPoint.Y )) or not RectPointOverlap( X0 , Y0 , X0 + W - 1 , Y0 + H - 1 , X , Y ); { Add the door. } if ( MF^.G = GG_Scene ) or ( MF^.G = GG_MetaScene ) then begin AddHiddenEntrance( GB , RE_X0 , RE_Y0 , 0 ); end else begin DrawTerrain( GB , RE_X0 , RE_Y0 , TERRAIN_Threshold , 0 ); if ( D mod 4 ) = 0 then begin DrawTerrain( GB , RE_X0 , RE_Y0 + 1 , WallType , 0 ); DrawTerrain( GB , RE_X0 , RE_Y0 - 1 , WallType , 0 ); end else begin DrawTerrain( GB , RE_X0 + 1 , RE_Y0 , WallType , 0 ); DrawTerrain( GB , RE_X0 - 1 , RE_Y0 , WallType , 0 ); end; AddDoor( GB , MF , RE_X0 , RE_Y0 ); end; end; var Tries,DX,DY: Integer; P: Point; begin { This may take several attempts to get a good entrance... } Tries := 50; while Tries > 0 do begin { Decide on a random direction and entry point. } Case Random( 4 ) of 0: begin DX := NodeX( Random( NXMax ) + 1 ); DY := Y0; if IsGoodEntrance( DX , DY , 2 , P ) then begin RenderEntrance( DX , DY , 2 , P ); Tries := Tries - ( 10 + Random( 50 ) ); if MF^.G = GG_Scene then Tries := -1; end; end; 1: begin DX := NodeX( Random( NXMax ) + 1 ); DY := Y0 + H - 1; if IsGoodEntrance( DX , DY , 6 , P ) then begin RenderEntrance( DX , DY , 6 , P ); Tries := Tries - ( 10 + Random( 50 ) ); if MF^.G = GG_Scene then Tries := -1; end; end; 2: begin DX := X0; DY := NodeY( Random( NYMax ) + 1 ); if IsGoodEntrance( DX , DY , 0 , P ) then begin RenderEntrance( DX , DY , 0 , P ); Tries := Tries - ( 10 + Random( 50 ) ); if MF^.G = GG_Scene then Tries := -1; end; end; else begin DX := X0 + W - 1; DY := NodeY( Random( NYMax ) + 1 ); if IsGoodEntrance( DX , DY , 4 , P ) then begin RenderEntrance( DX , DY , 4 , P ); Tries := Tries - ( 10 + Random( 50 ) ); if MF^.G = GG_Scene then Tries := -1; end; end; end; Dec( Tries ); end; end; begin { Initialize values. } Cells := Nil; FloorType := DecideTerrainType( MF , Cmd , STAT_MFFloor ); WallType := DecideTerrainType( MF , Cmd , STAT_MFBorder ); NXMax := W div 7; NYMax := H div 7; { The entire area starts out devoid of stuff. } RectFill( GB , WallType , 0 , X0 , Y0 , W , H ); { If we have enough space, add cells! } if ( NXMax > 0 ) and ( NYMax > 0 ) then begin DrawTheMaze; FillDungeon; if AStringHasBSTring( GetSpecial( MF ) , 'ADDEXIT' ) then AddWayOut; end; ProcessMonkeyMaze := Cells; end; Function ThingInSpot( GB: GameBoardPtr; X,Y: Integer ): Boolean; { Return TRUE if there's a thing in the specified spot, FALSE otherwise. } { The thing could be either another gear or an obstacle. } var M: GearPtr; it: Boolean; begin if not OnTheMap( GB , X , Y ) then Exit( True ); it := TerrMan[ TileTerrain( GB , X , Y ) ].Pass = -100; if ( GB^.Scene <> Nil ) and not it then begin M := GB^.Scene^.InvCom; while ( M <> Nil ) and not it do begin it := ( NAttValue( M^.NA , NAG_Location , NAS_X ) = X ) and ( NAttValue( M^.NA , NAG_Location , NAS_Y ) = Y ); M := M^.Next; end; end; ThingInSpot := it; end; Function SelectSpotInFeature( GB: GameBoardPtr; MF: GearPtr ): Point; { Select a tile within the boundaries of this particular } { map feature. } var P: Point; T: Integer; begin { We will test random points a maximum of 100 times, after which } { we'll just leave it wherever. } T := 100; repeat { Select random X and Y values within the interior space of this feature. } if ( MF^.Stat[ Stat_MFWidth ] > 4 ) and ( MF^.Stat[ Stat_MFHeight ] > 4 ) then begin P.X := Random( MF^.Stat[ Stat_MFWidth ] - 4 ) + MF^.Stat[ STAT_XPos ] + 2; P.Y := Random( MF^.Stat[ Stat_MFHeight ] - 4 ) + MF^.Stat[ STAT_YPos ] + 2; end else if ( MF^.Stat[ Stat_MFWidth ] > 2 ) and ( MF^.Stat[ Stat_MFHeight ] > 2 ) and ( T = 100 ) then begin P.X := Random( MF^.Stat[ Stat_MFWidth ] - 2 ) + MF^.Stat[ STAT_XPos ] + 1; P.Y := Random( MF^.Stat[ Stat_MFHeight ] - 2 ) + MF^.Stat[ STAT_YPos ] + 1; end else if ( MF^.Stat[ Stat_MFWidth ] > 1 ) and ( MF^.Stat[ Stat_MFHeight ] > 1 ) then begin P.X := Random( MF^.Stat[ Stat_MFWidth ] ) + MF^.Stat[ STAT_XPos ]; P.Y := Random( MF^.Stat[ Stat_MFHeight ] ) + MF^.Stat[ STAT_YPos ]; end else begin P.X := MF^.Stat[ STAT_XPos ]; P.Y := MF^.Stat[ STAT_YPos ]; T := 0; end; Dec( T ); until ( Not ThingInSpot( GB , P.X , P.Y ) ) or ( T < 1 ); SelectSpotInFeature := P; end; Procedure ExpandSuperprop( GB: GameBoardPtr; MF,SPReq: GearPtr ); { Expand this SuperProp request. MF is the map feature the prop will be } { placed in. SPReq is the SuperProp request we'll be dealing with. } var SPType: String; SPTemp,P: GearPtr; ox,oy,x,y,team: Integer; begin { Begin by selecting the template we'll be using for this superprop. } SPType := SAttValue( SPReq^.SA , 'REQUIRES' ) + ' ' + SceneContext( GB , GB^.Scene ); SPTemp := CloneGear( FindNextComponent( super_prop_list , SPType ) ); if SPTemp = Nil then begin DialogMsg( 'ERROR: Superprop not found for request "' + SPType +'".' ); Exit; end; { Calculate the northwest corner coordinates. } ox := MF^.Stat[ STAT_XPos ] + ( MF^.Stat[ STAT_MFWidth ] - SPTemp^.Stat[ STAT_MFWidth ] ) div 2; oy := MF^.Stat[ STAT_YPos ] + ( MF^.Stat[ STAT_MFHeight ] - SPTemp^.Stat[ STAT_MFHeight ] ) div 2; { Deploy all the invcoms of the superprop template. } P := SPTemp^.InvCom; while P <> Nil do begin { Delink this prop from the list. } DelinkGear( SPTemp^.InvCom , P ); { Megalist over the scripts from the SPReq. } BuildMegalist( P , SPReq^.SA ); { Determine the correct position, orientation, and team } { for this sub-prop. } X := OX + NAttValue( P^.NA , NAG_Location , NAS_X ); Y := OY + NAttValue( P^.NA , NAG_Location , NAS_Y ); SetNAtt( P^.NA , NAG_Location , NAS_X , X ); SetNAtt( P^.NA , NAG_Location , NAS_Y , Y ); team := NattValue( P^.NA , NAG_Location , NAS_Team ) + STAT_TeamA - 1; if ( team >= STAT_TeamA ) and ( team <= STAT_TeamD ) and ( SPReq^.Stat[ team ] <> 0 ) then begin SetNAtt( P^.NA , NAG_Location , NAS_Team , SPReq^.Stat[ team ] ); end else begin SetNAtt( P^.NA , NAG_Location , NAS_Team , NAttValue( SPReq^.NA , NAG_Location , NAS_Team ) ); end; { Scale the prop to the map in question. } if GB^.Scene^.G <> GG_World then P^.Scale := GB^.Scene^.V; InsertInvcom( GB^.Scene , P ); P := SPTemp^.InvCom; end; DisposeGear( SPTemp ); end; Procedure PlaceMetaTerrain( GB: GameBoardPtr; MF: GearPtr ); { This map feature may contain metaterrain gears which } { are meant to be placed inside of it. Do so now. } var MT,MT2: GearPtr; P: Point; begin MT := MF^.SubCom; while MT <> Nil do begin MT2 := MT^.Next; { Check the InvComs for MetaTerrain, placing non-doors } { on the map and deleting doors (since those will already } { have been cloned and placed when the wall was drawn). } if ( MT^.G = GG_MetaTerrain ) then begin DelinkGear( MF^.SubCOm , MT ); if MT^.S = GS_MetaDoor then begin { This is a door. Delete it. } DisposeGear( MT ); end else begin { This is not a door. Place it somewhere } { appropriate in the map feature. } P := SelectSpotInFeature( GB , MF ); SetNAtt( MT^.NA , NAG_Location , NAS_X , P.X ); SetNAtt( MT^.NA , NAG_Location , NAS_Y , P.Y ); if GB^.Scene^.G <> GG_World then MT^.Scale := GB^.Scene^.V; { Then, stick it into the scene's invcom, } { so DeployJjang will place it on the map. } InsertInvCom( GB^.Scene , MT ); end; end else if MT^.G = GG_SuperProp then begin ExpandSuperprop( GB , MF , MT ); RemoveGear( MF^.SubCom , MT ); end; MT := MT2; end; end; Procedure ShowArea( GB: GameBoardPtr; X0,Y0,W,H: Integer ); { Set the "visible" field for all tiles in the requested area to TRUE. } Var X,Y: Integer; begin for X := X0 to ( X0 + W - 1 ) do begin for Y := Y0 to ( Y0 + H - 1 ) do begin if OnTheMap( GB , X , Y ) then SetVisibility( GB ,X,Y , True ); end; end; end; Function MiniMapPosToRegularPos( MX,MY,D: Integer ): Point; { Convert MX,MY on the minimap to regular map coordinates. X and Y must } { be in the range 0..4. D is in the range 0..3, where 0 is regular } { orientation and 1..3 are ninety degree clockwise rotations. } var it: Point; begin if D = 0 then begin it.X := MX; it.Y := MY; end else if D = 1 then begin it.X := 4 - MY; it.Y := MX; end else if D = 2 then begin it.X := 4 - MX; it.Y := 4 - MY; end else begin it.X := MY; it.Y := 4 - MX; end; MiniMapPosToRegularPos := it; end; Procedure DrawMiniMap( GB: GameBoardPtr; MF: GearPtr; MapDesc: String; D: Integer ); { Draw the mini-map } var X,Y,MMX,MMY,T,ED: Integer; GBP: Point; Palette: Array [5..8] of Integer; PlacedItems: Array [0..4,0..4] of GearPtr; E: GearPtr; begin if Length( MapDesc ) < 25 then begin DialogMsg( 'ERROR: MiniMap fail for ' + GearName( MF ) + ': "' + MapDesc + '"' ); exit; end; { Locate the palette. } for t := 5 to 8 do Palette[ t ] := MF^.Stat[ t ]; if Palette[ STAT_MFFloor ] = 0 then Palette[ STAT_MFFloor ] := TERRAIN_Floor; if Palette[ STAT_MFBorder ] = 0 then Palette[ STAT_MFBorder ] := TERRAIN_Wall; if Palette[ STAT_MFMarble ] = 0 then Palette[ STAT_MFMarble ] := TERRAIN_OpenGround; if Palette[ STAT_MFSpecial ] = 0 then Palette[ STAT_MFSpecial ] := TERRAIN_GlassWall; { Clear the PlacedItems array. } for X := 0 to 4 do for Y := 0 to 4 do PlacedItems[ X , Y ] := Nil; X := MF^.Stat[ STAT_XPos ]; if MF^.Stat[ STAT_MFWidth ] > 6 then begin X := X + ( MF^.Stat[ STAT_MFWidth ] - 5 ) div 2; end; Y := MF^.Stat[ STAT_YPos ]; if MF^.Stat[ STAT_MFHeight ] > 6 then begin Y := Y + ( MF^.Stat[ STAT_MFHeight ] - 5 ) div 2; end; { Draw the terrain. } for MMX := 0 to 4 do begin for MMy := 0 to 4 do begin GBP := MiniMapPosToRegularPos( MMX , MMY , D ); GBP.X := GBP.X + X; GBP.Y := GBP.Y + Y; case MapDesc[ MMX + MMY * 5 + 1 ] of '+': begin SetTerrain( GB , GBP.X , GBP.Y , TERRAIN_Threshold ); AddDoor( GB , MF , GBP.X , GBP.Y ); end; '?': begin SetTerrain( GB , GBP.X , GBP.Y , Palette[ STAT_MFBorder ] ); InstallDoor( GB , MF , GBP.X , GBP.Y , 0 , 5 + Random( 11 ) ); end; '=': begin SetTerrain( GB , GBP.X , GBP.Y , TERRAIN_Threshold ); InstallDoor( GB , MF , GBP.X , GBP.Y , 5 + Random( 11 ) , 0 ); end; '#': SetTerrain( GB , GBP.X , GBP.Y , Palette[ STAT_MFBorder ] ); ',': SetTerrain( GB , GBP.X , GBP.Y , Palette[ STAT_MFMarble ] ); '&': SetTerrain( GB , GBP.X , GBP.Y , Palette[ STAT_MFSpecial ] ); '^': SetTerrain( GB , GBP.X , GBP.Y , TERRAIN_Wreckage ); '%': SetTerrain( GB , GBP.X , GBP.Y , TERRAIN_Rubble ); '-': SetTerrain( GB , GBP.X , GBP.Y , TERRAIN_Threshold ); ' ': {Do Nothing}; else SetTerrain( GB , GBP.X , GBP.Y , Palette[ STAT_MFFloor ] ); end; end; end; { Place the invcoms. } while MF^.InvCom <> Nil do begin E := MF^.InvCom; DelinkGear( MF^.InvCom , E ); InsertInvCom( GB^.Scene , E ); T := NAttValue( E^.NA , NAG_ComponentDesc , NAS_ELementID ); MMX := Pos( BStr( T ) , MapDesc ) - 1; if ( MMX < 0 ) or ( MMX > 24 ) then begin DialogMsg( 'ERROR: ' + GearName( E ) + ' ' + BStr( T ) + '/' + BStr( MMX ) + ' ' + MapDesc ); end else begin GBP := MiniMapPosToRegularPos( MMX mod 5 , MMX div 5 , D ); PlacedItems[ GBP.X , GBP.Y ] := E; GBP.X := GBP.X + X; GBP.Y := GBP.Y + Y; ED := ( NAttValue( E^.NA , NAG_ParaLocation , NAS_D ) + D * 2 ) mod 8; SetNAtt( E^.NA , NAG_Location , NAS_X , GBP.X ); SetNAtt( E^.NA , NAG_Location , NAS_Y , GBP.Y ); SetNAtt( E^.NA , NAG_Location , NAS_D , ED ); end; end; { Finally, render the SECRET MESSAGE, just like Groo the Wanderer but way more obvious. } { If a message is tagged onto the end of the minimap, render this using the various } { placed item's ROGUECHAR attributes. This way, in ASCII mode, the shops should spell } { out what they sell. } if Length( MapDesc ) >= 30 then begin { The message appears in the top line of the minimap. This is going to change based on } { the direction of rendering. } if ( D = 0 ) or ( D = 2 ) then begin if D = 0 then Y := 0 else Y := 4; for X := 0 to 4 do begin if PlacedItems[ X , Y ] <> Nil then begin SetSAtt( PlacedItems[ X , Y ]^.SA , 'roguechar <' + MapDesc[ 26 + X ] + '>' ); end; end; end else begin if D = 3 then X := 0 else X := 4; for Y := 0 to 4 do begin if PlacedItems[ X , Y ] <> Nil then begin SetSAtt( PlacedItems[ X , Y ]^.SA , 'roguechar <' + MapDesc[ 26 + Y ] + '>' ); end; end; end; end; end; Function TheRenderer( GB: GameBoardPtr; MF: GearPtr; X , Y , W , H , Style: Integer ): SAttPtr; { Do some damage to the game board in the shape of this feature. } { This function returns the STYLE of the part; } { it describes what kind of feature we should be drawing. } var Command_String,Cmd,Special: String; Cells: SAttPtr; begin { Determine the command string to use for this feature. } Command_String := ''; if MF <> Nil then Command_String := SAttValue( MF^.SA , 'PARAM' ); if Command_String = '' then Command_String := SAttValue( Standard_Param_List , 'PARAM' + BStr( Style ) ); if Command_String = '' then Command_String := SAttValue( Standard_Param_List , 'PARAMDEFAULT' ); Cells := Nil; while Command_String <> '' do begin cmd := UpCase( ExtractWord( Command_String ) ); if cmd = 'FILL' then begin ProcessFill( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'WALL' then begin ProcessWall( GB , MF , Command_String , X , Y , W , H , False , False ); end else if cmd = 'OWALL' then begin ProcessWall( GB , MF , Command_String , X , Y , W , H , True , False ); end else if cmd = 'DWALL' then begin ProcessWall( GB , MF , Command_String , X , Y , W , H , True , True ); end else if cmd = 'CARVE' then begin ProcessCarve( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'SCATTER' then begin ProcessScatter( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'ELLIPSE' then begin ProcessEllipse( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'LATTICE' then begin if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessLattice( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'MITOSE' then begin if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessMitose( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'MALL' then begin if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessMall( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'CLUB' then begin if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessClub( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'MONKEYMAZE' then begin if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessMonkeyMaze( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'CITY' then begin ProcessCity( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'CELLBOX' then begin if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessCellbox( GB , MF , Command_String , X , Y , W , H ); end else if cmd = 'PREDRAWN' then begin { This is a predrawn map- the only thing the renderer needs to do } { is parse the cells. } if Cells <> Nil then DisposeSAtt( Cells ); Cells := ProcessPredrawn( GB ); end; end; { If this map feature has a minimap stamp, apply that now. } if ( MF <> Nil ) and ( SAttValue( MF^.SA , 'MINIMAP' ) <> '' ) then begin DrawMiniMap( GB , MF , SAttValue( MF^.SA , 'MINIMAP' ) , NAttValue( MF^.NA , NAG_Location , NAS_D ) ); end; if ( GearName( GB^.Scene ) = 'DEBUG' ) and HeadMatchesString( 'ZONE_' , GearName( MF ) ) then begin DialogMsg( 'Rendering ' + GearName( MF ) + ': ' + SAttValue( MF^.SA , 'MINIMAP' ) ); end; { Handle any SPECIAL commands associated with this map feature. } if ( MF <> Nil ) then begin Special := GetSpecial( MF ); { If this feature is indicated as the starting point for this scene } { set the ParaX , ParaY attributes now. } if AStringHasBString( Special , SPECIAL_ShowAll ) then begin ShowArea( GB , X , Y , W , H ); end else if IsAScene( MF ) and ( MF^.Stat[ STAT_SpaceMap ] <> 0 ) then begin { Scrolling space maps also start out all revealed. } ShowArea( GB , X , Y , W , H ); end; if AStringHasBString( Special , SPECIAL_ConvertDoors ) then begin ConvertDoors( GB , SeekCurrentLevelGear( MF^.SubCom , GG_MetaTerrain , GS_MetaDoor ) , MF^.Stat[ STAT_XPos ] , MF^.Stat[ STAT_YPos ] , MF^.Stat[ STAT_MFWidth ] , MF^.Stat[ STAT_MFHeight ] ); end; end; { Place any metaterrain associated with this feature. } if MF <> Nil then PlaceMetaTerrain( GB , MF ); TheRenderer := Cells; end; Procedure DoGapFilling( GB: GameBoardPtr; Container: GearPtr; C_X,C_Y,C_W,C_H,SCheck,STerr: Integer; Gapfill_String: String; var Cells: SAttPtr ); { Search the container for empty regions, filling them with junk } { as appropriate. } { GH2- if any cells remain unfilled, fill them now. } const MaxGFStyle = 7; var GFStyle: Array [0..MaxGFStyle] of Integer; NumStyle: Integer; Procedure AddGFMF( X , Y , W , H: Integer ); { Add a new map feature in the specified location. } var Style: Integer; NewMF: GearPtr; begin Style := GFStyle[ Random( NumStyle ) ]; if Container <> Nil then begin { Create a new gear for this gap filler. } NewMF := NewGear( Nil ); InsertSubCom( Container , NewMF ); NewMF^.G := GG_MapFeature; NewMF^.S := Style; InitGear( NewMF ); NewMF^.Stat[ STAT_XPos ] := X; NewMF^.Stat[ STAT_YPos ] := Y; NewMF^.Stat[ STAT_MFHeight ] := H; NewMF^.Stat[ STAT_MFWidth ] := W; end else begin NewMF := Nil; end; { Mark this map feature as temporary. } SetNAtt( NewMF^.NA , NAG_EpisodeData , NAS_Temporary , 1 ); { Render it on the map. } TheRenderer( GB , NewMF , X , Y , W , H , Style ); end; var P: Point; T,W,H: Integer; C: SAttPtr; begin { Extract the GF styles. } NumStyle := 0; for t := 0 to MaxGFStyle do begin GFStyle[ T ] := ExtractValue( Gapfill_String ); if GFStyle[ t ] <> 0 then Inc( NumStyle ); end; if NumStyle = 0 then begin DisposeSAtt( Cells ); Exit; end; { Use up any remaining cells. } C := Cells; while C <> Nil do begin P.X := ExtractValue( C^.Info ); P.Y := ExtractValue( C^.Info ); W := ExtractValue( C^.Info ); H := ExtractValue( C^.Info ); AddGFMF( P.X , P.Y , W , H ); C := C^.Next; end; DisposeSAtt( Cells ); { If the container area meets our minimum size, add more stuff. } if ( C_W > 12 ) and ( C_H > 12 ) then begin { Try to place an item on the map 100 times. } for t := 1 to 15000 do begin { Choose a random width, height, and placement point in container. } W := Random( 15 ) + 3; H := Random( 15 ) + 3; P := RandomPointWithinBounds( Container , W , H ); { if this placement point is good, i.e. empty, then } { fill it with one of the style types taken from the } { GapFiller parameter string. } if PlacementPointIsGood( GB , Container , SCheck , STerr , P.X , P.Y , W , H ) then begin AddGFMF( P.X , P.Y , W , H ); end; end; { for t = 1 to 100 } end; end; Function SubZoneType( MF: GearPtr ): Integer; { Return the map generator type to be used for a sub-zone of this map feature. } var SZT: String; begin SZT := SAttValue( MF^.SA , 'SUBZONE' ); if SZT = '' then begin if IsAScene( MF ) then begin SZT := SAttValue( Standard_Param_List , 'SUBZONE' + BStr( MF^.STAT[ STAT_MapGenerator ] ) ); end else if MF^.G = GG_MapFeature then begin SZT := SAttValue( Standard_Param_List , 'SUBZONE' + BStr( MF^.S ) ); end; end; SubZoneType := ExtractValue( SZT ); end; Function NewSubZone( MF: GearPtr ): GearPtr; { Return a new map feature that's a subcomponent of the current one, } { complete with its parent's palette and chosen child type. } var it: GearPtr; begin it := NewGear( Nil ); it^.G := GG_MapFeature; it^.S := SubZoneType( MF ); InitGear( it ); it^.Stat[ STAT_MFHeight ] := 3; it^.Stat[ STAT_MFWidth ] := 3; if MF^.G = GG_MapFeature then begin it^.Stat[ STAT_MFFloor ] := MF^.Stat[ STAT_MFFloor ]; it^.Stat[ STAT_MFMarble ] := MF^.Stat[ STAT_MFMarble ]; it^.Stat[ STAT_MFBorder ] := MF^.Stat[ STAT_MFBorder ]; it^.Stat[ STAT_MFSpecial ] := MF^.Stat[ STAT_MFSpecial ]; end; InsertSubCom( MF , it ); NewSubZone := it; end; Function FindFreeZone( GB: GameBoardPtr; MF: GearPtr; var Cells: SAttPtr; SCheck,STerr: Integer ): GearPtr; { Attempt to locate a free space in which to add some new content. } var it: GearPtr; T: Integer; begin it := NewSubZone( MF ); { Because we're gonna place some content here, make the zone bigger than the default 3x3 } it^.Stat[ STAT_MFHeight ] := 5; it^.Stat[ STAT_MFWidth ] := 5; for t := 5 to 8 do it^.Stat[t] := MF^.Stat[t]; SetSAtt( it^.SA , 'name ' ); if not SelectPlacementPoint( GB , MF , it , Cells , SCheck , STerr ) then begin RemoveGear( MF^.SubCom , it ); FindFreeZone := Nil; end else begin FindFreeZone := it; end; end; Procedure FormatContentStrings( C: GearPtr; ID: Integer; P: String ); { Format this random content. } Procedure FormatThisPart( Part: GearPtr ); var S: SATtPtr; T: Integer; begin S := Part^.SA; while S <> Nil do begin ReplacePat( S^.Info , '%id%' , BStr( ID ) ); ReplacePat( S^.Info , '%param%' , P ); for t := 1 to Num_Plot_Elements do begin ReplacePat( S^.Info , '%name' + BStr( T ) + '%' , SAttValue( C^.SA , 'NAME_' + BStr( T ) ) ); ReplacePat( S^.Info , '%' + BStr( T ) + '%' , BStr( ElementID( C , T ) ) ); end; S := S^.Next; end; end; Procedure FormatAlongPath( Part: GearPtr ); begin while Part <> Nil do begin FormatThisPart( Part ); FormatAlongPath( Part^.InvCom ); FormatALongPath( Part^.SubCom ); Part := Part^.Next; end; end; begin FormatThisPart( C ); FormatAlongPath( C^.SubCom ); FormatAlongPath( C^.InvCom ); end; Function ContentInitOkay( C,Zone,Source: GearPtr; P: String; GB: GameBoardPtr; var Cells: SAttPtr; SCheck,STerr: Integer ): Boolean; { Return TRUE if this component is initialized okay, or FALSE otherwise. } { If initialization fails, delete the content C. } { ZONE refers to the map feature where this content is located. } { It must be a subcom of the "container". } { P is the parameter passed by the calling procedure. } { GB is the gameboard pointer. Duh. } var throw,cmd,ThrowType: String; AllOK,Optional,NewMF: Boolean; ThrowParam: String; SA: SAttPtr; TZone,I: GearPtr; begin { Initialize the values. } Inc( High_Component_ID ); SetNAtt( C^.NA , NAG_ComponentDesc , NAS_CompUID , High_Component_ID ); SetSAtt( C^.SA , 'HOME <' + GearName( ZONE ) + '>' ); { Initialize the InvComs. } I := C^.InvCom; while I <> Nil do begin { Character gears have to be individualized. } if ( I^.G = GG_Character ) and NotAnAnimal( I ) and ( NAttValue( C^.NA , NAG_Narrative , NAS_ContentID ) = 0 ) then begin IndividualizeNPC( I ); end; I := I^.Next; end; { Attempt to add this component to the adventure, selecting components and so forth. } { If this check fails, then the content C will be deleted. } AllOK := InsertRSC( Source , C , GB ); if not AllOk then Exit( False ); { Format all of the strings in this component. } FormatContentStrings( C , High_Component_ID , P ); NewMF := False; { Recursively add any further components that are needed. } SA := C^.SA; while ( SA <> Nil ) and AllOK do begin if HeadMatchesString( 'CONTENT' , SA^.Info ) then begin throw := RetrieveAString( SA^.Info ); { It's time to throw a request for a new component. } { PARAM 1: OPTIONAL or REQUIRED } cmd := ExtractWord( throw ); Optional := AStringHasBString( cmd , 'O' ); { PARAM 2: COMPONENT TYPE } ThrowType := ExtraCTWord( throw ); { PARAM 3: The component parameter. } ThrowParam := ExtractWord( throw ); { PARAM 4: COMPONENT DESTINATION } cmd := ExtractWord( throw ); if UpCase( cmd[1]) = 'L' then begin TZone := Zone; NewMF := False; end else begin TZone := FindFreeZone( GB , Zone^.Parent , Cells,SCheck,STerr ); NewMF := True; end; { If the scene request has resulted in an acceptable range, } { attempt to load the new parameter here. } if TZone <> Nil then begin if Not Optional then begin AllOK := AddContent( ThrowType , GB , Source , TZone , ThrowParam , Cells , SCheck , STerr ); if NewMF and not AllOk then RemoveGear( TZone^.Parent^.SubCom , TZone ); end else if Random( 2 ) = 1 then begin if not AddContent( ThrowType , GB , Source , TZone , ThrowParam , Cells , SCheck , STerr ) then begin if NewMF then RemoveGear( TZone^.Parent^.SubCom , TZone ); end; end else if NewMF then begin RemoveGear( TZone^.Parent^.SubCom , TZone ); end; end else AllOK := Optional; end; SA := SA^.Next; end; { If there was a problem, delete C. } { ASSERT: C will be located as a invcom of Source, having been placed there } { by the InsertStoryArc procedure. } if not AllOk then begin RemoveGear( Source^.InvCom , C ); end; ContentInitOkay := AllOk; end; Procedure InsertContentFragment( GB: GameBoardPtr; Adv,C: GearPtr ); { A plot has been loaded which is actually a content fragment. This is what we need } { to do: } { - Copy the plot scripts to the scene gear in megalist fashion. } { - Copy the personas needed to the scene and set their correct CIDs. } { - Move the requested gears to the scene, assign teams and homes. } { Don't move gears which have a PLACE string assigned; these have already been moved. } var ZONE,E,P,P2,Team,Door: GearPtr; T: Integer; Loc: Point; MiniMap,Z_Special,TeamData: String; begin { Copy the scripts to the local megalist. } BuildMegalist( GB^.Scene , C^.SA ); { Locate the zone assigned to this content. } Zone := SeekChildByName( GB^.Scene , SAttValue( C^.SA , 'HOME' ) ); MiniMap := SAttValue( C^.SA , 'MINIMAP' ); if ( MiniMap <> '' ) and ( Zone <> Nil ) then SetSAtt( Zone^.SA , 'MINIMAP <' + MINIMAP + '>' ); if Zone <> Nil then begin Z_Special := SAttValue( ZONE^.SA , 'SPECIAL' ) + ' ' + SAttValue( C^.SA , 'ZONE_SPECIAL' ); SetSAtt( Zone^.SA , 'SPECIAL <' + Z_Special + '>' ); end; { Copy over the door prototype, if present. } Door := SeekCurrentLevelGear( C^.SubCom , GG_MetaTerrain , GS_MetaDoor ); if Door <> Nil then begin DelinkGear( C^.SubCom , Door ); InsertSubCom( Zone , Door ); end; if GearName( GB^.Scene ) = 'DEBUG' then begin DialogMsg( '"' + SAttValue( C^.SA , 'requires' ) + '" inserted in ' + SAttValue( C^.SA , 'HOME' ) + ':' + MiniMap ); if ( Zone = Nil ) then DialogMsg( 'ERROR: ' + SAttValue( C^.SA , 'HOME' ) + ' not found.' ); end; for t := 1 to Num_Plot_Elements do begin { See if there's an element in this slot. } E := SeekPlotElement( Adv , C , T , GB ); { If there is an element, we have to deal with it. } if ( E <> Nil ) and ( E^.G >= 0 ) and ( SATtValue( C^.SA , 'PLACE' + BStr( T ) ) = '' ) then begin DelinkGearForMovement( GB , E ); { If E is prefab, don't store an original home, but do save the PlotID. } if AStringHasBString( SAttValue( C^.SA , 'ELEMENT' + BStr( T ) ) , 'PREFAB' ) then begin SetNAtt( E^.NA , NAG_ParaLocation , NAS_OriginalHome , 0 ); SetNAtt( E^.NA , NAG_Narrative , NAS_PlotID , NAttValue( GB^.Scene^.NA , NAG_Narrative , NAS_PlotID ) ); end; if ( MiniMap <> '' ) and ( Pos( BStr( T ) , MiniMap ) > 0 ) then begin { Since we have a minimap, store the element temporarily in the map feature. } { It will be deployed by the renderer. } InsertInvCom( Zone , E ); SetNAtt( E^.NA , NAG_ComponentDesc , NAS_ELementID , T ); end else begin InsertInvCom( GB^.Scene , E ); Loc := SelectSpotInFeature( GB , Zone ); if OnTheMap( GB , Loc.X , Loc.Y ) and not ThingInSpot( GB , Loc.X , Loc.Y ) then begin SetNAtt( E^.NA , NAG_Location , NAS_X , Loc.X ); SetNAtt( E^.NA , NAG_Location , NAS_Y , Loc.Y ); end else begin SetSATt( E^.SA , 'HOME <' + GEarName( Zone ) + '>' ); end; end; if IsMasterGear( E ) then begin Team := SeekChildByName( GB^.SCENE , SAttValue( C^.SA , 'TEAM' + BStr( T ) ) ); if Team <> Nil then begin SetNAtt( E^.NA , NAG_Location , NAS_Team , Team^.S ); end else begin TeamData := SAttValue( C^.SA , 'TEAMDATA' + BStr( T ) ); if ( TeamData <> '' ) or ( E^.G <> GG_Prop ) then begin SetSAtt( E^.SA , 'TEAMDATA <' + TeamData + '>' ); ChooseTeam( E , GB^.Scene ); end; end; end; end; end; { Copy the personas from this component. } P := C^.SubCom; while P <> Nil do begin P2 := P^.Next; if P^.G = GG_Persona then begin { Delink it from the plot. } DelinkGear( C^.SubCom , P ); { Set the correct CID code for this persona. } P^.S := ElementID( C , P^.S ); { Also record the Plot ID. } SetNAtt( P^.NA , NAG_Narrative , NAS_PlotID , NAttValue( GB^.Scene^.NA , NAG_Narrative , NAS_PlotID ) ); { Stick it where it needs to go. } InsertSubCom( GB^.Scene , P ); end; P := P2; end; end; Procedure InsertFinishedContent( GB: GameBoardPtr; Adv,Source: GearPtr ); { Here's what this procedure has to do: Search through the invcoms of SOurce } { for random scene content. When some has been found, call the insertion procedure } { to move it to the gameboard. Then, delete this plot and move on to the next } { one. } var C,C2: GearPtr; begin C := Source^.InvCom; while C <> Nil do begin C2 := C^.Next; if ( C^.G = GG_Plot ) and ( NAttValue( C^.NA , NAG_ComponentDesc , NAS_CompUID ) > 0 ) then begin { Insert the content, then delete from the adventure. } InsertContentFragment( GB , Adv , C ); RemoveGear( Source^.InvCom , C ); end; C := C2; end; end; Function CreateContentList( Scene: GearPtr; const Context: String ): NAttPtr; { Create a list holding all content that might be chosen for the specified context. } { This list is in the regular format of component lists- the G and S values both } { indicate a component index, and V indicates the component match weight. In this case } { a positive index indicates regular random scene content, while a negative index } { indicates unique scene content. } const standard_content_multiplier = 5; { Standard, non-unique content is more likely to show up } { than the unique stuff. This multiplier tells by how much. } var it: NAttPtr; { The list we're generating. } C: GearPtr; N,MW: Integer; { A counter, and the current match weight. } begin { First, harvest the content from the standard list. } it := Nil; C := random_scene_content; N := 1; while C <> nil do begin MW := StringMatchWeight( Context , SAttValue( C^.SA , 'REQUIRES' ) ); if MW > 0 then begin { Regular scene content gets a multiplier to match weight. } SetNAtt( it , N , N , MW * standard_content_multiplier ); end; Inc( N ); C := C^.Next; end; { Next, maybe add some unique content possibilities. } if Scene <> Nil then begin C := SeekCurrentLevelGear( FindRoot( Scene )^.InvCom , GG_ContentSet , 0 ); if C <> Nil then begin C := C^.InvCom; N := -1; while C <> nil do begin MW := StringMatchWeight( Context , SAttValue( C^.SA , 'REQUIRES' ) ); if MW > 0 then begin SetNAtt( it , N , N , MW ); end; Dec( N ); C := C^.Next; end; end; end; CreateContentList := it; end; Function ChooseSceneContent( Scene: GearPtr; var ShoppingList: NAttPtr; const Context: String ): GearPtr; { Given this shopping list, select a component for inclusion in SCENE. } { Positive indicies indicate regular scene content while negative indicies indicate } { unique content. } { Return a clone of the selected content, marked with the supplied context. } var it: NAttPtr; N: Integer; C,UniCon: GearPtr; begin if ShoppingList = Nil then Exit( Nil ); { Step one- select one of the shopping list entries. } it := RandomComponentListEntry( ShoppingList ); { If this list entry is positive, select the component from random_scene_content. } { Otherwise, select the component from the unique scene content collection. } N := it^.S; RemoveNAtt( ShoppingList , it ); if N > 0 then begin C := RetrieveGearSib( random_scene_content , N ); end else if N < 0 then begin if Scene <> Nil then begin UniCon := SeekCurrentLevelGear( FindRoot( Scene )^.InvCom , GG_ContentSet , 0 ); if UniCon <> Nil then begin C := RetrieveGearSib( UniCon^.InvCom , Abs( N ) ); end else begin DialogMsg( 'ERROR: Unique Content not found in ChooseSceneContent.' ); C := Nil; end; end else begin DialogMsg( 'ERROR: Scene not found in ChooseSceneContent.' ); C := Nil; end; end else begin { Error- we shouldn't get a zero here. } DialogMsg( 'ERROR: Zero generated in ChooseSceneContent.' ); C := Nil; end; { We now have a pointer to the prototype for this content. Clone it, set its } { context attribute, and return it. } if C <> Nil then begin C := CloneGear( C ); SetSAtt( C^.SA , 'CONTEXT <' + Context + '>' ); end; ChooseSceneContent := C; end; Function AddContent( CType: String; GB: GameboardPtr; Source,Zone: GearPtr; P: String; var Cells: SAttPtr; SCheck,STerr: Integer ): Boolean; { Add some random content to this map feature. } { A content type has been requested. Add it. If additional content(s) } { are requested, add those as well. If the installation fails } { delete the content currently added (upper level content has already been } { deleted by recursive calls to this function, and lower level content will } { be deleted if nessecary when the function exits). } { CType describes the context by which the new content should be chosen. } { It should include a type label preceded by "*". } { Source is the place where the components will be temporarily installed- } { it is either a story or the adventure itself. } { Zone is the specific map feature where the content will be placed. } { it is a subcom of a "container" map feature. } { P is the parameter passed to the new component. } { Cells, SCheck, and STerr are variables needed when adding a new map feature. } var AllOK: Boolean; CList: NAttPtr; C,C2: GearPtr; ContentID: LongInt; begin { Create the list of potential components. } { Start by calculating the context type for the random map content. } CType := CType + ' ' + SceneContext( GB , GB^.Scene ); CList := CreateContentList( GB^.Scene , CType ); if CList = Nil then Exit( False ); { Repeat... select one component randomly. Remove it from the list. } { Attempt to add it. If addition fails, try another component. Keep going } { until either a component has been added or we've run out of possibilities. } repeat C := ChooseSceneContent( GB^.Scene , CList , CType ); { ERROR CHECK } if C = Nil then begin DialogMsg( 'ERROR: FindNextComponent returned Nil, but CList not empty.' ); DialogMsg( 'CType:' + CType ); break; end; AllOK := ContentInitOkay( C , Zone , Source , P , GB , Cells , SCheck , STerr ); { If not everything could be loaded okay, C will already } { have been deleted. } until ( CList = Nil ) or AllOK; { We don't want to repeat unique content. So, if a piece of unique content has } { been added, delete it now. } if AllOK and ( GB^.Scene <> Nil ) then begin { Record the content ID. } ContentID := NAttValue( C^.NA , NAG_Narrative , NAS_ContentID ); { If the content has a unique ID, delete the prototype from the } { adventure. } if ContentID <> 0 then begin SetNAtt( C^.NA , NAG_Narrative , NAS_ContentID , 0 ); C2 := SeekGearByIDTag( FindRoot( GB^.Scene ) , NAG_Narrative , NAS_ContentID , ContentID ); if C2 <> Nil then RemoveGear( C2^.Parent^.InvCom , C2 ); end; end; { Get rid of any remaining components. } DisposeNAtt( CList ); AddContent := AllOK; end; Procedure AddUniqueContentSequence( CType: String; GB: GameboardPtr; Source,MF: GearPtr; P: String; var Cells: SAttPtr; SCheck,STerr: Integer; NumCon: Integer; DoSub: Boolean ); { This works kind of the same as the above procedure, but it will create a list } { of content and not add anything more than once. } var AllOK: Boolean; CList: NAttPtr; Zone,C,C2: GearPtr; ContentID: LongInt; T: Integer; UCon,U: NAttPtr; begin { Create the list of potential components. } { Start by calculating the context type for the random map content. } CType := CType + ' ' + SceneContext( GB , GB^.Scene ); CList := CreateContentList( GB^.Scene , CType ); if CList = Nil then Exit; { Initialize the list of unique content to be deleted. } UCon := Nil; for t := 1 to NumCon do begin { If CList = Nil, we've run out of content. Better break the loop. } if CList = Nil then Break; { Get a zone for the next piece of content. } if DoSub then begin Zone := FindFreeZone( GB , MF , Cells,SCheck,STerr ); end else begin Zone := MF; end; { Repeat... select one component randomly. Remove it from the list. } { Attempt to add it. If addition fails, try another component. Keep going } { until either a component has been added or we've run out of possibilities. } repeat C := ChooseSceneContent( GB^.Scene , CList , CType ); AllOK := ContentInitOkay( C , Zone , Source , P , GB , Cells , SCheck , STerr ); { If not everything could be loaded okay, C will already } { have been deleted. } until ( CList = Nil ) or AllOK; { We don't want to repeat unique content. So, if a piece of unique content has } { been added, delete it now. } if AllOK and ( GB^.Scene <> Nil ) then begin { Record the content ID. } ContentID := NAttValue( C^.NA , NAG_Narrative , NAS_ContentID ); { If the content has a unique ID, store it so we can delete it later. } if ContentID <> 0 then begin SetNAtt( C^.NA , NAG_Narrative , NAS_ContentID , 0 ); SetNAtt( UCon , ContentID , ContentID , ContentID ); end; end; end; { for t... } { Finally, delete any unique content which got used here. } if UCon <> Nil then begin U := UCon; while U <> Nil do begin C2 := SeekGearByIDTag( FindRoot( GB^.Scene ) , NAG_Narrative , NAS_ContentID , U^.G ); if C2 <> Nil then RemoveGear( C2^.Parent^.InvCom , C2 ); U := U^.Next; end; DisposeNAtt( UCon ); end; { Get rid of any remaining components. } DisposeNAtt( CList ); end; Procedure AddFeatureContent( GB: GameBoardPtr; MF: GearPtr; var Cells: SAttPtr; SCheck,STerr: Integer ); { Attempt to add content for this map feature. } const MODE_Fill = 1; MODE_Some = 2; MODE_Variety = 3; var Source,Zone: GearPtr; { Either the adventure itself or this scene's governing story. } SA: SAttPtr; throw,cmd,Content_Type,Content_Parameter: String; DoSub: Boolean; FCMode,Content_Num,Content_Chance: Integer; begin { Error check- we need the GB and the Scene. } if ( GB = Nil ) or ( GB^.Scene = Nil ) then Exit; { Locate the source. } { This will either be the adventure or, in the case of a story-linked metascene, a story. } Source := GB^.Scene; while ( Source <> Nil ) and ( Source^.G <> GG_Adventure ) and ( Source^.G <> GG_Story ) do Source := Source^.Parent; if Source = Nil then Exit; { If this map feature doesn't have a unique name, better give it one. This will be } { important for the map generator. } if ( SAttValue( MF^.SA , 'NAME' ) = '' ) and ( MF^.G = GG_MapFeature ) then begin SetSAtt( MF^.SA , 'name ' ); Inc( UniqueZoneNum ); end; { Look through the map feature for content requests. } { If the map feature is a scene (or metascene), the content zone will be a sub-region } { of the map. Otherwise, the content zone will be this map feature exactly and any } { branch content will be located in map features at this same level. } SA := MF^.SA; while SA <> Nil do begin if HeadMatchesString( 'CONTENT' , SA^.Info ) then begin throw := RetrieveAString( SA^.Info ); { It's time to throw a request for a new component. } { PARAM 1: SOME, FILL, or VARIETY } cmd := ExtractWord( throw ); if UpCase( cmd[1] ) = 'S' then begin { We want to add SOME content. This command should be } { followed by two numbers: the first is the maximum number } { of content frags to add, and the second is the percent chance } { of each frag appearing. } FCMode := MODE_Some; Content_Num := ExtractValue( throw ); Content_Chance := ExtractValue( throw ); end else if UpCase( cmd[1] ) = 'V' then begin { We want to add a VARIETY of content. This command should be } { followed by two numbers as well: In this case, the first number } { is the minimum number of content frags to add and the second } { number is the maximum. } FCMode := MODE_Variety; Content_Num := ExtractValue( throw ); Content_Chance := ExtractValue( throw ); end else begin FCMode := MODE_Fill; end; { PARAM 2: HERE or SUB } cmd := ExtractWord( throw ); DoSub := UpCase( cmd[1] ) = 'S'; { PARAM 3: The content type. } Content_Type := ExtraCTWord( throw ); { PARAM 4: The content parameter. } Content_Parameter := ExtraCTWord( throw ); if Content_Parameter = '' then Content_Parameter := 'na'; { Finally, actually add the content. } if FCMode = MODE_Some then begin while Content_Num > 0 do begin if Random( 100 ) < Content_Chance then begin if DoSub then begin Zone := FindFreeZone( GB , MF , Cells,SCheck,STerr ); end else begin Zone := MF; end; if Zone = Nil then break; AddContent( Content_Type , GB , Source , Zone , Content_Parameter , Cells , SCheck , STerr ); end; Dec( Content_Num ); end; end else if FCMode = MODE_Fill then begin while Cells <> Nil do begin if DoSub then begin Zone := FindFreeZone( GB , MF , Cells,SCheck,STerr ); end else begin Zone := MF; end; if Zone = Nil then break; AddContent( Content_Type , GB , Source , Zone , Content_Parameter , Cells , SCheck , STerr ); end; end else begin { Determine how many fragments to add, then send the info to the correct procedure. } if Content_Chance > Content_Num then Content_Num := Content_Num + Random( Content_Chance - Content_Num + 1 ); AddUniqueContentSequence( Content_Type , GB , Source , MF , Content_Parameter , Cells , SCheck , STerr , Content_Num , DoSub); end; end; SA := SA^.Next; end; { Finally, move all the content we've created into the adventure. } InsertFinishedContent( GB , FindRoot( GB^.Scene ) , Source ); end; Procedure RenderFeature( GB: GameBoardPtr; MF: GearPtr ); { Render the provided map feature on the provided game board } { in all it's glory. GB must already be initialized for this } { procedure to do it work. The order in which things will be } { done is as follows: } { - MF itself will be rendered. } { - MF's subcoms will be recursively rendered via this procedure. } { - If a GAPFILL string is defined, empty spaces within the } { boundaries of MF will be sought and stuffed with stuff. } var Cells: SAttPtr; SubFeature: GearPtr; Placement_String,Gapfill_String,Special_String: String; X,Y,W,H,Style,Select_Check,Select_Terrain: Integer; begin { Initialize miscellaneous values. } Cells := Nil; if MF = Nil then begin { This will be a basic-form scene. } Style := 0; X := 1; Y := 1; W := GB^.MAP_Width; H := GB^.MAP_Height; end else if IsAScene( MF ) or ( MF^.G = GG_World ) then begin Style := MF^.Stat[ STAT_MapGenerator ]; if SAttValue( MF^.SA , 'TERRAIN' ) = '' then SetSAtt( MF^.SA , 'TERRAIN <' + SAttValue( Standard_Param_List , 'TERRAIN' + BStr( Style ) ) + '>' ); X := 1; Y := 1; W := GB^.MAP_Width; H := GB^.MAP_Height; end else if MF^.G = GG_MapFeature then begin { If the map feature's SPECIAL string indicates this is a SUBZONE, } { better copy the subzone type from the parent map feature. } Special_String := SAttValue( MF^.SA , 'SPECIAL' ); if AStringHasBString( Special_String , 'SUBZONE' ) and ( MF^.Parent <> Nil ) then begin MF^.S := SubZoneType( MF^.Parent ); end; if AStringHasBString( Special_String , 'SHAREDPALETTE' ) and ( MF^.Parent <> Nil ) then begin { Copy the palette from the parent. } for X := STAT_MFFloor to STAT_MFSpecial do begin MF^.Stat[ X ] := MF^.Parent^.Stat[ X ]; end; end; Style := MF^.S; X := MF^.Stat[ STAT_XPos ]; Y := MF^.Stat[ STAT_YPos ]; W := MF^.Stat[ STAT_MFWidth ]; H := MF^.Stat[ STAT_MFHeight ]; end; { Do the drawing. } Cells := TheRenderer( GB , MF , X , Y , W , H , Style ); { Now that we know the style, determine the SELECTOR parameters. } Placement_String := ''; if MF <> Nil then Placement_String := SAttValue( MF^.SA , 'SELECTOR' ); if Placement_String = '' then Placement_String := SAttValue( Standard_Param_List , 'SELECTOR' + BStr( Style ) ); Select_Check := ExtractValue( Placement_String ); if Select_Check <> 0 then Select_Terrain := DecideTerrainType( MF , Placement_String , ExtractValue( Placement_String ) ); { Also the GapFill parameters. } GapFill_String := ''; if MF <> Nil then GapFill_String := SAttValue( MF^.SA , 'GAPFILL' ); if GapFill_String = '' then GapFill_String := SAttValue( Standard_Param_List , 'GAPFILL' + BStr( Style ) ); { Loop through MF's subcoms here. } if MF <> Nil then begin { First, do the placement. } SubFeature := MF^.SubCom; while SubFeature <> Nil do begin { Select placement of SubFeature within boundaries of MF. } if SubFeature^.G = GG_MapFeature then begin SelectPlacementPoint( GB , MF , SubFeature , Cells , Select_Check , Select_Terrain ); end; { Move to the next sub-feature. } SubFeature := SubFeature^.Next; end; { Next, add the random content. This will likely add more } { map features. } if not AStringHasBString( SAttValue( MF^.SA , 'SPECIAL' ) , SPECIAL_Unchartable ) then AddFeatureContent( GB, MF, Cells, Select_Check,Select_Terrain ); { Finally, do the rendering. } SubFeature := MF^.SubCom; while SubFeature <> Nil do begin { Select placement of SubFeature within boundaries of MF. } if SubFeature^.G = GG_MapFeature then begin { Call the renderer. } RenderFeature( GB , SubFeature ); end; { Move to the next sub-feature. } SubFeature := SubFeature^.Next; end; end; { If GAPFILL defined, check for empty spaces. } if GapFill_String <> '' then DoGapFilling( GB , MF , X , Y , W , H , Select_Check , Select_Terrain , Gapfill_String , Cells ); { Delete the cells, since we're finished with them. } if Cells <> Nil then DisposeSAtt( Cells ); end; Procedure AdjustDimensions( Scene: GearPtr ); { Thanks to the new random quests, it's now possible that a scene will be } { given more map content than it can hold... drat. This procedure checks } { the situation and with any help will resize the map so everything fits. } const MODE_MonkeyMaze = 1; MODE_Mall = 2; MODE_Club = 3; MODE_Cellbox = 4; Function NumCells( DMode: Integer ): Integer; { Based on the dimensions of SCENE and the mapgenerator indicated, } { return the number of cells that should be present. } { Note that this will not nessecarily be exactly equal to the number of } { cells the map generator produces, but it should be less than or equal to. } var X,Y,C: Integer; begin if DMode = MODE_MonkeyMaze then begin X := Scene^.Stat[ STAT_MapWidth ] div 7; Y := Scene^.Stat[ STAT_MapHeight ] div 7; C := ( X * Y ) div 3; end else if DMode = MODE_Mall then begin X := ( Scene^.Stat[ STAT_MapWidth ] - 1 ) div 6; if X > 1 then Dec( X ); Y := ( Scene^.Stat[ STAT_MapHeight ] - 1 ) div 7; C := X * Y; end else if DMode = MODE_Cellbox then begin X := ( Scene^.Stat[ STAT_MapWidth ] - 2 ) div 5; Y := ( Scene^.Stat[ STAT_MapHeight ] - 2 ) div 5; C := X * Y; end else begin X := ( Scene^.Stat[ STAT_MapWidth ] - 12 ) div 6; if Scene^.Stat[ STAT_MapHeight ] >= 19 then X := X * 2; Y := ( Scene^.Stat[ STAT_MapHeight ] - 12 ) div 6; C := X + Y; end; NumCells := C; end; var DMode: Integer; { The dimension-calculating mode. } MapGen: String; { The map generation string. } NeededCells: Integer; { The number of cells needed for this map. } MF: GearPtr; { A loop variable, for counting map features. } begin { Step one- determine the map generation script. If MONKEYMAP, CLUB or MALL are } { included we may need to resize. } MapGen := SAttValue( Scene^.SA , 'PARAM' ); if MapGen = '' then MapGen := SAttValue( Standard_Param_List , 'PARAM' + BStr( Scene^.Stat[ STAT_MapGenerator ] ) ); { Depending on which of the map generators are being used, we may not have any resizing } { to do at all. Only MonkeyMaze, Mall, and Club may be resized. } if AStringHasBString( MapGen , 'MONKEYMAZE' ) then DMode := MODE_MOnkeyMaze else if AStringHasBString( MapGen , 'MALL' ) then DMode := MODE_Mall else if AStringHasBString( MapGen , 'CLUB' ) then DMode := MODE_Club else if AStringHasBString( MapGen , 'CELLBOX' ) then DMode := MODE_CellBox else exit; { We've got a mode; firgure out how many cells are needed. } NeededCells := NAttValue( Scene^.NA , NAG_Narrative , NAS_NeededCells ); MF := Scene^.SubCom; while MF <> Nil do begin { For every map feature, minus the entrance grid, we're going to need } { some more cells. } if ( MF^.G = GG_MapFeature ) and ( UpCase( SAttValue( MF^.SA , 'DESIG' ) ) <> 'ENTRANCEGRID' ) then Inc( NeededCells ); MF := MF^.Next; end; { As long as our map isn't big enough (but hasn't yet reached maximum size), } { increase its dimensions. } while ( NumCells( DMode ) < NeededCells ) and (( Scene^.Stat[ STAT_MapWidth ] < MaxMapWidth ) or ( Scene^.Stat[ STAT_MapHeight ] < MaxMapWidth )) do begin if ( Random( 2 ) = 1 ) or ( Scene^.Stat[ STAT_MapWidth ] = MaxMapWidth ) then begin Scene^.Stat[ STAT_MapWidth ] := Scene^.Stat[ STAT_MapWidth ] + 5; if Scene^.Stat[ STAT_MapWidth ] > MaxMapWidth then Scene^.Stat[ STAT_MapWidth ] := MaxMapWidth; end else begin Scene^.Stat[ STAT_MapHeight ] := Scene^.Stat[ STAT_MapHeight ] + 5; if Scene^.Stat[ STAT_MapHeight ] > MaxMapWidth then Scene^.Stat[ STAT_MapHeight ] := MaxMapWidth; end; end; end; Procedure DeleteTempFeatures( var LList: GearPtr ); { Certain map features will have been marked as temporary. Delete those now. } var MF,MF2: GearPtr; begin MF := LList; while MF <> Nil do begin MF2 := MF^.Next; if ( MF^.G = GG_MapFeature) and ( NAttValue( MF^.NA , NAG_EpisodeData , NAS_Temporary ) <> 0 ) then begin RemoveGear( LList , MF ); end else begin DeleteTempFeatures( MF^.SubCom ); end; MF := MF2; end; end; function RandomMap( Scene: GearPtr ): GameBoardPtr; {Allocate a new GameBoard and stock it with random terrain.} var it: GameBoardPtr; FName: String; begin { Initialize the UniqueZoneNum variable. } UniqueZoneNum := 1; if Scene <> Nil then begin FName := SAttValue( Scene^.SA , 'MAP' ); if FName <> '' then begin { This scene is supposed to use a prefabricated map. Here's } { how we deal with this unpleasant situation: Create the gameboard } { by loading the map from disk. This map will likely have cells } { defined on it, but we won't worry about that here. Set the map } { generator to he PreGen type; this action will convert the MapEd } { cells to a list usable by this unit, and won't overwrite the } { map as defined. } it := LoadPredrawnMap( FName ); SetSAtt( Scene^.SA , 'PARAM ' ); end else begin AdjustDimensions( Scene ); if Scene^.STAT[ STAT_MAPWIDTH ] < 10 then Scene^.STAT[ STAT_MAPWIDTH ] := 10 else if Scene^.STAT[ STAT_MAPWIDTH ] > MaxMapWidth then Scene^.STAT[ STAT_MAPWIDTH ] := MaxMapWidth; if Scene^.STAT[ STAT_MAPHEIGHT ] < 10 then Scene^.STAT[ STAT_MAPHEIGHT ] := 10 else if Scene^.STAT[ STAT_MAPHEIGHT ] > MaxMapWidth then Scene^.STAT[ STAT_MAPHEIGHT ] := MaxMapWidth; it := NewMap( Scene^.STAT[ STAT_MAPWIDTH ] , Scene^.STAT[ STAT_MAPHEIGHT ] ); end; end else begin it := NewMap( 50 , 50 ); end; it^.Scene := Scene; High_Component_ID := 1; RenderFeature( it , Scene ); if Scene <> Nil then DeleteTempFeatures( Scene^.SubCom ); RandomMap := it; end; initialization Standard_Param_List := LoadStringList( RandMaps_Param_File ); random_scene_content := LoadRandomSceneContent( 'RANCON_*.txt' , series_directory ); super_prop_list := LoadRandomSceneContent( 'RANPROP_*.txt' , series_directory ); finalization DisposeSAtt( Standard_Param_List ); DisposeGear( random_scene_content ); DisposeGear( super_prop_list ); end. GH2/ghholder.pp0000644000175000017500000000513111326004556012233 0ustar kaolkaolunit ghholder; { This unit defines hands and weapon mounts. } { 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,texutil,ui4gh; { *** HOLDER FORMAT *** } { G = GG_Holder } { S = Holder Type } { V = Undefined } const GS_Hand = 0; GS_Mount = 1; function HolderName( Part: GearPtr ): String; Procedure CheckHolderRange( Part: GearPtr ); Function IsLegalHolderInv( Slot, Equip: GearPtr ): Boolean; implementation function HolderName( Part: GearPtr ): String; { Return a default name for this part type. } begin HolderName := MsgString( 'HolderName_' + BStr( Part^.S ) ); end; Procedure CheckHolderRange( Part: GearPtr ); { Examine everything about this part and make sure the values } { fall within the acceptable range. } var T: Integer; begin { Check S - Holder Type } if Part^.S < 0 then Part^.S := 1 else if Part^.S > ( 1 ) then Part^.S := 1; { Check V - Undefined } Part^.V := 1; { Check Stats - No Stats are defined. } for t := 1 to NumGearStats do Part^.Stat[ T ] := 0; end; Function IsLegalHolderInv( Slot, Equip: GearPtr ): Boolean; { Check EQUIP to see if it can be stored in SLOT. } { INPUTS: Slot and Equip must both be properly allocated gears. } { Mounting Points may mount weapons, movesys. } { Hands may hold weapons, sensors. } var it: Boolean; begin if Slot^.S = GS_Hand then begin Case Equip^.G of GG_Weapon: it := true; GG_Tool: it := true; else it := False; end; end else begin Case Equip^.G of GG_Weapon: it := true; GG_Tool: it := true; else it := False; end; end; { If the item is of a different scale than the holder, } { it can't be held. } if Equip^.Scale <> Slot^.Scale then it := False; IsLegalHolderInv := it; end; end. GH2/ghguard.pp0000644000175000017500000001736311326004555012071 0ustar kaolkaolunit ghguard; { This unit is meant to handle Shields and External Armor. } { 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,gears,ghmodule,ghchars,ghmecha,ui4gh; { *** SHIELD FORMAT *** } { G = GG_Shield } { S = Shield Type } { V = Armor Rating } { *** EXARMOR FORMAT *** } { G = GG_ExArmor } { S = Module Fit } { V = Armor Rating } { Stats = Stat modifiers, measured in tenths of a point } { *** HARNESS FORMAT *** } { G = GG_Harness } { S = Module Fit } { V = Complexity } const STAT_ShieldBonus = 1; { Bonus to defense roll when using this shield. } NumShieldType = 2; GS_PhysicalShield = 0; GS_EnergyShield = 1; Function ShieldName( Part: GearPtr ): String; Function ShieldBaseMass( Part: GearPtr ): Integer; Function ShieldValue( Part: GearPtr ): LongInt; Procedure CheckShieldRange( Part: GearPtr ); Function IsLegalShieldSub( Part, Equip: GearPtr ): Boolean; Function ArmorName( Part: GearPtr ): String; Function ArmorBaseMass( Part: GearPtr ): Integer; Function ArmorValue( Part: GearPtr ): LongInt; Procedure CheckArmorRange( Part: GearPtr ); Function IsLegalArmorSub( Equip: GearPtr ): Boolean; Function ArmorFitsMaster( Armor,Master: GearPtr ): Boolean; Function IsLegalHarnessSub( Equip: GearPtr ): Boolean; Procedure CheckHarnessRange( Part: GearPtr ); Function HarnessValue( Part: GearPtr ): LongInt; implementation Function ShieldName( Part: GearPtr ): String; { Return a name for this particular shield. } begin if Part^.S = GS_PhysicalShield then begin ShieldName := ReplaceHash( MsgString( 'SHIELDNAME_REGULAR' ) , BStr( Part^.V ) ); end else begin ShieldName := ReplaceHash( MsgString( 'SHIELDNAME_BEAM' ) , BStr( Part^.V ) ); end; end; Function ShieldBaseMass( Part: GearPtr ): Integer; { Return the weight of this shield. } { Regular shields are heavy; Energy shields are almost weightless. } var it: Integer; begin { The base weight of a shield is its PV + Bonus. } { Easier to defend with shields tend to be larger, so they are heavier. } if Part^.S = GS_PhysicalShield then begin it := Part^.V + Part^.Stat[ STAT_ShieldBonus ]; if it < 1 then it := 1; end else begin { Energy shields weigh 1 unit for the emitter. } it := 1; end; ShieldBaseMass := it; end; Function ShieldValue( Part: GearPtr ): LongInt; { Calculate the value of this shield, ignoring for the moment } { its subcomponents. } var it: LongInt; begin it := BaseArmorCost( Part , Part^.V ) div 2; if Part^.S = GS_EnergyShield then it := it * 3; { Modify the cost for the shield's defense bonus. } if Part^.Stat[ STAT_ShieldBonus ] > 0 then begin { +25% per positive point. } it := it * ( 4 + Part^.Stat[ STAT_ShieldBonus ] ) div 4; end else if Part^.Stat[ STAT_ShieldBonus ] < 0 then begin { -10% per negative point. } it := it * ( 10 + Part^.Stat[ STAT_ShieldBonus ] ) div 10; end; ShieldValue := it; end; Procedure CheckShieldRange( Part: GearPtr ); { Examine this sensor to make sure everything is legal. } begin { Check S - Shield Type } if Part^.S < 0 then Part^.S := 0 else if Part^.S >= NumShieldType then Part^.S := 0; { Check V - Armor Value } if Part^.V < 1 then Part^.V := 1 else if Part^.V > 10 then Part^.V := 10; { Check Stats - Stat 1 = Shield Bonus. } if Part^.Stat[ STAT_ShieldBonus ] < -5 then Part^.Stat[ STAT_ShieldBonus ] := -5 else if Part^.Stat[ STAT_ShieldBonus ] > 5 then Part^.Stat[ STAT_ShieldBonus ] := 5; end; Function IsLegalShieldSub( Part, Equip: GearPtr ): Boolean; { TRUE if EQUIP can be installed in the shield, FALSE otherwise. } begin if Part^.S = GS_PhysicalShield then begin if Equip^.G = GG_Weapon then IsLegalShieldSub := True else IsLegalShieldSub := False; end else IsLegalShieldSub := False; end; Function ArmorName( Part: GearPtr ): String; { Return a name for this particular armor. } begin ArmorName := ReplaceHash( ReplaceHash( MsgString( 'ARMORNAME' ) , BStr( Part^.V ) ) , MsgString( 'MODULENAME_'+BStr( Part^.S ) ) ); end; Function ArmorBaseMass( Part: GearPtr ): Integer; { Return the weight of this armor. } begin ArmorBaseMass := Part^.V * 2; end; Function ArmorValue( Part: GearPtr ): LongInt; { Return the cost of this armor. } var Cost: LongInt; begin { Start with the base price of the armor. } Cost := BaseArmorCost( Part , Part^.V ); { Add the value from the statmodifiers. } Cost := Cost + StatModifierCost( Part ) * 2; ArmorValue := Cost; end; Procedure CheckArmorRange( Part: GearPtr ); { Examine this sensor to make sure everything is legal. } var T: Integer; begin { Check S - Module Type } if Part^.S < 1 then Part^.S := GS_Storage else if Part^.S > NumModule then Part^.S := GS_Storage; { Check V - Armor Value } { Note that PV-0 armor may represent normal clothing. } if Part^.V < 0 then Part^.V := 0 else if Part^.V > 10 then Part^.V := 10; { Check Stats - Stat modifiers. } { Stat modifiers are only legal on personal scale armor. } if Part^.Scale = 0 then begin for t := 1 to NumGearStats do begin if Part^.Stat[t] < 0 then Part^.Stat[t] := 0 else if Part^.Stat[t] > 10 then Part^.Stat[t] := 10; end; end else for t := 1 to NumGearStats do Part^.Stat[t] := 0; end; Function IsLegalArmorSub( Equip: GearPtr ): Boolean; { Return TRUE if EQUIP can be mounted in armor, FALSE otherwise. } begin case Equip^.G of GG_Weapon: IsLegalArmorSub := True; GG_MoveSys: IsLegalArmorSub := True; else IsLegalArmorSub := False end; end; Function ArmorFitsMaster( Armor,Master: GearPtr ): Boolean; { Determine whether or not ARMOR fits MASTER, based upon the } { armor's FITS string. } var ADesc,MDesc: String; begin ADesc := SATTValue( Armor^.SA , 'FITS' ); if Master = Nil then begin MDesc := ''; end else begin case Master^.G of GG_Mecha: MDesc := MechaTraitDesc( Master ); GG_Character: MDesc := NPCTraitDesc( Master ); else MDesc := ''; end; end; ArmorFitsMaster := PartMatchesCriteria( MDesc , ADesc ); end; Function IsLegalHarnessSub( Equip: GearPtr ): Boolean; { Return TRUE if EQUIP can be placed in a harness, or FALSE otherwise. } begin if Equip = Nil then begin IsLegalHarnessSub := False; end else case Equip^.G of GG_Weapon, GG_MoveSys,GG_PowerSource,GG_Computer: IsLegalHarnessSub := True; else IsLegalHarnessSub := False; end; end; Procedure CheckHarnessRange( Part: GearPtr ); { Check to make sure that all of this harness's values are in the } { legal range. } begin { Check S - Module Type } if Part^.S < 1 then Part^.S := GS_Storage else if Part^.S > NumModule then Part^.S := GS_Storage; { Check V - Storage Slots } if Part^.V < 1 then Part^.V := 1 else if Part^.V > 10 then Part^.V := 10; { Scale must be 0. } Part^.Scale := 0; end; Function HarnessValue( Part: GearPtr ): LongInt; { Return the value of this harness. } begin HarnessValue := Part^.V * 25; end; end. GH2/navigate.pp0000644000175000017500000005123011333736447012247 0ustar kaolkaolunit navigate; { This unit is the flow controller for the RPG bits of the game. } { It decides where the PC is, then when the PC exits a scene it } { decides where to go next. } { 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,backpack, {$IFDEF ASCII} vidgfx; {$ELSE} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$ENDIF} Const Max_Number_Of_Plots = 40; Plots_Per_Generation = 5; Procedure SaveChar( PC: GearPtr ); Procedure SaveEgg( Egg: GearPtr ); Procedure Navigator( Camp: CampaignPtr; Scene: GearPtr; var PCForces: GearPtr ); Procedure StartCampaign( Egg: GearPtr ); Procedure RestoreCampaign( RDP: RedrawProcedureType ); implementation uses arenaplay,arenascript,interact,gearutil,narration,texutil,ghprop,rpgdice,ability, ghchars,ghweapon,movement,ui4gh,gearparser,playwright,randmaps,mpbuilder, {$IFDEF ASCII} vidmap,vidmenus; {$ELSE} {$IFDEF CUTE} cutemap,glmenus; {$ELSE} glmap,glmenus; {$ENDIF} {$ENDIF} Procedure DebugMessage( msg: String ); { Display a debugging message, and refresh the screen right away. } begin DialogMsg( msg ); ClrScreen; InfoBox( ZONE_Dialog ); RedrawConsole; DoFlip; end; Procedure SaveChar( PC: GearPtr ); { Save this character to disk, in the "SaveGame" directory. } var Leader: GearPtr; FName: String; { Filename for the character. } F: Text; { The file to write to. } begin Leader := PC; while ( Leader <> Nil ) and ( ( Leader^.G <> GG_Character ) or ( NAttValue( Leader^.NA , NAG_CharDescription , NAS_CharType ) <> 0 ) ) do Leader := Leader^.Next; if Leader = Nil then Exit; FName := Save_Character_Base + GearName(Leader) + Default_File_Ending; Assign( F , FName ); Rewrite( F ); WriteCGears( F , PC ); Close( F ); end; Procedure SaveEgg( Egg: GearPtr ); { Save this character to disk, in the "SaveGame" directory. } var Leader: GearPtr; FName: String; { Filename for the character. } F: Text; { The file to write to. } begin Leader := Egg^.SubCom; while ( Leader <> Nil ) and ( ( Leader^.G <> GG_Character ) or ( NAttValue( Leader^.NA , NAG_CharDescription , NAS_CharType ) <> 0 ) ) do Leader := Leader^.Next; if Leader = Nil then Exit; FName := Save_Egg_Base + GearName(Leader) + Default_File_Ending; Assign( F , FName ); Rewrite( F ); WriteCGears( F , Egg ); Close( F ); end; Function NoLivingPlayers( PList: GearPtr ): Boolean; { Return TRUE if the provided list of gears contains no } { living characters. Return FALSE if it contains at least } { one. } var it: Boolean; begin { Start by assuming TRUE, then set to FALSE if a character is found. } it := TRUE; { Loop through all the gears in the list. } while PList <> Nil do begin if ( PList^.G = GG_Character ) and NotDestroyed( PList ) and ( NAttValue( PList^.NA , NAG_CharDescription , NAS_CharType ) = 0 ) then begin it := False; end; PList := PList^.Next; end; { Return the result. } NoLivingPlayers := it; end; Procedure CampaignUpkeep( Camp: CampaignPtr ); { Do some gardening work on this campaign. This procedure keeps } { everything in the CAMP structure shiny, fresh, and working. } { - Load a new PLOT, if appropriate. } { - Delete dynamic scenes. } var Part,Part2: GearPtr; N,N2: NAttPtr; begin { Get rid of any dynamic scenes that have outlived their usefulness. } { If a SCENE is found in the InvComs, it must be dynamic. } Part := Camp^.Source^.InvCom; while Part <> Nil do begin Part2 := Part^.Next; if ( Part^.G = GG_Scene ) or ( Part^.G = GG_MetaScene ) then begin DeleteFrozenLocation( GearName( Part ) , Camp^.Maps ); RemoveGear( Camp^.Source^.InvCom , Part ); end; Part := Part2; end; { Get rid of any PlotStatuses saved for plots which have concluded. } N := Camp^.Source^.NA; while N <> Nil do begin N2 := N^.Next; if ( N^.G = NAG_PlotStatus ) and ( N^.S > 0 ) then begin Part := SeekGearByIDTag( Camp^.Source^.InvCom , NAG_PlotStatus , N^.S , 1 ); if Part = Nil then begin RemoveNAtt( Camp^.Source^.NA , N ); end; end; N := N2; end; end; Procedure Navigator( Camp: CampaignPtr; Scene: GearPtr; var PCForces: GearPtr ); { This is the role-playing flow controller. It decides what scene } { of an adventure gear to load next. } var N: Integer; begin repeat if SCene <> Nil then N := ScenePlayer( Camp , Scene , PCForces ); { Move to the destination scene, if appropriate. } if N > 0 then begin { Perform upkeep on the campaign- delete dynamic scenes, } { load new plots, yadda yadda yadda. } CampaignUpkeep( Camp ); Scene := FindActualScene( Camp^.Source , N ); end else if N < 0 then begin Scene := FindMetascene( Camp^.Source , N ); { If no destination scene was implied, check to see if there's } { a dynamic scene waiting to be processed. } end else if SCRIPT_DynamicEncounter <> Nil then begin Scene := SCRIPT_DynamicEncounter; { Stick the scene into the campaign. Normally scenes } { are filed under SubComs, but in this case we'll store } { it as an InvCom so we'll remember to delete it later. } InsertInvCom( Camp^.Source , Scene ); { Set the DynamicEncounter var to Nil, since we've moved } { the scene to the campaign and don't want the ArenaScript } { procedures to try and modify or delete it any more. } SCRIPT_DynamicEncounter := Nil; { Set N to >0, since we don't want the "until..." } { condition to exit. } N := 1; end; until ( N = 0 ) or NoLivingPlayers( PCForces ) or ( Scene = Nil ); { If the game is over because the PC died, do a [MORE] prompt. } if NoLivingPlayers( PCForces ) then begin MoreKey; end; end; Procedure InitializeCampaignScenes( Adv: GearPtr; var HiSceneID: Integer ); { Initialize the scenes of this adventure. This involves providing them all } { with unique IDs, inserting content where needed, adding entrances to superscenes. } Procedure CheckAlongPath( S: GearPtr ); { Search along this path, initializing everything. } begin while S <> Nil do begin if S^.G = GG_Scene then begin S^.S := HiSceneID; Inc( HiSceneID ); if AStringHasBString( SAttValue( S^.SA , 'TYPE' ) , 'DUNGEON' ) and ( SAttValue( S^.SA , 'DENTRANCE' ) <> '' ) and ( NAttValue( S^.NA , NAG_Narrative , NAS_DungeonLevel ) = 0 ) then begin ExpandDungeon( S ); end; ConnectScene( S , True ); end; CheckAlongPath( S^.SubCom ); S := S^.Next; end; end; Procedure InitNamedExits( Adv,Part: GearPtr ); { Search for gears with a DESTINATION string attribute. Try to } { set the destination stat for these gears. } var dest: String; scene: GearPtr; begin while Part <> Nil do begin dest := SAttValue( Part^.SA , 'DESTINATION' ); if dest <> '' then begin scene := SeekGearByName( Adv , dest ); if ( Scene <> Nil ) and (( Scene^.G = GG_Scene ) or ( Scene^.G = GG_World )) and ( Part^.G = GG_MetaTerrain ) then begin Part^.Stat[ STAT_Destination ] := Scene^.S; end; end; InitNamedExits( Adv , Part^.SubCom ); InitNamedExits( Adv , Part^.InvCom ); Part := Part^.Next; end; end; begin CheckAlongPath( Adv ); InitNamedExits( Adv , Adv ); { Store the HiSceneID for later use. } SetNAtt( Adv^.NA , NAG_Narrative , NAS_MaxSceneID , HiSceneID ); end; Procedure InitializeCampaignNPCs( Adv: GearPtr ); { Provide unique character IDs for each of the pre-loaded characters. } Procedure CheckAlongPath( P: GearPtr ); var S: GearPtr; { The Persona for this NPC. } CID: LongInt; begin while P <> Nil do begin if P^.G = GG_Character then begin CID := NewCID( Adv ); SetNAtt( P^.NA , NAG_Personal , NAS_CID , CID ); S := SeekGearByName( Adv , GearName( P ) + ' PERSONA' ); if S <> Nil then S^.S := CID; end; CheckAlongPath( P^.SubCom ); CheckAlongPath( P^.InvCom ); P := P^.Next; end; end; begin CheckAlongPath( Adv ); end; Procedure InitializeAdventureContent( Adv,HomeTown,Egg: GearPtr ); { Initialize the static adventure content. This consists mostly of } { searching through the structure for content requests and filling } { those recursively as needed. } { Also add the content contained within the Egg. Most of this content } { will require quests for placement/initialization. } const NumSubQuests = 8; var MasterList: GearPtr; { The master list of adventure content components. } Procedure CheckAlongPath( LList: GearPtr ); { Check along this list of scenes for content requests, } { also checking the sub- and inv-coms. } var ConReq: String; SA: SAttPtr; begin while LList <> Nil do begin if LList^.G = GG_Scene then begin { A scene can have multiple quests defined. } SA := LList^.SA; while SA <> Nil do begin if HeadMatchesString( 'QUEST' , SA^.Info ) then begin ConReq := RetrieveAString( SA^.Info ); if not AddQuest( Adv , FindRootScene( LList ) , Nil , MasterList , ConReq ) then begin if XXRan_Debug then DialogMsg( 'ERROR: AddQuest failed for ' + ConReq ); end; end; SA := SA^.Next; end; end; CheckAlongPath( LList^.SubCom ); CheckAlongPath( LList^.InvCom ); LList := LList^.Next; end; end; Procedure PlaceEggNPCs( LList: GearPtr ); { Take the NPCs from the egg and place them in the adventure. } { Actually, we won't be placing them, but clones of them... } { anyhow, go and do it. } const Default_NPC_Quest = '*EGG_Default'; var ConReq: String; begin while LList <> Nil do begin if LList^.G = GG_Character then ExpandCharacter( LList ); ConReq := SAttValue( LList^.SA , 'QUEST' ); { If this content request is empty, assign the default value. } if ConReq = '' then ConReq := Default_NPC_Quest; if not AddQuest( Adv , HomeTown , LList , MasterList , ConReq ) then begin if XXRan_Debug then DialogMsg( 'ERROR: AddQuest failed for ' + ConReq + '/' + GearName( LList ) ); end; LList := LList^.Next; end; end; begin { Load the component library. } MasterList := LoadQuestFragments; { Start checing the adventure scenes for content requests. } CheckAlongPath( Adv^.SubCom ); { Place the NPCs from the egg. Some of these will likely make use } { of the quest fragments loaded above. } PlaceEggNPCs( Egg^.InvCom ); { Get rid of the master list. } DisposeGear( MasterList ); end; Procedure VerifySceneExits( LList: GearPtr ); { Check all of the exits you can find. If any of them are negative, this is a bad thing. } { Fix the problem by pointing them to their parent scene. } Function GetScene( S: GearPtr ): GearPtr; { Locate the scene that's the most recent ancestor of S. } begin while ( S <> Nil ) and ( S^.G <> GG_Scene ) do S := S^.Parent; GetScene := S; end; var Scene: GearPtr; begin while LList <> Nil do begin if ( LList^.G = GG_MetaTerrain ) and ( LList^.Stat[ STAT_Destination ] < 0 ) then begin { This metaterrain is in violation. Fix it. } Scene := GetScene( LList ); if ( Scene <> Nil ) and ( Scene^.Parent <> Nil ) then begin LList^.Stat[ STAT_Destination ] := Scene^.Parent^.S; if LList^.Stat[ STAT_Destination ] < 1 then begin DialogMsg( 'ERROR: Invalid SceneID for ' + GearName( LList ) + '.' ); LList^.Stat[ STAT_Destination ] := 0; end; end else begin DialogMsg( 'ERROR: Parent scene not found for ' + GearName( LList ) + '.' ); end; end; VerifySceneExits( LList^.SubCom ); VerifySceneExits( LList^.InvCom ); LList := LList^.Next; end; end; Procedure StartCampaign( Egg: GearPtr ); { Start a new RPG campaign. } { - Load the atlas files, then assemble them into an adventure. } { - Initialize all the cities. } { - Insert the PC's central story. } const Default_Residence_Desig = '*EGG_RESIDENCE_Apartment'; var Camp: CampaignPtr; PCForces,TruePC,Atlas,S,S2,W,Story,Club: GearPtr; Factions,Artifacts: GearPtr; HighWorldID: Integer; Base,Changes: String; { Context Strings. } begin {$IFNDEF ASCII} Idle_Display; {$ENDIF} { Extract the PCForces from the Egg. } PCForces := Nil; while Egg^.SubCom <> Nil do begin S := Egg^.SubCom; DelinkGear( Egg^.SubCom , S ); AppendGear( PCForces , S ); end; { Locate the TruePC. } TruePC := PCForces; while ( TruePC <> Nil ) and ( ( TruePC^.G <> GG_Character ) or ( NAttValue( TruePC^.NA , NAG_CharDescription , NAS_CharType ) <> 0 ) ) do TruePC := TruePC^.Next; { Expand the TruePC. Maybe. } if TruePC^.SubCom = Nil then begin ExpandCharacter( TruePC ); end; { Give the PC a personal communicator. Maybe. } if TruePC^.InvCom = Nil then begin Artifacts := LoadNewItem( 'Personal Communicator' ); if Artifacts <> Nil then InsertInvCom( TruePC , Artifacts ); end; Camp := NewCampaign; Camp^.Source := LoadFile( 'adventurestub.txt' , Series_Directory ); { The Adventure source needs to store the PC's faction. } SetNAtt( Camp^.Source^.NA , NAG_Personal , NAS_FactionID , NAttValue( TruePC^.NA , NAG_Personal , NAS_FactionID ) ); Atlas := AggregatePattern( 'ATLAS_*.txt' , Series_Directory ); { Insert the factions into the adventure. } Factions := AggregatePattern( 'FACTIONS_*.txt' , Series_Directory ); InsertInvCom( Camp^.Source , Factions ); { Insert the artifacts into the adventure. } Artifacts := AggregatePattern( 'ARTIFACT_*.txt' , Series_Directory ); S := AddGear( Camp^.Source^.InvCom , Camp^.Source ); SetSAtt( S^.SA , 'name ' ); S^.G := GG_ArtifactSet; InsertInvCom( S , Artifacts ); { Insert the unique scene content into the adventure. } Artifacts := AggregatePattern( 'UNICON_*.txt' , Series_Directory ); S := AddGear( Camp^.Source^.InvCom , Camp^.Source ); SetSAtt( S^.SA , 'name ' ); S^.G := GG_ContentSet; InsertInvCom( S , Artifacts ); { Assign unique IDs to all the content bits. } HighWorldID := 1; S := Artifacts; while S <> Nil do begin SetNAtt( S^.NA , NAG_Narrative , NAS_ContentID , HighWorldID ); S := S^.Next; Inc( HighWorldID ); end; { Assemble the subcoms of the adventure. } { First, move over all the WORLDs. Then, move over all the SCENEs. } { Assign unique IDs for everything. } S := Atlas; HighWorldID := 1; while S <> Nil do begin S2 := S^.Next; if S^.G = GG_World then begin DelinkGear( Atlas , S ); InsertSubCom( Camp^.Source , S ); S^.S := HighWorldID; Inc( HighWorldID ); end; S := S2; end; { Proceed with the scenes. } S := Atlas; while S <> Nil do begin S2 := S^.Next; if S^.G = GG_Scene then begin DelinkGear( Atlas , S ); W := SeekGearByName( Camp^.Source , SAttValue( S^.SA , 'WORLD' ) ); if ( W <> Nil ) and ( ( W^.G = GG_Scene ) or ( W^.G = GG_World ) ) then begin InsertSubCom( W , S ); end else begin InsertSubCom( Camp^.Source , S ); end; end; S := S2; end; { We are now finished with the atlas. Dispose of it. } DisposeGear( Atlas ); { Locate the PC's home town. Insert a "Cavalier Club" as the starting location. } { Also insert the PC's residence. The residence type should be listed } { in the EGG. } S := SeekGearByName( Camp^.Source , SAttValue( TruePC^.SA , 'HOMETOWN' ) ); if S <> Nil then S := SeekUrbanArea( S ); if S <> Nil then begin Club := LoadSingleMecha( 'stub_cavalierclub.txt' , Series_Directory ); InsertSubCom( S , Club ); Atlas := LoadFile( 'EGG_scenes.txt' , Series_Directory ); Base := SAttValue( Egg^.SA , 'RESIDENCE' ); if Base = '' then Base := Default_Residence_Desig; Club := SeekGearByDesig( Atlas , Base ); if Club <> Nil then begin DelinkGear( Atlas , Club ); SetSAtt( Club^.SA , 'DESIG ' ); InsertSubCom( S , Club ); end; DisposeGear( Atlas ); end; { Once everything is sorted where it's supposed to go, initialize the scenes. } { They all need unique ID numbers, the dungeons need expansion and the cities } { need random content. } InitializeCampaignScenes( Camp^.Source , HighWorldID ); { Next initialize the NPCs. } InitializeCampaignNPCs( Camp^.Source ); { Locate the PC's home town again, this time to record the scene ID. } { We're also going to need this scene ID for the central story below. } S := SeekGearByName( Camp^.Source , SAttValue( TruePC^.SA , 'HOMETOWN' ) ); if S <> Nil then begin SetNAtt( TruePC^.NA , NAG_Narrative , NAS_HomeTownID , S^.S ); end; { Insert the central story. } Story := LoadFile( 'corestorystub.txt' , Series_Directory ); SetNAtt( Story^.NA , NAG_ElementID , XRP_EpisodeScene , S^.S ); SetNAtt( Story^.NA , NAG_ElementID , XRP_TargetFac , NAttValue( TruePC^.NA , NAG_Personal , NAS_FactionID ) ); SetNAtt( Camp^.Source^.NA , NAG_Personal , NAS_FactionID , NAttValue( TruePC^.NA , NAG_Personal , NAS_FactionID ) ); { Copy the PC's personal context to the story. } Base := SAttValue( Story^.SA , 'CONTEXT' ); Changes := SAttValue( TruePC^.SA , 'CONTEXT' ); AlterDescriptors( Base , Changes ); SetSAtt( Story^.SA , 'CONTEXT <' + Base + '>' ); InsertInvCom( Camp^.Source , Story ); { Insert the static adventure quests. } InitializeAdventureContent( Camp^.Source , S , Egg ); { Verify that the exits have been handled correctly. } VerifySceneExits( Camp^.Source ); { By now, we should be finished with the EGG. Get rid of it. } DisposeGear( Egg ); { Locate the Cavalier Club. This is to be the starting location. } { Being the first location entered by the PC, the Cavalier Club has } { the imaginative designation of "00000". } S := SeekGearByDesig( Camp^.Source , 'PCHOME' ); if S <> Nil then begin Navigator ( Camp , S , PCForces ); end; DisposeCampaign( Camp ); DisposeGear( PCForces ); end; Procedure RestoreCampaign( RDP: RedrawProcedureType ); { Select a previously saved unit from the menu. If no unit is } { found, jump to the CreateNewUnit procedure above. } var RPM: RPGMenuPtr; rpgname: String; { Campaign Name } Camp: CampaignPtr; F: Text; { A File } PC,Part,P2: GearPtr; DoSave: Boolean; begin { Create a menu listing all the units in the SaveGame directory. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu ); BuildFileMenu( RPM , Save_Campaign_Base + Default_Search_Pattern ); PC := Nil; { If any units are found, allow the player to load one. } if RPM^.NumItem > 0 then begin RPMSortAlpha( RPM ); DialogMSG('Select campaign file to load.'); rpgname := SelectFile( RPM , RDP ); if rpgname <> '' then begin Assign(F, Save_Game_Directory + rpgname ); reset(F); Camp := ReadCampaign(F); Close(F); Navigator( Camp , Camp^.GB^.Scene , PC ); DoSave := Camp^.Source^.V <> 0; DisposeCampaign( Camp ); end else begin DoSave := False; end; end else begin { The menu was empty... print the info message. } DialogMsg( MsgString( 'NEWRPGCAMP_NoCamps' ) ); DoSave := False; end; if ( PC <> Nil ) and ( DoSave or Always_Save_Character ) then begin if not NoLivingPlayers( PC ) then begin Part := PC; while Part <> Nil do begin P2 := Part^.Next; { Lancemates don't get saved to the character file. } if ( Part^.G = GG_Character ) and ( NAttValue( Part^.NA , NAG_CharDescription , NAS_CharType ) <> NAV_CTPrimary ) then begin RemoveGear( PC , Part ); end else begin { Everything else does get saved. } StripNAtt( Part , NAG_Visibility ); StripNAtt( Part , NAG_EpisodeData ); StripNAtt( Part , NAG_WeaponModifier ); StripNAtt( Part , NAG_Action ); StripNAtt( Part , NAG_Location ); StripNAtt( Part , NAG_Damage ); StripNAtt( Part , NAG_ReactionScore ); StripNAtt( Part , NAG_FactionScore ); StripNAtt( Part , NAG_Condition ); StripNAtt( Part , NAG_StatusEffect ); StripNAtt( Part , NAG_Narrative ); end; Part := P2; end; SaveChar( PC ); end; end; if PC <> Nil then DisposeGear( PC ); DisposeRPGMenu( RPM ); end; end. GH2/skilluse.pp0000644000175000017500000004720711326004555012303 0ustar kaolkaolunit SkillUse; { This unit should cover the usage of skills for the RPG game. } { Actually, it doesn't cover the usage of all skills- most of } { them get implemented in other places (combat skills in the } { attacker unit, conversation skills in the interact unit, etc). } { This unit covers those skills which pretty well need their } { own interface/code... repair skills, picking pockets, etc. } { 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; const Repair_Mental_Strain = 1; Repair_Max_Tries = 5; Performance_Range = 9; Performance_Base_Cash = -50; TRIGGER_Applause = 'APPLAUSE'; Repair_Skill_Needed: Array [0..NumMaterial] of Byte = ( NAS_Repair, NAS_Medicine, NAS_Repair ); Function TotalRepairableDamage( Target: GearPtr; Material: Integer ): LongInt; Procedure ApplyRepairPoints( Target: GearPtr; Material: Integer; var RP: LongInt; CureStatus: Boolean ); Procedure ApplyEmergencyRepairPoints( Target: GearPtr; Material: Integer; var RP: LongInt ); Function RepairNeededBySkill( Target: GearPtr; Skill: Integer ): LongInt; Function CanRepairUsingSkill( NPC,Target: GearPtr; Skill: Integer ): Boolean; Function UseRepairSkill( GB: GameBoardPtr; PC,Target: GearPtr; Skill: Integer ): Boolean; Procedure DoCompleteRepair( Target: GearPtr ); Function SelectPerformanceTarget( GB: GameBoardPtr; PC: GearPtr ): GearPtr; Function UsePerformance( GB: GameBoardPtr; PC,NPC: GearPtr ): LongInt; implementation uses ability,action,gearutil,ghholder,ghmodule,ghmovers,ghswag, ghweapon,movement,interact,rpgdice,texutil,narration,ghsupport; Function TotalRepairableDamage( Target: GearPtr; Material: Integer ): LongInt; { Search through TARGET, and calculate how much damage it has } { that can be repaired using SKILL. } var Part: GearPtr; AD,SD,TCom,SCom,it: LongInt; T: Integer; begin { Normally damage must be positive I know, but I just had a bug } { which resulted in negative damage. This prevented the rest of } { the damage to a mek/character from being repaired. So, taking } { absolute value should fix all the mess & prevent it from } { happening again. } SD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_StrucDamage ) ); AD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_ArmorDamage ) ); it := 0; { If this part is damaged, and if the needed repair skill is } { the skill we're looking for, add the damage to the total. } if NAttValue( Target^.NA , NAG_GearOps , NAS_Material ) = Material then begin it := AD + SD; { Modify for complexity. } if not IsMasterGear( Target ) then begin TCom := ComponentComplexity( Target ); SCom := SubComComplexity( Target ); if SCom > TCom then begin it := ( it * SCom ) div TCom; end; end; { Check for status effects. } for t := 1 to Num_Status_FX do begin if SX_Repairable[t] and ( NAttValue( Target^.NA , NAG_StatusEffect , T ) <> 0 ) then begin it := it + SX_RepCost[ T ]; end; end; end; { Check the sub-components for damage. } Part := Target^.SubCom; while Part <> Nil do begin it := it + TotalRepairableDamage( Part , Material ); Part := Part^.Next; end; { Check the inv-components for damage. } Part := Target^.InvCom; while Part <> Nil do begin it := it + TotalRepairableDamage( Part , Material ); Part := Part^.Next; end; TotalRepairableDamage := it; end; Procedure ApplyRepairPoints( Target: GearPtr; Material: Integer; var RP: LongInt; CureStatus: Boolean ); { Search through TARGET, and restore DPs to parts } { that can be repaired using SKILL. } var Part: GearPtr; SD,AD,TCom,SCom,ARP,RPNeeded: LongInt; T: Integer; begin { Only examine TARGET for damage if it's of a type that can be } { repaired using SKILL. } if NAttValue( Target^.NA , NAG_GearOps , NAS_Material ) = Material then begin { Calculate structural damage and armor damage. } SD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_StrucDamage ) ); if ( SD > 0 ) and ( RP > 0 ) then begin { Modify for complexity. } ARP := RP; RPNeeded := SD; if not IsMasterGear( Target ) then begin TCom := ComponentComplexity( Target ); SCom := SubComComplexity( Target ); if SCom > TCom then begin RPNeeded := ( RPNeeded * SCom ) div TCom; ARP := ( ARP * TCom ) div SCom; if ARP < 1 then ARP := 1; end; end; SD := SD - ARP; RP := RP - RPNeeded; if SD < 0 then SD := 0; SetNAtt( Target^.NA , NAG_Damage , NAS_StrucDamage , SD ); end; AD := Abs( NAttValue( Target^.NA , NAG_Damage , NAS_ArmorDamage ) ); if ( AD > 0 ) and ( RP > 0 ) then begin { Modify for complexity. } ARP := RP; RPNeeded := AD; if not IsMasterGear( Target ) then begin TCom := ComponentComplexity( Target ); SCom := SubComComplexity( Target ); if SCom > TCom then begin RPNeeded := ( RPNeeded * SCom ) div TCom; ARP := ( ARP * TCom ) div SCom; if ARP < 1 then ARP := 1; end; end; AD := AD - ARP; RP := RP - RPNeeded; if AD < 0 then AD := 0; SetNAtt( Target^.NA , NAG_Damage , NAS_ArmorDamage , AD ); end; { Check for status effects. } if CureStatus then begin for t := 1 to Num_Status_FX do begin if SX_Repairable[ t ] and ( NAttValue( Target^.NA , NAG_StatusEffect , T ) <> 0 ) then begin if RP >= SX_RepCost[ t ] then begin RP := RP - SX_RepCost[ t ]; SetNAtt( Target^.NA , NAG_StatusEffect , T , 0 ); end; end; end; end; end; { Check the sub-components for damage. } Part := Target^.SubCom; while ( Part <> Nil ) and ( RP > 0 ) do begin ApplyRepairPoints( Part , Material , RP , CureStatus ); Part := Part^.Next; end; { Check the inv-components for damage. } Part := Target^.InvCom; while ( Part <> Nil ) and ( RP > 0 ) do begin ApplyRepairPoints( Part , Material , RP , CureStatus ); Part := Part^.Next; end; end; Procedure ApplyEmergencyRepairPoints( Target: GearPtr; Material: Integer; var RP: LongInt ); { Try to apply the repair points first to those parts of TARGET nessecary } { for it to function. If there are any points left over, apply these to the rest. } Procedure ApplyPointsToPart( G,S: Integer ); { Locate a part with the Gear G and S descriptors provided, } { and apply repair points to it first. } var Part: GearPtr; begin Part := SeekGear( Target , G , S , False ); if ( Part <> Nil ) and ( RP > 0 ) and Destroyed( Part ) then begin ApplyRepairPoints( Part, Material, RP, False ); end; end; begin if Target^.G = GG_Character then begin ApplyPointsToPart( GG_Module , GS_Head ); ApplyPointsToPart( GG_Module , GS_Body ); if RP > 0 then ApplyRepairPoints( Target, Material, RP, False ); end else if Target^.G = GG_Mecha then begin ApplyPointsToPart( GG_Support , GS_Engine ); ApplyPointsToPart( GG_Module , GS_Body ); if RP > 0 then ApplyRepairPoints( Target, Material, RP, False ); end else ApplyRepairPoints( Target, Material, RP, False ); if RP > 0 then ApplyRepairPoints( Target, Material, RP, True ); end; Function RepairNeededBySkill( Target: GearPtr; Skill: Integer ): LongInt; { Return the amount of damage that can be affected by the listed skill. } var T,Total,RP: Longint; begin Total := 0; for t := 0 to NumMaterial do begin if ( Repair_Skill_Needed[ t ] = Skill ) then begin RP := TotalRepairableDamage( Target , T ); if RP > 0 then Total := Total + RP; end; end; RepairNeededBySkill := Total; end; Function AmountOfRepairFuel( PC: GearPtr; Material: Integer ): LongInt; { Return the total amount of repair fuel that the PC has. } var Total: LongInt; Procedure SeekRFAlongTrack( LList: GearPtr ); begin while LList <> Nil do begin if ( LList^.G = GG_RepairFuel ) and ( LList^.S = Material ) then begin Total := Total + LList^.V; end else begin SeekRFAlongTrack( LList^.SubCom ); SeekRFAlongTrack( LList^.InvCom ); end; LList := LList^.Next; end; end; begin PC := FindRoot( PC ); Total := 0; SeekRFAlongTrack( PC^.InvCom ); SeekRFAlongTrack( PC^.SubCom ); AmountOfRepairFuel := Total; end; Function CanRepairUsingSkill( NPC,Target: GearPtr; Skill: Integer ): Boolean; { Return TRUE if this target has repairable damage which can be healed using SKILL, } { and the NPC has the required repair fuel. Return FALSE otherwise. } var CanRepair: Boolean; T,Total,RP: Longint; begin CanRepair := False; for t := 0 to NumMaterial do begin if ( Repair_Skill_Needed[ t ] = Skill ) then begin RP := TotalRepairableDamage( Target , T ); if RP > 0 then begin { Alright, we found some damage to repair. } { Check for repair fuel. } if AmountOfRepairFuel( NPC , T ) > 0 then CanRepair := True; end; end; end; CanRepairUsingSkill := CanRepair; end; Function UseRepairSkill( GB: GameBoardPtr; PC,Target: GearPtr; Skill: Integer ): Boolean; { The PC wants to use the requested repair SKILL on TARGET. } { Roll to see how many DPs will be restored, apply these DPs } { to the TARGET, then reduce PC's MPs. } { Return TRUE if the repair process went smoothly, or FALSE if it was } { halted due to a shortage of materials. Note that this procedure will return } { TRUE in the case of a critical failure even if the PC has no repair fuel. } Function Repair_Skill_Target: Integer; { Return a good skill target for repair skills. } { This will be decreased as TARGET's scale increases. } var RST: Integer; begin if Target^.Scale = 0 then begin RST := 4; end else if Target^.Scale = 1 then begin RST := 3; end else begin RST := 2; end; if Destroyed( Target ) then RST := RST + 3; Repair_Skill_Target := RST; end; Procedure SpendRepairFuel( PC: GearPtr; Material , RP: LongInt ); { Spend the requested repair fuel. If any fuel is depleted, } { remove it from the inventory. } Procedure SpendRFAlongTrack( LList: GearPtr ); { Search for repair fuel to use, then use it. } var L2: GearPtr; begin while ( LList <> Nil ) and ( RP > 0 ) do begin L2 := LList^.Next; if ( LList^.G = GG_RepairFuel ) and ( LList^.S = Material ) then begin if RP >= LList^.V then begin RP := RP - LList^.V; if IsInvCom( LList ) then RemoveGear( LList^.Parent^.InvCom , LList ) else RemoveGear( LList^.Parent^.SubCom , LList ); end else begin LList^.V := LList^.V - RP; RP := 0; end; end else begin SpendRFAlongTrack( LList^.SubCom ); SpendRFAlongTrack( LList^.InvCom ); end; LList := L2; end; end; begin PC := FindRoot( PC ); SpendRFAlongTrack( PC^.InvCom ); SpendRFAlongTrack( PC^.SubCom ); end; Function ActivateRepair( Material: Integer; var SkRoll: Integer ): Boolean; { Activate the repair. Return the number of repair points used. } { Reduce SkRoll by this same amount. } { Return TRUE if repairfuel was found for this repair job, or FALSE } { if no repair at all could take place. } var RP: LongInt; RepairFuel: LongInt; RFFound: Boolean; begin RP := TotalRepairableDamage( Target , Material ); RFFound := False; { Locate the repair fuel. } RepairFuel := AmountOfRepairFuel( PC , Material ); if RepairFuel > 0 then begin { The amount of damage recovered will not exceed the skill roll * 2. } if RP > ( SkRoll * 2 ) then RP := ( SkRoll * 2 ); { Nor will it exceed the amount of repair fuel. } if RP > RepairFuel then RP := RepairFuel; { The skill roll will be reduced by the amount of damage to be repaired. } SkRoll := SkRoll - ( RP div 2 ); { Spend the repair fuel. } SpendRepairFuel( PC , Material , RP ); { Apply the repair points. } ApplyRepairPoints( Target , Material , RP , True ); RFFound := True; end; ActivateRepair := RFFound; end; var RP: LongInt; T,tries,SkRoll,SkTar,PercentDamage: Integer; IsSafeRepair,HadRepairFuel: Boolean; TMaster: GearPtr; begin { First, locate the PC. } PC := LocatePilot( PC ); if PC = Nil then Exit( False ); TMaster := FindMaster( Target ); if TMaster = Nil then TMaster := Target; PercentDamage := PercentDamaged( TMaster ); { Depending upon the situation, this repair will either fix some damage or all the } { damage in one go. If in a safe area and repairing a mecha which is not currently in play, } { the entire thing can be fixed. On the other hand, if in a dangerous area or working on a } { mecha which is in play, only a limited amount of DP will be restored. } { If the target is destroyed, this never counts as a safe repair. } IsSafeRepair := IsSafeArea( GB ) and ( not OnTheMap( GB , FindRoot( Target ) ) ) and ( ( TMaster = Nil ) or NotDestroyed( TMaster ) ); { Assume we have no repair fuel, unless we find some. } HadRepairFuel := False; { Make a skill roll against the base difficulty number. This will determine the rate } { at which points may be restored. } SkTar := Repair_Skill_Target; SkRoll := SkillRoll( GB , PC , Skill , STAT_Craft , SkTar , 0 , IsSafeArea( GB ) , True ) - SkTar; tries := 1; if IsSafeRepair then begin { Safe repairs get a bonus to the repair rate, since you don't have to worry } { about people shooting at you. This bonus also helps to mitigate the effect of } { high and low skill rolls. } SkRoll := SkRoll + SkillValue( PC , Skill , STAT_Craft ); if SkRoll < 5 then SkRoll := 5; { Because a safe repair will repair everything in one go, call the } { repair activator with an arbitrarily huge skillroll. } for t := 0 to NumMaterial do begin RP := TotalRepairableDamage( Target , T ); if ( Repair_Skill_Needed[ t ] = Skill ) and ( RP > 0 ) then begin tries := tries + RP div SkRoll; SkTar := 10000; HadRepairFuel := HadRepairFuel or ActivateRepair( T , SkTar ); end; end; { Don't make the PC wait for longer than 10 actions. } if tries > 10 then tries := 10; end else if SkRoll > 0 then begin { Apply the skill roll against all legal materials. } for t := 0 to NumMaterial do begin RP := TotalRepairableDamage( Target , T ); if ( Repair_Skill_Needed[ t ] = Skill ) and ( RP > 0 ) and ( SkRoll > 0 ) then begin HadRepairFuel := HadRepairFuel or ActivateRepair( T , SkRoll ); end; end; end else begin { The repair attempt failed. } { At this point repair fuel is a moot point, so return TRUE. } HadRepairFuel := True; end; { If you fail to revive a dead character, there's not much else you can do. } if ( TMaster <> Nil ) and ( TMaster^.G = GG_Character ) and Destroyed( TMaster ) and HadRepairFuel then begin AddNAtt( TMaster^.NA , NAG_Damage , NAS_StrucDamage , 30 ); end; { Determine the percentage of damage repaired. This will determine the XP award. } PercentDamage := PercentDamaged( TMaster ) - PercentDamage; if ( PercentDamage > 0 ) and IsMasterGear( TMaster ) then begin DoleExperience( PC , PercentDamage div 2 ); DoleSkillExperience( PC , Skill , ( PercentDamage + 1 ) div 2 ); end; { Using repair takes time and concentration. } WaitAMinute( GB , PC , ReactionTime( PC ) * Tries ); AddMentalDown( PC , Tries + Random( 3 ) ); UseRepairSkill := HadRepairFuel; end; Procedure DoCompleteRepair( Target: GearPtr ); { Repair everything that can be repaired on Target. } { Basically, go through all the repair skills and apply as many points } { as are needed of each. } var T: Integer; Pts: LongInt; begin for t := 0 to ( NumMaterial - 1 ) do begin if TotalRepairableDamage( Target , T ) > 0 then begin Pts := TotalRepairableDamage( Target , T ); ApplyRepairPoints( Target , T , Pts , True ); end; end; end; Function PerformSkillTar( NPC: GearPtr ): Integer; { Return the performance skill target for this particular NPC. } var it: Integer; begin it := CStat( NPC , STAT_Ego ) + NAttValue( NPC^.NA , NAG_Personal , NAS_PerformancePenalty ) - 5; if it < 5 then it := 5; PerformSkillTar := it; end; Function SelectPerformanceTarget( GB: GameBoardPtr; PC: GearPtr ): GearPtr; { Search through the map and locate someone who has not yet responded to } { the PC's music today. If nobody is found, return NIL. } const PerformanceRange = 8; var M,it: GearPtr; team: Integer; begin { Start looking through the gameboard. } M := GB^.Meks; it := Nil; while M <> Nil do begin team := NAttValue( M^.NA , NAG_Location , NAS_Team ); if ( M^.G = GG_Character ) and ( team <> NAV_DefPlayerTeam ) and ( team <> NAV_LancemateTeam ) and ( M <> PC ) and ( Range( GB , M , PC ) <= PerformanceRange ) and OnTheMap( GB , M ) and GearActive( M ) and ( not AreEnemies( GB , M , PC ) ) and NotAnAnimal( M ) and ( NAttValue( M^.NA , NAG_Personal , NAS_CashOnHandRestock ) <= GB^.ComTime ) then begin { This is a potential target. Check to see if its skill target is lower } { than that of the current candidate. } if it = Nil then begin it := M; end else if PerformSkillTar( M ) < PerformSkillTar( it ) then begin it := M; end; end; M := M^.Next; end; SelectPerformanceTarget := it; end; Function UsePerformance( GB: GameBoardPtr; PC,NPC: GearPtr ): LongInt; { The PC is about to use a performance skill. } { 1) Select a nearby non-lancemate NPC } { 2) Make a performance roll } { 3) Profit } { Return -1 for a bad performance, 0 for a mediocre performance, } { and a positive number if the PC made any tips. } var SkRoll,SkRank,Target: Integer; { Skill roll target } N: Integer; { Number of successes } Cash: LongInt; begin { Reduce stamina and mental now. } { Performing is both mentally and physically exhausting. } if Random( 2 ) = 1 then begin AddStaminaDown( PC , 1 ); end else begin AddMentalDown( PC , 1 ); end; Cash := 0; Target := PerformSkillTar( NPC ); if Target < 5 then Target := 5; { Set the recharge timer for this target. } SetNAtt( NPC^.NA , NAG_Personal , NAS_CashOnHandRestock , GB^.ComTime + 43200 + Random( 86400 ) ); { Add to the performance resistance. } if Random( 2 ) = 1 then AddNAtt( NPC^.NA , NAG_Personal , NAS_PerformancePenalty , 1 ); SkRoll := SkillRoll( GB , PC , NAS_Performance , STAT_Charm , Target , 0 , True , False ); if SkRoll > Target then begin DoleSkillExperience( PC , NAS_Performance , Target ); if SkRoll > ( Target + 5 ) then begin { On a good roll, the PC earns some money. } SkRank := SkillRank( PC , NAS_Performance ) + 1; N := SkRoll - Target - 5; if N > SkRank then N := SkRank else if N < 1 then N := 1; Cash := SkillAdvCost( Nil , N ) div 10; AddNAtt( PC^.NA , NAG_Experience , NAS_Credits , Cash ); end; { Set the applause trigger. } SetTrigger( GB , TRIGGER_Applause ); end else if ( SkRoll + PersonalityCompatability( PC , NPC ) - 5 ) < Target then begin AddMoraleDmg( PC , Rollstep( 1 ) ); Cash := -1; end; UsePerformance := Cash; end; end. GH2/www_build.pas0000644000175000017500000001072711326004554012613 0ustar kaolkaolProgram www_build; { Update the GH2 web pages. } { 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,gearparser,texutil,gearutil,description; var MechaBlueprint: GearPtr; MechaTemplate,TheLine,FName,FList: SAttPtr; PageList: SAttPtr; MechaDesc,MD: SAttPtr; PageID: Integer; F: Text; msg,prev_id,next_id,mecha_name: String; GHVersion: String; begin { Create the main index page. } { First we need to know the version number being built. } GHVersion := ParamStr( 1 ); MechaTemplate := LoadStringList( 'web_input/index_template.txt' ); Assign( F , 'web_output/index.html' ); Rewrite( F ); TheLine := MechaTemplate; while TheLine <> Nil do begin msg := TheLine^.Info; ReplacePat( msg , '*version*' , GHVersion ); writeln( F , msg ); TheLine := TheLine^.Next; end; DisposeSAtt( MechaTemplate ); Close( F ); { Load the mecha page template. } MechaTemplate := LoadStringList( 'web_input/mecha_template.txt' ); { Check the mecha directory for blueprints. } FList := CreateFileList( 'design/*.txt' ); { Sort the mecha by alphabetical order. } SortStringList( FList ); { Initialize the page list. } PageList := Nil; { Create each individual mecha page. } FName := FList; PageID := 0; while FName <> Nil do begin MechaBluePrint := LoadFile( 'design/' + FName^.info ); StoreSAtt( PageList , GearName( MechaBluePrint ) ); Assign( F , 'web_output/mecha_' + BStr( PageID ) + '.html' ); Rewrite( F ); if PageID = 0 then Prev_id := BStr( NumSAtts( FList ) - 1 ) else Prev_id := BStr( PageID - 1 ); if FName^.Next = Nil then Next_ID := '0' else Next_ID := BStr( PageID + 1 ); TheLine := MechaTemplate; while TheLine <> Nil do begin { Do the needed substitutions here. } msg := TheLine^.Info; if UpCase( msg ) = '*STATS*' then begin msg := MechaDescription( MechaBlueprint ); end else if UpCase( msg ) = '*COST*' then begin msg := '$' + BStr( GearValue( MechaBlueprint ) ); end else if UpCase( msg ) = '*DESC*' then begin { If there's an external description file, use that. } { Otherwise use the desc string attribute. } MechaDesc := LoadStringList( 'web_input/mecha_' + GearName( MechaBlueprint ) + '.txt' ); if MechaDesc <> Nil then begin msg := ''; MD := MechaDesc; while MD <> Nil do begin writeln( F , MD^.Info ); MD := MD^.Next; end; DisposeSAtt( MechaDesc ); end else begin msg := '

' + SAttValue( MechaBlueprint^.SA , 'desc' ) + '

'; end; end else begin ReplacePat( msg , '*name*' , GearName( MechaBlueprint ) ); ReplacePat( msg , '*prev*' , Prev_id ); ReplacePat( msg , '*next*' , Next_id ); end; writeln( F , msg ); TheLine := TheLine^.Next; end; Close( F ); DisposeGear( MechaBluePrint ); Inc( PageID ); FName := FName^.Next; end; { Create the mecha index page. } DisposeSATt( MechaTemplate ); MechaTemplate := LoadStringList( 'web_input/mecha_index.txt' ); Assign( F , 'web_output/mecha_index.html' ); Rewrite( F ); TheLine := MechaTemplate; while TheLine <> Nil do begin if UpCase( TheLine^.Info ) = '*MECHA_LIST*' then begin MD := PageList; PageID := 0; while MD <> Nil do begin writeln( F , '
  • ' + MD^.Info + '' ); Inc( PageID ); MD := MD^.Next; end; end else begin writeln( F , TheLine^.Info ); end; TheLine := TheLine^.Next; end; Close( F ); { Dispose of dynamic resources. } DisposeSAtt( FList ); DisposeSAtt( PageList ); DisposeSATt( MechaTemplate ); end. GH2/rnpc.pas0000644000175000017500000000675711326004555011563 0ustar kaolkaolProgram rnpc; { This program will generate random NPC age, gender, and } { colors. } {$LONGSTRINGS ON} uses gears,gearparser,texutil,gearutil,description; 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 rnpc_color = Record R,G,B,A: Integer; end; ColorDesc = Record name: String; rgb: rnpc_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; 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 RandomColor( ColorSet: Integer ): Integer; { 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( 2 ); { 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 RandomColor := it; end else begin { Use bright purple, as a warning that a bug has occurred. } RandomColor := 1; end; end; var NPC: GearPtr; C: Integer; begin LoadColorList; NPC := LoadNewNPC( 'Citizen' , True ); writeln( GearName( NPC ) ); writeln( JobAgeGenderDesc( NPC ) ); C := RandomColor( CS_Skin ); writeln( 'Skin: ' + Available_Colors[ C ].name + ' (' , Available_Colors[ C ].rgb.r , ' ' , Available_Colors[ C ].rgb.g , ' ' , Available_Colors[ C ].rgb.b , ')' ); C := RandomColor( CS_Hair ); writeln( 'Hair: ' + Available_Colors[ C ].name + ' (' , Available_Colors[ C ].rgb.r , ' ' , Available_Colors[ C ].rgb.g , ' ' , Available_Colors[ C ].rgb.b , ')' ); DisposeGear( NPC ); end. GH2/minigame.pp0000644000175000017500000001574711326004555012242 0ustar kaolkaolunit minigame; { This unit will hold descriptions for any minigames that may be used. } { 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,minitype; Function DoConcert( GB: GameBoardPtr; PC: GearPtr; AudienceSize,SkTarget: Integer ): Integer; implementation uses arenacfe,action,ghchars,texutil,ui4gh, {$IFDEF ASCII} vidgfx,vidmenus,vidinfo; {$ELSE} {$IFDEF CUTE} cutegfx,glmenus,glinfo; {$ELSE} glgfx,glmenus,glinfo; {$ENDIF} {$ENDIF} var MG_GB: GameBoardPtr; MG_PC: GearPtr; MG_Prompt: String; MG_Audience: AudienceListPtr; MG_Menu: RPGMenuPtr; Procedure ConcertRedraw; { The screen redraw routine for concerts. } var N: Integer; begin CombatDIsplay( MG_GB ); SetupConcertDisplay; ConcertStatus( MG_PC , MG_Audience^ ); GameMsg( MG_Prompt , ZONE_ConcertCaption , InfoGreen ); if MG_Menu <> Nil then begin N := CurrentMenuItemValue( MG_Menu ); case N of CMG_Trait_Emotion: CMessage( 'Emotion' , ZONE_ConcertDesc , PlayerBlue ); CMG_Trait_Beat: CMessage( 'Beat' , ZONE_ConcertDesc , EnemyRed ); CMG_Trait_Melody: CMessage( 'Melody' , ZONE_ConcertDesc , MelodyYellow ); end; end; end; Function DoConcert( GB: GameBoardPtr; PC: GearPtr; AudienceSize,SkTarget: Integer ): Integer; { The PC is going to hold a concert. Return the final concert score. } { AudienceSize tells how big the audience is. It should be in the range } { of 3 to 10. } { SkTarget is the basic skill target roll for performance rolls. } { The score returned can be read as being in the range 0-100, but scores } { above 100 are also possible. Just accept that 100 is a "flawless" score. } var AL: AudienceList; Function CreateSongMenu: RPGMenuPtr; { Create the song menu. } var RPM: RPGMenuPtr; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ConcertMenu ); RPM^.Mode := RPMNoCancel; AddRPGMenuItem( RPM , 'Emotion Song' , CMG_Trait_Emotion ); AddRPGMenuItem( RPM , 'Beat Song' , CMG_Trait_Beat ); AddRPGMenuItem( RPM , 'Melody Song' , CMG_Trait_Melody ); CreateSongMenu := RPM; end; Function NumAudience: Integer; { Return the number of mobs in the audience. } var N,T: Integer; begin N := 0; for t := 1 to MaxAudienceSize do begin if AL[t].Mood > 0 then Inc( N ); end; NumAudience := N; end; Function MGScore: Integer; { Return the score for the current concert situation. } const PointValue: Array [0..Num_Audience_Moods] of Integer = ( -150,0,10,30,60,100,150 ); var T,N,Total: Integer; begin N := 0; Total := 0; for t := 1 to MaxAudienceSize do begin if AL[t].Mood >= 0 then begin Inc( N ); Total := Total + PointValue[ AL[t].Mood ]; end; end; if N < 1 then begin Total := 0; N := 1; end; if Total < 0 then Total := 0; MGScore := Total div N; end; Procedure ApplySong( SongTrait: Integer ); { The PC has selected what song to play. Make skill rolls for } { all the audience mobs. If the skill roll beats the target, } { the mob's mood increases. If the skill roll is less than } { half the target, the mob's mood decreases... and maybe they } { will walk out. } const Audience_X_Song: Array [0..2,0..2] of Integer = ( (-3, 2,10), (10,-3, 2), ( 2,10,-3) ); var T,MobTarget,SkRoll: Integer; begin for t := 1 to MaxAudienceSize do begin if AL[t].Mood > 0 then begin MobTarget := SkTarget + Audience_X_Song[ AL[t].Trait , SongTrait ] + Abs( AL[t].Mood - 3 ); if MobTarget < 5 then MobTarget := 5; SkRoll := SkillRoll( GB , PC , NAS_Performance , STAT_Charm , MobTarget , 0 , True , True ); if SkRoll > MobTarget then begin if AL[t].Mood < Num_Audience_Moods then Inc( AL[t].Mood ); end else if SkRoll <= ( MobTarget div 2 ) then begin Dec( AL[t].Mood ); end; end; end; end; var T,Song,A0,S0,S1: Integer; RPM: RPGMenuPtr; begin { Initialize the display values. } MG_GB := GB; MG_PC := PC; MG_Audience := @AL; MG_Prompt := MsgString( 'CONCERT_Begin' ); { Range check. } if AudienceSize < 3 then AudienceSize := 3 else if AudienceSize > MaxAudienceSize then AudienceSize := MaxAudienceSize; if SkTarget < 10 then SkTarget := 10; { Initialize the audience. } for t := 1 to MaxAudienceSize do begin if T <= AudienceSize then begin AL[t].Mood := 3 + Random( 2 ); AL[t].Trait := Random( 3 ); end else begin AL[t].Mood := MOOD_Absent; end; end; { Create the song menu. } RPM := CreateSongMenu; MG_Menu := RPM; { While the concert is still ongoing... } Song := 1; while ( NumAudience > 0 ) and ( Song < 6 ) do begin { Request a song from the player. } { Select a song. } T := SelectMenu( RPM , @ConcertRedraw ); { Make the skill rolls for this song. } { First, store the scores, so as to provide a status update afterwards. } s0 := MGScore; a0 := NumAudience; ApplySong( T ); S1 := MGScore; if NumAudience < a0 then begin MG_Prompt := MsgString( 'CONCERT_LoseAudience' ); end else if s0 > S1 then begin MG_Prompt := MsgString( 'CONCERT_ConcertBomb' ); end else if S0 < S1 then begin MG_Prompt := MsgString( 'CONCERT_ConcertGood' ); end else begin MG_Prompt := MsgString( 'CONCERT_ConcertMeh' ); end; MG_Prompt := MG_Prompt + ' ' + MsgString( 'CONCERT_Song' + BStr( Song ) ); Inc( Song ); end; DisposeRPGMenu( RPM ); { Show the concert outcome. } T := MGScore; RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_ConcertMenu ); MG_Menu := RPM; RPM^.Mode := RPMNoCancel; AddRPGMenuItem( RPM , MsgString( 'Exit' ) , 0 ); if T > 110 then begin MG_Prompt := MsgString( 'CONCERT_TerrificEnd' ); end else if T > 75 then begin MG_Prompt := MsgString( 'CONCERT_GoodEnd' ); end else if T > 50 then begin MG_Prompt := MsgString( 'CONCERT_MediumEnd' ); end else if T > 0 then begin MG_Prompt := MsgString( 'CONCERT_PoorEnd' ); end else begin MG_Prompt := MsgString( 'CONCERT_TerribleEnd' ); end; Song := SelectMenu( RPM , @ConcertRedraw ); DisposeRPGMenu( RPM ); { The concert should take some time. } QuickTime( GB , Song * 600 + 100 + Random( 200 ) ); DoConcert := T; end; end. GH2/menugear.pp0000644000175000017500000003535311374513724012260 0ustar kaolkaolunit menugear; { This is the RPGMenus / Gear Tree utilities unit. } { 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; {$ELSE} glmenus; {$ENDIF} Procedure BuildGearMenu( RPM: RPGMenuPtr; Master: GearPtr; G: Integer; IncludeDestroyed: Boolean ); Procedure BuildGearMenu( RPM: RPGMenuPtr; Master: GearPtr; G: Integer ); Procedure BuildGearMenu( RPM: RPGMenuPtr; Master: GearPtr ); Procedure BuildEquipmentMenu( RPM: RPGMenuPtr; Master: GearPtr ); Procedure BuildInventoryMenu( RPM: RPGMenuPtr; Master: GearPtr; UseSaleTag: Boolean ); Procedure BuildSlotMenu( RPM: RPGMenuPtr; Master,Item: GearPtr ); Procedure BuildSubMenu( RPM: RPGMenuPtr; Master,Item: GearPtr; DoMultiplicityCheck: Boolean ); Function LocateGearByNumber( Master: GearPtr; Num: Integer ): GearPtr; Function FindNextWeapon( GB: GameBoardPtr; Master,Weapon: GearPtr; MinRange: Integer ): GearPtr; Function FindGearIndex( Master , FindThis: GearPtr ): Integer; Procedure BuildSiblingMenu( RPM: RPGMenuPtr; LList: GearPtr ); Procedure AlphaKeyMenu( RPM: RPGMenuPtr ); implementation uses effects,gearutil,ghswag,ghweapon,texutil; Procedure BuildGearMenu( RPM: RPGMenuPtr; Master: GearPtr; G: Integer; IncludeDestroyed: Boolean ); { Search through MASTER, adding to menu RPM any part which } { corresponds to descriptor G. Add each matching part to the } { menu, along with its locator number. } var N: Integer; { PROCEDURES BLOCK } Procedure CheckAlongPath( Part: GearPtr; AddToMenu: Boolean ); { CHeck along the path specified. } var PartOK: Boolean; begin while Part <> Nil do begin Inc(N); PartOK := NotDestroyed( Part ); if ( Part^.G = G ) and AddToMenu and ( PartOK or IncludeDestroyed ) then AddRPGMenuItem( RPM , GearName( Part ) , N ); if Part^.G = GG_Cockpit then begin { Don't add parts beyond the cockpit barrier. } CheckAlongPath( Part^.InvCom , False ); CheckAlongPath( Part^.SubCom , False ); end else begin CheckAlongPath( Part^.InvCom , AddToMenu and ( PartOK or IncludeDestroyed ) ); CheckAlongPath( Part^.SubCom , AddToMenu and ( PartOK or IncludeDestroyed ) ); end; Part := Part^.Next; end; end; begin N := 0; if Master^.G = G then AddRPGMenuItem( RPM , GearName( Master ) , 0 ); CheckAlongPath( Master^.InvCom , True ); CheckAlongPath( Master^.SubCom , True ); end; { BuildGearMenu } Procedure BuildGearMenu( RPM: RPGMenuPtr; Master: GearPtr; G: Integer ); { Call the above procedure, counting even destroyed gears. } begin BuildGearMenu( RPM , Master , G , True ); end; Procedure BuildGearMenu( RPM: RPGMenuPtr; Master: GearPtr ); { Search through MASTER, adding to menu all parts. } const InvStr = '+'; SubStr = '>'; var N: Integer; { PROCEDURES BLOCK } Procedure CheckAlongPath( Part: GearPtr; TabPos,Prefix: String ); { CHeck along the path specified. } begin while Part <> Nil do begin Inc(N); if Part^.G <> GG_AbsolutelyNothing then AddRPGMenuItem( RPM , TabPos + Prefix + GearName( Part ) , N ); CheckAlongPath( Part^.InvCom , TabPos + ' ' , InvStr ); CheckAlongPath( Part^.SubCom , TabPos + ' ' , SubStr ); Part := Part^.Next; end; end;{CheckAlongPath} begin N := 0; AddRPGMenuItem( RPM , GearName( Master ) , 0 ); CheckAlongPath( Master^.InvCom , ' ' , '+' ); CheckAlongPath( Master^.SubCom , ' ' , '>' ); end; { BuildGearMenu } Procedure BuildEquipmentMenu( RPM: RPGMenuPtr; Master: GearPtr ); { Create a menu for this master's equipment. Equipment is defined as } { an InvCom of any part other than the master itself. } var N: Integer; Procedure CheckAlongPath( Part: GearPtr; IsInv: Boolean ); { CHeck along the path specified. } var msg: String; begin while Part <> Nil do begin Inc(N); if ( Part^.G <> GG_AbsolutelyNothing ) and IsInv then begin { Creating a message line for this equipment is made tricky by the } { fact that a pilot riding in a mecha has a separate inventory from } { the mecha itself. } if IsMasterGear( Part^.Parent ) then begin msg := '[' + GearName( Part^.Parent ) + '] ' + GearName( Part ); end else begin msg := GearName( Part ) + ' ['; if FindMaster(Part)^.Parent <> Nil then msg := msg + GearName( FindMaster( Part ) ) + ':'; msg := msg + GearName( Part^.Parent ) + ']'; end; AddRPGMenuItem( RPM , msg , N , SAttValue( Part^.SA , 'DESC' ) ); end; CheckAlongPath( Part^.InvCom , True ); CheckAlongPath( Part^.SubCom , False ); Part := Part^.Next; end; end;{CheckAlongPath} begin N := 0; CheckAlongPath( Master^.InvCom , False ); CheckAlongPath( Master^.SubCom , False ); end; {BuildEquipmentMenu} Procedure BuildInventoryMenu( RPM: RPGMenuPtr; Master: GearPtr; UseSaleTag: Boolean ); { Create a menu for this master's inventory. Inventory is defined as } { any InvCom of the master. } var N,T: Integer; Part: GearPtr; num_items,num_extras: Integer; was_already_added: Array of Boolean; Procedure CountTheKids( P: GearPtr ); { This procedure ignores the sub/inv components of things } { in the general inventory, but they have to be counted so } { the locator numbers will work properly. } begin While P <> Nil do begin Inc( N ); if P^.InvCom <> Nil then CountTheKids( P^.InvCom ); if P^.SubCom <> Nil then CountTheKids( P^.SubCom ); P := P^.Next; end; end; Function IMString( P: GearPtr; num_copies: Integer ): String; { Given part P, return a string to use in the menu. } var msg,msg2: String; ShotsUsed: Integer; begin msg := FullGearName( P ); { Add extra information, depending upon item type. } if UseSaleTag then begin msg2 := SAttValue( P^.SA , 'SALETAG' ); if msg2 <> '' then msg := msg + ' (' + msg2 + ')'; end else begin if P^.G = GG_Weapon then begin msg := msg + ' (DC:' + BStr( ScaleDC( P^.V , P^.Scale ) ) + ')'; end else if ( P^.G = GG_ExArmor ) or ( P^.G = GG_Shield ) then begin msg := msg + ' [AC:' + BStr( GearMaxArmor( P ) ) + ']'; end else if P^.G = GG_Ammo then begin ShotsUsed := NAttValue( P^.NA , NAG_WeaponModifier , NAS_AmmoSpent ); msg := msg + ' (' + BStr( P^.STat[ STAT_AmmoPresent ] - ShotsUSed ) + '/' + BStr( P^.Stat[ STAT_AmmoPresent ] ) + 'a)'; end; end; if num_copies > 0 then msg := msg + ' x' + Bstr( num_copies + 1 ); IMString := Msg; end; Function CountIdenticalSibs( P1: GearPtr; T1: Integer ): Integer; { Return how many identical siblings this gear has. } var P2: GearPtr; it: Integer; begin it := 0; P2 := P1^.Next; while P2 <> Nil do begin inc( T1 ); if GearsAreIdentical( P1 , P2 ) then begin Inc( it ); was_already_added[ t1 ] := True; end; P2 := P2^.Next; end; CountIdenticalSibs := it; end; begin N := 0; Part := Master^.InvCom; if Part = Nil then Exit; { Determine the number of items in the inventory, and initialize the Was_Already_Added array. } num_items := NumSiblingGears( Part ); SetLength( was_already_added , num_items + 1 ); for t := 1 to num_items do was_already_added[ t ] := False; T := 0; while Part <> Nil do begin { N is the tree index for the current part. T is its sibling index. } Inc( N ); Inc( T ); if not was_already_added[ t ] then begin num_extras := CountIdenticalSibs( Part , T ); AddRPGMenuItem( RPM , IMString( Part , num_extras ) , N , SAttValue( Part^.SA , 'DESC' ) ); end; if Part^.InvCom <> Nil then CountTheKids( Part^.InvCom ); if Part^.SubCom <> Nil then CountTheKids( Part^.SubCom ); Part := Part^.Next; end; end; Procedure BuildSlotMenu( RPM: RPGMenuPtr; Master,Item: GearPtr ); { Search through MASTER, adding to menu all parts which can } { equip ITEM. } var N: Integer; { PROCEDURES BLOCK } Procedure CheckAlongPath( Part: GearPtr ); { CHeck along the path specified. } begin while Part <> Nil do begin Inc(N); if IsLegalInvCom( Part , Item ) and PartActive( Part ) then AddRPGMenuItem( RPM , GearName( Part ) , N ); CheckAlongPath( Part^.InvCom ); CheckAlongPath( Part^.SubCom ); Part := Part^.Next; end; end;{CheckAlongPath} begin N := 0; CheckAlongPath( Master^.InvCom ); CheckAlongPath( Master^.SubCom ); end; { BuildSlotMenu } Procedure BuildSubMenu( RPM: RPGMenuPtr; Master,Item: GearPtr; DoMultiplicityCheck: Boolean ); { Search through MASTER, adding to menu all parts which can } { take ITEM as a subcomponent. } var N: Integer; { PROCEDURES BLOCK } Function MenuMsg( Part: GearPtr ): String; begin MenuMsg := GearName( Part ) + ' (' + BStr( SubComComplexity( Part ) ) + '/' + BStr( ComponentComplexity( Part ) ) + ')'; end; Procedure CheckThisBit( Part: GearPtr ); { Check this bit, and maybe add it to the menu. } begin if DoMultiplicityCheck then begin if CanBeInstalled( Part , Item ) then AddRPGMenuItem( RPM , MenuMsg( Part ) , N ); end else begin if IsLegalSubCom( Part , Item ) then AddRPGMenuItem( RPM , GearName( Part ) , N ); end; end; Procedure CheckAlongPath( Part: GearPtr ); { CHeck along the path specified. } begin while Part <> Nil do begin Inc(N); CheckThisBit( Part ); CheckAlongPath( Part^.InvCom ); CheckAlongPath( Part^.SubCom ); Part := Part^.Next; end; end;{CheckAlongPath} begin N := 0; CheckThisBit( Master ); CheckAlongPath( Master^.InvCom ); CheckAlongPath( Master^.SubCom ); end; { BuildSubMenu } Procedure BuildSiblingMenu( RPM: RPGMenuPtr; LList: GearPtr ); { Build a menu of these sibling gears. The index numbers will be } { sibling indicies, not the universal gear numbers used in other } { procedures here. } var N: Integer; begin N := 1; while LList <> Nil do begin AddRPGMenuItem( RPM , FullGearName( LList ) , N ); Inc( N ); LList := LList^.Next; end; end; Function LocateGearByNumber( Master: GearPtr; Num: Integer ): GearPtr; { Locate the Nth part in the tree. } var N: Integer; TheGearWeWant: GearPtr; { PROCEDURES BLOCK. } Procedure CheckAlongPath( Part: GearPtr ); { CHeck along the path specified. } begin while ( Part <> Nil ) and ( TheGearWeWant = Nil ) do begin Inc(N); if N = Num then TheGearWeWant := Part; if TheGearWeWant = Nil then CheckAlongPath( Part^.InvCom ); if TheGearWeWant = Nil then CheckAlongPath( Part^.SubCom ); Part := Part^.Next; end; end; begin TheGearWeWant := Nil; N := 0; { Part 0 is the master gear itself. } if Num < 1 then Exit( Master ); CheckAlongPath( Master^.InvCom ); if TheGearWeWant = Nil then CheckAlongPath( Master^.SubCom ); LocateGearByNumber := TheGearWeWant; end; { LocateGearByNumber } Function FindNextWeapon( GB: GameBoardPtr; Master,Weapon: GearPtr; MinRange: Integer ): GearPtr; { This procedure will check recursively through MASTER looking } { for the first weapon (ready to fire) in standard order following PART. } { If MinRange > 0, the weapon's range or throwing range must exceed MinRange. } { If no further weapons are found, it will return the first } { weapon. } var FirstWep,NextWep: GearPtr; FoundStart: Boolean; { PROCEDURES BLOCK } Function WeaponIsOkay( W: GearPtr ): Boolean; { Return TRUE if W is ready to fire and meets our other criteria, or } { FALSE otherwise. } begin if MinRange = 0 then begin WeaponIsOkay := ReadyToFire( GB , Master , W ); end else begin WeaponIsOkay := ReadyToFire( GB , Master , W ) and ( ( WeaponRange( GB , W , RANGE_Long ) >= MinRange ) or ( ThrowingRange( GB , Master , W ) >= MinRange ) ); end; end; Procedure CheckAlongPath( Part: GearPtr ); { CHeck along the path specified. } begin while Part <> Nil do begin if WeaponIsOkay( Part ) then begin if FirstWep = Nil then FirstWep := Part; if FoundStart and ( NextWep = Nil ) then NextWep := Part; end; if Part = Weapon then FoundStart := True; CheckAlongPath( Part^.InvCom ); CheckAlongPath( Part^.SubCom ); Part := Part^.Next; end; end; begin FirstWep := Nil; NextWep := Nil; if Weapon = Nil then FoundStart := True else FoundStart := False; CheckAlongPath( Master^.InvCom ); CheckAlongPath( Master^.SubCom ); { Return either the next weapon or the first weapon, } { depending upon what we found. } if NextWep = Nil then begin if FirstWep = Nil then FindNextWeapon := Weapon else FindNextWeapon := FirstWep; end else FindNextWeapon := NextWep; end; { FindNextWeapon } Function FindGearIndex( Master , FindThis: GearPtr ): Integer; { Search through master looking for FINDTHIS. } { Once found, return its index number. Return -1 if it } { cannot be found. } var N,it: Integer; { PROCEDURES BLOCK } Procedure CheckAlongPath( Part: GearPtr ); { CHeck along the path specified. } begin while ( Part <> Nil ) and ( it = -1 ) do begin Inc(N); if ( Part = FindThis ) then it := N; CheckAlongPath( Part^.InvCom ); CheckAlongPath( Part^.SubCom ); Part := Part^.Next; end; end; begin N := 0; it := -1; if Master = FindThis then it := 0; CheckAlongPath( Master^.InvCom ); CheckAlongPath( Master^.SubCom ); FindGearIndex := it; end; { FindGearIndex } Procedure AlphaKeyMenu( RPM: RPGMenuPtr ); { Alter this menu so that each item in it has a letter key } { hotlinked. } { This procedure has nothing to do with gears, but it's easier } { to stick it here than keep two copies in the conmenus and } { sdlmenus units. What I really need is a separate menu-utility } { unit, I guess. } var Key: Char; MI: RPGMenuItemPtr; begin { The hotkeys start with 'a'. } Key := 'a'; MI := RPM^.firstitem; while MI <> Nil do begin { Alter the message. } MI^.msg := Key + ') ' + MI^.msg; { Add the key. } AddRPGMenuKey( RPM , Key , MI^.value ); { Move to the next letter in the series. } { note that only 52 letters can be assigned. } if key = 'z' then key := 'A' else inc( key ); MI := MI^.Next; end; end; end. GH2/maped.pas0000644000175000017500000002233211326004556011673 0ustar kaolkaolProgram maped; { 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} { This is the map editor for GH2. Note that this program must be compiled in ASCII mode. } uses gears,locale,vidgfx,randmaps,video,ui4gh,texutil,vidmenus; const METerrGfx: Array [1..NumTerr] of Char = ( '.', '=', '#', '=', '%', '.', '.', '^', '^', '^', '.', '#', '#', '.', '+', '.', '=', '-', '.', '#', '.', '%', '-', '&', '&', '#', '&' ); METerrColor: Array [1..NumTerr] of Byte = ( LightGreen, LightGreen, LightGreen, LightBlue, DarkGray, LightGray, LightCyan, DarkGray, LightGray, White, DarkGray, LightGray, White, DarkGray, DarkGray, LightBlue, LightBlue, LightBlue, Brown, Yellow, LightBlue, Brown,DarkGray,LightGray,LightGray, LightCyan,LightGray ); Function CellOnTile( GB: GameBoardPtr; X,Y: Integer ): GearPtr; { Look at tile X,Y and return the cell associated with it. } var M,C: GearPtr; MX,MY: Integer; begin M := GB^.Meks; C := Nil; while ( M <> Nil ) and ( C = Nil ) do begin MX := M^.Stat[ STAT_PDMC_X ]; MY := M^.Stat[ STAT_PDMC_Y ]; if ( MX <= X ) and (( MX + M^.Stat[ STAT_PDMC_W ]) > X ) and ( MY <= Y ) and (( MY + M^.Stat[ STAT_PDMC_H ]) > Y ) then begin C := M; end; M := M^.Next; end; CellOnTile := C; end; Procedure MEDisplayMap( GB: GameBoardPtr; Pen_X,Pen_Y: Integer ); { Display the map. Display all tiles and the locations of map cells. } { The Pen_X , Pen_Y position should be centered on the screen. } const { These arrays tell where to put the direction indicator for a } { map cell. Note that the bright spot points in the direction that } { would be south in the minimap; this is the direction that's used } { as the front of a store tile. } Sweet_X: Array [0..3] of byte = ( 2 , 0 , 2 , 4 ); Sweet_Y: Array [0..3] of byte = ( 4 , 2 , 0 , 2 ); var Map_Zone: VGFX_Rect; TX,TY,MX,MY,T: Integer; M: GearPtr; BGColor: Byte; begin ClrScreen; Map_Zone.X := 1; Map_Zone.Y := 1; Map_Zone.W := ScreenColumns - RightColumnWidth; Map_Zone.H := ScreenRows - 5; ClipZone( Map_Zone ); for TX := 1 to Map_Zone.W do begin for TY := 1 to Map_Zone.H do begin MX := TX + Pen_X - Map_Zone.W div 2; MY := TY + Pen_Y - Map_Zone.H div 2; if OnTheMap( GB , MX , MY ) then begin M := CellOnTile( GB , MX , MY ); if ( MX = Pen_X ) and ( MY = Pen_Y ) then begin BGColor := Green; end else if M <> Nil then begin { Depending on the direction of this cell, } { color this tile either blue or cyan. } if (( MX - M^.Stat[ STAT_PDMC_X ] ) = Sweet_X[ M^.Stat[ STAT_PDMC_D ] ] ) and (( MY - M^.Stat[ STAT_PDMC_Y ] ) = Sweet_Y[ M^.Stat[ STAT_PDMC_D ] ] ) then begin BGColor := Cyan; end else begin BGColor := Blue; end; end else begin BGColor := Black; end; T := TileTerrain( GB , MX , MY ); DrawGlyph( METerrGfx[ T ] , TX , TY , METerrColor[T] , BGColor ); end; end; { For TY } end; { For TX } { Restore the clip zone. } MaxClipZone; end; Procedure MapEditInfo( Pen , X , Y: Integer ); { Display some info about the pen, and print out some instructions while you're } { at it. } const Instructions = ' [ ] Change Pen' + #13 + ' S Save Map' + #13 + ' C Clear Map' + #13 + ' c Add/Rotate Cell' + #13 + ' d Delete Cell' + #13 + ' Q Quit Editor'; begin GameMsg( METerrGfx[ Pen ] + ' ' + MsgString( 'TerrNAME_' + BStr( Pen ) ) , ZONE_Info , METerrColor[ Pen ] ); GameMsg( BStr( X ) + ' , ' + BStr( Y ) + #13 + Instructions , ZONE_Menu , StdWhite ); RedrawConsole; end; Procedure ClearMap( GB: GameBoardPtr; Pen: Integer ); { Clear the map using the requested pen terrain. } var X,Y: Integer; begin for X := 1 to GB^.Map_Width do begin for Y := 1 to GB^.Map_Height do begin SetTerrain( GB , X , Y , Pen ); end; end; end; Procedure EditCell( GB: GameBoardPtr; X , Y: Integer ); { if there's no cell in this tile, add one. If there is a cell } { in this tile, rotate it. } var Cell: GearPtr; begin Cell := CellOnTile( GB , X , Y ); if Cell = Nil then begin { No cell. Add one. } { At the moment, no check is made to make sure that cells don't } { overlap. This could result in some massive uglification. } Cell := NewGear( Nil ); AppendGear( GB^.Meks , Cell ); Cell^.Stat[ STAT_PDMC_X ] := X; Cell^.Stat[ STAT_PDMC_Y ] := Y; Cell^.Stat[ STAT_PDMC_W ] := 5; Cell^.Stat[ STAT_PDMC_H ] := 5; Cell^.Stat[ STAT_PDMC_D ] := 0; end else begin Cell^.Stat[ STAT_PDMC_D ] := ( Cell^.Stat[ STAT_PDMC_D ] + 1 ) mod 4; end; end; Procedure DeleteCell( GB: GameBoardPtr; X , Y: Integer ); { if there's a cell in this tile, delete it. } var Cell: GearPtr; begin Cell := CellOnTile( GB , X , Y ); if Cell <> Nil then begin RemoveGear( GB^.Meks , Cell ); end; end; Procedure EditMap( GB: GameBoardPtr; const FName: String ); { Edit the given map. Save it to disk if need be. } var A: CHar; Pen,X,Y: Integer; Procedure RepositionCursor( D: Integer ); begin if OnTheMap( GB , X + AngDir[ D , 1 ] , Y + AngDir[ D , 2 ] ) then begin X := X + AngDir[ D , 1 ]; Y := Y + AngDir[ D , 2 ]; end; end; begin { Initialize our tools. } Pen := 1; X := 1; Y := 1; DialogMsg( 'Editing ' + fname + '.' ); repeat MEDisplayMap( GB , X , Y ); MapEditInfo( Pen , X , Y ); DoFlip; A := RPGKey; if A = KeyMap[ KMC_North ].KCode then begin RepositionCursor( 6 ); end else if A = KeyMap[ KMC_South ].KCode then begin RepositionCursor( 2 ); end else if A = KeyMap[ KMC_West ].KCode then begin RepositionCursor( 4 ); end else if A = KeyMap[ KMC_East ].KCode then begin RepositionCursor( 0 ); end else if A = KeyMap[ KMC_NorthEast ].KCode then begin RepositionCursor( 7 ); end else if A = KeyMap[ KMC_SouthWest ].KCode then begin RepositionCursor( 3 ); end else if A = KeyMap[ KMC_NorthWest ].KCode then begin RepositionCursor( 5 ); end else if A = KeyMap[ KMC_SouthEast ].KCode then begin RepositionCursor( 1 ); end else if A = ']' then begin Pen := Pen + 1; if Pen > NumTerr then pen := 1; end else if A = '[' then begin Pen := Pen - 1; if Pen < 1 then pen := NumTerr; end else if A = ' ' then begin SetTerrain( GB , X , Y , Pen ); end else if A = 'c' then begin EditCell( GB , X , Y ); end else if A = 'd' then begin DeleteCell( GB , X , Y ); end else if A = 'S' then begin DialogMsg( 'Saving...' ); SavePredrawnMap( GB , FName ); DialogMsg( 'Saved map ' + fname + '.' ); end else if A = 'C' then begin ClearMap( GB , Pen ); end; until A = 'Q'; { Get rid of the map. } DisposeMap( GB ); end; Procedure RedrawOpening; { The opening menu redraw procedure. } begin ClrScreen; InfoBox( ZONE_Menu ); RedrawConsole; end; Procedure CreateNewMap; { Create a brand new map and pass it to the editor. } var fname: String; RPM: RPGMenuPtr; W,H,T: Integer; GB: GameBoardPtr; begin fname := GetStringFromUser( 'Enter filename for this map.' , @RedrawOpening ); if fname <> '' then begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); for t := 1 to 20 do AddRPGMenuItem( RPM , BSTr( t * 5 ) + ' tiles' , T * 5 ); DialogMsg( 'Enter width' ); W := SelectMenu( RPM , @RedrawOpening ); if W <> -1 then begin DialogMsg( 'Enter height' ); H := SelectMenu( RPM , @RedrawOpening ); if H <> -1 then begin GB := NewMap( W , H ); EditMap( GB , fname ); end; end; DisposeRPGMenu( RPM ); end; end; Procedure EditOldMap; { Load an old map from disk and pass it to the editor. } var fname: String; RPM: RPGMenuPtr; GB: GameBoardPtr; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); BuildFileMenu( RPM , Series_Directory + 'MAP_*.txt' ); if RPM^.NumItem > 0 then begin fname := SelectFile( RPM , @RedrawOpening ); if fname <> '' then begin GB := LoadPredrawnMap( fname ); EditMap( GB , fname ); end; end; DisposeRPGMenu( RPM ); end; var RPM: RPGMenuPtr; N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu ); AddRPGMenuItem( RPM , 'Create New Map' , 1 ); AddRPGMenuItem( RPM , 'Load Existing Map' , 2 ); AddRPGMenuItem( RPM , 'Exit MapEd' , -1 ); repeat N := SelectMenu( RPM , @RedrawOpening ); Case N of 1: CreateNewMap; 2: EditOldMap; end; until N = -1; DisposeRPGMenu( RPM ); end. GH2/spaceships.pp0000644000175000017500000000271211326004555012602 0ustar kaolkaolunit spaceships; { This unit handles spaceships. } { 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; Function NewSpaceship( GB: GameBoardPtr; const Desig: String ); implementation uses gearparser,gearutil; var Spaceship_Prototype_List: GearPtr; Function NewSpaceship( GB: GameBoardPtr; const Desig: String ); { Create a new spaceship of type DESIG. Give it a unique name and scene IDs. } var Ship: GearPtr; begin end; initialization Spaceship_Prototype_List := AggregatePattern( 'SHIP_*.txt' , Series_Directory ); finalization DisposeGear( Spaceship_Prototype_List ); end. GH2/training.pp0000644000175000017500000004034611326004555012260 0ustar kaolkaolunit training; { Training is hereby cut off from pcaction.pp; that unit is far too } { bloaty to begin with. } { 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} vidgfx; {$ELSE} {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} {$ENDIF} Procedure DoTraining( GB: GameBoardPtr; PC: GearPtr; RD: RedrawProcedureType ); implementation uses ghchars,gearutil,texutil,ability,description,ui4gh, {$IFDEF ASCII} vidmenus,vidinfo; {$ELSE} glmenus,glinfo; {$ENDIF} var TRAINING_GB: GameBoardPtr; TRAINING_PC: GearPtr; TRAINING_Redrawer: RedrawProcedureType; Procedure TrainingRedraw; { Redraw the training screen. } begin TRAINING_Redrawer; CharacterDisplay( TRAINING_PC , TRAINING_GB ); InfoBox( ZONE_Menu ); InfoBox( ZONE_Info ); CMessage( 'FREE XP: ' + BStr( NAttValue( TRAINING_PC^.NA , NAG_Experience , NAS_TotalXP ) - NAttValue( TRAINING_PC^.NA , NAG_Experience , NAS_SpentXP ) ) , ZONE_Menu1 , InfoHilight ); end; Procedure NewSkillRedraw; { Redraw the training screen. } begin TRAINING_Redrawer; CharacterDisplay( TRAINING_PC , TRAINING_GB ); InfoBox( ZONE_Menu ); InfoBox( ZONE_Info ); CMessage( BStr( NumberOfSpecialties( TRAINING_PC ) ) + '/' + BStr( NumberOfSkillSlots( TRAINING_PC ) ) , ZONE_Menu1 , InfoHilight ); end; Procedure DoTraining( GB: GameBoardPtr; PC: GearPtr; RD: RedrawProcedureType ); { The player wants to spend some of this character's } { accumulated experience points. Go to it! } Procedure ImproveSkills( PC: GearPtr ); { The PC is going to improve his or her skills. } var FXP: LongInt; { Free XP Points } SkMenu: RPGMenuPtr; { Training Hall Menu } Sk: NAttPtr; { A skill counter } N: LongInt; { A number } SI,TI: Integer; { Selected Item , Top Item } msg: String; begin { Initialize the Selected Item and Top Item to the } { top of the list. } SI := 1; TI := 1; repeat { The number of free XP is the total XP minus the spent XP. } FXP := NAttValue( PC^.NA , NAG_Experience , NAS_TotalXP ) - NAttValue( PC^.NA , NAG_Experience , NAS_SpentXP ); { Create the skill menu. } SkMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); Sk := PC^.NA; SkMenu^.dtexcolor := InfoGreen; AttachMenuDesc( SkMenu , ZONE_Info ); while Sk <> Nil do begin if ( Sk^.G = NAG_Skill ) and ( Sk^.S > 0 ) and not SkillMan[ Sk^.S ].Hidden then begin { Add this skill to the menu. This is going to be one doozy of a long description. } AddRPGMenuItem( SkMenu , MsgString( 'SKILLNAME_' + BStr( Sk^.S ) ) + ' +' + BStr( Sk^.V ) + ' (' + BStr( SkillAdvCost( PC , Sk^.V ) ) + ' XP)' , Sk^.S , SkillDescription( Sk^.S ) ); end; Sk := Sk^.Next; end; RPMSortAlpha( SkMenu ); AddRPGMenuItem( SkMenu , MsgString( 'RANDCHAR_ASPDone' ) , -1 ); { Restore SelectItem , TopItem from the last time. } SkMEnu^.SelectItem := SI; SkMenu^.TopItem := TI; N := SelectMenu( SkMenu , @TrainingRedraw ); { Save the last cursor position, then dispose of } { the menu. } SI := SkMenu^.SelectItem; TI := SkMenu^.TopItem; DisposeRPGMenu( SkMenu ); if N > 0 then begin { Locate the exact skill being improved. } Sk := FindNAtt( PC^.NA , NAG_Skill , N ); { Use N to store this skill's cost. } N := SkillAdvCost( PC , Sk^.V ); { If the PC has enough free XP, this skill will be improved. } { Otherwise, do nothing. } if N > FXP then begin DialogMsg( ReplaceHash( MsgString( 'TRAINING_NotEnoughXP' ) , GearName( PC ) ) ); end else begin { Improve the skill, pay the XP. } msg := ReplaceHash( MsgString( 'TRAINING_Improved' ) , GearName( PC ) ); msg := ReplaceHash( msg , MsgString( 'SKILLNAME_' + BStr( Sk^.S ) ) ); DialogMsg( msg ); AddNAtt( PC^.NA , NAG_Skill , Sk^.S , 1 ); AddNAtt( PC^.NA , NAG_Experience , NAS_SpentXP , N ); end; end; until N = -1; end; Function NumAllowedStatAdvances: LongInt; { Return the number of stat advances this PC is allowed. } var XP: LongInt; begin XP := NAttValue( PC^.NA , NAG_Experience , NAS_TotalXP ); if XP > 100000 then XP := 100000; NumAllowedStatAdvances := XP div 5000; end; Function OneStatCanBeAdvanced: Boolean; { Return TRUE if at least one stat is capable of being } { advanced, or FALSE otherwise. } var t,N: Integer; begin { To start with, count up the number of advancements so far. } N := 0; for t := 1 to NumGearStats do begin N := N + NAttValue( PC^.NA , NAG_StatImprovementLevel , T ); end; OneStatCanBeAdvanced := N < NumAllowedStatAdvances; end; Function StatImprovementCost( CIV: Integer ): LongInt; { Return the cost of improving this stat. } begin StatImprovementCost := ( CIV + 1 ) * 500; end; Procedure ImproveStats( PC: GearPtr ); { The PC is going to improve his or her stats. } var FXP: LongInt; { Free XP Points } StMenu: RPGMenuPtr; { Training Hall Menu } CIV: Integer; { Current Improvement Value. } N,T,SI,TI: Integer; { Selected Item , Top Item } XP: LongInt; msg: String; begin { Initialize the Selected Item and Top Item to the } { top of the list. } SI := 1; TI := 1; repeat { The number of free XP is the total XP minus the spent XP. } FXP := NAttValue( PC^.NA , NAG_Experience , NAS_TotalXP ) - NAttValue( PC^.NA , NAG_Experience , NAS_SpentXP ); { Create the skill menu. } StMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); for t := 1 to NumGearStats do begin { Find out how many times this stat has been } { improved thus far. } CIV := NAttValue( PC^.NA , NAG_StatImprovementLevel , T ); AddRPGMenuItem( StMenu , MsgString( 'StatName_' + BStr( T ) ) + ' (' + BStr( StatImprovementCost( CIV ) ) + ' XP)' , T ); end; AddRPGMenuItem( StMenu , MsgString( 'RANDCHAR_ASPDone' ) , -1 ); { Restore SelectItem , TopItem from the last time. } StMEnu^.SelectItem := SI; StMenu^.TopItem := TI; N := SelectMenu( StMenu , @TrainingRedraw ); { Save the last cursor position, then dispose of } { the menu. } SI := StMenu^.SelectItem; TI := StMenu^.TopItem; DisposeRPGMenu( StMenu ); if N > 0 then begin { Find out how many times this stat has been } { improved thus far. } CIV := NAttValue( PC^.NA , NAG_StatImprovementLevel , N ); XP := StatImprovementCost( CIV ); if XP > FXP then begin DialogMsg( ReplaceHash( MsgString( 'TRAINING_NotEnoughXP' ) , GearName( PC ) ) ); end else begin { Improve the skill, pay the XP. } msg := ReplaceHash( MsgString( 'TRAINING_Improved' ) , GearName( PC ) ); msg := ReplaceHash( msg , MsgString( 'StatName_' + BStr( N ) ) ); DialogMsg( msg ); Inc( PC^.Stat[ N ] ); AddNAtt( PC^.NA , NAG_Experience , NAS_SpentXP , XP ); AddNAtt( PC^.NA , NAG_StatImprovementLevel , N , 1 ); end; end; until ( N = -1 ) or not OneStatCanBeAdvanced; end; Procedure ForgetLowSkill( PC: GearPtr ); { The PC wants to forget a currently known skill. } { Choose the skill with the lowest rank to delete. } var LowSkill,LowSkillRank,T,R: Integer; begin LowSkill := 1; LowSkillRank := 9999; for t := 1 to NumSkill do begin R := NAttValue( PC^.NA , NAG_Skill , T ); { Can't forget the hidden skills. } if ( R > 0 ) and not SkillMan[t].Hidden then begin if R < LowSkillRank then begin LowSkill := T; LowSkillRank := R; end else if ( R = LowSkillRank ) and ( Random( 2 ) = 1 ) then begin LowSkill := T; LowSkillRank := R; end; end; end; SetNAtt( PC^.NA , NAG_Skill , LowSkill , 0 ); { Also remove any talents based on this skill. } for t := 1 to NumTalent do begin if Talent_PreReq[ T , 1 ] = LowSkill then SetNAtt( PC^.NA , NAG_Talent , T , 0 ); end; end; Procedure GetNewSkill( PC: GearPtr ); { The PC is going to purchase a new skill. } var FXP: LongInt; { Free XP Points } SkMenu: RPGMenuPtr; { Training Hall Menu } N,N2: LongInt; { A number } SkillLimit: Integer; { Highest skill index that can be learned. } { NPCs are generally stuck with the skills they } { start with, but everyone can learn the 10 basic } { combat skills. } msg: String; begin { The number of free XP is the total XP minus the spent XP. } FXP := NAttValue( PC^.NA , NAG_Experience , NAS_TotalXP ) - NAttValue( PC^.NA , NAG_Experience , NAS_SpentXP ); { Create the skill menu. } { We only want this menu to contain skills the PC does } { not currently know. } SkMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( SkMenu , ZONE_Info ); SkMenu^.dtexcolor := InfoGreen; if NAttValue( PC^.NA , NAG_Location , NAS_Team ) = NAV_LancemateTeam then SkillLimit := Num_Basic_Combat_Skills else SkillLimit := NumSkill; for N := 1 to SkillLimit do begin if ( NAttValue( PC^.NA , NAG_Skill , N ) = 0 ) and not SkillMan[ N ].Hidden then begin AddRPGMenuItem( SkMenu , MsgString( 'SkillName_' + BStr( N ) ) + ' (' + BStr( SkillAdvCost( PC , 0 ) ) + ' XP)' , N , SkillDescription( N ) ); end; end; RPMSortAlpha( SkMenu ); AddRPGMenuItem( SkMenu , ' Cancel' , -1 ); N := SelectMenu( SkMenu , @NewSkillRedraw ); DisposeRPGMenu( SkMenu ); if N > 0 then begin { If the PC has enough free XP, this skill will be improved. } { Otherwise, do nothing. } if SkillAdvCost( PC , 0 ) > FXP then begin DialogMsg( ReplaceHash( MsgString( 'TRAINING_NotEnoughXP' ) , GearName( PC ) ) ); end else begin { Improve the skill, pay the XP. } if NumberOfSpecialties( PC ) >= NumberOfSkillSlots( PC ) then begin SkMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( SkMenu , ZONE_Info ); SkMenu^.dtexcolor := InfoGreen; AddRPGMenuItem( SkMenu , MsgSTring( 'LearnSkill_AcceptPenalty' ) , 1 , MsgString( 'LearnSkill_Warning' ) ); AddRPGMenuItem( SkMenu , MsgString( 'LearnSkill_ForgetPrevious' ) , 2 , MsgString( 'LearnSkill_Warning' ) ); AddRPGMenuItem( SkMenu , MsgString( 'Cancel' ) , -1 , MsgString( 'LearnSkill_Warning' ) ); N2 := SelectMenu( SkMenu , @NewSkillRedraw ); if N2 = -1 then begin { Cancelled learning new skill. } N := -1; end else if N2 = 2 then begin { Will forget previous skill. } ForgetLowSkill( PC ); end; DisposeRPGMenu( SkMenu ); end; if ( N >= 1 ) and ( N <= NumSkill ) then begin msg := ReplaceHash( MsgString( 'TRAINING_Learned' ) , GearName( PC ) ); msg := ReplaceHash( msg , MsgString( 'SKILLNAME_' + BStr( N ) ) ); DialogMsg( msg ); SetNAtt( PC^.NA , NAG_Skill , N , 1 ); AddNAtt( PC^.NA , NAG_Experience , NAS_SpentXP , SkillAdvCost( PC , 0 ) ); FXP := FXP - SkillAdvCost( PC , 0 ); end; end; end; end; Procedure GetNewTalent( PC: GearPtr ); { The PC is going to purchase a new talent. } var FXP: LongInt; { Free XP Points } TMenu: RPGMenuPtr; { Training Hall Menu } N: LongInt; { A number } msg: String; begin { The number of free XP is the total XP minus the spent XP. } FXP := NAttValue( PC^.NA , NAG_Experience , NAS_TotalXP ) - NAttValue( PC^.NA , NAG_Experience , NAS_SpentXP ); { Create the skill menu. } { We only want this menu to contain skills the PC does } { not currently know. } TMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( TMenu , ZONE_Info ); TMenu^.dtexcolor := InfoGreen; for N := 1 to NumTalent do begin if CanLearnTalent( PC , N ) then begin AddRPGMenuItem( TMenu , MsgString( 'TALENT' + BStr( N ) ) , N , MsgString( 'TALENTDESC' + BStr( N ) ) ); end; end; RPMSortAlpha( TMenu ); AddRPGMenuItem( TMenu , ' Cancel' , -1 ); repeat N := SelectMenu( TMenu , @TrainingRedraw ); if N > 0 then begin { If the PC has enough free XP, this skill will be improved. } { Otherwise, do nothing. } if 1000 > FXP then begin DialogMsg( MsgString( 'CANTAFFORDTALENT' ) ); end else if NumFreeTalents( PC ) < 1 then begin DialogMsg( MsgString( 'NOFREETALENTS' ) ); end else begin msg := ReplaceHash( MsgString( 'TRAINING_Learned' ) , GearName( PC ) ); msg := ReplaceHash( msg , MsgString( 'TALENT' + BStr( N ) ) ); DialogMsg( msg ); ApplyTalent( PC , N ); AddNAtt( PC^.NA , NAG_Experience , NAS_SpentXP , 1000 ); FXP := FXP - 1000; { Having purchased a skill, we want to leave this procedure. } N := -1; end; end; until N = -1; DisposeRPGMenu( TMenu ); end; Procedure ReviewTalents( PC: GearPtr ); { The PC is going to review his talents. } var TMenu: RPGMenuPtr; { Training Hall Menu } N: LongInt; { A number } begin { Create the skill menu. } TMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( TMenu , ZONE_Info ); TMenu^.dtexcolor := InfoGreen; for N := 1 to NumTalent do begin if HasTalent( PC , N ) then begin AddRPGMenuItem( TMenu , MsgString( 'TALENT' + BStr( N ) ) , N , MsgString( 'TALENTDESC' + BStr( N ) ) ); end; end; RPMSortAlpha( TMenu ); AddRPGMenuItem( TMenu , ' Exit' , -1 ); N := SelectMenu( TMenu , @TrainingRedraw ); DisposeRPGMenu( TMenu ); end; Procedure ReviewCyberware( PC: GearPtr ); { The PC is going to review his talents. } var S: GearPtr; { Subcoms of PC. } TMenu: RPGMenuPtr; { Training Hall Menu } begin { Create the cyber menu. } TMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AttachMenuDesc( TMenu , ZONE_Info ); TMenu^.dtexcolor := InfoGreen; S := PC^.SubCom; while S <> Nil do begin if S^.G = GG_Modifier then begin AddRPGMenuItem( TMenu , GearName( S ) , 0 , ExtendedDescription( GB , S ) + ' (' + SAttValue( S^.SA , 'CYBERSLOT' ) + ')' ); end; S := S^.Next; end; RPMSortAlpha( TMenu ); AddRPGMenuItem( TMenu , ' Exit' , -1 ); SelectMenu( TMenu , @TrainingRedraw ); DisposeRPGMenu( TMenu ); end; var DTMenu: RPGMenuPtr; N: Integer; begin { Error check - PC must point to the character record. } if PC^.G <> GG_Character then PC := LocatePilot( PC ); if PC = Nil then Exit; TRAINING_PC := PC; TRAINING_GB := GB; TRAINING_Redrawer := RD; repeat DTMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Menu2 ); AddRPGMenuItem( DTMenu , MsgString( 'TRAINING_ImproveSkill' ) , 1 ); AddRPGMenuItem( DTMenu , MsgString( 'TRAINING_NewSkill' ) , 2 ); AddRPGMenuItem( DTMenu , MsgString( 'TRAINING_ReviewCyberware' ) , 6 ); AddRPGMenuItem( DTMenu , MsgString( 'TRAINING_ReviewTalents' ) , 5 ); if ( NumFreeTalents( PC ) > 0 ) and ( NAttValue( PC^.NA , NAG_Location , NAS_Team ) <> NAV_LancemateTeam ) then begin AddRPGMenuItem( DTMenu , MsgString( 'TRAINING_NewTalent' ) , 4 ); end; if OneStatCanBeAdvanced then begin AddRPGMenuItem( DTMenu , MsgString( 'TRAINING_ImproveStat' ) , 3 ); end; AddRPGMenuItem( DTMenu , MsgString( 'Exit' ) , -1 ); N := SelectMenu( DTMenu , @TrainingRedraw ); DisposeRPGMenu( DTMenu ); if N = 1 then ImproveSkills( PC ) else if N = 3 then ImproveStats( PC ) else if N = 2 then GetNewSkill( PC ) else if N = 4 then GetNewTalent( PC ) else if N = 5 then ReviewTalents( PC ) else if N = 6 then ReviewCyberware( PC ); until N = -1; end; end. GH2/texutil.pp0000644000175000017500000004260211326004556012141 0ustar kaolkaolunit texutil; {This unit contains various useful functions for dealing} {with strings.} { 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 STRINGS; Procedure DeleteWhiteSpace(var S: String); Procedure DeleteFirstChar(var S: String); Function ExtractWord(var S: String): String; Function ExtractValue(var S: String): LongInt; Function ExtractReal(var S: String): Real; Function RetrieveAString(const S: String): String; Function RetrieveBracketString(const S: String): String; Function RetrieveAPreamble(const S: String ): String; Function BStr( N: LongInt ): String; Function SgnStr( N: Integer ): String; Function WideStr( N,Width: LongInt ): String; Function Acronym( phrase: String ): String; {can't const} Function Acronym( const phrase: String ; NumPlaces: Byte ): String; Function Concentrate( const S: String ): String; Function Sgn( N: LongInt ): Integer; Function PartMatchesCriteria( const Part_In,Desc_In: String ): Boolean; Function PartAtLeastOneMatch( const Part_In,Desc_In: String ): Boolean; Function AStringHasBString( const A,B: String ): Boolean; Function HeadMatchesString( const H,S: String ): Boolean; Function QuickPCopy( const msg: String ): PChar; Function IsPunctuation( C: Char ): Boolean; Procedure ReplacePat( var msg: String; const pat_in,s: String ); Function ReplaceHash( const msg, s: String ): String; Function StringMatchWeight( Part,Desc: String ): Integer; Procedure AlterDescriptors( var Original,Change: String ); Procedure AtoAn( var msg: String ); Procedure AddTraits( var Trait_List: String; Traits_To_Add: String ); Function QuoteString( A: String ): String; Procedure AddToQuoteString( var QList,QToAdd: String ); Function NoQItemsMatch( const QList: String; var QToCheck: String ): Boolean; implementation Procedure DeleteWhiteSpace(var S: String); {Delete any whitespace which is at the beginning of} {string S. If S is nothing but whitespace, or if it} {contains nothing, return an empty string.} { BUGS - None detected. Test harnessed and everything.} var P: Integer; begin { Error check } if S = '' then Exit; {Locate the first relevant char.} P := 1; while (P < Length(S)) and ((S[P] = ' ') or (S[P] = #9)) do begin Inc(P); end; {Copy the string from the first nonspace to the end.} if (S[P] = ' ') or (S[P] = #9) then S := '' else S := Copy(S,P,Length(S)); end; Procedure DeleteFirstChar(var S: String); { Remove the first character from string S. } begin {Copy the string from the first nonspace to the end.} if Length( S ) < 2 then S := '' else S := Copy(S,2,Length(S)); end; Function ExtractWord(var S: String): String; {Extract the next word from string S.} {Return this substring as the function's result;} {truncate S so that it is now the remainder of the string.} {If there is no word to extract, both S and the function} {result will be set to empty strings.} { BUGS - None found.} var P: Integer; it: String; begin {To start the process, strip all whitespace from the} {beginning of the string.} DeleteWhiteSpace(S); {Error check- make sure that we have something left to} {extract! The string could have been nothing but white space.} if S <> '' then begin {Determine the position of the next whitespace.} P := Pos(' ',S); if P = 0 then P := Pos(#9,S); {Extract the command.} if P <> 0 then begin it := Copy(S,1,P-1); S := Copy(S,P,Length(S)); end else begin it := Copy(S,1,Length(S)); S := ''; end; end else begin it := ''; end; ExtractWord := it; end; Function ExtractValue(var S: String): LongInt; {This is similar to the above procedure, but} {instead of a word it extracts a numeric value.} {Return 0 if the extraction should fail for any reason.} var S2: String; it,C: LongInt; begin S2 := ExtractWord(S); Val(S2,it,C); if C <> 0 then it := 0; ExtractValue := it; end; Function ExtractReal(var S: String): Real; {This is similar to the above procedure, but} {instead of a word it extracts a numeric value.} {Return 0 if the extraction should fail for any reason.} var S2: String; it: Real; C: Byte; begin S2 := ExtractWord(S); Val(S2,it,C); if C <> 0 then it := 0; ExtractReal := it; end; Function RetrieveAString(const S: String): String; {Retrieve an Alligator String from S.} {Alligator Strings are defined as the part of the string} {that both alligarors want to eat, i.e. between < and >.} var A1,A2: Integer; begin {Locate the position of the two alligators.} A1 := Pos('<',S); A2 := Pos('>',S); {If the string has not been declared with <, return} {an empty string.} if A1 = 0 then Exit(''); {If the string has not been closed with >, return the} {entire remaining length of the string.} if A2 = 0 then A2 := Length(S)+1; RetrieveAString := Copy(S,A1+1,A2-A1-1); end; Function RetrieveBracketString(const S: String): String; { Like the above, but the string is surrounded by ( and ) . } var A1,A2: Integer; begin {Locate the position of the two alligators.} A1 := Pos('(',S); A2 := Pos(')',S); {If the string has not been declared with <, return} {an empty string.} if A1 = 0 then Exit(''); {If the string has not been closed with >, return the} {entire remaining length of the string.} if A2 = 0 then A2 := Length(S)+1; RetrieveBracketString := Copy(S,A1+1,A2-A1-1); end; Function RetrieveAPreamble( const S: String ): String; { Usually an alligator string will have some kind of label in } { front of it. This function will retrieve the label in its } { entirety. } { LIMITATION: Doesn't return the character immediately before } { the AString, which should be a space. } var A1: Integer; msg: String; begin A1 := Pos('<',S); if A1 <> 0 then begin msg := Copy(S, 1 , A1-2); end else begin msg := ''; end; RetrieveAPreamble := msg; end; Function BStr( N: LongInt ): String; { This function functions as the BASIC Str function. } var it: String; begin Str(N, it); BStr := it; end; Function SgnStr( N: Integer ): String; { Convert the string to a number, including either a '+' or '-'. } var it: String; begin it := BStr( N ); if N>= 0 then it := '+' + it; SgnStr := it; end; Function WideStr( N,Width: LongInt ): String; { Pack the string with zeroes until it's the specified width. } { This command is being used for my clock. } var msg: String; begin msg := BStr( Abs( N ) ); while Length( msg ) < Width do msg := '0' + msg; if N < 0 then msg := '-' + msg; WideStr := msg; end; function IsAlpha( C: Char ): Boolean; { Return TRUE if C is a letter, FALSE otherwise. } begin if ( UpCase( C ) >= 'A' ) and ( UpCase( C ) <= 'Z' ) then IsAlpha := True else IsAlpha := False; end; Function Acronym( phrase: String ): String; {can't const} { Copy all the capital letters from the PHRASE, and construct an acronym. } var A: String; { A String. In honor of the C64. } T: Integer; { A loop counter. In honor of the C64. } begin A := ''; for t := 1 to Length( phrase ) do begin if ( phrase[T] = UpCase( phrase[T] ) ) and IsAlpha( phrase[T] ) then A := A + phrase[T]; end; Acronym := A; end; Function Acronym( const phrase: String ; NumPlaces: Byte ): String; { This function works like the above one, but pad out the acronym to } { NumPlaces characters. } var A: String; begin A := Acronym( phrase ); if Length( A ) > NumPlaces then begin A := Copy( A , 1 , NumPlaces ); end else if Length( A ) < NumPlaces then begin while Length( A ) < NumPlaces do A := A + ' '; end; Acronym := A; end; Function Concentrate( const S: String ): String; { Remove all white space from this string, leaving nothing } { but concentrated alphanumeric goodness. } var T: Integer; CS: String; begin CS := ''; for T := 1 to Length( S ) do begin { If this character is neither a space nor a tab, } { add it to our concentrated string. } if (S[T] <> ' ') and (S[T] = #9) then CS := CS + S[T]; end; Concentrate := CS; end; Function Sgn( N: LongInt ): Integer; { Return the sign of this number, just like in BASIC. } begin if N > 0 then Sgn := 1 else if N < 0 then Sgn := -1 else Sgn := 0; end; Function StringMatchWeight( Part,Desc: String ): Integer; { Return the match weight of PART to DESC. If the weight is 0, then } { PART doesn't match DESC. All important traits listed in DESC must } { be found in PART. Optional traits are preceded by a ~; these don't have } { to be included in PART but increase the weight if they are. } { Negative traits must be preceded by a -; these must _not_ be present } { in Part. } Function ExtractOrClause(var S: String): String; {Extract the next trait from the or-list.} var P: Integer; it: String; begin {Error check- make sure that we have something left to} {extract! The string could have been nothing but white space.} if S <> '' then begin {Determine the position of the next whitespace.} P := Pos('|',S); if P = 0 then P := Pos(')',S); {Extract the command.} if P <> 0 then begin it := Copy(S,1,P-1); S := Copy(S,P+1,Length(S)); end else begin it := Copy(S,1,Length(S)); S := ''; end; end else begin it := ''; end; ExtractOrClause := it; end; var Trait,T2: String; it,MatchFound: Boolean; N: Integer; begin Part := UpCase( Part ); Desc := UpCase( Desc ); { Assume TRUE unless a trait is found that isn't in NDesc. } it := True; N := -1; DeleteWhiteSpace( Desc ); while Desc <> '' do begin Trait := ExtractWord( Desc ); if Trait <> '' then begin if Trait[1] = '~' then begin DeleteFirstChar( Trait ); if Pos( Trait , Part ) > 0 then Inc( N ); end else if Trait[1] = '-' then begin { A trait beginning with a "-" must NOT be present. } DeleteFirstChar( Trait ); if Pos( Trait , Part ) <> 0 then begin it := False; end; end else if Trait[1] = '(' then begin { A set of traits surrounded by parenthesis and separated by |s } { is an or-list. One of the traits must be present. } DeleteFirstChar( Trait ); MatChFound := False; repeat T2 := ExtractOrClause( Trait ); if Pos( T2 , Part ) <> 0 then MatchFound := True; until ( Trait = '' ) or MatchFound; if MatchFound then Inc( N ) else it := False; end else if UpCase( Trait ) = 'COMMON' then begin { A trait marked as COMMON will appear more often, despite number } { of matches. Use sparingly. } N := N + 5; end else begin if Pos( Trait , Part ) = 0 then begin it := False; end else begin Inc( N ); end; end; end; { if Trait <> '' } end; if IT and ( N >= 0 ) then begin if N < 4 then begin StringMatchWeight := N + 1; end else begin StringMatchWeight := ( N * N div 2 ) - N + 2; end; end else begin StringMatchWeight := 0; end; end; Function PartMatchesCriteria( const Part_In,Desc_In: String ): Boolean; { Return TRUE if the provided part description matches the provided } { search criteria. Return FALSE otherwise. } { A match is had if all the words in DESC are found in PART. } begin PartMatchesCriteria := StringMatchWeight( Part_In + ' ONE_MATCH' , Desc_In + ' ONE_MATCH' ) > 0; end; Function PartAtLeastOneMatch( const Part_In,Desc_In: String ): Boolean; { Return TRUE if the provided part description partially matches the provided } { search criteria. Return FALSE otherwise. } { A match is had if at least one word in DESC is found in PART. } var Trait: String; Part, Desc: String; N: Integer; begin Part := UpCase( Part_In ); Desc := UpCase( Desc_In ); N := 0; DeleteWhiteSpace( Desc ); while Desc <> '' do begin Trait := ExtractWord( Desc ); if Pos( Trait , Part ) <> 0 then Inc( N ); end; PartAtLeastOneMatch := N > 0; end; Function AStringHasBString( const A,B: String ): Boolean; { Return TRUE if B is contained in A, FALSE otherwise. } begin AStringHasBString := Pos( UpCase( B ) , UpCase( A ) ) > 0; end; Function HeadMatchesString( const H,S: String ): Boolean; { Return TRUE if the beginning Len(H) characters of S are H. } var T : String; begin T := Copy( S , 1 , Length( H ) ); HeadMatchesString := UpCase( T ) = UpCase( H ); end; Function QuickPCopy( const msg: String ): PChar; { Life is short. Copy msg to a pchar without giving me any attitude about it. } { Remember to deallocate that sucker when you're done playing with it. } var pmsg: PChar; begin pmsg := StrAlloc( length(msg ) + 1 ); StrPCopy( pmsg , msg ); QuickPCopy := pmsg; end; Function IsPunctuation( C: Char ): Boolean; { Return TRUE if C is some kind of punctuation, or FALSE otherwise. } { This is used for the message scripting commands so please } { forgive me if my definition of punctuation in this function } { is not the same as my own. } begin case C of '.',',',':',';','@','!','/','?','''': IsPunctuation := True; else IsPunctuation := False; end; end; Procedure ReplacePat( var msg: String; const pat_in,s: String ); { Replace all instances of PAT in MSG with S. } var N: Integer; pat: String; begin pat := UpCase( pat_in); { Error check- if S contains the pattern there could be an infinite loop. } if AStringHasBString( S , pat ) then Exit; repeat N := Pos( pat , UpCase( msg ) ); if N <> 0 then begin msg := Copy( msg , 1 , N - 1 ) + S + Copy( msg , N + Length( pat ) , Length( msg ) ); end; until N = 0; end; Function ReplaceHash( const msg,s: String ): String; { Look for a hash sign in MSG. Replace it with S. } var N: Integer; msg_out: String; begin N := Pos( '#' , msg ); if N <> 0 then begin msg_out := Copy( msg , 1 , N - 1 ) + S + Copy( msg , N + 1 , Length( msg ) ); end else begin msg_out := msg; end; ReplaceHash := msg_out; end; Procedure AlterDescriptors( var Original,Change: String ); { Alter the XRan descriptors held in ORIGINAL, based on the changes } { requested by CHANGE. Note that the contents of CHANGE will be utterly destrouyed } { by this process. } var cmd: String; N: Integer; begin while change <> '' do begin cmd := extractword( Change ); if cmd <> '' then begin N := Pos( Copy( cmd , 1 , 2 ) , Original ); if N > 0 then begin Original := Copy( Original , 1 , N - 1 ) + cmd + Copy( Original , N + Length( cmd ) , Length( Original ) ); end else begin Original := Original + ' ' + cmd; end; end; end; end; Procedure AtoAn( var msg: String ); { Go through the message, replacing "A" with "An" where appropriate. } const vowels = 'aeiouAEIOU'; var w,msg_out: String; begin msg_out := ''; while msg <> '' do begin w := ExtractWord( msg ); DeleteWhiteSpace( msg ); if UpCase( w ) = 'A' then begin if Pos( msg[1] , vowels ) <> 0 then w := w + 'n'; end; msg_out := msg_out + ' ' + w; end; msg := msg_out; end; Procedure AddTraits( var Trait_List: String; Traits_To_Add: String ); { TRAIT_LIST is a list of traits, such as that which may occur in a TYPE string. } { Add the traits in TRAITS_TO_ADD to the list, but don't add any that already } { occur in the list. } var T: String; begin while Traits_To_Add <> '' do begin T := ExtractWord( Traits_To_Add ); if ( T <> '' ) and not AStringHasBString( Trait_List , T ) then Trait_List := Trait_List + ' ' + T; end; end; Function QuoteString( A: String ): String; { Replace all spaces with quotes, so any single word from A can be } { searched for like "this". } var B,W: String; begin B := '"'; while A <> '' do begin W := ExtractWord( A ); if W <> '' then B := B + W + '"'; end; QuoteString := B; end; Procedure AddToQuoteString( var QList,QToAdd: String ); { Add some traits to a quote string. Make sure that everything is separated by quotes. } var A: String; begin while QToAdd <> '' do begin A := ExtractWord( QToAdd ); if A <> '' then begin if QList = '' then QList := '"'; QList := QList + A + '"'; end; end; end; Function NoQItemsMatch( const QList: String; var QToCheck: String ): Boolean; { Check to make sure that none of the words in QToCheck are present in QList. } { This procedure will completely destroy QToCheck, by the way. } var AllOK: Boolean; A: String; begin { Assume TRUE unless a match is found. } AllOK := True; while ( QToCheck <> '' ) and AllOK do begin A := ExtractWord( QToCheck ); if ( A <> '' ) and AStringHasBString( QList , '"' + A + '"' ) then AllOK := False; end; NoQItemsMatch := AllOK; end; end. GH2/ability.pp0000644000175000017500000006733211401151246012100 0ustar kaolkaolunit 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 ); 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 ); 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; 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. GH2/attacktest.pas0000644000175000017500000000627311326004556012762 0ustar kaolkaolprogram attacktest; uses gears,gearutil,locale,gearparser,effects,randmaps,ghchars,ghweapon,texutil; const Tar_X = 9; Tar_Y = 8; Num_Trials = 20000; var results: SAttPtr; Function TestAttack( GB: GameBoardPtr; AMaster,Attacker,Target: GearPtr; SkLvl: Integer; UseSpotWeakness: Boolean ): LongInt; { Run this attack a lot of times, and return the average damage done. } var T,TT: LongInt; Dmg,Total: LongInt; begin Total := 0; StripNAtt( FindRoot( Attacker ) , NAG_Damage ); StripNAtt( FindRoot( Target ) , NAG_Damage ); for T := 1 to Num_Trials do begin for tt := 1 to NumSkill do begin SetNAtt( AMaster^.NA , NAG_Skill , TT , SkLvl ); SetNAtt( Target^.NA , NAG_Skill , TT , 7 ); end; if not UseSpotWeakness then SetNAtt( AMaster^.NA , NAG_Skill , 18 , 0 ); DoAttack( GB , Attacker , Target , Tar_X , Tar_Y , 0 , 0 ); { Measure the damage done. } Dmg := AmountOfDamage( Target ); Total := Total + Dmg; { Delete the damage from both attacker and target. } StripNAtt( FindRoot( Attacker ) , NAG_Condition ); StripNAtt( FindRoot( Target ) , NAG_Condition ); StripNAtt( FindRoot( Attacker ) , NAG_Damage ); StripNAtt( FindRoot( Target ) , NAG_Damage ); StripNAtt( FindRoot( Attacker ) , NAG_WeaponModifier ); StripNAtt( FindRoot( Target ) , NAG_WeaponModifier ); end; TestAttack := Total div Num_Trials; end; Procedure TestThings( GB: GameBoardPtr; AMaster , Target: GearPtr; SkLvl: LongInt; UseSpotWeakness: Boolean ); { Test Armed Combat vs Martial Arts at the requested skill level. } var Attacker: GearPtr; Dmg: LongInt; begin Attacker := SeekGearByName( AMaster^.SubCom , 'Night Scythe' ); if Attacker = Nil then StoreSAtt( results, 'Night Scythe not found!' ); Dmg := TestAttack( GB , AMaster , Attacker , Target , SkLvl , UseSpotWeakness ); StoreSAtt( Results, 'Night Scythe at ' + BStr( SkLvl ) + ': ' + BStr( Dmg ) ); Attacker := SeekGearByName( AMaster^.SubCom , 'arm' ); if Attacker = Nil then StoreSAtt( results, 'Arm not found!' ); Dmg := TestAttack( GB , AMaster , Attacker , Target , SkLvl , UseSpotWeakness ); StoreSAtt( Results, 'Martial Arts at ' + BStr( SkLvl ) + ': ' + BStr( Dmg ) ); end; var GB: GameBoardPtr; Scene,AMaster,Target: GearPtr; begin AMaster := LoadNewMonster( 'Assassin Lord' ); SetSAtt( AMaster^.SA , 'JOB ' ); Target := LoadNewMonster( 'Hunter-Destroyer' ); Scene := LoadNewSTC( 'SCENE_EmptyBuilding' ); GB := RandomMap( Scene ); AppendGear( GB^.Meks , AMaster ); AppendGear( GB^.Meks , Target ); SetNAtt( AMaster^.NA , NAG_Location , NAS_X , Tar_X - 1 ); 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; SetNAtt( AMaster^.NA , NAG_Talent , NAS_KungFu , 1 ); SetNAtt( AMaster^.NA , NAG_Talent , 23 , 0 ); TestThings( GB , AMaster , Target , 5 , True ); TestThings( GB , AMaster , Target , 10 , True ); TestThings( GB , AMaster , Target , 15 , True ); TestThings( GB , AMaster , Target , 20 , True ); SaveStringList( 'out.txt' , results ); DisposeSAtt( results ); DisposeMap( GB ); end. GH2/vidmenus.pp0000644000175000017500000005310411365256066012304 0ustar kaolkaolunit vidmenus; { BUGS - If SelectMenu is handed an empty menu, all heck will } { break loose. This can be a particular problem for SelectFile. } { 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 video,dos,vidgfx,texutil,ui4gh; const {These two constants are used to tell the SELECT procedure whether or not} {the user is allowed to cancel.} RPMNormal = 0; RPMNoCancel = 1; RPMEscCancel = 0; { Menu can't be cancelled by mouse. Since the ASCII version } { doesn't use the mouse, this is just RPMNormal by another name. } RPMNoCleanup = 2; {If you want the menu left on the screen after we've finished, use this.} type RPGMenuKeyPtr = ^RPGMenuKey; RPGMenuKey = Record k: Char; value: integer; {The value returned when this key is pressed.} next: RPGMenuKeyPtr; end; RPGMenuItemPtr = ^RPGMenuItem; RPGMenuItem = Record msg: string; {The text which appears in the menu} value: integer; {A value, returned by SelectMenu. -1 is reserved for Cancel} desc: string; {Pointer to the item description. If Nil, no desc.} next: RPGMenuItemPtr; end; RPGMenu = Record active: boolean; itemcolor,selcolor,dtexcolor: Byte; Menu_Zone,Desc_Zone: vgfx_Rect; mode: Byte; topitem,selectitem,numitem: integer; {fields holding info about the status of the menu.} FirstItem: RPGMenuItemPtr; FirstKey: RPGMenuKeyPtr; end; RPGMenuPtr = ^RPGMenu; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer; const desc: string): RPGMenuItemPtr; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer): RPGMenuItemPtr; Procedure DisposeRPGMenuItem( var LList: RPGMenuItemPtr ); Procedure ClearMenu( RPM: RPGMenuPtr ); Procedure RemoveRPGMenuItem(RPM: RPGMenuPtr; var LMember: RPGMenuItemPtr); Procedure AddRPGMenuKey(RPM: RPGMenuPtr; k: Char; value: Integer); Function CreateRPGMenu(icolor,scolor: Byte; Z: vgfx_zone): RPGMenuPtr; Procedure AttachMenuDesc( RPM: RPGMenuPtr; Z: vgfx_zone ); Procedure DisposeRPGMenu(var RPM: RPGMenuPtr); Procedure DisplayMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ); Function RPMLocateByPosition(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; Function RPMLocateByValue(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; Function SelectMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): integer; Procedure RPMSortAlpha(RPM: RPGMenuPtr); Function CurrentMenuItemValue( RPM: RPGMenuPtr ): Integer; Function SetItemByValue( RPM: RPGMenuPtr ; V: Integer ): RPGMenuItemPtr; Procedure SetItemByPosition( RPM: RPGMenuPtr ; N: Integer ); Procedure BuildFileMenu( RPM: RPGMenuPtr; const SearchPattern: String ); Function SelectFile( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): String; implementation Function LastMenuItem(MIList: RPGMenuItemPtr): RPGMenuItemPtr; {This procedure will find the last item in the linked list.} begin {While the menu item is pointing to a next menu item, it's not the last. duh.} {So, move through the list until we hit a Nil pointer.} while MIList^.next <> Nil do begin {Check the next one.} MIList := MIList^.next; end; {We've found a MI.next = Nil. Yay! Return its address.} LastMenuItem := MIList; end; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer; const desc: string): RPGMenuItemPtr; {This procedure will add an item to the RPGToolMenu.} {The new item will be added as the last item in the list.} var it: ^RPGMenuItem; {Here's a pointer for the item we're creating.} temp: RPGMenuItemPtr; begin {Allocate memory for it.} New(it); {Check to make sure that the allocation succeeded.} if it = Nil then begin exit( Nil ); end; {Initialize it to the correct values.} it^.msg := msg; it^.value := value; it^.next := Nil; it^.desc := desc; {The desc field is assigned the value of PChar since it} {is assumed that we arent responsible for the allocation,} {disposal, or contents of this string.} {Locate the last item in the list, then assign "it" to it.} {If the list is currently empty, stick "it" in as the first item.} if RPM^.firstitem = Nil then begin RPM^.firstitem := it; end else begin temp := LastMenuItem(RPM^.FirstItem); temp^.next := it; end; {Increment the NumItem field.} Inc(RPM^.numitem); AddRPGMenuItem := it; end; Function AddRPGMenuItem(var RPM: RPGMenuPtr; const msg: string; value: integer): RPGMenuItemPtr; { Just like the above, but no desc. } begin AddRPGMenuItem := AddRPGMenuItem( RPM , msg , value , '' ); end; Procedure DisposeRPGMenuItem( var LList: RPGMenuItemPtr ); { Get rid of this list of items. } { WARNING - If you call this procedure for a menu, it will not } { change the value of the NumItems field!!! This will cause } { problems when trying to use the menu. Unless you know exactly } { what you're doing, use the ClearMenu procedure instead. } var NextItem: RPGMenuItemPtr; begin while LList <> Nil do begin NextItem := LList^.Next; Dispose( LList ); LList := NextItem; end; end; Procedure ClearMenu( RPM: RPGMenuPtr ); { Deallocate all the items in this menu, and set the number of } { items to 0. } begin DisposeRPGMenuItem( RPM^.FirstItem ); RPM^.NumItem := 0; RPM^.SelectItem := 1; RPM^.topitem := 1; end; Procedure RemoveRPGMenuItem(RPM: RPGMenuPtr; var LMember: RPGMenuItemPtr); {Locate and extract member LMember from list LList.} {Then, dispose of LMember.} var a,b: RPGMenuItemPtr; begin { Make sure LMember isn't Nil } if LMember = Nil then Exit; {Initialize A and B} B := RPM^.FirstItem; A := Nil; {Locate LMember in the list. A will thereafter be either Nil,} {if LMember if first in the list, or it will be equal to the} {element directly preceding LMember.} while (B <> LMember) and (B <> Nil) do begin A := B; B := B^.next; end; if B = Nil then begin {Major FUBAR. The member we were trying to remove can't} {be found in the list.} writeln('ERROR- RemoveLink asked to remove a link that doesnt exist.'); end else if A = Nil then begin {There's no element before the one we want to remove,} {i.e. it's the first one in the list.} RPM^.FirstItem := B^.Next; Dispose(B); end else begin {We found the attribute we want to delete and have another} {one standing before it in line. Go to work.} A^.next := B^.next; Dispose(B); end; { Reduce the number of items in the menu. } Dec(RPM^.NumItem); end; Procedure AddRPGMenuKey(RPM: RPGMenuPtr; k: Char; value: Integer); {Add a dynamically defined RPGMenuKey to the menu.} var it: RPGMenuKeyPtr; begin New(it); if it = Nil then begin exit; end; {Initialize the values.} it^.k := k; it^.value := value; it^.next := RPM^.FirstKey; RPM^.FirstKey := it; end; Function CreateRPGMenu(icolor,scolor: Byte; Z: vgfx_zone): RPGMenuPtr; {This function creates a new RPGMenu record, and returns the address.} var it: ^RPGMenu; {Here's a pointer for the menu we're making.} begin {Allocate memory for it.} New(it); {Check to make sure that we've actually initialized something.} if it = Nil then exit( Nil ); {Initialize the elements of the record.} it^.itemcolor := icolor; it^.selcolor := scolor; it^.Menu_Zone := ZoneToRect( Z ); it^.Desc_Zone.W := 0; {A width value of 0 means there is no desc window.} it^.Mode := RPMNormal; it^.FirstItem := Nil; it^.FirstKey := Nil; it^.dtexcolor := icolor; it^.active := False; {TopItem refers to the highest item on the screen display.} it^.topitem := 1; {SelectItem refers to the item currently being pointed at by the selector.} it^.selectitem := 1; {NumItem refers to the total number of items currently in the linked list.} it^.numitem := 0; {Return the address.} CreateRPGMenu := it; end; Procedure AttachMenuDesc( RPM: RPGMenuPtr; Z: vgfx_zone ); { Set the area for description items to zone Z. } begin RPM^.Desc_Zone := ZoneToRect( Z ); end; Procedure DisposeRPGMenu(var RPM: RPGMenuPtr); {This procedure is called when you want to get rid of the menu. It will deallocate} {the memory for the RPGMenu record and also for all of the linked RPGMenuItems.} var c,d: RPGMenuKeyPtr; begin {Check to make sure that we've got a valid pointer here.} if RPM = Nil then begin exit; end; {Save the location of the first menu item...} DisposeRPGMenuItem( RPM^.FirstItem ); c := RPM^.FirstKey; {... then get rid of the menu record.} Dispose(RPM); RPM := Nil; {Keep processing the menu items until we hit a Nil nextitem.} while c <> Nil do begin d := c^.next; Dispose(c); c := d; end; end; Function RPMLocateByPosition(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; {Locate the i'th element of the item list, then return its address.} var a: RPGMenuItemPtr; {Our pointer} t: integer; {Our counter} begin {Error check, first off.} if i > RPM^.numitem then begin exit( Nil ); end; a := RPM^.FirstItem; t := 1; if i > 1 then begin for t := 2 to i do a := a^.next; end; RPMLocateByPosition := a; end; Function RPMLocateByValue(RPM: RPGMenuPtr; i: integer): RPGMenuItemPtr; {Locate the i'th element of the item list, then return its address.} var t,a: RPGMenuItemPtr; {Our counter and a pointer} begin a := Nil; t := RPM^.FirstItem; while ( a = Nil ) and ( t <> Nil ) do begin if t^.value = i then a := t; t := t^.Next; end; RPMLocateByValue := a; end; Function MenuHeight( RPM: RPGMenuPtr ): Integer; { Return the height of the menu, in text rows. } var MH: Integer; begin MH := RPM^.Menu_Zone.h; if MH < 1 then MH := 1; MenuHeight := MH; end; Procedure RPMRefreshDesc(RPM: RPGMenuPtr); {Refresh the menu description box, if appropriate.} begin {Check to make sure that this menu has a description box, first off.} if RPM^.Desc_Zone.W > 0 then begin GameMsg( RPMLocateByPosition(RPM,RPM^.selectitem)^.desc , RPM^.Desc_Zone , RPM^.dtexcolor ); end; end; Procedure DisplayMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ); {Display the menu on the screen.} var topitem: RPGMenuItemPtr; a: RPGMenuItemPtr; {A pointer to be used while printing.} t: integer; height: integer; {The width of the menu display.} NextColor: Byte; Y: Integer; begin {Error check- make sure the menu has items in it.} if RPM^.FirstItem = Nil then Exit; { If a redraw procedure has been specified, call it. } if ReDrawer <> Nil then ReDrawer; ClrZone( RPM^.Menu_Zone ); ClipZone( RPM^.Menu_Zone ); {Calculate the height of the menu.} height := MenuHeight( rpm ); {Locate the top of the menu.} topitem := RPMLocateByPosition(RPM,RPM^.topitem); Y := RPM^.Menu_Zone.Y; a := topitem; for t := 1 to Height do begin {If we're at the currently selected item, highlight it.} if ((t + RPM^.topitem - 1) = RPM^.selectitem) and RPM^.Active then NextColor := RPM^.selcolor else NextColor := RPM^.itemcolor; TextColor( NextColor ); TextOut( RPM^.Menu_Zone.X , Y , a^.msg ); a := a^.next; {Check to see if we've prematurely encountered the end of the list.} if a = Nil then break; Inc( Y ); end; {Restore the window to its regular size.} MaxClipZone; {If there's an associated Desc field, display it now.} RPMRefreshDesc(RPM); end; Procedure RPMReposition( RPM: RPGMenuPtr; FullScroll: Boolean ); {The selected item has just changed, and is no longer visible on screen.} {Adjust the RPGMenu's topitem field to an appropriate value.} begin {When this function is called, there are two possibilities: either the} {selector has moved off the bottom of the page or the top.} if RPM^.selectitem < RPM^.topitem then begin {The selector has moved off the bottom of the list. The new page} {display should start with SelectItem on the bottom.} if FullScroll then begin RPM^.topitem := RPM^.selectitem - MenuHeight( RPM ) + 1; end else begin RPM^.topitem := RPM^.selectitem; end; {Error check- if this moves topitem below 1, that's bad.} if RPM^.topitem < 1 then RPM^.topitem := 1; end else begin {The selector has moved off the top of the list. The new page should} {start with SelectItem at the top, unless this would make things look} {funny.} if FullScroll then begin if ((RPM^.selectitem + MenuHeight( RPM ) - 1) > RPM^.numitem) then begin {There will be whitespace at the bottom of the menu if we assign} {SelectItem to TopItem. Make TopItem equal to the effective last} {page.} RPM^.topitem := RPM^.numitem - MenuHeight( RPM ) + 1; if RPM^.topitem < 1 then RPM^.topitem := 1; end else RPM^.topitem := RPM^.selectitem; end else if ((RPM^.topitem + MenuHeight( RPM ) - 1) < RPM^.numitem) then begin Inc( RPM^.TopItem ); end; end; end; Procedure RPMUpKey( RPM: RPGMenuPtr; FullScroll: Boolean ); {Someone just pressed the UP key, and we're gonna process that input.} {PRECONDITIONS: RPM has been initialized properly, and is currently being} { displayed on the screen.} begin {Decrement the selected item by one.} Dec(RPM^.selectitem); {If this causes it to go beneath one, wrap around to the last item.} if RPM^.selectitem = 0 then RPM^.selectitem := RPM^.numitem; {If the movement takes the selected item off the screen, do a redisplay.} {Otherwise, indicate the newly selected item.} if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem) >= MenuHeight( RPM )) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,FullScroll); end; end; Procedure RPMDownKey( RPM: RPGMenuPtr; FullScroll: Boolean ); {Someone just pressed the DOWN key, and we're gonna process that input.} {PRECONDITIONS: RPM has been initialized properly, and is currently being} { displayed on the screen.} begin {Increment the selected item.} Inc(RPM^.selectitem); {If this takes the selection out of bounds, restart at the first item.} if RPM^.selectitem = RPM^.numitem + 1 then RPM^.selectitem := 1; {If the movement takes the selected item off the screen, do a redisplay.} {Otherwise, indicate the newly selected item.} if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem) >= MenuHeight( RPM ) ) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,FullScroll); end; end; Function SelectMenu( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): integer; {This function will allow the user to browse through the menu and will} {return a value based upon the user's selection.} var getit: char; {Character used to store user input} r: integer; {The value we'll be sending back.} m: RPGMenuKeyPtr; UK: Boolean; {Has a special MenuKey been pressed?} begin {The menu is now active!} RPM^.Active := True; {Show the menu to the user.} DisplayMenu( RPM , ReDrawer ); DoFlip; {Initialize UK} UK := False; {Start the loop. Remain in this loop until either the player makes a selection} {or cancels the menu using the ESC key.} repeat DisplayMenu(RPM,ReDrawer); DoFlip; {Read the input from the keyboard.} getit := RPGKey; {Certain keys need processing- if so, process them.} case getit of {Selection Movement Keys} RPK_Up: RPMUpKey( RPM , True ); RPK_Down: RPMDownKey( RPM , True ); {If we receive an ESC, better check to make sure we're in a} {cancelable menu. If not, convert the ESC to an unused key.} #27: If RPM^.Mode = RPMNoCancel then getit := 'Q'; { If we get a backspace, conver that to ESC. } #8: If RPM^.Mode <> RPMNoCancel then getit := #27; { Convert enter to space. } #13,#10: getit := ' '; end; {Check to see if a special MENU KEY has been pressed.} if RPM^.FirstKey <> Nil then begin m := RPM^.FirstKey; while m <> Nil do begin if getit = m^.k then begin UK := True; r := m^.value; end; m := m^.next; end; end; {Check for a SPACE or ESC.} until (getit = ' ') or (getit = #27) or UK; {The menu is no longer active.} RPM^.Active := False; {We have to send back a different value depending upon whether a selection} {was made or the menu was cancelled. If an item was selected, return its} {value field. The value always returned by a cancel will be -1.} {If a MenuKey was pressed, r already contains the right value.} if getit = ' ' then begin r := RPMLocateByPosition(RPM,RPM^.selectitem)^.value; end else if not UK then r := -1; SelectMenu := r; end; Procedure RPMSortAlpha(RPM: RPGMenuPtr); {Given a menu, RPM, sort its items based on the alphabetical} {order of their msg fields.} {I should mention here that I haven't written a sorting} {algorithm in years, and only once on a linked list (CS assignment).} {I think this is an insertion sort... I checked on internet for} {examples of sorting techniques, found a bunch of contradictory} {information, and decided to just write the easiest thing that} {would work. Since we're dealing with a relatively small number} {of items here, speed shouldn't be that big a concern.} var sorted: RPGMenuItemPtr; {The sorted list} a,b,c,d: RPGMenuItemPtr;{Counters. We always need them, you know.} youshouldstop: Boolean; {Can you think of a better name?} begin {Initialize A and Sorted.} a := RPM^.firstitem; Sorted := Nil; while a <> Nil do begin b := a; {b is to be added to sorted} a := a^.next; {increase A to the next item in the menu} {Give b's Next field a value of Nil.} b^.next := nil; {Locate the correct position in Sorted to store b} if Sorted = Nil then {This is the trivial case- Sorted is empty.} Sorted := b else if UpCase( b^.msg ) < Upcase( Sorted^.msg ) then begin {b should be the first element in the list.} c := sorted; sorted := b; sorted^.next := c; end else begin {c and d will be used to move through Sorted.} c := Sorted; {Locate the last item lower than b} youshouldstop := false; repeat d := c; c := c^.next; if c = Nil then youshouldstop := true else if UpCase( c^.msg ) > UpCase( b^.msg ) then begin youshouldstop := true; end; until youshouldstop; b^.next := c; d^.next := b; end; end; RPM^.firstitem := Sorted; end; Function CurrentMenuItemValue( RPM: RPGMenuPtr ): Integer; { Determine the value of the current menu item, and return it. } { Return 0 if the item is not found. } var Item: RPGMenuItemPtr; begin item := RPMLocateByPosition( RPM , RPM^.SelectItem ); if item = Nil then begin CurrentMenuItemValue := 0; end else begin CurrentMenuItemValue := item^.value; end; end; Function SetItemByValue( RPM: RPGMenuPtr ; V: Integer ): RPGMenuItemPtr; { Search through the list, and set the SelectItem } { field to the first menu item which matches V. } var T: Integer; MI: RPGMenuItemPtr; begin if RPM = Nil then exit; MI := RPM^.FirstItem; T := 1; while (MI <> Nil) and (MI^.Value <> V) do begin MI := MI^.Next; Inc( T ); end; if MI <> Nil then begin RPM^.SelectItem := T; if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem) > MenuHeight( RPM ) ) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,True); end; end; SetItemByValue := MI; end; Procedure SetItemByPosition( RPM: RPGMenuPtr ; N: Integer ); { Search through the list, and set the SelectItem } { field to the Nth menu item. } begin if RPM = Nil then exit; if N <= RPM^.NumItem then begin RPM^.SelectItem := N; if (RPM^.selectitem < RPM^.topitem) or ((RPM^.selectitem - RPM^.topitem + 1) > MenuHeight( RPM ) ) then begin {Determine an appropriate new value for topitem.} RPMReposition(RPM,True); end; end; end; Procedure BuildFileMenu( RPM: RPGMenuPtr; const SearchPattern: String ); { Do a DosSearch for files matching SearchPattern, then add } { each of the files found to the menu. } var F: SearchRec; N: Integer; begin N := 1; FindFirst( SearchPattern , AnyFile , F ); While DosError = 0 do begin AddRPGMenuItem( RPM , F.Name , N ); Inc(N); FindNext( F ); end; end; Function SelectFile( RPM: RPGMenuPtr; ReDrawer: RedrawProcedureType ): String; { RPM is a menu created by the BuildFileMenu procedure. } { So, select one of the items and return the item name, which } { should be a filename. } var N: Integer; { The number of the file selected. } Name: String; { The name of the filename selected. } begin { Do the menu selection first. } N := SelectMenu( RPM , ReDrawer ); if N = -1 then begin { Selection was canceled. So, return an empty string. } Name := ''; end else begin { Locate the selected element of the menu. } Name := RPMLocateByPosition(RPM,RPM^.SelectItem)^.msg; end; SelectFile := Name; end; end. GH2/fcolor.pas0000644000175000017500000000361511326004554012072 0ustar kaolkaolProgram 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. GH2/specialsys.pp0000644000175000017500000002115611347637673012642 0ustar kaolkaolunit specialsys; { This unit handles special systems- GG_Usable gears. } { 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; Function CanDoTransformation( GB: GameBoardPtr; mek,part: GearPtr ): Boolean; Procedure DoTransformation( GB: GameBoardPtr; mek,part: GearPtr; ForReal: Boolean ); Function AIShouldTransform( GB: GameBoardPtr; mek,part: GearPtr ): Boolean; Function CanLRScanHere( GB: GameBoardPtr; Part: GearPtr ): Boolean; Function LongRangeScanEPCost( GB: GameBoardPtr; Part: GearPtr ): LongInt; Procedure DoLongRangeScan( GB: GameBoardPtr; PC , Part: GearPtr ); Function AIShouldLRScan( GB: GameBoardPtr; mek,part: GearPtr ): Boolean; implementation uses movement,ghmodule,ability,ui4gh,texutil,gearutil,ghprop,action, {$IFDEF ASCII} vidgfx {$ELSE} {$IFDEF CUTE} cutegfx {$ELSE} glgfx {$ENDIF} {$ENDIF} ; Function CanDoTransformation( GB: GameBoardPtr; mek,part: GearPtr ): Boolean; { Return TRUE if the provided mecha can transform to the requested form } { given the terrain conditions, or FALSE otherwise. } var P: Point; MMFound: Boolean; T,Terrain: Integer; begin { Do the transformation first. } DoTransformation( GB , mek , part , False ); { Assume FALSE unless shown to be true. } MMFound := False; { Determine the position of this mecha. } P := GearCurrentLocation( Mek ); if OnTheMap( GB , P.X , P.Y ) then begin { Check to make sure there's at least one movemode usable in the new form } { which is not blocked in this tile. } Terrain := TileTerrain( GB , P.X , P.Y ); for T := 1 to NumMoveMode do begin if BaseMoveRate( GB^.Scene , Mek , T ) > 0 then begin if not IsBlockingTerrainForMM( GB, Mek, Terrain, T ) then begin MMFound := True; Break; end; end; end; end; DoTransformation( GB , mek , part , False ); CanDoTransformation := MMFound; end; Procedure DoTransformation( GB: GameBoardPtr; mek,part: GearPtr; ForReal: Boolean ); { This mecha is about to do a transformation. } { The steps involved are: } { - Swap the mecha form. } { - Swap the mecha's visual representation. } { - Transform limbs as needed. } { - Set a new movement mode. } { If this function is called, assume that the transformation is legal. } { If FORREAL is true this is an actual transformation; if false, we're just } { checking something out. } var SpriteName,msg: String; Form: Integer; TMod: GearPtr; begin { Swap the form. } Form := mek^.s; mek^.S := Part^.V; Part^.V := Form; { Swap the sprites. } SpriteName := SAttValue( mek^.SA , 'SDL_SPRITE' ); SetSAtt( mek^.SA , 'SDL_SPRITE <' + SAttValue( part^.SA , 'SDL_SPRITE2' ) + '>' ); SetSAtt( part^.SA , 'SDL_SPRITE2 <' + SpriteName + '>' ); SpriteName := SAttValue( mek^.SA , 'CUTE_SPRITE' ); SetSAtt( mek^.SA , 'CUTE_SPRITE <' + SAttValue( part^.SA , 'CUTE_SPRITE2' ) + '>' ); SetSAtt( part^.SA , 'CUTE_SPRITE2 <' + SpriteName + '>' ); { Transform the limbs as needed. } TMod := Mek^.SubCom; while TMod <> Nil do begin if ( TMod^.G = GG_Module ) and ( TMod^.Stat[ STAT_VariableModuleForm ] <> 0 ) then begin if FORMxMODULE[ Mek^.S , TMod^.Stat[ STAT_PrimaryModuleForm ] ] then begin TMod^.S := TMod^.Stat[ STAT_PrimaryModuleForm ]; end else if FORMxMODULE[ Mek^.S , TMod^.Stat[ STAT_VariableModuleForm ] ] then begin TMod^.S := TMod^.Stat[ STAT_VariableModuleForm ]; end else begin TMod^.S := TMod^.Stat[ STAT_PrimaryModuleForm ]; end; end; TMod := TMod^.Next; end; if ForReal then begin { Set a new movement mode. } GearDownToLowestMM( Mek , GB , NAttValue( Mek^.NA , NAG_Location , NAS_X ) , NAttValue( Mek^.NA , NAG_Location , NAS_Y ) ); { Set call time. } SetNAtt( Mek^.NA , NAG_Action , NAS_CallTime , GB^.ComTime + ReactionTime( Mek ) ); { Display the message. } msg := MsgString( 'TRANSFORM_Announce' ); msg := ReplaceHash( msg , PilotName( Mek ) ); msg := ReplaceHash( msg , MsgString( 'FORMNAME_' + BStr( Mek^.S ) ) ); DialogMsg( msg ); end; end; Function AIShouldTransform( GB: GameBoardPtr; mek,part: GearPtr ): Boolean; { Given this transformation system, should the AI in question transform? } begin if not CanDoTransformation( GB , mek , part ) then begin AIShouldTransform := False; end else begin { For now, let's just transform as much as possible. } AIShouldTransform := True; end; end; Function CanLRScanHere( GB: GameBoardPtr; Part: GearPtr ): Boolean; { Return TRUE if the scanner can be used here, or FALSE otherwise. } begin CanLRScanHere := GB^.Scale <= ( Part^.Scale + 1 ); end; Function LongRangeScanEPCost( GB: GameBoardPtr; Part: GearPtr ): LongInt; { Return the cost of this long range scan, in energy points. } var it: LongInt; begin { Power usage is ( Map Scale )^2 * Scanner Size * 7 } it := Part^.V * 7; if GB^.Scale > 1 then it := it * GB^.Scale * GB^.Scale; LongRangeScanEPCost := it; end; Function LongRangeScanRange( GB: GameBoardPtr; Part: GearPtr ): Integer; { Return the range of this scanner. } var R: Integer; begin { The range differs depending on whether we're doing a local scan or not. } if GB^.Scale <= Part^.Scale then begin R := Part^.V * 5 + 10; end else begin R := Part^.V * 5; end; LongRangeScanRange := R; end; Procedure DoLongRangeScan( GB: GameBoardPtr; PC , Part: GearPtr ); { Use a long range scanner. This will light up any unseen encounters or } { models within range, at the price of lots of energy. } var R,team,N: Integer; m2: GearPtr; msg: String; begin R := LongRangeScanRange( GB , Part ); Team := NAttValue( PC^.NA , NAG_Location , NAS_Team ); N := 0; { Look through the meks on the gameboard and try to spot them one by one. } m2 := GB^.meks; while m2 <> Nil do begin if OnTheMap( GB , M2 ) and ( Range( GB , PC , M2 ) <= R ) then begin if not MekCanSeeTarget( gb , PC , M2 ) then begin { If this enemy mecha has not yet been spotted, } { there's a chance it will become visible. } if IsMasterGear( M2 ) then begin { M2 has just been spotted. } RevealMek( GB , M2 , PC ); Inc( N ); end else if ( Team = NAV_DefPlayerTeam ) and ( M2^.G = GG_MetaTerrain ) and ( M2^.S = GS_MetaEncounter ) and ( M2^.Stat[ STAT_MetaVisibility ] >= 0 ) then begin RevealMek( GB , M2 , PC ); Inc( N ); end; end; end; m2 := m2^.next; end; { Report the results. } msg := ReplaceHash( MsgString( 'LONGRANGESCAN_Announce' ) , PilotName( PC ) ); if N = 0 then begin msg := msg + MsgString( 'LONGRANGESCAN_NoTargets' ); end else if N = 1 then begin msg := msg + MsgString( 'LONGRANGESCAN_OneTarget' ); end else begin msg := msg + ReplaceHash( MsgString( 'LONGRANGESCAN_ManyTargets' ) , BStr( N ) ); end; DialogMsg( msg ); { Spend the energy. } SpendEnergy( FindRoot( Part ) , LongRangeScanEPCost( GB , Part ) ); { The scanner must wait a minute. } WaitAMinute( GB , FindRoot( Part ) , ReactionTime( FindRoot( Part ) ) ); end; Function AIShouldLRScan( GB: GameBoardPtr; mek,part: GearPtr ): Boolean; { As usual, the computer cheats. This NPC will use the long range } { scanner if there are enemy models within range. } var R: Integer; M2: GearPtr; ShouldScan: Boolean; begin { Start by checking the power- don't scan if you don't have the juice. } if LongRangeScanEPCost( GB, Part ) > ( EnergyPoints( Mek ) - Random( 200 ) ) then Exit( False ); R := LongRangeScanRange( GB , Part ); ShouldScan := False; { Look through the meks on the gameboard and try to spot them one by one. } M2 := GB^.meks; while m2 <> Nil do begin if OnTheMap( GB , M2 ) and ( Range( GB , Mek , M2 ) <= R ) then begin if IsMasterGear( M2 ) and AreEnemies( GB , Mek , M2 ) and not MekCanSeeTarget( gb , Mek , M2 ) then begin ShouldScan := True; Break; end; end; m2 := m2^.next; end; AIShouldLRScan := ShouldScan; end; end. GH2/cutegfx.pp0000644000175000017500000012751011376734117012122 0ustar kaolkaolunit cutegfx; { 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 } { This is a 2DSD interface for GH2, because acronyms rock. } {$MODE FPC} {$LONGSTRINGS ON} {$IFDEF ASCII} Interrupt compilation now. {$ENDIF} interface uses SDL,SDL_TTF,SDL_Image,gears,texutil,dos,ui4gh; Type SensibleSpritePtr = ^SensibleSprite; SensibleSprite = Record Name,Color: String; W,H: Integer; { Width and Height of each cell. } Img: PSDL_Surface; Next: SensibleSpritePtr; end; RedrawProcedureType = Procedure; const Avocado: TSDL_Color = ( r:136; g:141; b:101 ); Bacardi: TSDL_Color = ( r:121; g:105; b:137 ); Jade: TSDL_Color = ( r: 66; g:121; b:119 ); BrightJade: TSDL_Color = ( r:100; g:200; b:180 ); StdBlack: TSDL_Color = ( r: 5; g: 5; b: 5 ); StdWhite: TSDL_Color = ( r:255; g:255; b:255 ); MenuItem: TSDL_Color = ( r: 88; g:161; b:159 ); MenuSelect: TSDL_Color = ( r:125; g:250; b:125 ); TerrainGreen: TSDL_Color = ( r:100; g:210; b: 0 ); PlayerBlue: TSDL_Color = ( r: 0; g:141; b:211 ); AllyPurple: TSDL_Color = ( r:236; g: 0; b:211 ); EnemyRed: TSDL_Color = ( r:230; g: 0; b: 0 ); NeutralGrey: TSDL_Color = ( r:150; g:150; b:150 ); DarkGrey: TSDL_Color = ( r:100; g:100; b:100 ); InfoGreen: TSDL_Color = ( r: 0; g:240; b: 0 ); InfoHiLight: TSDL_Color = ( r: 70; g:255; b: 70 ); TextboxGrey: TSDL_Color = ( r:130; g:120; b:125 ); NeutralBrown: TSDL_Color = ( r:230; g:191; b: 81 ); BorderBlue: TSDL_Color = ( r: 0; g:101; b:151 ); Cyan: TSDL_Color = ( r: 0; g:255; b:155 ); MelodyYellow: TSDL_Color = ( r:250; g:200; b: 0 ); BorderColor: TSDL_Color = ( r:200; g: 50; b: 0 ); ScreenWidth = 800; ScreenHeight = 600; FontSize = 11; SmallFontSize = 9; Right_Column_Width = 180; ZONE_TextInputPrompt: TSDL_Rect = ( x:screenwidth div 2 - 210; y: screenheight div 2 - 35; w:420; h:30 ); ZONE_TextInput: TSDL_Rect = ( x:screenwidth div 2 - 210; y:screenheight div 2 + 5; w:420; h:30 ); ZONE_TextInputBigBox: TSDL_Rect = ( x:screenwidth div 2 - 220; y:screenheight div 2 - 45; w:440; h:90 ); ZONE_TextInputSmallBox: TSDL_Rect = ( x:screenwidth div 2 - 215; y:screenheight div 2; w:430; h:40 ); Model_Status_Width = 250; Model_Status_Height = 120; ZONE_Dialog: TSDL_Rect = ( x: 30 + Model_Status_Width; y: ScreenHeight - Model_Status_Height - 10; w: ScreenWidth - (50 + Model_Status_Width); h: Model_Status_Height ); ZONE_PCStatus: TSDL_Rect = ( x: 20; y: ScreenHeight - Model_Status_Height - 10; w: Model_Status_Width; h: Model_Status_Height ); KEY_REPEAT_DELAY = 200; KEY_REPEAT_INTERVAL = 75; RPK_MouseButton = #$90; RPK_TimeEvent = #$91; RPK_RightButton = #$92; Console_History_Length = 240; Dialog_Area_Height = Model_Status_Height; ZONE_MoreText: TSDL_Rect = ( x:10; y:10; w: ScreenWidth - 20 ; h: ScreenHeight - 50 ); ZONE_MorePrompt: TSDL_Rect = ( x:10; y: ScreenHeight - 40 ; w:ScreenWidth - 20; h:30 ); ZONE_Info: TSDL_Rect = ( x: ScreenWidth - Right_Column_Width - 10 ; y:10; w:Right_Column_Width; h:150 ); ZONE_Menu: TSDL_Rect = ( x: ScreenWidth - Right_Column_Width - 10 ; y:170; w:Right_Column_Width; h:ScreenHeight - 220 - Dialog_Area_Height ); ZONE_Menu1: TSDL_Rect = ( x: ScreenWidth - Right_Column_Width - 10 ; y:170; w:Right_Column_Width; h:130 ); ZONE_Menu2: TSDL_Rect = ( x: ScreenWidth - Right_Column_Width - 10 ; y:310; w:Right_Column_Width; h:ScreenHeight - 350 - Dialog_Area_Height ); ZONE_CharGenMenu: TSDL_Rect = ( x:ScreenWidth - Right_Column_Width - 20; y:190; w:Right_Column_Width; h:ScreenHeight-250 ); ZONE_CharGenCaption: TSDL_Rect = ( x:ScreenWidth - Right_Column_Width - 20; y:ScreenHeight-40; w:Right_Column_Width; h:20 ); ZONE_CharGenDesc: TSDL_Rect = ( x:10; y:ScreenHeight - Dialog_Area_Height; w:ScreenWidth - Right_Column_Width - 50; h:Dialog_Area_Height-10 ); ZONE_CharGenPrompt: TSDL_Rect = ( x:ScreenWidth - Right_Column_Width - 20; y:10; w:Right_Column_Width; h:160 ); ZONE_InteractStatus: TSDL_Rect = ( x: ScreenWidth div 2 - 225; y:ScreenHeight Div 2 - 210; w:345; h:30 ); ZONE_InteractMsg: TSDL_Rect = ( x: ScreenWidth div 2 - 225; y:ScreenHeight Div 2 - 140; w:345; h: 105 ); ZONE_InteractPhoto: TSDL_Rect = ( x: ScreenWidth div 2 + 125; y:ScreenHeight Div 2 - 200; w: 100; h: 150 ); ZONE_InteractInfo: TSDL_Rect = ( x:ScreenWidth div 2 - 225; y:ScreenHeight Div 2 - 175; w: 345; h: 30 ); ZONE_InteractMenu: TSDL_Rect = ( x: ScreenWidth div 2 - 225; y:ScreenHeight Div 2 - 30; w:450; h: 120 ); ZONE_InteractTotal: TSDL_Rect = ( x: ScreenWidth div 2 - 230; y: ScreenHeight Div 2 - 215; w: 460; h: 310 ); { The ITEMS ZONE is used for both the backpack and shopping interfaces. } ItemsLeftWidth = 345; ItemsRightWidth = 225; ItemsZoneLeftTab = ( ScreenWidth - ItemsLeftWidth - ItemsRightWidth - 10 ) div 2; ItemsZoneRightTab = ItemsZoneLeftTab + ItemsLeftWidth + 10; ZONE_ItemsTotal: tSDL_Rect = ( x: ItemsZoneLeftTab - 10; y:ScreenHeight Div 2 - 220; w: ItemsLeftWidth + ItemsRightWidth + 30; h: 330 ); ZONE_ShopCaption: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 210; w: ItemsLeftWidth; h: 30 ); ZONE_ShopMsg: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 140; w:ItemsLeftWidth; h: 105 ); ZONE_ShopMenu: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 30; w:ItemsLeftWidth; h: 120 ); ZONE_ItemsInfo: TSDL_Rect = ( x: ItemsZoneRightTab; y:ScreenHeight Div 2 - 210; w: ItemsRightWidth; h: 275 ); ZONE_ItemsPCInfo: TSDL_Rect = ( x: ItemsZoneRightTab; y:ScreenHeight Div 2 + 70; w: ItemsRightWidth; h: 30 ); ZONE_FieldHQMenu: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 210; w:ItemsLeftWidth; h: 275 ); ZONE_BackpackInstructions: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 210; w: ItemsLeftWidth; h: 30 ); ZONE_EqpMenu: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 140; w:ItemsLeftWidth; h: 105 ); ZONE_InvMenu: TSDL_Rect = ( x: ItemsZoneLeftTab; y:ScreenHeight Div 2 - 30; w:ItemsLeftWidth; h: 120 ); CaptionWidth = Model_Status_Width; ZONE_Caption: TSDL_Rect = ( x: ScreenWidth div 2 - ( CaptionWidth div 2 ); y: 20; w: CaptionWidth; h: Model_Status_Height ); SubCaptionWidth = FontSize * 20; ZONE_SubCaption: TSDL_Rect = ( x: ScreenWidth div 2 - ( SubCaptionWidth div 2 ); y: 35 + Model_Status_Height; w: SubCaptionWidth; h: FontSize + 2 ); ZONE_CharacterInfo: TSDL_Rect = ( x: ScreenWidth div 2 - 275; y: ScreenHeight Div 2 - 200; w: 450; h: 295 ); SideInfoWidth = FontSize * 16; SideInfoHeight = ( FontSize + 2 ) * 6; ZONE_RightInfo: TSDL_Rect = ( x: ScreenWidth - SideInfoWidth - 10; y: 15; w: SideInfoWidth; h: SideInfoHeight ); ZONE_LeftInfo: TSDL_Rect = ( x: 10; y: 15; w: SideInfoWidth; h: SideInfoHeight ); ZONE_SuperGetItem: TSDL_Rect = ( x:Screenwidth div 2 - 110; y:Screenheight div 2 - 135; w:220; h:270 ); ZONE_GetItemMenu: TSDL_Rect = ( x:Screenwidth div 2 - 100; y:Screenheight div 2 - 125; w:200; h:250 ); ZONE_UsagePrompt: TSDL_Rect = ( x:500; y:190; w:130; h:170 ); ZONE_UsageMenu: TSDL_Rect = ( x:50; y:155; w:380; h:245 ); ZONE_MemoTotal: TSDL_Rect = ( x:ScreenWidth div 2 - 205; y:ScreenHeight div 2 - 195; w: 410; h:280 ); ZONE_MemoText: TSDL_Rect = ( x:ScreenWidth div 2 - 200; y:ScreenHeight div 2 - 190; w:400; h:200 ); ZONE_MemoMenu: TSDL_Rect = ( x:ScreenWidth div 2 - 200; y:ScreenHeight div 2 + 25; w:400; h:50 ); { The SelectArenaMission zones. } ZONE_SAMMenu: TSDL_Rect = ( x:ScreenWidth div 2 - 200; y:ScreenHeight div 2 - 190; w:400; h:200 ); ZONE_SAMText: TSDL_Rect = ( x:ScreenWidth div 2 - 200; y:ScreenHeight div 2 + 25; w:400; h:50 ); ZONE_WorldMap: TSDL_Rect = ( x: ScreenWidth div 2 - 160; y: ScreenHeight div 2 - 160; W:320; H:320 ); ZONE_Clock: TSDL_Rect = ( x: ScreenWidth - 150; y: 30; w: 120; H:20 ); Monologue_Width = 400; Monologue_Height = 205; ZONE_MonologueTotal: TSDL_Rect = ( x: Screenwidth div 2 - Monologue_Width div 2 - 10; y: ScreenHeight div 3 - Monologue_Height div 2 - 10; w: Monologue_Width + 20; h: Monologue_Height + 20 ); ZONE_MonologueInfo: TSDL_Rect = ( x: Screenwidth div 2 - Monologue_Width div 2; y: ScreenHeight div 3 - Monologue_Height div 2; w: Monologue_Width; h:30 ); ZONE_MonologueText: TSDL_Rect = ( x: Screenwidth div 2 - Monologue_Width div 2; y: ScreenHeight div 3 - Monologue_Height div 2 + 40; w: Monologue_Width - 110; h: Monologue_Height - 40 ); ZONE_MonologuePortrait: TSDL_Rect = ( x: Screenwidth div 2 + Monologue_Width div 2 - 100; y: ScreenHeight div 3 - Monologue_Height div 2 + 40; w: 100; h: 150 ); Arena_List_Width = 240; Arena_List_Height = ScreenHeight - ( Dialog_Area_Height + 40 ); ZONE_ArenaPilotMenu: TSDL_Rect = ( x: 10; Y: 10; w: Arena_List_Width; h: Arena_List_Height ); ZONE_ArenaMechaMenu: TSDL_Rect = ( x: 50 + Arena_List_Width; Y: 10; w: Arena_List_Width; h: Arena_List_Height ); ZONE_ArenaInfo: TSDL_Rect = ( x: screenwidth - 10 - ItemsRightWidth; Y: 10; w: ItemsRightWidth; h: Arena_List_Height ); Concert_Zone_Width = 500; Concert_X0 = ( ScreenWidth - Concert_Zone_Width ) div 2; Concert_X1 = Concert_X0 + 110; Concert_Text_Width = Concert_Zone_Width - 110; Concert_Zone_Height = 300; Concert_y0 = ( ScreenHeight - Concert_Zone_Height - Dialog_Area_Height - 20 ) div 2; Concert_Audience_Height = 140; Concert_Y1 = Concert_Y0 + Concert_Audience_Height + 10; ZONE_ConcertTotal: TSDL_Rect = ( x: Concert_X0 ; y: Concert_Y0; w: Concert_Zone_Width; h: Concert_Zone_Height ); ZONE_ConcertAudience: TSDL_Rect = ( x: Concert_X0 ; y: Concert_Y0; w: Concert_Zone_Width; h: Concert_Audience_Height ); ZONE_ConcertCaption: TSDL_Rect = ( x: Concert_X1 ; y: Concert_Y1; w: Concert_Text_Width; h: 40 ); ZONE_ConcertMenu: TSDL_Rect = ( x: Concert_X1 ; y: Concert_Y1 + 45; w: Concert_Text_Width; h: 80 ); ZONE_ConcertDesc: TSDL_Rect = ( x: Concert_X1 ; y: Concert_Y1 + 130; w: Concert_Text_Width; h: 20 ); ZONE_ConcertPhoto: TSDL_Rect = ( x: Concert_X0 ; y: Concert_Y1; w: 100; h: 150 ); ZONE_Title_Screen_Version: TSDL_Rect = ( x: 555 ; y: 222; w: 50; h: 20 ); ZONE_Title_Screen_Menu: TSDL_Rect = ( x: 585 ; y: 255; w: 160; h: 140 ); Animation_Phase_Period = 6000; var Actual_Screen,Game_Screen: PSDL_Surface; Game_Font,Small_Font: PTTF_Font; Game_Sprites: SensibleSpritePtr; Last_Clock_Update: QWord; Animation_Phase: Integer; Mouse_X, Mouse_Y: LongInt; Cursor_Sprite: SensibleSpritePtr; Console_History: SAttPtr; Title_Screen: SensibleSpritePtr; Ersatz_Mouse_Sprite: SensibleSpritePtr; RK_NumKeys: PInt; RK_KeyState: PUInt8; Procedure DoFlip; Procedure QuickText( msg: String; MyDest: TSDL_Rect; C: TSDL_Color; F: PTTF_Font ); Procedure QuickTextC( msg: String; MyDest: TSDL_Rect; C: TSDL_Color; F: PTTF_Font ); Procedure QuickTextRJ( msg: String; MyDest: TSDL_Rect; C: TSDL_Color; F: PTTF_Font ); Procedure DisposeSpriteList(var LList: SensibleSpritePtr); Procedure RemoveSprite(var LMember: SensibleSpritePtr); procedure DrawSprite( Spr: SensibleSpritePtr; MyDest: TSDL_Rect; Frame: Integer ); procedure DrawSprite( Spr: SensibleSpritePtr; MyCanvas: PSDL_Surface; MyDest: TSDL_Rect; Frame: Integer ); function LocateSprite( const Name, Color: String; W,H: Integer ): SensibleSpritePtr; function LocateSprite( const Name: String; W,H: Integer ): SensibleSpritePtr; Procedure CleanSpriteList; function RPGKey: Char; Procedure ClrZone( const Z: TSDL_Rect ); Procedure ClrScreen; Procedure GetNextLine( var TheLine , msg , NextWord: String; Width: Integer ); Function PrettyPrint( msg: string; Width: Integer; var FG: TSDL_Color; DoCenter: Boolean ): PSDL_Surface; Procedure CMessage( msg: String; Z: TSDL_Rect; var C: TSDL_Color ); Procedure GameMSG( msg: string; Z: TSDL_Rect; var C: TSDL_Color ); Function IsMoreKey( A: Char ): Boolean; Procedure MoreKey; Function TextLength( F: PTTF_Font; msg: String ): LongInt; Function GetStringFromUser( Prompt: String; ReDrawer: RedrawProcedureType ): String; Function MoreHighFirstLine( LList: SAttPtr ): Integer; Procedure MoreText( LList: SAttPtr; FirstLine: Integer ); Procedure RedrawConsole; Procedure DialogMSG(msg: string); Procedure ClearExtendedBorder( Dest: TSDL_Rect ); Procedure DrawBPBorder; Procedure DrawGetItemBorder; Procedure SetupInteractDisplay( TeamColor: TSDL_Color ); Procedure SetupServicesDisplay; Procedure SetupFHQDisplay; Procedure SetupMemoDisplay; Procedure DrawMonologueBorder; Procedure InfoBox( Dest: TSDL_Rect ); Procedure Idle_Display; Procedure SetupArenaDisplay; Procedure SetupArenaMissionMenu; Procedure SetupConcertDisplay; Procedure SetupTitleScreenDisplay; implementation const WindowName: PChar = 'GearHead II (2D Version)'; IconName: PChar = 'GearHead II'; Procedure DoFlip; { Flip out, man! This flips from the newly drawn screen to the physical screen. } { Go look up Double Buffering on Wikipedia for more info. } var MyDest: TSDL_Rect; begin if Ersatz_Mouse then begin MyDest.X := Mouse_X; MyDest.Y := Mouse_Y; DrawSprite( Ersatz_Mouse_Sprite , MyDest , 0 ); end; SDL_Flip( Game_Screen ); Animation_Phase := ( Animation_Phase + 1 ) mod Animation_Phase_Period; end; Procedure QuickText( msg: String; MyDest: TSDL_Rect; C: TSDL_Color; F: PTTF_Font ); { Quickly draw some text to the screen, without worrying about } { line-splitting or justification or anything. } var pline: PChar; MyText: PSDL_Surface; begin pline := QuickPCopy( msg ); MyText := TTF_RenderText_Solid( F , pline , C ); {$IFDEF LINUX} if MyText <> Nil then SDL_SetColorKey( MyText , SDL_SRCCOLORKEY , SDL_MapRGB( MyText^.Format , 0 , 0, 0 ) ); {$ENDIF} Dispose( pline ); SDL_BlitSurface( MyText , Nil , Game_Screen , @MyDest ); SDL_FreeSurface( MyText ); end; Procedure QuickTextC( msg: String; MyDest: TSDL_Rect; C: TSDL_Color; F: PTTF_Font ); { Quickly draw some text to the screen, without worrying about } { line-splitting or justification or anything. } { The text will be centered in the given zone. } var pline: PChar; MyText: PSDL_Surface; begin if msg = '' then Exit; pline := QuickPCopy( msg ); MyText := TTF_RenderText_Solid( F , pline , C ); {$IFDEF LINUX} if MyText <> Nil then SDL_SetColorKey( MyText , SDL_SRCCOLORKEY , SDL_MapRGB( MyText^.Format , 0 , 0, 0 ) ); {$ENDIF} Dispose( pline ); MyDest.X := MyDest.X + ( MyDest.W - MyText^.W ) div 2; SDL_BlitSurface( MyText , Nil , Game_Screen , @MyDest ); SDL_FreeSurface( MyText ); end; Procedure QuickTextRJ( msg: String; MyDest: TSDL_Rect; C: TSDL_Color; F: PTTF_Font ); { Quickly draw some text to the screen, without worrying about } { line-splitting or justification or anything. } { This variation on the procedure is right-justified. } var pline: PChar; MyText: PSDL_Surface; begin pline := QuickPCopy( msg ); MyText := TTF_RenderText_Solid( F , pline , C ); {$IFDEF LINUX} if MyText <> Nil then SDL_SetColorKey( MyText , SDL_SRCCOLORKEY , SDL_MapRGB( MyText^.Format , 0 , 0, 0 ) ); {$ENDIF} Dispose( pline ); MyDest.X := MyDest.X - MyText^.W; SDL_BlitSurface( MyText , Nil , Game_Screen , @MyDest ); SDL_FreeSurface( MyText ); end; Procedure DrawAnimImage( Image,Canvas: PSDL_Surface; W,H,Frame: Integer; var MyDest: TSDL_Rect ); { This procedure is modeled after the command from Blitz Basic. } var MySource: TSDL_Rect; begin MySource.W := W; MySource.H := H; if W > Image^.W then W := Image^.W; MySource.X := ( Frame mod ( Image^.W div W ) ) * W; MySource.Y := ( Frame div ( Image^.W div W ) ) * H; SDL_BlitSurface( Image , @MySource , Canvas , @MyDest ); end; Function ScaleColorValue( V , I: Integer ): Byte; { Scale a color value. } begin V := ( V * I ) div 200; if V > 255 then V := 255; ScaleColorValue := V; end; Function MakeSwapBitmap( MyImage: PSDL_Surface; RSwap,YSwap,GSwap: PSDL_Color ): PSDL_Surface; { Given a bitmap, create an 8-bit copy with pure colors. } { 0 : Transparent (0,0,255) } { 1 - 63 : Grey Scale } { 64 - 127 : Pure Red } { 128 - 191 : Pure Yellow } { 192 - 255 : Pure Green } { Then, swap those colors out for the requested colors. } var MyPal: Array [0..255] of TSDL_Color; T: Integer; MyImage2: PSDL_Surface; begin { Initialize the palette. } for t := 1 to 64 do begin MyPal[ T - 1 ].r := ( t * 4 ) - 1; MyPal[ T - 1 ].g := ( t * 4 ) - 1; MyPal[ T - 1 ].b := ( t * 4 ) - 1; MyPal[ T + 63 ].r := ( t * 4 ) - 1; MyPal[ T + 63 ].g := 0; MyPal[ T + 63 ].b := 0; MyPal[ T + 127 ].r := ( t * 4 ) - 1; MyPal[ T + 127 ].g := ( t * 4 ) - 1; MyPal[ T + 127 ].b := 0; MyPal[ T + 191 ].r := 0; MyPal[ T + 191 ].g := ( t * 4 ) - 1; MyPal[ T + 191 ].b := 0; end; MyPal[ 0 ].r := 0; MyPal[ 0 ].g := 0; MyPal[ 0 ].b := 255; { Create replacement surface. } MyImage2 := SDL_CreateRGBSurface( SDL_SWSURFACE , MyImage^.W , MyImage^.H , 8 , 0 , 0 , 0 , 0 ); SDL_SetPalette( MyImage2 , SDL_LOGPAL or SDL_PHYSPAL , MyPal , 0 , 256 ); SDL_FillRect( MyImage2 , Nil , SDL_MapRGB( MyImage2^.Format , 0 , 0 , 255 ) ); SDL_SetColorKey( MyImage2 , SDL_SRCCOLORKEY or SDL_RLEACCEL , SDL_MapRGB( MyImage2^.Format , 0 , 0, 255 ) ); { Blit from the original to the copy. } SDL_BlitSurface( MyImage , Nil , MyImage2 , Nil ); { Redefine the palette. } for t := 1 to 64 do begin MyPal[ T + 63 ].r := ScaleColorValue( RSwap^.R , t * 4 ); MyPal[ T + 63 ].g := ScaleColorValue( RSwap^.G , t * 4 ); MyPal[ T + 63 ].b := ScaleColorValue( RSwap^.B , t * 4 ); MyPal[ T + 127 ].r := ScaleColorValue( YSwap^.R , t * 4 ); MyPal[ T + 127 ].g := ScaleColorValue( YSwap^.G , t * 4 ); MyPal[ T + 127 ].b := ScaleColorValue( YSwap^.B , t * 4 ); MyPal[ T + 191 ].r := ScaleColorValue( GSwap^.R , t * 4 ); MyPal[ T + 191 ].g := ScaleColorValue( GSwap^.G , t * 4 ); MyPal[ T + 191 ].b := ScaleColorValue( GSwap^.B , t * 4 ); end; SDL_SetPalette( MyImage2 , SDL_LOGPAL or SDL_PHYSPAL , MyPal , 0 , 256 ); MakeSwapBitmap := MyImage2; end; Procedure GenerateColor( var ColorString: String; var ColorStruct: TSDL_Color ); { Generate the color from the string. } var n: Integer; begin n := ExtractValue( ColorString ); if n > 255 then n := 255; ColorStruct.R := n; n := ExtractValue( ColorString ); if n > 255 then n := 255; ColorStruct.G := n; n := ExtractValue( ColorString ); if n > 255 then n := 255; ColorStruct.B := n; end; Function LocateSpriteByNameColor( const name,color: String ): SensibleSpritePtr; { Locate the sprite which matches the name provided. } { If no such sprite exists, return Nil. } var S: SensibleSpritePtr; begin S := Game_Sprites; while ( S <> Nil ) and ( ( S^.Name <> name ) or ( S^.Color <> Color ) ) do begin S := S^.Next; end; LocateSpriteByNameColor := S; end; Function NewSprite: SensibleSpritePtr; { Add an empty sprite description to the list. } var it: SensibleSpritePtr; begin New(it); if it = Nil then exit( Nil ); {Initialize values.} it^.Next := Game_Sprites; Game_Sprites := it; NewSprite := it; end; Function AddSprite( name, color: String; W,H: Integer ): SensibleSpritePtr; { Add a new element to the Sprite List. Load the image for this sprite } { from disk, if possible. } var fname: PChar; it: SensibleSpritePtr; tmp: PSDL_Surface; RSwap,YSwap,GSwap: TSDL_Color; begin {Allocate memory for our new element.} it := NewSprite; if it = Nil then Exit( Nil ); it^.Name := Name; it^.Color := Color; it^.W := W; it^.H := H; name := FSearch( name , Graphics_Directory ); if name <> '' then begin fname := QuickPCopy( name ); { Attempt to load the image. } it^.Img := IMG_Load( fname ); if it^.Img <> Nil then begin { Set transparency color. } SDL_SetColorKey( it^.Img , SDL_SRCCOLORKEY or SDL_RLEACCEL , SDL_MapRGB( it^.Img^.Format , 0 , 0, 255 ) ); { If a color swap has been specified, handle that here. } if Color <> '' then begin GenerateColor( Color , RSwap ); GenerateColor( Color , YSwap ); GenerateColor( Color , GSwap ); tmp := MakeSwapBitmap( it^.Img , @RSwap , @YSwap , @GSwap ); SDL_FreeSurface( it^.Img ); it^.img := tmp; end; { Convert to the screen mode. } { This will make blitting far quicker. } tmp := SDL_ConvertSurface( it^.Img , Game_Screen^.Format , SDL_SRCCOLORKEY ); SDL_FreeSurface( it^.Img ); it^.Img := TMP; end; Dispose( fname ); end else begin it^.Img := Nil; end; {Return a pointer to the new element.} AddSprite := it; end; Procedure DisposeSpriteList(var LList: SensibleSpritePtr); {Dispose of the list, freeing all associated system resources.} var LTemp: SensibleSpritePtr; begin while LList <> Nil do begin LTemp := LList^.Next; if LList^.Img <> Nil then SDL_FreeSurface( LList^.Img ); Dispose(LList); LList := LTemp; end; end; Procedure RemoveSprite(var LMember: SensibleSpritePtr); {Locate and extract member LMember from list LList.} {Then, dispose of LMember.} var a,b: SensibleSpritePtr; begin {Initialize A and B} B := Game_Sprites; A := Nil; {Locate LMember in the list. A will thereafter be either Nil,} {if LMember if first in the list, or it will be equal to the} {element directly preceding LMember.} while (B <> LMember) and (B <> Nil) do begin A := B; B := B^.next; end; if B = Nil then begin {Major FUBAR. The member we were trying to remove can't} {be found in the list.} writeln('ERROR- RemoveLink asked to remove a link that doesnt exist.'); end else if A = Nil then begin {There's no element before the one we want to remove,} {i.e. it's the first one in the list.} Game_Sprites := B^.Next; B^.Next := Nil; DisposeSpriteList(B); end else begin {We found the attribute we want to delete and have another} {one standing before it in line. Go to work.} A^.next := B^.next; B^.Next := Nil; DisposeSpriteList(B); end; LMember := Nil; end; procedure DrawSprite( Spr: SensibleSpritePtr; MyDest: TSDL_Rect; Frame: Integer ); { Draw a sensible sprite. } begin { First make sure that we have some valid sprite data... } if ( Spr <> Nil ) and ( Spr^.Img <> Nil ) then begin { All the info checks out. Print it. } DrawAnimImage( Spr^.Img , Game_Screen , Spr^.W , Spr^.H , Frame , MyDest ); end; end; procedure DrawSprite( Spr: SensibleSpritePtr; MyCanvas: PSDL_Surface; MyDest: TSDL_Rect; Frame: Integer ); { Draw a sensible sprite to an arbitrary canvas. } begin { First make sure that we have some valid sprite data... } if ( Spr <> Nil ) and ( Spr^.Img <> Nil ) then begin { All the info checks out. Print it. } DrawAnimImage( Spr^.Img , MyCanvas , Spr^.W , Spr^.H , Frame , MyDest ); end; end; function LocateSprite( const Name,Color: String; W,H: Integer ): SensibleSpritePtr; { Try to locate the requested sprite in the requested color. If the sprite } { is already loaded, then return its address. If not, load it and color it. } var S: SensibleSpritePtr; begin { First, find the sprite. If by some strange chance it hasn't been } { loaded yet, load it now. } S := LocateSpriteByNameColor( Name , Color ); if S = Nil then S := AddSprite( Name , Color , W , H ); { Set the width and height fields. } S^.W := W; S^.H := H; LocateSprite := S; end; function LocateSprite( const Name: String; W,H: Integer ): SensibleSpritePtr; { Find the requested sprite, either in memory or from disk. } var S: SensibleSpritePtr; begin { First, find the sprite. If by some strange chance it hasn't been } { loaded yet, load it now. } LocateSprite := LocateSprite( Name , '' , W , H ); end; Procedure CleanSpriteList; { Go through the sprite list and remove those sprites we aren't likely to } { need immediately... i.e., erase those ones which have a COLOR string defined. } var S,S2: SensibleSpritePtr; begin S := Game_Sprites; while S <> Nil do begin S2 := S^.Next; if S^.Color <> '' then begin RemoveSprite( S ); end; S := S2; end; end; function RPGKey: Char; { Read a readable key from the keyboard and return its ASCII value. } { This function will always return within a close approximation of 30ms } { from the last time it was called. It will also update the array of } { keypresses. } var a: String; event : TSDL_Event; Procedure ProcessThatEvent; { An event has been recieved. Process it. } begin if event.type_ = SDL_KEYDOWN then begin { Check to see if it was an ASCII character we recieved. } case event.key.keysym.sym of SDLK_Up,SDLK_KP8: a := RPK_Up; SDLK_Down,SDLK_KP2: a := RPK_Down; SDLK_Left,SDLK_KP4: a := RPK_Left; SDLK_Right,SDLK_KP6: a := RPK_Right; SDLK_KP7: a := RPK_UpLeft; SDLK_KP9: a := RPK_UpRight; SDLK_KP1: a := RPK_DownLeft; SDLK_KP3: a := RPK_DownRight; SDLK_Backspace: a := #8; SDLK_KP_Enter: a := #10; SDLK_KP5: a := '5'; else if( event.key.keysym.unicode < $80 ) and ( event.key.keysym.unicode > 0 ) then begin a := Char( event.key.keysym.unicode ); end; end; end else if ( event.type_ = SDL_MOUSEButtonDown ) then begin { Return a mousebutton event, and call GHFlip to set the mouse position } { variables. } if event.button.button = SDL_BUTTON_LEFT then begin a := RPK_MouseButton; end else if event.button.button = SDL_BUTTON_RIGHT then begin a := RPK_RightButton; end; end; end; var D: QWord; PResult: Integer; begin if Minimal_Screen_Refresh then begin a := RPK_TimeEvent; repeat if SDL_WaitEvent( @event ) = 1 then begin ProcessThatEvent; end; until a <> RPK_TimeEvent; end else begin { Go through the accumulated events looking for good ones. } a := RPK_TimeEvent; repeat PResult := SDL_PollEvent( @event ); if PResult = 1 then begin { See if this event is a keyboard one... } ProcessThatEvent; end; until ( PResult <> 1 ) or ( a <> RPK_TimeEvent ); { If necessary, do a delay. } if SDL_GetTicks < ( Last_Clock_Update + 20 ) then begin D := Last_Clock_Update + 30 - SDL_GetTicks; SDL_Delay( D ); end; Last_Clock_Update := SDL_GetTicks + 30; end; RK_KeyState := SDL_GetKeyState( RK_NumKeys ); SDL_GetMouseState( Mouse_X , Mouse_Y ); if a <> '' then RPGKey := a[1] else RPGKey := 'Z'; end; Procedure ClrZone( const Z: TSDL_Rect ); { Clear the specified screen zone. } begin SDL_FillRect( game_screen , @Z , SDL_MapRGB( Game_Screen^.Format , 0 , 0 , 0 ) ); end; Procedure ClrScreen; { Clear the specified screen zone. } begin SDL_FillRect( game_screen , Nil , SDL_MapRGBA( Game_Screen^.Format , 0 , 0 , 255 , 0 ) ); end; Function TextLength( F: PTTF_Font; msg: String ): LongInt; { Determine how long "msg" will be using the default "game_font". } var pmsg: PChar; { Gotta convert to pchar, pain in the ass... } W,Y: LongInt; { W means width I guess... Y is anyone's guess. Height? } begin { Convert the string to a pchar. } pmsg := QuickPCopy( msg ); { Call the alleged size calculation function. } TTF_SizeText( F , pmsg , W , Y ); { get rid of the PChar, since it's served its usefulness. } Dispose( pmsg ); TextLength := W; end; Procedure GetNextLine( var TheLine , msg , NextWord: String; Width: Integer ); { Get a line of text of maximum width "Width". } var LC: Boolean; { Loop Condition. So I wasn't very creative when I named it, so what? } begin { Loop condition starts out as TRUE. } LC := True; { Start building the line. } repeat NextWord := ExtractWord( Msg ); if TextLength( Game_Font , THEline + ' ' + NextWord) < Width then THEline := THEline + ' ' + NextWord else LC := False; until (not LC) or (NextWord = '') or ( TheLine[Length(TheLine)] = #13 ); { If the line ended due to a line break, deal with it. } if ( TheLine[Length(TheLine)] = #13 ) then begin { Display the line break as a space. } TheLine[Length(TheLine)] := ' '; NextWord := ExtractWord( msg ); end; end; Function PrettyPrint( msg: string; Width: Integer; var FG: TSDL_Color; DoCenter: Boolean ): PSDL_Surface; { Create a SDL_Surface containing all the text within "msg" formatted } { in lines of no longer than "width" pixels. Sound simple? Mostly just } { tedious, I'm afraid. } var SList,SA: SAttPtr; S_Total,S_Temp: PSDL_Surface; MyDest: SDL_Rect; pline: PChar; NextWord: String; THELine: String; {The line under construction.} begin { CLean up the message a bit. } DeleteWhiteSpace( msg ); if msg = '' then Exit( Nil ); {THELine = The first word in this iteration} THELine := ExtractWord( msg ); NextWord := ''; SList := Nil; {Start the main processing loop.} while TheLine <> '' do begin GetNextLine( TheLine , msg , NextWord , Width ); { Output the line. } { Next append it to whatever has already been created. } StoreSAtt( SList , TheLine ); { Prepare for the next iteration. } TheLine := NextWord; end; { while TheLine <> '' } { Create a bitmap for the message. } if SList <> Nil then begin { Create a big bitmap to hold everything. } S_Total := SDL_CreateRGBSurface( SDL_SWSURFACE , width , TTF_FontLineSkip( game_font ) * NumSAtts( SList ) , 32 , $FF000000 , $00FF0000 , $0000FF00 , $000000FF ); MyDest.X := 0; MyDest.Y := 0; { Add each stored string to the bitmap. } SA := SList; while SA <> Nil do begin pline := QuickPCopy( SA^.Info ); S_Temp := TTF_RenderText_Solid( game_font , pline , fg ); Dispose( pline ); { We may or may not be required to do centering of the text. } if DoCenter then begin MyDest.X := ( Width - TextLength( Game_Font , SA^.Info ) ) div 2; end else begin MyDest.X := 0; end; SDL_BlitSurface( S_Temp , Nil , S_Total , @MyDest ); SDL_FreeSurface( S_Temp ); MyDest.Y := MyDest.Y + TTF_FontLineSkip( game_font ); SA := SA^.Next; end; DisposeSAtt( SList ); end else begin S_Total := Nil; end; PrettyPrint := S_Total; end; Procedure CMessage( msg: String; Z: TSDL_Rect; var C: TSDL_Color ); { Print a message to the screen, centered in the requested rect. } { Clear the specified zone before doing so. } var MyText: PSDL_Surface; MyDest: TSDL_Rect; begin MyText := PrettyPrint( msg , Z.W , C , True ); if MyText <> Nil then begin MyDest := Z; MyDest.Y := MyDest.Y + ( Z.H - MyText^.H ) div 2; SDL_SetClipRect( Game_Screen , @Z ); SDL_BlitSurface( MyText , Nil , Game_Screen , @MyDest ); SDL_FreeSurface( MyText ); SDL_SetClipRect( Game_Screen , Nil ); end; end; Procedure GameMSG( msg: string; Z: TSDL_Rect; var C: TSDL_Color ); { As above, but no pageflip. } var MyText: PSDL_Surface; begin MyText := PrettyPrint( msg , Z.W , C , True ); if MyText <> Nil then begin SDL_SetClipRect( Game_Screen , @Z ); SDL_BlitSurface( MyText , Nil , Game_Screen , @Z ); SDL_FreeSurface( MyText ); SDL_SetClipRect( Game_Screen , Nil ); end; end; Function IsMoreKey( A: Char ): Boolean; { Return TRUE if A is a "more" key, that should skip to the next message in a list. } begin IsMoreKey := ( A = ' ' ) or ( A = #27 ) or ( A = RPK_MouseButton ); end; Procedure MoreKey; { Wait for the user to press either the space bar or the ESC key. } var A: Char; begin { Keep reading keypresses until either a space or an ESC is found. } repeat A := RPGKey; until IsMoreKey( A ); end; Procedure ClearExtendedBorder( Dest: TSDL_Rect ); { Draw the inner box for border displays. } begin Dest.X := Dest.X - 1; Dest.Y := Dest.Y - 1; Dest.W := Dest.W + 2; Dest.H := Dest.H + 2; SDL_FillRect( game_screen , @Dest , SDL_MapRGB( Game_Screen^.Format , 0 , 0 , 0 ) ); end; Function GetStringFromUser( Prompt: String; ReDrawer: RedrawProcedureType ): String; { Does what it says. } const AllowableCharacters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890()-=_+,.?"'; MaxInputLength = 80; var A: Char; it: String; MyDest: TSDL_Rect; begin { Initialize string. } it := ''; repeat { Set up the display. } if ReDrawer <> Nil then ReDrawer; ClearExtendedBorder( ZONE_TextInputBigBox ); SDL_FillRect( game_screen , @ZONE_TextInputBigBox , SDL_MapRGB( Game_Screen^.Format , BorderBlue.R , BorderBlue.G , BorderBlue.B ) ); SDL_FillRect( game_screen , @ZONE_TextInputSmallBox , SDL_MapRGB( Game_Screen^.Format , StdBlack.R , StdBlack.G , StdBlack.B ) ); CMessage( Prompt , ZONE_TextInputPrompt , StdWhite ); CMessage( it , ZONE_TextInput , InfoGreen ); MyDest.Y := ZONE_TextInput.Y + 2; MyDest.X := ZONE_TextInput.X + ( ZONE_TextInput.W div 2 ) + ( TextLength( Game_Font , it ) div 2 ); DrawSprite( Cursor_Sprite , MyDest , ( Animation_Phase div 2 ) mod 4 ); DoFlip; A := RPGKey; if ( A = #8 ) and ( Length( it ) > 0 ) then begin it := Copy( it , 1 , Length( it ) - 1 ); end else if ( Pos( A , AllowableCharacters ) > 0 ) and ( Length( it ) < MaxInputLength ) then begin it := it + A; end; until ( A = #13 ) or ( A = #27 ); GetStringFromUser := it; end; Function MoreHighFirstLine( LList: SAttPtr ): Integer; { Determine the highest possible FirstLine value. } var it: Integer; begin it := NumSAtts( LList ) - ( ZONE_MoreText.H div TTF_FontLineSkip( game_font ) ) + 1; if it < 1 then it := 1; MoreHighFirstLine := it; end; Procedure MoreText( LList: SAttPtr; FirstLine: Integer ); { Browse this text file across the majority of the screen. } { Clear the screen upon exiting, though restoration of the } { previous display is someone else's responsibility. } Procedure DisplayTextHere; var T: Integer; MyDest: TSDL_Rect; MyImage: PSDL_Surface; CLine: SAttPtr; { Current Line } PLine: PChar; begin ClrScreen; InfoBox( ZONE_MorePrompt ); InfoBox( ZONE_MoreText ); CMessage( MsgString( 'MORETEXT_Prompt' ) , ZONE_MorePrompt , InfoGreen ); { Set the clip area. } SDL_SetClipRect( Game_Screen , @ZONE_MoreText ); MyDest := ZONE_MoreText; { Error check. } if FirstLine < 1 then FirstLine := 1 else if FirstLine > MoreHighFirstLine( LList ) then FirstLine := MoreHighFirstLine( LList ); CLine := RetrieveSATt( LList , FirstLine ); for t := 1 to ( ZONE_MoreText.H div TTF_FontLineSkip( game_font ) ) do begin if CLine <> Nil then begin pline := QuickPCopy( CLine^.Info ); MyImage := TTF_RenderText_Solid( game_font , pline , NeutralGrey ); Dispose( pline ); SDL_BlitSurface( MyImage , Nil , Game_Screen , @MyDest ); SDL_FreeSurface( MyImage ); MyDest.Y := MyDest.Y + TTF_FontLineSkip( game_font ); CLine := CLine^.Next; end; end; { Restore the clip area. } SDL_SetClipRect( Game_Screen , Nil ); DoFlip; end; var A: Char; begin { Display the screen. } DisplayTextHere; repeat { Get input from user. } A := RPGKey; { Possibly process this input. } if ( A = '2' ) or ( A = RPK_Down ) then begin Inc( FirstLine ); DisplayTextHere; end else if ( A = '8' ) or ( A = RPK_Up ) then begin Dec( FirstLine ); DisplayTextHere; end; until ( A = #27 ) or ( A = 'Q' ) or ( A = #8 ); end; Procedure RedrawConsole; { Redraw the console. Yay! } var SL: SAttPtr; MyDest: TSDL_Rect; NumLines,LineNum: Integer; begin {Clear the message area, and set clipping bounds.} InfoBox( ZONE_Dialog ); SDL_SetClipRect( Game_Screen , @ZONE_Dialog ); MyDest := ZONE_Dialog; NumLines := ( ZONE_Dialog.H div TTF_FontLineSkip( game_font ) ) + 1; LineNum := NumLines; SL := RetrieveSAtt( Console_History , NumSAtts( Console_History ) - NumLines + 1 ); if SL = Nil then begin SL := Console_History; LineNum := NumSAtts( Console_History ); end; while LineNum > 0 do begin { Set the coords for this line. } MyDest.X := ZONE_Dialog.X; MyDest.Y := ZONE_Dialog.Y + ZONE_Dialog.H - LineNum * TTF_FontLineSkip( game_font ); { Output the line. } QuickText( SL^.Info , MyDest , InfoGreen , Game_font ); Dec( LineNum ); SL := SL^.Next; end; { Restore the clip zone to the full screen. } SDL_SetClipRect( Game_Screen , Nil ); end; Procedure DialogMSG( msg: string ); { Print a message in the scrolling dialog box, } { then store the line in Console_History. } { Don't worry about screen output since the console will be redrawn the next time } { the screen updates. } var NextWord: String; THELine: String; {The line under construction.} SA: SAttPtr; begin { CLean up the message a bit. } DeleteWhiteSpace( msg ); if msg = '' then Exit; msg := '> ' + Msg; {THELine = The first word in this iteration} THELine := ExtractWord( msg ); NextWord := ''; {Start the main processing loop.} while TheLine <> '' do begin GetNextLine( TheLine , msg , NextWord , ZONE_Dialog.w ); { If appropriate, save the line. } if TheLine <> '' then begin if NumSAtts( Console_History ) >= Console_History_Length then begin SA := Console_History; RemoveSAtt( Console_History , SA ); end; StoreSAtt( Console_History , TheLine ); end; { Prepare for the next iteration. } TheLine := NextWord; end; { while TheLine <> '' } end; Procedure DrawBPBorder; { Draw borders for the backpack display. } begin ClearExtendedBorder( ZONE_ItemsTotal ); SDL_FillRect( game_screen , @ZONE_ItemsTotal , SDL_MapRGB( Game_Screen^.Format , PlayerBlue.R , PlayerBlue.G , PlayerBlue.B ) ); ClearExtendedBorder( ZONE_BackpackInstructions ); ClearExtendedBorder( ZONE_InvMenu ); ClearExtendedBorder( ZONE_EqpMenu ); ClearExtendedBorder( ZONE_ItemsInfo ); ClearExtendedBorder( ZONE_ItemsPCInfo ); end; Procedure DrawGetItemBorder; { Draw borders for the get item display. } begin ClearExtendedBorder( ZONE_SuperGetItem ); SDL_FillRect( game_screen , @ZONE_SuperGetItem , SDL_MapRGB( Game_Screen^.Format , BorderBlue.R , BorderBlue.G , BorderBlue.B ) ); InfoBox( ZONE_GetItemMenu ); end; Procedure SetupInteractDisplay( TeamColor: TSDL_Color ); { Draw the display for the interaction interface. } begin ClearExtendedBorder( ZONE_InteractTotal ); SDL_FillRect( game_screen , @ZONE_InteractTotal , SDL_MapRGB( Game_Screen^.Format , TeamColor.R , TeamColor.G , TeamColor.B ) ); ClearExtendedBorder( ZONE_InteractStatus ); ClearExtendedBorder( ZONE_InteractMsg ); ClearExtendedBorder( ZONE_InteractMenu ); ClearExtendedBorder( ZONE_InteractPhoto ); ClearExtendedBorder( ZONE_InteractInfo ); end; Procedure SetupServicesDisplay; { Draw the display for the services interface. } begin ClearExtendedBorder( ZONE_ItemsTotal ); SDL_FillRect( game_screen , @ZONE_ItemsTotal , SDL_MapRGB( Game_Screen^.Format , PlayerBlue.R , PlayerBlue.G , PlayerBlue.B ) ); ClearExtendedBorder( ZONE_ShopCaption ); ClearExtendedBorder( ZONE_ShopMsg ); ClearExtendedBorder( ZONE_ShopMenu ); ClearExtendedBorder( ZONE_ItemsInfo ); ClearExtendedBorder( ZONE_ItemsPCInfo ); end; Procedure SetupFHQDisplay; { Draw the display for the services interface. } begin ClearExtendedBorder( ZONE_ItemsTotal ); SDL_FillRect( game_screen , @ZONE_ItemsTotal , SDL_MapRGB( Game_Screen^.Format , PlayerBlue.R , PlayerBlue.G , PlayerBlue.B ) ); ClearExtendedBorder( ZONE_FieldHQMenu ); ClearExtendedBorder( ZONE_ItemsInfo ); ClearExtendedBorder( ZONE_ItemsPCInfo ); end; Procedure SetupMemoDisplay; { Set up the memo display. } begin InfoBox( ZONE_MemoTotal ); ClearExtendedBorder( ZONE_MemoText ); ClearExtendedBorder( ZONE_MemoMenu ); end; Procedure DrawMonologueBorder; { Draw the border for the monologue. } begin ClearExtendedBorder( ZONE_MonologueTotal ); SDL_FillRect( game_screen , @ZONE_MonologueTotal , SDL_MapRGB( Game_Screen^.Format , PlayerBlue.R , PlayerBlue.G , PlayerBlue.B ) ); ClearExtendedBorder( ZONE_MonologueInfo ); ClearExtendedBorder( ZONE_MonologueText ); ClearExtendedBorder( ZONE_MonologuePortrait ); end; Procedure InfoBox( Dest: TSDL_Rect ); { Do a box for drawing something else inside of. } begin Dest.X := Dest.X - 5; Dest.Y := Dest.Y - 5; Dest.W := Dest.W + 10; Dest.H := Dest.H + 10; ClearExtendedBorder( Dest ); SDL_FillRect( game_screen , @(Dest) , SDL_MapRGB( Game_Screen^.Format , 10 , 0 , 70 ) ); end; Procedure Idle_Display; { Something is happening that's likely to take a long time. Load an idle } { image from disk and show it to the user. } var FList: SAttPtr; PFName: PChar; MyImage: PSDL_Surface; MyDest: TSDL_Rect; begin { Create a list of all the images in the idle_pics drawer. } FList := CreateFileList( Graphics_Directory + 'poster_*.*' ); if FList <> Nil then begin { Load one at random, and display it. } PFName := QuickPCopy( Graphics_Directory + SelectRandomSAtt( FList )^.Info ); MyImage := IMG_Load( PFName ); SDL_BlitSurface( MyImage , Nil , Game_Screen , Nil ); DoFlip; SDL_FreeSurface( MyImage ); Dispose( PFName ); DisposeSAtt( FList ); end; end; Procedure SetupArenaDisplay; { Draw the borders for all the arena-mode menus. } begin SDL_FillRect( game_screen , Nil , SDL_MapRGBA( Game_Screen^.Format , BorderBlue.R , BorderBlue.G , BorderBlue.B , 255 ) ); InfoBox( ZONE_ArenaInfo ); InfoBox( ZONE_ArenaPilotMenu ); InfoBox( ZONE_ArenaMechaMenu ); InfoBox( ZONE_PCStatus ); RedrawConsole; end; Procedure SetupArenaMissionMenu; { Set up the menu from which the mission will be selected in arena mode. } begin InfoBox( ZONE_MemoTotal ); ClearExtendedBorder( ZONE_SAMText ); ClearExtendedBorder( ZONE_SAMMenu ); end; Procedure SetupConcertDisplay; { Set up the concert display. } begin InfoBox( ZONE_ConcertTotal ); ClearExtendedBorder( ZONE_ConcertAudience ); ClearExtendedBorder( ZONE_ConcertCaption ); ClearExtendedBorder( ZONE_ConcertMenu ); ClearExtendedBorder( ZONE_ConcertDesc ); ClearExtendedBorder( ZONE_ConcertPhoto ); end; Procedure SetupTitleScreenDisplay; { Draw the title screen. } begin SDL_BlitSurface( Title_Screen^.Img , Nil , Game_Screen , Nil ); end; initialization SDL_Init( SDL_INIT_VIDEO ); if DoFullScreen then begin Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 32, SDL_DOUBLEBUF or SDL_FULLSCREEN ); end else begin { Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 0, SDL_DOUBLEBUF or SDL_HWSURFACE );} Game_Screen := SDL_SetVideoMode(ScreenWidth, ScreenHeight, 0, SDL_DOUBLEBUF ); end; if Ersatz_Mouse then SDL_ShowCursor( SDL_Disable ); ClrScreen; SDL_SetColorKey( Game_Screen , SDL_SRCCOLORKEY or SDL_RLEACCEL , SDL_MapRGB( Game_Screen^.Format , 0 , 0 , 255 ) ); SDL_EnableUNICODE( 1 ); SDL_EnableKeyRepeat( KEY_REPEAT_DELAY , KEY_REPEAT_INTERVAL ); TTF_Init; Game_Font := TTF_OpenFont( Graphics_Directory + 'VeraBd.ttf' , FontSize ); Small_Font := TTF_OpenFont( Graphics_Directory + 'VeraBd.ttf' , SmallFontSize ); Game_Sprites := Nil; Cursor_Sprite := LocateSprite( 'cursor.png' , 8 , 16 ); Title_Screen := LocateSprite( 'title_screen.png' , 800 , 600 ); Ersatz_Mouse_Sprite := LocateSprite( 'ersatz_mouse.png' , 16 , 16 ); Console_History := Nil; Last_Clock_Update := 0; if Splash_Screen_At_Start then begin Randomize(); Idle_Display; end; SDL_WM_SetCaption( WindowName , IconName ); finalization DisposeSAtt( Console_History ); DisposeSpriteList( Game_Sprites ); TTF_CloseFont( Game_Font ); TTF_CloseFont( Small_Font ); TTF_Quit; SDL_FreeSurface( Game_Screen ); SDL_Quit; end. GH2/colormenu.pp0000644000175000017500000003622711326004555012453 0ustar kaolkaolunit 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, {$IFDEF CUTE} cutegfx; {$ELSE} glgfx; {$ENDIF} 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: Integer; Redrawer: RedrawProcedureType ): String; Function RandomColorString( ColorSet: Integer ): String; implementation uses texutil,ui4gh; const Swatch_Columns = 20; Swatch_Rows = 5; Swatch_Width = 16; Swatch_Height = 24; cm_panel_width = 600; cm_panel_height = 456; cm_window_x = 100; cm_window_y = 65; 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: 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; 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; 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 ) ); 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. } ClearExtendedBorder( ZONE_colormenu_base ); SDL_FillRect( game_screen , @ZONE_colormenu_base , SDL_MapRGB( Game_Screen^.Format , PlayerBlue.R , PlayerBlue.G , PlayerBlue.B ) ); DrawSprite( cm_panel , ZONE_colormenu_base , 0 ); { Display the sprite we're editing. } MySprite := LocateSprite( colormenu_imagename, colormenu_imagepalette, colormenu_imagewidth, colormenu_imageheight ); MyDest := ZONE_colormenu_sprite; 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 ); DrawSprite( MySprite , MyDest , 0 ); 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], colormenu_colorset[ t ], colormenu_currentpen[ t ], colormenu_rowoffset[ t ], colormenu_Curs_X, colormenu_Curs_Y ); end else begin DrawColorSelectionBox( ZONE_colorselectionboxes[t], 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 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 if ( Mouse_X >= ZONE_swatch_area[t].X ) and ( Mouse_Y >= ZONE_swatch_area[t].Y ) and ( Mouse_X < ( ZONE_swatch_area[t].X + ZONE_swatch_area[t].W ) ) and ( Mouse_Y < ( ZONE_swatch_area[t].Y + ZONE_swatch_area[t].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 - ZONE_swatch_area[t].X ) div Swatch_Width; HitY := ( Mouse_Y - ZONE_swatch_area[t].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: 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_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. GH2/gearhead.pas0000644000175000017500000001410611401151246012336 0ustar kaolkaolprogram gearhead; { 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} {$IFNDEF DEBUG} {$IFNDEF ASCII} {$IFNDEF LINUX} {$APPTYPE GUI} {$ENDIF} {$ENDIF} {$ENDIF} uses gears,navigate,randmaps,locale,arenaplay,ghchars,gearutil,gearparser, ability,chargen,backpack,ui4gh,gh2arena,menugear, {$IFDEF ASCII} vidgfx,vidmap,vidmenus; {$ELSE} {$IFDEF CUTE} cutegfx,cutemap,glmenus; {$ELSE} glgfx,glmap,glmenus; {$ENDIF} {$ENDIF} const Version = '0.628'; Procedure RedrawOpening; { The opening menu redraw procedure. } begin SetupTitleScreenDisplay; CMessage( Version , ZONE_Title_Screen_Version , StdWhite ); if Console_History <> Nil then begin RedrawConsole; end; end; Procedure GenerateNewPC; { Call the character creator, and save the resultant } { character to disk. } var PC: GearPtr; begin PC := CharacterCreator( 0 ); if PC <> Nil then begin { Write this character to disk. } SaveEgg( PC ); { Get rid of the PC gear. } DisposeGear( PC ); end; end; Procedure StartRPGCampaign; { Load & run the adventure. } var RPM: RPGMenuPtr; uname: String; PC: GearPtr; F: Text; begin PC := Nil; { Create a menu listing all the characters in the SaveGame directory. } RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu ); BuildFileMenu( RPM , Save_Egg_Base + Default_Search_Pattern ); if RPM^.NumItem > 0 then begin RPMSortAlpha( RPM ); AddRPGMenuItem( RPM , MsgString( 'STARTRPG_NewChar' ) , -2 ); DialogMSG('Select character file.'); uname := SelectFile( RPM , @RedrawOpening ); if uname = MsgString( 'STARTRPG_NewChar' ) then begin PC := CharacterCreator( 0 ); end else if uname <> '' then begin Assign(F, Save_Game_Directory + uname ); reset(F); PC := ReadCGears(F); Close(F); end; end else begin { The menu was empty... make a new PC! } PC := CharacterCreator( 0 ); end; if PC <> Nil then begin StartCampaign( PC ); end; DisposeRPGMenu( RPM ); end; Procedure DesignDirBrowser; { Browse the mecha files on disk. } { NOTE: This procedure must be called from the Arena opening menu, so that } { the RedrawOpening procedure is properly initialized. } var MekMenu: RPGMenuPtr; fname: String; part: GearPtr; begin MekMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu ); BuildFileMenu( MekMenu , Design_Directory + Default_Search_Pattern ); RPMSortAlpha( MekMenu ); AddRPGMenuItem( MekMenu , ' Exit' , -1 ); repeat fname := SelectFile( MekMenu , @RedrawOpening ); if fname <> '' then begin part := LoadFile( fname , Design_Directory ); if Part <> Nil then begin if Part^.Next = Nil then begin { Only one mecha in this file. Just view it. } MechaPartBrowser( Part , @RedrawOpening ); end else begin { Multiple mecha in this file. Better write another } { procedure... } BrowseDesignFile( Part , @RedrawOpening ); end; DisposeGear( Part ); end; end; until fname = ''; DisposeRPGMenu( MekMenu ); end; Procedure SeriesDirBrowser; { Browse the series files on disk. } { NOTE: This procedure must be called from the Arena opening menu, so that } { the RedrawOpening procedure is properly initialized. } var MekMenu: RPGMenuPtr; fname: String; part: GearPtr; begin MekMenu := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu ); BuildFileMenu( MekMenu , Series_Directory + Default_Search_Pattern ); RPMSortAlpha( MekMenu ); AddRPGMenuItem( MekMenu , ' Exit' , -1 ); repeat fname := SelectFile( MekMenu , @RedrawOpening ); if fname <> '' then begin part := LoadFile( fname , Series_Directory ); if Part <> Nil then begin if Part^.Next = Nil then begin { Only one mecha in this file. Just view it. } MechaPartBrowser( Part , @RedrawOpening ); end else begin { Multiple mecha in this file. Better write another } { procedure... } BrowseDesignFile( Part , @RedrawOpening ); end; DisposeGear( Part ); end; end; until fname = ''; DisposeRPGMenu( MekMenu ); end; var RPM: RPGMenuPtr; N: Integer; begin RPM := CreateRPGMenu( MenuItem , MenuSelect , ZONE_Title_Screen_Menu ); AddRPGMenuItem( RPM , 'Create Character' , 1 ); AddRPGMenuItem( RPM , 'Load RPG Campaign' , 2 ); AddRPGMenuItem( RPM , 'Start RPG Campaign' , 3 ); AddRPGMenuItem( RPM , 'Load Arena Campaign' , 6 ); AddRPGMenuItem( RPM , 'Start Arena Campaign' , 7 ); AddRPGMenuItem( RPM , 'View Design Files' , 4 ); if XXRan_Debug then begin AddRPGMenuItem( RPM , 'View Series Files' , 5 ); end; AddRPGMenuItem( RPM , 'Quit Game' , -1 ); repeat if not STARTUP_OK then DialogMsg( 'ERROR: Main game directories not found. Please check installation of the game.' ); N := SelectMenu( RPM , @RedrawOpening ); case N of 1: GenerateNewPC; 2: RestoreCampaign( @RedrawOpening ); 3: StartRPGCampaign; 4: DesignDirBrowser; 5: SeriesDirBrowser; 6: RestoreArenaCampaign( @RedrawOpening ); 7: StartArenaCampaign; end; { Get rid of the console history from previous games. } DisposeSAtt( Console_History ); DisposeSAtt( Skill_Roll_History ); until N = -1; {deallocate all dynamic resources.} DisposeRPGMenu( RPM ); end. GH2/vidinfo.pp0000644000175000017500000007175211365256066012121 0ustar kaolkaolunit vidinfo; { This unit holds the information display 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 locale,gears,vidgfx,minitype; Procedure DisplayModelStatus( GB: GameBoardPtr; M: GearPtr; Z: VGFX_Zone ); Procedure QuickModelStatus( GB: GameBoardPtr; M: GearPtr ); Procedure NPCPersonalInfo( NPC: GearPtr; Z: VGFX_Zone ); Procedure DoMonologueDisplay( GB: GameBoardPtr; NPC: GearPtr; msg: String ); Procedure DisplayInteractStatus( GB: GameBoardPtr; NPC: GearPtr; React,Endurance: Integer ); Procedure CharacterDisplay( PC: GearPtr; GB: GameBoardPtr ); Procedure InjuryViewer( PC: GearPtr; redraw: RedrawProcedureType ); Procedure BrowserInterfaceInfo( GB: GameBoardPtr; Part: GearPtr; Z: VGFX_Zone ); Procedure BrowserInterfaceMystery( Part: GearPtr; Z: VGFX_Zone ); Procedure ArenaTeamInfo( Source: GearPtr; Z: VGFX_Zone ); Procedure TacticsTimeInfo( GB: GameBoardPtr ); Procedure ConcertStatus( PC: GearPtr; AL: AudienceList ); Procedure PersonadexInfo( NPC,HomeTown: GearPtr; Z: VGFX_Zone ); implementation uses video,ghweapon,ghchars,ability,ghmodule,gearutil,description, movement,texutil,ui4gh,narration; const SX_Char: Array [1..Num_Status_FX] of Char = ( 'P','B','R','S','H', 'V','T','D','R','P', 'A','G','L','N','Z', 'X','S','R','S','I', '@','@','@','@','@', '!','?','D','B' ); SX_Color: Array [1..Num_Status_FX] of Byte = ( Magenta, LightRed, LightGreen, Magenta, Yellow, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Cyan, Red, Yellow, Magenta, Red, Red, Red, Red, Red, Red, Magenta, LightRed, DarkGray ); Function MaxTArmor( Part: GearPtr ): LongInt; { Find the max amount of armor on this gear, counting external armor. } var it: LongInt; S: GearPtr; begin it := GearMaxArmor( Part ); S := Part^.InvCom; while S <> Nil do begin if S^.G = GG_ExArmor then it := it + GearMaxArmor( S ); S := S^.Next; end; MaxTArmor := it; end; Function CurrentTArmor( Part: GearPtr ): LongInt; { Find the current amount of armor on this gear, counting external armor. } var it: LongInt; S: GearPtr; begin it := GearCurrentArmor( Part ); S := Part^.InvCom; while S <> Nil do begin if S^.G = GG_ExArmor then it := it + GearCurrentArmor( S ); S := S^.Next; end; CurrentTArmor := it; end; Function StatusColor( Full , Current: LongInt ): Byte; { Given a part's Full and Current hit ratings, decide on a good status color. } begin if Full = Current then StatusColor := LightGreen else if Current > ( Full div 2 ) then StatusColor := Green else if Current > ( Full div 4 ) then StatusColor := Yellow else if Current > ( Full div 8 ) then StatusColor := LightRed else if Current > 0 then StatusColor := Red else StatusColor := DarkGray; end; Function EnduranceColor( Full , Current: LongInt ): Byte; { Choose colour to show remaining endurance (Stamina or Mental points)} begin { This is absolute rather than relative. } if Full = Current then EnduranceColor := LightGreen else if Current > 5 then EnduranceColor := Green else if Current > 0 then EnduranceColor := Yellow else EnduranceColor := LightRed; end; Function HitsColor( Part: GearPtr ): LongInt; { Decide upon a nice color to represent the hits of this part. } begin if PartActive( Part ) then HitsColor := StatusColor( GearMaxDamage( Part ) , GearCurrentDamage( Part ) ) else HitsColor := StatusColor( 100 , 0 ); end; Function ArmorColor( Part: GearPtr ): LongInt; { Decide upon a nice color to represent the armor of this part. } begin ArmorColor := StatusColor( MaxTArmor( Part ) , CurrentTArmor( Part ) ); end; Function ArmorDamageColor( Part: GearPtr ): LongInt; { Decide upon a nice color to represent the armor of this part. } var MA,CA: LongInt; { Max Armor, Current Armor } begin MA := MaxTArmor( Part ); CA := CurrentTArmor( Part ); if MA = 0 then begin ArmorDamageColor := Magenta; end else if ( CA >= ( MA * 3 div 4 ) ) then begin ArmorDamageColor := Black; end else if ( CA > MA div 4 ) then begin ArmorDamageColor := Blue; end else begin ArmorDamageColor := LightGray; end; end; Procedure ShowStatus( Part: GearPtr; OX,OY: Integer ); { Display all this part's status conditions. } { The status display starts at OX,OY and proceeds to the right until exiting the clip zone. } Procedure DrawStatusGlyph( Img: Char; C: Byte ); begin DrawGlyph( Img , OX , OY , C , Black ); Inc( OX ); end; var T: LongInt; begin { Show the character's status conditions. } { Display whether or not the mecha is hidden. } if ( Part^.Parent = Nil ) and IsHidden( Part ) then begin DrawStatusGlyph( 'S' , DarkGray ); end; { Hunger and morale come next. } if Part^.G = GG_Character then begin T := NAttValue( Part^.NA , NAG_Condition , NAS_Hunger ) - Hunger_Penalty_Starts; if T > ( NumGearStats * 3 ) then begin DrawStatusGlyph( 'H' , LightRed ); end else if T > ( NumGearStats * 2 ) then begin DrawStatusGlyph( 'H' , Yellow ); end else if T > 0 then begin DrawStatusGlyph( 'H' , Green ); end; T := NAttValue( Part^.NA , NAG_Condition , NAS_MoraleDamage ); if T < -20 then begin DrawStatusGlyph( '+' , LightGreen ); end else if T > ( 65 ) then begin DrawStatusGlyph( '-' , LightRed ); end else if T > ( 40 ) then begin DrawStatusGlyph( '-' , Yellow ); end else if T > 20 then begin DrawStatusGlyph( '-' , Green ); end; end else if Part^.G = GG_Mecha then begin { Mecha may be overloaded. } T := NAttValue( Part^.NA , NAG_Condition , NAS_PowerSpent );; if T > 10 then begin DrawStatusGlyph( 'O' , LightRed ); end else if T > 10 then begin DrawStatusGlyph( 'O' , Yellow ); end else if T > 0 then begin DrawStatusGlyph( 'O' , Blue ); end; end; for t := 1 to Num_Status_FX do begin if NAttValue( Part^.NA , NAG_StatusEffect , T ) <> 0 then begin DrawStatusGlyph( SX_Char[ T ] , SX_Color[ T ] ); end; end; if NAttValue( Part^.NA , NAG_EpisodeData , NAS_Ransacked ) = 1 then DrawStatusGlyph( '$' , DarkGray ); end; Procedure DisplayModules( Mek: GearPtr; X0,Y0: Integer ); { Draw a lovely little diagram detailing this mek's modules. } { X0 is the center of the display, Y0 is the top of the display. } var N: Integer; MD: GearPtr; Flayed, Gutted : Boolean; Procedure AddPartsToDiagram( GS: Integer ); { Add parts to the status diagram whose gear S value } { is equal to the provided number. } var X: Integer; FG, BG: Byte; begin MD := Mek^.SubCom; while ( MD <> Nil ) do begin if ( MD^.G = GG_Module ) and ( MD^.S = GS ) then begin FG := HitsColor( MD ); BG := ArmorDamageColor( MD ); if (FG = DarkGray) And (BG <> Black) then FG := Black; if Flayed Or (Gutted And (GS = GS_Body)) then begin if Gutted then FG := White else FG := LightMagenta; BG := Red; end; if Odd( N ) then X := X0 - ( N div 2 ) - 1 else X := X0 + ( N div 2 ); Inc( N ); Case GS of GS_Head: DrawGlyph( 'o' , X , Y0 , FG , BG ); GS_Turret: DrawGlyph('=' , X , Y0 , FG , BG ); GS_Storage: DrawGlyph('x' , X , Y0 , FG , BG ); GS_Body: DrawGlyph('B' , X , Y0 , FG , BG ); GS_Arm: DrawGlyph('+' , X , Y0 , FG , BG ); GS_Wing: DrawGlyph('W' , X , Y0 , FG , BG ); GS_Tail: DrawGlyph('t' , X , Y0 , FG , BG ); GS_Leg: DrawGlyph('l' , X , Y0 , FG , BG ); end; end; MD := MD^.Next; end; end; begin if Mek^.G = GG_Prop then begin DrawGlyph( '@' , X0 , Y0 + 1 , HitsColor( Mek ) , ArmorDamageColor( Mek ) ); end else begin { this "if" is just a shortcut } if GearOperational(Mek) then begin Gutted := False; Flayed := False; end else begin Gutted := (NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Gutted) = 1); Flayed := (NAttValue( Mek^.NA , NAG_EpisodeData , NAS_Flayed) = 1); end; { Draw the status diagram for this mek. } { Line One - Heads, Turrets, Storage } N := 0; AddPartsToDiagram( GS_Head ); AddPartsToDiagram( GS_Turret ); if N < 1 then N := 1; { Want storage to either side of body. } AddPartsToDiagram( GS_Storage ); Inc( Y0 ); { Line Two - Torso, Arms, Wings } N := 0; AddPartsToDiagram( GS_Body ); AddPartsToDiagram( GS_Arm ); AddPartsToDiagram( GS_Wing ); Inc( Y0 ); { Line Three - Tail, Legs } N := 0; AddPartsToDiagram( GS_Tail ); if N < 1 then N := 1; { Want legs to either side of body; tail in middle. } AddPartsToDiagram( GS_Leg ); Inc( Y0 ); end; { Restore background color to black. } TextBackground( Black ); end; Procedure DisplayMoveStatus( GB: GameBoardPtr; Part: GearPtr; CX,CY: Integer ); { Display the movement compass centered on CX,CY. } var D,Z: Integer; begin D := NAttValue( Part^.NA , NAG_Location , NAS_D ); Z := MekAltitude( gb , Part ); if Z >= 0 then begin TextColor( NeutralGrey ); TextOut( CX , CY , BStr( Z ) ); end else begin TextColor( Blue ); TextOut( CX , CY , BStr( Abs( Z ) ) ); end; DrawGlyph( '+' , CX + AngDir[D,1] , CY + AngDir[D,2] , White , Black ); DrawGlyph( '=' , CX - AngDir[D,1] , CY - AngDir[D,2] , DarkGray , Black ); { Speedometer. } if Speedometer( GB^.Scene , Part ) > 0 then begin if NAttValue( Part^.NA , NAG_Action , NAS_MoveAction ) = NAV_FullSPeed then begin DrawGlyph( 'G' , CX - 2 , CY - 1 , LightCyan , Black ); end else begin DrawGlyph( 'G' , CX - 2 , CY - 1 , Cyan , Black ); end; DrawGlyph( 'S' , CX - 2 , CY , DarkGray , Black ); end else begin DrawGlyph( 'G' , CX - 2 , CY - 1 , DarkGray , Black ); if CurrentMoveRate( GB^.Scene , Part ) > 0 then begin DrawGlyph( 'S' , CX - 2 , CY , Cyan , Black ); end else begin DrawGlyph( 'S' , CX - 2 , CY , DarkGray , Black ); end; end; end; Procedure CharacterHPSPMP( M: GearPtr; X0,Y0: Integer ); { Display the HP, SP, and MP for this model. } var CurP,MaxP: Integer; msg: String; begin TextColor( LightGray ); TextOut( X0 , Y0 , 'HP:' ); TextOut( X0 , Y0 + 1 , 'SP:' ); TextOut( X0 , Y0 + 2 , 'MP:' ); CurP := GearCurrentDamage( M ); MaxP := GearMaxDamage( M ); msg := BStr( CurP ); TextColor( StatusColor( MaxP , CurP ) ); TextOut( X0 + 7 - Length( msg ) , Y0 , msg ); CurP := CurrentStamina( M ); MaxP := CharStamina( M ); msg := BStr( CurP ); TextColor( EnduranceColor( MaxP , CurP ) ); TextOut( X0 + 7 - Length( msg ) , Y0 + 1 , msg ); CurP := CurrentMental( M ); MaxP := CharMental( M ); msg := BStr( CurP ); TextColor( EnduranceColor( MaxP , CurP ) ); TextOut( X0 + 7 - Length( msg ) , Y0 + 2 , msg ); end; Procedure CharStatDisplay( M: GearPtr; X0,Y0: Integer ); { Display the stats for this character. } var T,S: Integer; begin for t := 1 to 4 do begin TextColor( LightGray ); TextOut( X0 + ( T - 1 ) * 6 , Y0 + 0 , MsgString( 'STATABRV_' + BStr( t ) ) ); TextOut( X0 + ( T - 1 ) * 6 , Y0 + 1 , MsgString( 'STATABRV_' + BStr( t + 4 ) ) ); S := CSTat( M , T ); if S > M^.Stat[ T ] then TextColor( LightGreen ) else if S < M^.Stat[ T ] then TextColor( LightRed ) else TextColor( Green ); TextOut( X0 + ( T - 1 ) * 6 + 3 , Y0 + 0 , BStr( S ) ); S := CSTat( M , T + 4 ); if S > M^.Stat[ T + 4 ] then TextColor( LightGreen ) else if S < M^.Stat[ T + 4 ] then TextColor( LightRed ) else TextColor( Green ); TextOut( X0 + ( T - 1 ) * 6 + 3 , Y0 + 1 , BStr( S ) ); end; end; Procedure MechaMVTRSE( M: GearPtr; X0,Y0: Integer ); { Display the MV, TR, and SE for this model. } var msg: String; begin TextColor( LightGray ); TextOut( X0 , Y0 , 'MV:' ); TextOut( X0 , Y0 + 1 , 'TR:' ); TextOut( X0 , Y0 + 2 , 'SE:' ); TextColor( LightGreen ); msg := SgnStr( MechaManeuver( M ) ); TextOut( X0 + 7 - Length( msg ) , Y0 , msg ); msg := SgnStr( MechaTargeting( M ) ); TextOut( X0 + 7 - Length( msg ) , Y0 + 1 , msg ); msg := SgnStr( MechaSensorRating( M ) ); TextOut( X0 + 7 - Length( msg ) , Y0 + 2 , msg ); end; Procedure DisplayModelStatus( GB: GameBoardPtr; M: GearPtr; Z: VGFX_Zone ); { Display the status for this model in the upper status area. } var MyDest: VGFX_Rect; msg: String; begin MyDest := ZoneToRect( Z ); InfoBox( ZONE_Info ); ClipZone( MyDest ); TextColor( White ); msg := MechaPilotName( M ); TextOut( MyDest.X + ( MyDest.W - Length( msg ) ) div 2 , MyDest.Y , msg ); DisplayMoveStatus( GB , M , MyDest.X + 3 , MyDest.Y + 2 ); DisplayModules( M , MyDest.X + MyDest.W div 2 , MyDest.Y + 1 ); if M^.G = GG_Character then begin CharacterHPSPMP( M , MyDest.X + MyDest.W - 7 , MyDest.Y + 1 ); CharStatDisplay( M , MyDest.X , MyDest.Y + 4 ); end else if M^.G = GG_Mecha then begin MechaMVTRSE( M , MyDest.X + MyDest.W - 7 , MyDest.Y + 1 ); end; ShowStatus( M , MyDest.X + 1 , MyDest.Y + MyDest.H - 1 ); MaxClipZone; end; Procedure QuickModelStatus( GB: GameBoardPtr; M: GearPtr ); { Display the model status in the info area. } begin DisplayModelStatus( GB , M , ZONE_Caption ); end; Procedure NPCPersonalInfo( NPC: GearPtr; Z: VGFX_Zone ); { Display the name, job, age, and gender of the NPC. } var R: VGFX_Rect; begin R := ZoneToRect( Z ); R.H := 1; CMessage( GearName( NPC ) , R , LightGreen ); Inc( R.Y ); CMessage( JobAgeGenderDesc( NPC ) , R , Green ); end; Procedure DoMonologueDisplay( GB: GameBoardPtr; NPC: GearPtr; msg: String ); { Show the NPC's portrait, name, and description, as well as the message. } var Z_Text: VGFX_Rect; begin Z_Text := ZoneToRect( ZONE_MonologueText ); InfoBox( ZONE_MonologueInfo ); InfoBox( Z_Text ); NPCPersonalInfo( NPC , ZONE_MonologueInfo ); GameMsg( Msg , Z_Text , InfoHiLight ); end; Procedure DisplayInteractStatus( GB: GameBoardPtr; NPC: GearPtr; React,Endurance: Integer ); { Display the interact status. } var StatusRect: VGFX_Rect; msg: String; C: Byte; T: Integer; begin NPCPersonalInfo( NPC , ZONE_InteractName ); StatusRect := ZoneToRect( ZONE_InteractStatus ); if React > 0 then begin msg := ''; for T := 0 to ( React div 4 ) do msg := msg + '+'; C := LightGreen; end else if React < 0 then begin msg := ''; for T := 0 to ( Abs(React) div 4 ) do msg := msg + '-'; C := LightRed; end else begin msg := '~~~'; C := Yellow; end; TextColor( Green ); TextOut( StatusRect.X , StatusRect.Y , '[:)]' ); TextColor( C ); TextOut( StatusRect.X + 4 , StatusRect.Y , msg ); msg := ''; if Endurance > 10 then Endurance := 10; for t := 1 to Endurance do msg := msg + '>'; TextColor( Green ); TextOut( StatusRect.X + StatusRect.W - 14 , StatusRect.Y , '[Zz]' ); TextColor( LightGreen ); TextOut( StatusRect.X + StatusRect.W - 10 , StatusRect.Y , msg ); end; Procedure CharacterDisplay( PC: GearPtr; GB: GameBoardPtr ); { Display the character stats, background, etc. } var MyDest: VGFX_Rect; msg: String; T,S,R,FID: LongInt; C: Byte; Mek: GearPtr; begin { Begin with one massive error check... } if PC = Nil then Exit; if PC^.G <> GG_Character then PC := LocatePilot( PC ); if PC = Nil then Exit; { Set up the display. } MyDest := ZoneToRect( ZONE_CharacterDisplay ); InfoBox( ZONE_CharacterDisplay ); ClipZone( MyDest ); ClrZone( MyDest ); { Start by printing the character's name and AgeGenderJob line. } TextColor( White ); msg := GearName( PC ); TextOut( MyDest.X + MyDest.W div 2 - Length( msg ) div 2 , MyDest.Y , msg ); TextColor( Green ); msg := JobAgeGenderDesc( PC ); TextOut( MyDest.X + MyDest.W div 2 - Length( msg ) div 2 , MyDest.Y + 1 , msg ); { Show the stats. } for t := 1 to 8 do begin TextColor( LightGray ); TextOut( MyDest.X + 1 , MyDest.Y + 2 + T , MsgString( 'STATNAME_' + BStr( T ) ) ); { Find the adjusted stat value for this stat. } S := CStat( PC , T ); R := ( S + 2 ) div 3; if R > 7 then R := 7; { Determine an appropriate color for the stat, depending } { on whether its adjusted value is higher or lower than } { the basic value. } if S > PC^.Stat[ T ] then C := LighTGreen else if S < PC^.Stat[ T ] then C := LightRed else C := Green; TextColor( C ); msg := BStr( S ); TextOut( MyDest.X + 14 - Length( msg ) , MyDest.Y + 2 + T , msg ); TextOut( MyDest.X + 15 , MyDest.Y + 2 + T , MsgString( 'STATRANK' + BStr( R ) ) ); end; { Output the PC's total XP and free XP. } TextColor( LightGray ); TextOut( MyDest.X + 25 , MyDest.Y + 3 , MsgString( 'INFO_XP' ) ); S := NAttVAlue( PC^.NA , NAG_Experience , NAS_TotalXP ); msg := BStr( S ); TextColor( Green ); TextOut( MyDest.X + MyDest.W - 1 - Length( msg ) , MyDest.Y + 3 , msg ); TextColor( LightGray ); TextOut( MyDest.X + 25 , MyDest.Y + 4 , MsgString( 'INFO_XPLeft' ) ); S := S - NAttVAlue( PC^.NA , NAG_Experience , NAS_SpentXP ); msg := BStr( S ); TextColor( Green ); TextOut( MyDest.X + MyDest.W - 1 - Length( msg ) , MyDest.Y + 4 , msg ); TextColor( LightGray ); TextOut( MyDest.X + 25 , MyDest.Y + 5 , MsgString( 'INFO_Credits' ) ); S := NAttVAlue( PC^.NA , NAG_Experience , NAS_Credits ); msg := '$' + BStr( S ); TextColor( Green ); TextOut( MyDest.X + MyDest.W - 1 - Length( msg ) , MyDest.Y + 5 , msg ); { Print info on the PC's mecha, if appropriate. } if ( GB <> Nil ) then begin Mek := FindPilotsMecha( GB^.Meks , PC ); if Mek <> Nil then begin TextColor( LightGray ); TextOut( MyDest.X + 25 , MyDest.Y + 7 , MsgString( 'INFO_MekSelect' ) ); msg := FullGearName( Mek ); TextColor( Green ); TextOut( MyDest.X + MyDest.W - 1 - Length( msg ) , MyDest.Y + 8 , msg ); end; end; { Print info on the PC's faction, if appropriate. } FID := NAttValue( PC^.NA , NAG_Personal , NAS_FactionID ); if ( FID <> 0 ) and ( GB <> Nil ) and ( GB^.Scene <> Nil ) then begin Mek := SeekFaction( GB^.Scene , FID ); if Mek <> Nil then begin TextColor( LightGray ); TextOut( MyDest.X + 25 , MyDest.Y + 9 , MsgString( 'INFO_Faction' ) ); msg := GearName( Mek ); TextColor( Green ); TextOut( MyDest.X + MyDest.W - 1 - Length( msg ) , MyDest.Y + 10 , msg ); end; end; { Print the biographical information. } msg := SAttValue( PC^.SA , 'BIO1' ); if msg <> '' then begin MyDest.X := MyDest.X + 2; MyDest.W := MyDest.W - 4; MyDest.Y := MyDest.Y + 11; MyDest.H := MyDest.H - 11; GameMsg( msg , MyDest , Green ); end; MaxClipZone; end; Procedure InjuryViewer( PC: GearPtr; redraw: RedrawProcedureType ); { Display a brief listing of all the PC's major health concerns. } { Display a brief listing of all the PC's major health concerns. } var YPos: Integer; MyDest: VGFX_Rect; Procedure WriteStatus( msg: String; C: Byte ); begin TextColor( C ); TextOut( MyDest.X , YPos , msg ); Inc( YPos ); end; Procedure ShowSubInjuries( Part: GearPtr ); { Show the injuries of this part, and also for its subcoms. } var MD,CD: Integer; begin while Part <> Nil do begin MD := GearMaxDamage( Part ); CD := GearCurrentDamage( Part ); if not PartActive( Part ) then begin WriteStatus( GearName( Part ) + MsgString( 'INFO_IsDisabled' ) , StatusColor( MD , CD ) ); end else if CD < MD then begin WriteStatus( GearName( Part ) + MsgString( 'INFO_IsHurt' ) , StatusColor( MD , CD ) ); end; ShowSubInjuries( Part^.SubCom ); Part := Part^.Next; end; end; Procedure RealInjuryDisplay; var SP,MP,T: Integer; begin { Begin with one massive error check... } if PC = Nil then Exit; if PC^.G <> GG_Character then PC := LocatePilot( PC ); if PC = Nil then Exit; { Show exhaustion status first. } SP := CurrentStamina( PC ); MP := CurrentMental( PC ); if ( SP = 0 ) and ( MP = 0 ) then begin WriteStatus( MsgString( 'INFO_FullExhausted' ) , Red ); end else if ( SP = 0 ) or ( MP = 0 ) then begin WriteStatus( MsgString( 'INFO_PartExhausted' ) , LightRed ); end; { Hunger next. } T := NAttValue( PC^.NA , NAG_Condition , NAS_Hunger ) - Hunger_Penalty_Starts; if T > ( NumGearStats * 3 ) then begin WriteStatus( MsgString( 'INFO_ExtremeHunger' ) , Red ); end else if T > ( NumGearStats * 2 ) then begin WriteStatus( MsgString( 'INFO_Hunger' ) , LightRed ); end else if T > 0 then begin WriteStatus( MsgString( 'INFO_MildHunger' ) , Yellow ); end; { Low morale next. } T := NAttValue( PC^.NA , NAG_Condition , NAS_MoraleDamage ); if T > 65 then begin WriteStatus( MsgString( 'INFO_ExtremeMorale' ) , Red ); end else if T > 40 then begin WriteStatus( MsgString( 'INFO_Morale' ) , LightRed ); end else if T > 20 then begin WriteStatus( MsgString( 'INFO_MildMorale' ) , Yellow ); end; for t := 1 to Num_Status_FX do begin if NAttValue( PC^.NA , NAG_StatusEffect , T ) <> 0 then begin WriteStatus( MsgString( 'INFO_Status' + BStr( T ) ) , Red ); end; end; { Show limb injuries. } ShowSubInjuries( PC^.SubCom ); end; var msg: String; begin MyDest := ZoneToRect( ZONE_CharacterDisplay ); Redraw; InfoBox( MyDest ); ClrZone( MyDest ); ClipZone( MyDest ); msg := MsgString( 'INFO_InjuriesTitle' ); TextColor( StdWhite ); TextOut( MyDest.X + MyDest.W div 2 - Length( msg ) div 2 , MyDest.Y , msg ); YPos := MyDest.Y + 1; RealInjuryDisplay; MaxClipZone; DoFlip; MoreKey; end; Procedure BrowserInterfaceInfo( GB: GameBoardPtr; Part: GearPtr; Z: VGFX_Zone ); { Display the basic information for this gear. } var MyDest: VGFX_Rect; msg: String; X,YEnd,N: Integer; Cost: LongInt; begin MyDest := ZoneToRect( Z ); ClrZone( MyDest ); msg := GearName( Part ); TextColor( InfoHilight ); TextOut( MyDest.X + MyDest.W div 2 - Length( msg ) div 2 , MyDest.Y , msg ); YEnd := MyDest.Y + MyDest.H - 1; { Display the part's armor rating. } if IsMasterGear( Part ) then begin N := PercentDamaged( Part ); msg := BStr( N ) + '%'; TextColor( StatusColor( 100 , N ) ); TextOut( MyDest.X , MyDest.Y + 1 , msg ); X := MyDest.X + 1; end else begin N := GearCurrentArmor( Part ); if N > 0 then msg := '[' + BStr( N ) else msg := '[-'; msg := msg + '] '; TextColor( ArmorColor( Part ) ); TextOut( MyDest.X , MyDest.Y + 1 , msg ); X := MyDest.X + Length( msg ) + 1; { Display the part's damage rating. } N := GearCurrentDamage( Part ); if N > 0 then msg := BStr( N ) else msg := '-'; TextColor( HitsColor( Part ) ); TextOut( X , MyDest.Y + 1 , msg ); end; textColor( DarkGray ); TextOut( X + Length( msg ) , MyDest.Y + 1 , 'DP' ); { Display the part's mass. } N := ( GearMass( Part ) + 1 ) div 2; if N > 0 then begin msg := MassString( Part ); TextOut( MyDest.X + MyDest.W - Length( msg ) , MyDest.Y + 1 , msg ); end; { Display the cost. } if Part^.G = GG_Mecha then begin Cost := GearValue( Part ); if Cost > 0 then begin msg := 'PV:' + BStr( Cost ); TextOut( MyDest.X , MyDest.Y + 2 , msg ); end; end else begin Cost := GearCost( Part ); if ( Cost > 0 ) then begin msg := '$' + BStr( Cost ); TextOut( MyDest.X , MyDest.Y + 2 , msg ); end; end; { Display the spaces. } if not IsMasterGear( Part ) then begin if ( Part^.G = GG_Module ) or ( Part^.G = GG_ExArmor ) or ( Part^.G = GG_Shield ) or ( Part^.G = GG_Harness ) then begin msg := BStr( SubComComplexity( Part ) ) + '/' + BStr( ComponentComplexity( Part ) ) + ' slots used'; TextOut( MyDest.X + MyDest.W - Length( msg ) , MyDest.Y + 2 , msg ); end else begin msg := BStr( ComponentComplexity( Part ) ) + ' slots'; TextOut( MyDest.X + MyDest.W - Length( msg ) , MyDest.Y + 2 , msg ); end; end; { Next, the extended description and the regular description. } { Create the zone for this text. } MyDest.Y := MyDest.Y + 3; MyDest.H := MyDest.H - 3; { See if there is an extended description. } msg := ExtendedDescription( GB , Part ); if msg <> '' then begin GameMsg( msg , MyDest , Green ); MyDest.Y := vg_y + 2; MyDest.H := YEnd - MyDest.Y + 1; end; GameMsg( SAttValue( Part^.SA , 'DESC' ) , MyDest , Green ); end; Procedure BrowserInterfaceMystery( Part: GearPtr; Z: VGFX_Zone ); { This gear is a mystery. Display its name, and that's about it. } var MyDest: VGFX_Rect; msg: String; begin MyDest := ZoneToRect( Z ); ClrZone( MyDest ); msg := GearName( Part ); TextColor( InfoHilight ); TextOut( MyDest.X + MyDest.W div 2 - Length( msg ) div 2 , MyDest.Y , msg ); end; Procedure ArenaTeamInfo( Source: GearPtr; Z: VGFX_Zone ); { Print the important information for this team. } var MyDest: VGFX_Rect; msg: String; Fac: GearPtr; Renown: Integer; begin MyDest := ZoneToRect( Z ); TextColor( White ); msg := GearName( Source ); TextOut( MyDest.X + ( MyDest.W - Length( msg ) ) div 2 , MyDest.Y , msg ); TextColor( LightGreen ); msg := '$' + BStr( NAttValue( Source^.NA , NAG_Experience , NAS_Credits ) ); TextOut( MyDest.X + ( MyDest.W - Length( msg ) ) div 2 , MyDest.Y + 1 , msg ); Fac := SeekFaction( Source , NAttValue( Source^.NA , NAG_Personal , NAS_FactionID ) ); if Fac <> Nil then begin TextColor( LightGray ); msg := GearName( Fac ); TextOut( MyDest.X + ( MyDest.W - Length( msg ) ) div 2 , MyDest.Y + 2 , msg ); end; Renown := NAttValue( Source^.NA , NAG_CharDescription , NAS_Renowned ); msg := RenownDesc( Renown ); TextColor( Green ); TextOut( MyDest.X + ( MyDest.W - Length( msg ) ) div 2 , MyDest.Y + 3 , msg ); end; Procedure TacticsTimeInfo( GB: GameBoardPtr ); { Tell how much free time the currently active model has. } var TimeLeft,W,T: LongInt; MyDest: VGFX_Rect; begin TimeLeft := TacticsRoundLength - ( GB^.ComTime - NAttValue( GB^.Scene^.NA , NAG_SceneData , NAS_TacticsTurnStart ) ); MyDest := ZoneToRect( ZONE_Clock ); TextColor( White ); TextOut( MyDest.X , MyDest.Y , 'TIME:' ); W := (( MyDest.W - 5 ) * TimeLeft ) div TacticsRoundLength; for t := 1 to W do begin DrawGlyph( '=' , MyDest.X + T + 4 , MyDest.Y , LightGreen , Black ); end; end; Procedure ConcertStatus( PC: GearPtr; AL: AudienceList ); { Display the status information for the concert minigame. } Const Mood_Glyph: Array [0..Num_Audience_Moods] of Char = ( '-','-','-','~','+','+','+' ); Mood_Color: Array [0..Num_Audience_Moods] of Byte = ( DarkGray, Red, Yellow, Yellow, Yellow, Green, LightGreen ); Trait_Color: Array [0..2] of Byte = ( LightBlue, LightRed, Yellow ); var MyDest: VGFX_Rect; T: Integer; begin MyDest := ZoneToRect( ZONE_ConcertAudience ); CMessage( '=AUDIENCE=' , ZONE_ConcertAudience , DarkGray ); for t := 1 to MaxAudienceSize do begin if AL[t].Mood <> MOOD_Absent then begin DrawGlyph( Mood_Glyph[AL[t].Mood] , MyDest.X + T - 1, MyDest.Y , Mood_Color[AL[t].Mood] , Black ); if AL[t].Mood <> MOOD_WalkOut then begin DrawGlyph( '@' , MyDest.X + T - 1, MyDest.Y + 1 , Trait_Color[AL[t].Trait] , Black ); end; end; end; end; Procedure PersonadexInfo( NPC,HomeTown: GearPtr; Z: VGFX_Zone ); { Display personality info about this NPC. } var MyDest: VGFX_Rect; msg: String; begin MyDest := ZoneToRect( Z ); ClipZone( MyDest ); TextColor( White ); TextOut( MyDest.X , MyDest.Y , MsgString( 'PDEX_Attitude' ) + ':' ); TextOut( MyDest.X , MyDest.Y + 1 , MsgString( 'PDEX_Motivation' ) + ':' ); TextOut( MyDest.X , MyDest.Y + 2 , MsgString( 'PDEX_Location' ) + ':' ); TextOut( MyDest.X , MyDest.Y + 3 , MsgString( 'PDEX_SkillRank' ) + ':' ); TextColor( LightGreen ); msg := MsgString( 'Attitude_' + BStr( NAttValue( NPC^.NA , NAG_XXRan , NAS_XXChar_Attitude ) ) ); TextOut( MyDest.X + MyDest.W - Length( Msg ) , MyDest.Y , msg ); msg := MsgString( 'Motivation_' + BStr( NAttValue( NPC^.NA , NAG_XXRan , NAS_XXChar_Motivation ) ) ); TextOut( MyDest.X + MyDest.W - Length( Msg ) , MyDest.Y + 1 , msg ); if HomeTown = Nil then msg := '???' else msg := GearName( HomeTown ); TextOut( MyDest.X + MyDest.W - Length( Msg ) , MyDest.Y + 2 , msg ); if IsACombatant( NPC ) then msg := RenownDesc( NAttValue( NPC^.NA , NAG_CharDescription , NAS_Renowned ) ) else msg := 'N/A'; TextOut( MyDest.X + MyDest.W - Length( Msg ) , MyDest.Y + 3 , msg ); MyDest.Y := MyDest.Y + 4; MyDest.H := MyDest.H - 4; GameMsg( SAttValue( NPC^.SA , 'BIO' ) , MyDest , LightGray ); MaxClipZone; end; end. GH2/minitype.pp0000644000175000017500000000310111326004555012267 0ustar kaolkaolunit minitype; { Contains type definitions for the minigame unit. These are separated out } { so that the xxinfo units can see what's going on without using magic numbers. } { 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 Const MaxAudienceSize = 12; { Maximum audience size for concert minigame. } Num_Audience_Moods = 6; { Number of moods, excluding absent mobs and walking out. } MOOD_Absent = -1; MOOD_WalkOut = 0; CMG_Trait_Emotion = 0; CMG_Trait_Beat = 1; CMG_Trait_Melody = 2; Type AudienceMember = Record { Records an audience mob for the concert minigame. } Mood,Trait: Integer; end; AudienceList = Array [1..MaxAudienceSize] of AudienceMember; AudienceListPtr = ^AudienceList; implementation end. GH2/doc/0000755000175000017500000000000011402363703010640 5ustar kaolkaolGH2/doc/SA Glossary.txt0000644000175000017500000001621411374513724013504 0ustar kaolkaolString 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 URBAN, ASTER 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 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. GH2/doc/known bugs.txt0000644000175000017500000000044511326004541013456 0ustar kaolkaolQuest: 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. GH2/doc/city moods.txt0000644000175000017500000000276311326004541013460 0ustar kaolkaolMoods 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. GH2/doc/standard context types.txt0000644000175000017500000000053611326004541015774 0ustar kaolkaolCONTEXT 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 GH2/doc/Credits.txt0000644000175000017500000000040511326004541012772 0ustar kaolkaolI 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. Portraits Ladene Kosaka Tung Nguyen Item Images Francisco Munoz Phil Munoz GH2/doc/glossary.txt0000644000175000017500000000453411326004541013247 0ustar kaolkaol 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. GH2/doc/typelist.txt0000644000175000017500000000610311326004541013253 0ustar kaolkaolTYPES 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) GH2/doc/random scene content.txt0000644000175000017500000000677411326004541015405 0ustar kaolkaolRANDOM 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. GH2/doc/xxranplotgen.txt0000644000175000017500000002744711374513724014160 0ustar kaolkaol************************************************** *** 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:)* 4. Partner (R:) 5. Target NPC (T:) 6. Scene for next component (S:) S:meta for metascene, S:perm for perminant scene 7. Root location of current episode (L:) 8. Target Item (I:) 9. Amore NPC (A:) 10. Hanging Thread NPC (H:) 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. GLOBAL DESCRIPTORS These describe the arc of the plot between plots +P?? Propp State +C?? Personal Story +H?? Hanging Thread BACKGROUND DESCRIPTORS These describe the PC's background and motivation +B?? Background State +G?? PC's Goal +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) +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 +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. 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 *** ************************************ +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) GH2/doc/man_umek.txt0000644000175000017500000001501711326004541013176 0ustar kaolkaol% 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. GH2/doc/element search.txt0000644000175000017500000000545011374513724014274 0ustar kaolkaol.: 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 SEX:MALE, SEX:FEMALE PCFAC, NOFAC RECHARGED @A.???, @M.??? Attitude, Motivation for NPCs 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. GH2/doc/persona fragments.txt0000644000175000017500000000460511326004541015021 0ustar kaolkaolPersona 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. GH2/doc/Style Guide.txt0000644000175000017500000000722611326004541013523 0ustar kaolkaol 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!" GH2/doc/man_chara.txt0000644000175000017500000001527311326004541013317 0ustar kaolkaol% 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 GH2/doc/arena_missions.txt0000644000175000017500000000574611326004541014424 0ustar kaolkaolArena 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. GH2/doc/megaplot reference.txt0000644000175000017500000001540111365256063015141 0ustar kaolkaolA 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 GH2/doc/ASLref.txt0000644000175000017500000003707211401151245012521 0ustar kaolkaol1. 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 GH2/doc/adventure_structure.txt0000644000175000017500000000014211326004541015510 0ustar kaolkaolARENA CAMPAIGN Adventure Sub PC units End Inv Factions Mission Scene End GH2/doc/quests.txt0000644000175000017500000001207711326004541012731 0ustar kaolkaol*************************** *** 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 GH2/doc/standard scripts.txt0000644000175000017500000000170611326004541014652 0ustar kaolkaol******************** *** 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 GH2/doc/rules_v2.txt0000644000175000017500000000551111326004541013141 0ustar kaolkaolTHE 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 GH2/doc/city_types.txt0000644000175000017500000000146311365256063013611 0ustar kaolkaol***************************************************** *** 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 *** ****************************** GH2/doc/the_egg.txt0000644000175000017500000000403111326004541012776 0ustar kaolkaol**************************************************** *** 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 GH2/doc/man_mecha.txt0000644000175000017500000000607211326004541013313 0ustar kaolkaol% 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. GH2/doc/tech_chardesc.txt0000644000175000017500000000072611326004541014162 0ustar kaolkaolCharacter 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 GH2/doc/map generator.txt0000644000175000017500000000055211326004541014124 0ustar kaolkaol***************************** *** 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. GH2/rpgdice.pp0000644000175000017500000004021111326004555012051 0ustar kaolkaolunit rpgdice; {This unit handles some of my frequently-wanted dice} {routines.} { 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 Const DieSize: Array [1..5] of byte = (4,6,8,10,12); DieStep: Array [1..10,1..5] of byte = ( { d4 d6 d8 d10 d12 } ( 1, 0, 0, 0, 0), ( 0, 1, 0, 0, 0), ( 0, 0, 1, 0, 0), ( 0, 0, 0, 1, 0), ( 0, 0, 0, 0, 1), ( 0, 1, 1, 0, 0), ( 0, 1, 0, 1, 0), ( 0, 0, 1, 1, 0), ( 0, 0, 0, 2, 0), ( 0, 0, 0, 1, 1) ); Function Dice(die: integer): Integer; Function RollStep(n: Integer): Integer; Function RollStat(n: integer): integer; {Roll Nd6; take the three highest values, add them together,} {and return the result. N must be in the range of 1 to 10.} implementation const { Limit on value returned (the Pascal distribution has no natural upper bound). The optimized version requires that the limit be 30 units under the maximum value of Integer ($7fff - 30 = 32737). However, it may be desired to set a lower value to avoid overflows in procedures that call RandPascal(). } SafetyLimit = $7fff - 30; {These tables uniform distributed random integers in range 0 .. $3fffffff to integer values distributed differently. The first (30-N) entries of table N follow a Pascal distribution with parameters (p=1/2, r = N). The remaining entries follow a binomial distribution with parameters (p=1/2, n = 30). These odd distributions allow the equivalent of 30 coinflips to be done with a single call to Random(). } Tables : Array[1..30,1..30] of LongInt = ( ($20000000,$30000000,$38000000,$3c000000,$3e000000,$3f000000, $3f800000,$3fc00000,$3fe00000,$3ff00000,$3ff80000,$3ffc0000, $3ffe0000,$3fff0000,$3fff8000,$3fffc000,$3fffe000,$3ffff000, $3ffff800,$3ffffc00,$3ffffe00,$3fffff00,$3fffff80,$3fffffc0, $3fffffe0,$3ffffff0,$3ffffff8,$3ffffffc,$3ffffffe,$3fffffff), ($10000000,$20000000,$2c000000,$34000000,$39000000,$3c000000, $3dc00000,$3ec00000,$3f500000,$3fa00000,$3fcc0000,$3fe40000, $3ff10000,$3ff80000,$3ffbc000,$3ffdc000,$3ffed000,$3fff6000, $3fffac00,$3fffd400,$3fffe900,$3ffff400,$3ffff9c0,$3ffffcc0, $3ffffe50,$3fffff20,$3fffff8c,$3fffffc4,$3fffffe1,$3fffffff), ($08000000,$14000000,$20000000,$2a000000,$31800000,$36c00000, $3a400000,$3c800000,$3de80000,$3ec40000,$3f480000,$3f960000, $3fc38000,$3fddc000,$3fecc000,$3ff54000,$3ffa0800,$3ffcb400, $3ffe3000,$3fff0200,$3fff7580,$3fffb4c0,$3fffd740,$3fffea00, $3ffff428,$3ffff9a4,$3ffffc98,$3ffffe2e,$3fffffe1,$3fffffff), ($04000000,$0c000000,$16000000,$20000000,$28c00000,$2fc00000, $35000000,$38c00000,$3b540000,$3d0c0000,$3e2a0000,$3ee00000, $3f51c000,$3f97c000,$3fc24000,$3fdbc000,$3feae400,$3ff3cc00, $3ff8fe00,$3ffc0000,$3ffdbac0,$3ffeb7c0,$3fff4780,$3fff98c0, $3fffc674,$3fffe00c,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($02000000,$07000000,$0e800000,$17400000,$20000000,$27e00000, $2e700000,$33980000,$37760000,$3a410000,$3c358000,$3d8ac000, $3e6e4000,$3f030000,$3f62a000,$3f9f3000,$3fc50a00,$3fdc6b00, $3feab480,$3ff35a40,$3ff88a80,$3ffba120,$3ffd7450,$3ffe8688, $3fff267e,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($01000000,$04000000,$09400000,$10400000,$18200000,$20000000, $27380000,$2d680000,$326f0000,$36580000,$3946c000,$3b68c000, $3ceb8000,$3df74000,$3eacf000,$3f261000,$3f758d00,$3fa8fc00, $3fc9d840,$3fde9940,$3feb91e0,$3ff39980,$3ff886e8,$3ffb86b8, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00800000,$02400000,$05c00000,$0b000000,$11900000,$18c80000, $20000000,$26b40000,$2c918000,$3174c000,$355dc000,$38634000, $3aa76000,$3c4f5000,$3d7e2000,$3e521800,$3ee3d280,$3f466740, $3f881fc0,$3fb35c80,$3fcf7730,$3fe18858,$3fed07a0,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00400000,$01400000,$03800000,$07400000,$0c680000,$12980000, $194c0000,$20000000,$2648c000,$2bdec000,$309e4000,$3480c000, $37941000,$39f1b000,$3bb7e800,$3d050000,$3df46940,$3e9d6840, $3f12c400,$3f631040,$3f9943b8,$3fbd6608,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00200000,$00b00000,$02180000,$04ac0000,$088a0000,$0d910000, $136e8000,$19b74000,$20000000,$25ef6000,$2b46d000,$2fe3c800, $33bbec00,$36d6ce00,$39475b00,$3b262d80,$3c8d4b60,$3d9559d0, $3e540ee8,$3edb8f94,$3f3a69a6,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00100000,$00600000,$013c0000,$02f40000,$05bf0000,$09a80000, $0e8b4000,$14214000,$1a10a000,$20000000,$25a36800,$2ac39800, $2f3fc200,$330b4800,$36295180,$38a7bf80,$3a9a8570,$3c17efa0, $3d35ff44,$3e08c76c,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00080000,$00340000,$00b80000,$01d60000,$03ca8000,$06b94000, $0aa24000,$0f61c000,$14b93000,$1a5c9800,$20000000,$2561cc00, $2a50c700,$2eae0780,$326bac80,$3589b600,$38121db8,$3a1506ac, $3ba582f8,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00040000,$001c0000,$006a0000,$01200000,$02754000,$04974000, $079cc000,$0b7f4000,$101c3800,$153c6800,$1a9e3400,$20000000, $25286380,$29eb3580,$2e2b7100,$31da9380,$34f6589c,$3785afa4, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00020000,$000f0000,$003c8000,$00ae4000,$0191c000,$03148000, $0558a000,$086bf000,$0c441400,$10c03e00,$15af3900,$1ad79c80, $20000000,$24f59ac0,$299085e0,$2db58cb0,$3155f2a6,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00010000,$00080000,$00224000,$00684000,$00fd0000,$0208c000, $03b0b000,$060e5000,$09293200,$0cf4b800,$1151f880,$1614ca80, $1b0a6540,$20000000,$24c842f0,$293ee7d0,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00008000,$00044000,$00134000,$003dc000,$009d6000,$01531000, $0281e000,$04481800,$06b8a500,$09d6ae80,$0d945380,$11d48f00, $166f7a20,$1b37bd10,$20000000,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00004000,$00024000,$000ac000,$00244000,$0060d000,$00d9f000, $01ade800,$02fb0000,$04d9d280,$07584080,$0a764a00,$0e256c80, $124a7350,$16c11830,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00002000,$00013000,$0005f800,$00151c00,$003af600,$008a7300, $011c2d80,$020b96c0,$0372b4a0,$05657a90,$07ede248,$0b09a764, $0eaa0d5a,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00001000,$0000a000,$00034c00,$000c3400,$00239500,$00570400, $00b998c0,$016297c0,$026aa630,$03e81060,$05eaf954,$087a505c, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000800,$00005400,$0001d000,$00070200,$00154b80,$003627c0, $0077e040,$00ed3c00,$01abf118,$02ca00bc,$045a7d08,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000400,$00002c00,$0000fe00,$00040000,$000ca5c0,$002166c0, $004ca380,$009cefc0,$0124706c,$01f73894,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000200,$00001700,$00008a80,$00024540,$00077580,$00146e20, $003088d0,$0066bc48,$00c5965a,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000100,$00000c00,$00004b40,$00014840,$00045ee0,$000c6680, $001e77a8,$004299f8,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000080,$00000640,$000028c0,$0000b880,$00028bb0,$00077918, $0012f860,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000040,$00000340,$00001600,$00006740,$00017978,$00047948, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000020,$000001b0,$00000bd8,$0000398c,$0000d982,$0002a965, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000010,$000000e0,$0000065c,$00001ff4,$00007cbb,$0002a965, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000008,$00000074,$00000368,$000011ae,$00007cbb,$0002a965, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000004,$0000003c,$000001d2,$000011ae,$00007cbb,$0002a965, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000002,$0000001f,$000001d2,$000011ae,$00007cbb,$0002a965, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff), ($00000001,$0000001f,$000001d2,$000011ae,$00007cbb,$0002a965, $000bb8d4,$002ac92c,$00841829,$015e6777,$0328dace,$066a66b2, $0b922edb,$12b592c5,$1b608c18,$249f73e8,$2d4a6d3b,$346dd125, $3995994e,$3cd72532,$3ea19889,$3f7be7d7,$3fd536d4,$3ff4472c, $3ffd569b,$3fff8345,$3fffee52,$3ffffe2e,$3fffffe1,$3fffffff) ); Function Dice(die: integer): Integer; {Roll a die- D(6), D(8), D(100), whatever.} {Die rolling is done as per Earthdawn- whenever a maximum is} {rolled, the score is kept and the die rerolled. } var total,dr: Integer; begin {Range check} if die < 2 then die := 2; total := 0; repeat dr := Random( die ) + 1; total := total + dr; until dr <> Die; Dice := total; end; Function RandPascal(Average : Integer) : Integer; { The Pascal distribution (no relation to the Pascal language) is a discrete distibution, with a lower bound of 0 and an infinite upper bound (ignoring the degenerate case of a zero average). At Average = 1, it is exactly the same as a geometric distribution (1/2 chance of 0, 1/4 chance of 1, 1/8 chance of 2, and so on). As Average increases it will look more and more like a Gaussian (Bell curve) distribution. For this particular family of Pascal distributions (where the "p" parameter is 1/2), the standard deviation is the square root of (2 * Average). The distributions form a smooth progression -- the distribution of "RandPascal(x)+RandPascal(y)" is the same as "RandPascal(x+y)". } var Result, Rolls: Integer; SelectedTable : Integer; Selector: LongInt; begin Result := 0; while Average > 0 do begin if Average > 30 then SelectedTable := 30 else SelectedTable := Average; { Obtain a 30-bit random integer, hopefully uniform} Selector := Random($40000000); { Open coded binary search to find the number of successes associated with the selector value. The kink at the end (Rolls < 30) is because the table length is one short of a power of two } Rolls := 0; if Selector >= Tables[SelectedTable,Rolls + 16] then Rolls := Rolls + 16; if Selector >= Tables[SelectedTable,Rolls + 8] then Rolls := Rolls + 8; if Selector >= Tables[SelectedTable,Rolls + 4] then Rolls := Rolls + 4; if Selector >= Tables[SelectedTable,Rolls + 2] then Rolls := Rolls + 2; if (Rolls < 30) and (Selector >= Tables[SelectedTable,Rolls + 1]) then Rolls := Rolls + 1; { Add number of "White" coinflips to result } Result := Result + Rolls; { Subtract number of "Black" coinflips from Average. If Average becomes nonpositive, we are done. } Average := Average - (30 - Rolls); if Result >= SafetyLimit then begin Result := SafetyLimit; Average := 0; end; end; RandPascal := Result; end; Function RollStep(n: Integer): Integer; {Roll a dice step number, a la Earthdawn.} var RS: Integer; begin if N > 0 then begin RS := RandPascal( N ); if RS < 1 then RS := 1; end else begin RS := 0; end; RollStep := RS; end; Function RollStat(n: integer): integer; {Roll Nd6; take the three highest values, add them together,} {and return the result. N must be in the range of 1 to 10.} var k: array [1..10] of integer; t,tt: integer; {Loop counters.} l: integer; {in theory, the low value.} stat: integer; {The total value rolled} begin {Range check.} if n>10 then n := 10; {Initialize stat} stat := 0; {Roll the indicated number of dice.} for t := 1 to n do begin {Roll the die} k[t] := Random(6) + 1; {Add it to the total} stat := stat + k[t]; end; {If we rolled more dice than we need, go through and eliminate} {the low rolls.} if n > 3 then for t := 1 to n-3 do begin {locate the first nonzero value for l} l := 1; while k[l] = 0 do Inc(l); for tt := 1 to n do begin if (k[tt] > 0) and (k[tt] < k[l]) then l := tt end; stat := stat - k[l]; k[l] := 0; end; RollStat := stat; end; initialization {Set the random seed} Randomize; end. GH2/arenacfe.pp0000644000175000017500000013214411347637672012226 0ustar kaolkaolunit 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} {$IFDEF CUTE} cutegfx,cutemap,glinfo,sdl; {$ELSE} glgfx,glmap,glinfo,sdl; {$ENDIF} {$ENDIF} var NPC_Chatter_Standard: SAttPtr; 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; {$IFDEF CUTE} 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 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 ) ); {$IFNDEF ASCII} {$IFDEF CUTE} FocusOn( M ); {$ELSE} P := GearCurrentLocation( M ); P.X := P.X - 1; P.Y := P.Y - 1; if not Use_Isometric_Mode then origin_d_target := ( ( ( NAttValue( M^.NA , NAG_Location , NAS_D ) + 4 ) * Num_Rotation_Angles ) div 8 ) mod Num_Rotation_Angles; {$ENDIF} {$ELSE} FocusOn( M ); {$ENDIF} repeat {$IFNDEF ASCII} {$IFNDEF CUTE} { For OpenGL mode, we need to move the display to the new model. } { In order to prevent confusion, make this a nice smooth scroll. } if Abs( P.X - Origin_X ) < Scroll_Step then Origin_X := P.X else if Origin_X > P.X then Origin_X := Origin_X - Scroll_Step else if Origin_X < P.X then Origin_X := Origin_X + Scroll_Step; if Abs( P.Y - Origin_Y ) < Scroll_Step then Origin_Y := P.Y else if Origin_Y > P.Y then Origin_Y := Origin_Y - Scroll_Step else if Origin_Y < P.Y then Origin_Y := Origin_Y + Scroll_Step; {$ENDIF} {$ENDIF} 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 MoreText( Console_History , MoreHighFirstLine( Console_History ) ); CombatDisplay( GB ); 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. GH2/series/0000755000175000017500000000000011402363703011365 5ustar kaolkaolGH2/series/STC_quest_scenes.txt0000644000175000017500000002265211365256063015357 0ustar kaolkaolMetaScene name <#'s Workshop> desig type mapwidth 12 mapheight 12 BoxMap IndustrialTiles Ceiling special start msg1 NeededCells 2 content sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room minimap <.......##...1#...##......> inv Elevator name MiniMapComponent 1 Destination -1 end end MetaScene name <#'s Workyard> desig type mapwidth 17 mapheight 12 BoxMap IndustrialTiles Ceiling special NeededCells 2 content sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room minimap <.......##...1#...##......> inv Elevator name MiniMapComponent 1 Destination -1 end end MetaScene name <# Basement> desig type special BoxMap RockyTiles Ceiling % L1 = Victory Indicator % L2 = Initialization Counter MapWidth 22 MapHeight 32 NeededCells 2 content Start nu2 Msg1 Msg2 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Room sub StairsUp Destination -1 end end MetaScene name <#'s Black Market> desig type mapwidth 15 mapheight 15 MallMap Ceiling special Tolerance 15 NeededCells 3 Content1 Content2 Content3 Start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end MetaScene name desig BoxMap Ceiling MapWidth 32 MapHeight 32 terrain special start GoCheckBattle GoBoringStart nu1 nu2 GoEndBattle Msg1 Msg2 Msg3 Msg5 Msg6 Content3 sub Team 1 name SetEnemy 2 ParaX 5 ParaY 5 Team 2 name type SetFaction 5 SetEnemy 1 Deploy ParaX 25 ParaY 25 end MetaScene 0 2 name desig entrance <*QUEST-INACTIVE> ArenaMap MapWidth 50 MapHeight 50 type special terrain start GoCheckBattle GoBoringStart nu1 nu2 GoEndBattle Msg1 Msg2 Msg3 Msg5 Msg6 sub Team 1 name SetEnemy 2 ParaX 5 ParaY 25 Team 2 name SetFaction 5 SetEnemy 1 Deploy ParaX 45 ParaY 25 end %% ******************** %% *** DUNGEONS *** %% ******************** MetaScene name <# Tower> desig special type dentrance <*GoUp> content1 content2 content3 mapwidth 25 mapheight 25 ComplexMap Ceiling LockedDoorChance 15 SecretDoorChance 5 NeededCells 3 start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 room width 3 height 3 sub StairsDown Destination -1 end end MetaScene name <# Sewer> desig special type dentrance <*GoDown> content1 content2 content3 mapwidth 30 mapheight 30 MonkeyMap Ceiling LockedDoorChance 12 SecretDoorChance 20 MarbleType 7 NeededCells 3 start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 room width 3 height 3 sub StairsUp Destination -1 end end MetaScene name desig special type terrain dentrance <*GoDown> content1 content2 content3 % Suffocation effect... Vacuum mapwidth 35 mapheight 35 MonkeyMap Ceiling LockedDoorChance 12 SecretDoorChance 10 IndustrialTiles NeededCells 3 start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 room width 3 height 3 sub Elevator Destination -1 end end MetaScene name desig special type terrain dentrance <*GoDown> content1 content2 content3 content4 % Suffocation effect... Vacuum mapwidth 49 mapheight 49 MonkeyMap Ceiling LockedDoorChance 17 SecretDoorChance 20 IndustrialTiles NeededCells 5 start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 room width 3 height 3 sub Elevator Destination -1 end end MetaScene name <# Asteroid> desig special type terrain habitat dentrance <*GoDown> content1 content2 content3 % Suffocation effect... Vacuum Ceiling mapwidth 30 mapheight 30 CaveMap RockyTiles NeededCells 3 start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 room width 3 height 3 sub StairsUp Destination -1 end end MetaScene name <# Mine> desig special type terrain habitat dentrance <*GoDown> content1 content2 content3 % Suffocation effect... Vacuum mapwidth 30 mapheight 30 CaveMap Ceiling RockyTiles NeededCells 3 start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 room width 3 height 3 sub StairsUp Destination -1 end end GH2/series/ARENADATA_NPCMessages.txt0000644000175000017500000000530711326004535015603 0ustar kaolkaol% % This file contains a single persona. The persona contains all the messages that can be spoken by the % ArenaHQ personalities. % Persona PCHealed <# was recovered from the battlefield.> PCDead <# didn't return from that last mission.> MechaFixed MechaDestroyed SalvageReport MechaObtained ReportEarnings ReportEarnings_1 CReportEarnings_1 ReportEarnings_2 CReportEarnings_2 ReportEarnings_3 CReportEarnings_3 ReportEarnings_4 CReportEarnings_4 ReportEarnings_5 CReportEarnings_5 ReportEarnings_6 CReportEarnings_6 ReportEarnings_7 CReportEarnings_7 ReportEarnings_8 CReportEarnings_8 ReportExpenses ReportExpenses_1 CReportExpenses_1 ReportExpenses_2 CReportExpenses_2 ReportExpenses_3 CReportExpenses_3 ReportExpenses_4 CReportExpenses_4 ReportExpenses_5 CReportExpenses_5 ReportExpenses_6 CReportExpenses_6 ReportExpenses_7 <> CReportExpenses_7 ReportExpenses_8 <> CReportExpenses_8 ReportExpenses_9 <> CReportExpenses_9 ReportExpenses_10 <> CReportExpenses_10 ReportExpenses_11 <> CReportExpenses_11 ReportExpenses_12 <> CReportExpenses_12 GainPromotion LosePromotion GH2/series/tmp_oldcomponents.txt0000644000175000017500000007024311326004535015700 0ustar kaolkaolPlot name desc requires <#A +Tgs P:COR ~F:COR S:META> Size 4 % Here's how this component works: A scene is created which includes in it a % CORE_CE_Spy component, and several CORE_CE_RedHerring components. When/if the % PC locates the spy, it will set a local variable. % The CORE_CE_Spy content is responsible for both the WinComp call and also the % storynote. % If the case can't be solved within 24 hours, the PC loses. % E1 = Scene to guard Element1 % P1 = Initialization Counter % P2 = Time Limit start update .next <+T-- +F--> sub MetaScene 1 % L1 = Variable set when clue discovered/lost % if L1 is positive, the PC solved the case. % if L1 is negative, the PC either destroyed the clue or is taking a fall. ClubMap MapWidth 24 MapHeight 24 type special nu1 Content1 Content2 %% The first time this scene is entered, start the 24-hour timer. start sub Team 1 Team 2 name setally 1 Passive room name desig Content1 Content2 end end Plot name desc requires <#A +Ttt +Fmi (+P--|+Pme|+Pun|+Pla|+Pen) ~F:CRM ~P:POL ~P:GOV> % E1 is the scene where this component will take place % E2 is the NPC that the PC will speak with % E3 is a metascene for the PC to guard element1 element2 element3 % P1 = Initialization Counter % P2 = Initial victory count % P3 = Mission Accepted Indicator start GoLose .loss <+T--> update return GoNoWin .loadreward <+Tre +F--> .loadnoreward <+T-- +F--> sub Persona 2 rumor <%name2% needs an adventurer for some kind of mission.> greeting *GoKnownGreeting <*IHaveAJobForYou GoBriefing> *GoUnknownGreeting <*AreYouHereAboutJob GoBriefing> *GoBriefing <*CORE_GuardJob GoAccept GoDeny> *GoAccept <*GoodLuckOnMission GoR1Exit ChatNPCFac &EnemyFac> GoR1Exit .changes <+Tgs +F--> *GoDeny <*RejectMission GoR2Exit> GoR2Exit .changes2 <+T-- +F--> STC MS_OFFICE SetID 1 end Plot name desc Size 2 requires <#A +Tda S:++> % E1 = Scene for meeting % E2 = NPC to meet element1 element2 start GoLose .loss <+T-- +F--> update sub Persona 2 special rumor <%name2% is waiting for you in %name1%.> *greeting <*DateGreeting GoQuestion> *GoQuestion <*DateQuestion GoActivity> *GoActivity <*DateActivity GoResult> GoResult GoCheckLow GoSuccess .next <+T-- +F--> Msg1 Msg2 <\ELEMENT 2 backed out on your date.> Msg3 Msg4 Msg5 Msg6 Msg7 STC MS_CLUB special SetID 1 end Plot name desc requires <#A +Tre S:++ +Fda> % E1 = Scene for meeting % E2 = NPC to meet % E3 = Scene for date element1 element2 element3 % P1 = Initialization Counter start GoLose .loss <+T-- +F--> update sub Persona 2 special rumor <%name2% wants to speak with you in %name1%.> *Greeting <*MissionWasSuccess GoContinue na na> GoContinue result1 .date <+Tda +F--> Msg1 Msg2 <\ELEMENT 2 invited you on a date to \ELEMENT 3 .> Prompt1 STC MS_CAFE SetID 1 end Plot name desc requires <#A +Ttt +Fda> % E1 = Scene for meeting % E2 = NPC to meet % E3 = Scene for date element1 element2 element3 % P1 = Initialization Counter start GoLose .loss <+T-- +F--> update sub Persona 2 special rumor <%name2% wants to speak with you in %name1%.> Greeting result1 .date <+Tda +F--> result2 .loss <+T-- +F--> Msg1 Msg2 Msg3 Msg4 Msg5 Prompt1 Prompt2 STC MS_CAFE SetID 1 end Plot name desc requires <#A +Ttt +Fad (T:ADVEN|T:MILIT|T:ENEMY) ~C:ADVEN ~C:MILIT ~P:PDASS ~+P-- ~+Gpo> % E1 = Scene for meeting % E2 = NPC to meet % E3 = Outdoors scene % E4 = Encounter % E5 = Scene for immediate date if PC can convince NPC of that element1 element2 element3 element4 place4 <3> element5 % P1 = Initialization Counter % P2 = Victory score at start % P3 = In zero, can maybe get date start GoLose .loss <+T-- +F--> update % Upon return, if the PC won the duel, the NPC will agree to a date. return .next <+Tre +Fda> Msg1 <\ELEMENT 2 's Mecha> sub Persona 2 special % V1 = Have been challenged rumor <%name2% is waiting for you in %name1%.> Greeting *GoHowAreYou <*HowAreYou GoShimli> *GoShimli <*SHIMLI_CHATTER GoAsk> *GoAsk <*PCInviteNPC GoChallenge GoLose> GoChallenge GoLose .loss <+T-- +F--> result1 .next <+Tdu> result2 result3 result4 result5 .date <+Tda +F--> GoR5Fail Msg1 Msg2 <\ELEMENT 2 challenged you to a duel in \ELEMENT 3 , to prove your worthiness.> Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 <\ELEMENT 2 rejected your romantic advances.> Msg9 Msg10 <\ELEMENT 2 agreed to go on a date at \ELEMENT 5 .> Msg11 Prompt1 CPrompt1 Prompt2 Prompt2_1 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt5 STC MS_CAFE SetID 1 end inv STC ENCOUNTER-DUEL end Plot name Size 1 desc requires <#A +Tgs ~+Ffi S:++> % E1 is the scene where this will take place % E2 is the NPC to tell the PC about the security problem element1 element2 element3 element4 place4 <3 !Near 1> % P1 = Initialization Counter start GoLose .loss <+T-- +F--> update sub Persona 2 special *greeting <*DefenseCheckIn GoBriefing> GoBriefing .changes <+Tin +Ffi> Msg1 Msg2 STC MS_OFFICE SetID 1 end inv NPC Soldier job STC ENCOUNTER-SEEKPC name EncounterMove 100 end Plot name Size 0 desc requires <#A +T01 C:MEDIA> % E1 is the scene where the "Intro NPC" is % E2 is the "Intro NPC" % E3 is the scene for the party element1 element2 place2 <1 pass> element3 update Msg1 <\ELEMENT 2 strikes up a conversation with you.> inv NPC Citizen home PCFriend end sub Persona 2 % THE INTRO % - All +T01 components should feature a intro character. They don't have to, % of course, but it'll be a major pissoff if they don't. % - The intro character should follow this form, to insure that all the major % bits are in place: the introduction, the question, the story info, and the % advice. % V1 = Have given story info. greeting <&SetEnemyFac E4 StoryNote 2 WinComp E3 .next if= V1 1 else GoGreeting Goto GoAdvice> .next <+Tsh +Fpa> *GoGreeting <*INTRO_Introduction GoAskQuestion> *GoAskQuestion <*INTRO_Question GoStoryInfo> GoStoryInfo *GoAdvice <*INTRO_Advice> result1 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 Msg2 Prompt1 Prompt1_1 end Plot name desc requires <#A +T01 +Glo> % E1 is the metascene to be used for the meeting. % E2 is the scene where the newly created love interest normally resides. % E3 is the scene where the "Intro NPC" is % E4 is the "Intro NPC" % E5 is the love interest you're going to meet element1 element2 element3 element4 place4 <3 pass> element5 place5 <2 Pass> element6 place6 <2 Pass> element7 place7 <2 Pass> element8 place8 <2 Pass> % V1 = Date Selector update GoCheckScience GoCheckSinger GoCheckCitizen GoFormatE5 Msg1 <\ELEMENT 4 strikes up a conversation with you.> inv NPC Citizen home PCFriend NPC Bounty Hunter PCFriend NPC Scientist PCFriend Charm 13 NPC Singer PCFriend NPC Nurse PCFriend end sub Persona 4 % THE INTRO % - All +T01 components should feature a intro character. They don't have to, % of course, but it'll be a major pissoff if they don't. % - The intro character should follow this form, to insure that all the major % bits are in place: the introduction, the question, the story info, and the % advice. % V1 = Have given story info. greeting <&SetTargetChar E5 StoryNote 2 WinComp E1 .next if= V1 1 else GoGreeting Goto GoAdvice> .next <+Tgt +Fad> *GoGreeting <*INTRO_Introduction GoAskQuestion> *GoAskQuestion <*INTRO_Question GoStoryInfo> GoStoryInfo *GoAdvice <*INTRO_Advice> result1 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 Msg2 Prompt1 Prompt1_1 end %% COMP_P--.txt %% %% Peaceful Life components Plot name desc requires <#A +P-- +Tgs ~+Gmo ~+Gkn ~P:cor E:--> Size 1 % E1 is the scene where this component will take place % E2 is the natural home of the security guard % E3 is the security guard that the PC will speak with % E4 is the artifact the guard will tell the PC about % E5 is the other cavalier who's also looking for the item element1 element2 element3 place3 <2 sd ally> element4 element5 % P1 = Initialization Counter start GoLose .loss <+T--> update sub Persona 3 rumor <%name3% is the security guard at %name1%.> *greeting <*DefenseCheckIn GoBriefing> GoBriefing result1 result2 result3 .win <+Pla +T-- +F--> result4 result5 .loss <+T-- +F--> result6 result7 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 Prompt7 STC MS_OFFICE SetID 1 end inv NPC Soldier job chardesc old sociable sociable sociable lawful EquipChar 7000 NPC Bounty Hunter job EquipChar 20000 end Plot name desc requires <#A +P-- +Ttt +Fmi T:++ T:COR P:COR ~T:Ally> % E1 is the scene where this component will take place % E2 is the NPC that the PC will speak with % E3 is metascene to either guard or socialize at element1 element2 element3 % P1 = Initialization Counter % P2 = Initial victory count % P3 = Job Accepted Counter start GoLose .loss <+T--> update return GoNoWin .loadreward <+Tre +F--> .loadnoreward <+T-- +F--> sub Persona 2 rumor <%name2% has some work for you.> *greeting <*IHaveAJobForYou GoGiveJob> GoGiveJob result1 .warehouse <+Tgs +F--> result2 .conference <+Tsh +F--> Msg1 Msg2 Msg3 <\ELEMENT 2 sent you to guard \ELEMENT 3 for \NARRATIVE 3 .> Msg4 Msg5 <\ELEMENT 2 sent you to a convention at \ELEMENT 3 for \NARRATIVE 3 .> Prompt1 Prompt2 STC MS_OFFICE SetID 1 end Plot name Size 1 desc requires <#A +P-- +Ttt (+Frt|+Fin|+F--) T:++ (T:MILIT|T:ADVEN|T:POLIC) -T:Enemy> %% E1 is the scene where this will take place %% E2 is the NPC that the PC is looking for %% E3 is the item being sought element1 element2 element3 element4 place4 <3> start GoLose .loss <+T-- +F--> update sub Persona 2 special rumor <%name2% has been seen in %name1%.> *Greeting <*HowAreYou GoReveal> GoReveal GoWin .next <+Pun +Tin +Ffi> result1 result2 Msg1 Msg2 <\ELEMENT 2 revealed that enemies are gathering outside of \NARRATIVE 7 .> Msg3 Msg4 Prompt1 Prompt2 CPrompt2 STC MS_Cafe SetID 1 end inv STC ENCOUNTER-SEEKPC name EncounterMove 50 end %% COMP_Pla.txt %% %% +Pla PC learns of the existence of an artifact %% COMP_Pme %% +Pme PC meets an enemy pilot under somewhat peaceful circumstances Plot name % V1 = Initialization size 5 desc requires <#A +T-- +Pme E:++ E:Friend -F:ALLY ~+Glo ~+Gpe> changes <+T-- +Pun> update % E1 is where the encounter takes place % E2 is the encounter itself % E3 is the NPC character element1 element2 Place2 <1> element3 sub MetaScene 2 2 rumor % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 30 MapHeight 30 Start nu1 nu2 .next <+T-- +Pun> Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 25 ParaY 25 end Persona 3 special *greeting <*COMP_Pme_MFH_Greeting GoFinal> GoFinal 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 Msg2 <\ELEMENT 3 leaves the area.> end inv STC ENCOUNTER-WANDER name EncounterMove 5 end %% COMP_Pun %% +Pun An unknown enemy attacks Plot name size 1 desc requires <#A +Pun +Ttt (+Fin|+Frt) T:++ -T:Enemy S:++> %% E1 is the scene where this will take place %% E2 is the NPC that the PC is looking for element1 element2 element3 start GoLose .loss <+T-- +F--> update sub Persona 2 special rumor <%name2% has been seen in %name1%.> *Greeting <*GreetThenDiscuss GoReveal> GoReveal GoWin .next <+Tgs +Ffi> GoLose .lose <+T-- +F--> result1 RESULT2 result3 result4 Msg1 Msg2 Msg3 <\ELEMENT 2 told you the invaders were planning to strike \SCENE E3 .> Msg4 Msg5 Prompt1 Prompt1_1 Prompt2 CPrompt2 Prompt2_1 Prompt3 Prompt4 STC MS_CLUB end GH2/series/MEGA_LM_Extras.txt0000644000175000017500000004462111347637671014604 0ustar kaolkaol%% %% *LM_WannaJoin_Accepted Content %% %% A lancemate has been accepted into a faction. Yay! %% %% PARAM1: The Lancemate %% PARAM2: The faction to be joined %% Content name desc requires <*LM_WannaJoin_Accepted 2:MILIT> % E1 is the lancemate % E2 is the faction % P%id%01 = One time only counter update end sub Persona 1 greeting *.%id%_GoAnnounce <*LM_JoinFaction&IsMilitary %2%> .%id%_Skills .%id%_Facs Msg%id%01 <%name1% joined %name2%.> end Content name desc requires <*LM_WannaJoin_Accepted 2:POLIC> % E1 is the lancemate % E2 is the faction % P%id%01 = One time only counter update end sub Persona 1 greeting *.%id%_GoAnnounce <*LM_JoinFaction&IsPolice %2%> .%id%_Skills .%id%_Facs Msg%id%01 <%name1% joined %name2%.> end Content name desc requires <*LM_WannaJoin_Accepted 2:CORPO> % E1 is the lancemate % E2 is the faction % P%id%01 = One time only counter update end sub Persona 1 greeting *.%id%_GoAnnounce <*LM_JoinFaction&IsCorporate %2%> .%id%_Skills .%id%_Facs Msg%id%01 <%name1% joined %name2%.> end Content name desc requires <*LM_WannaJoin_Accepted 2:POLIT> % E1 is the lancemate % E2 is the faction % P%id%01 = One time only counter update end sub Persona 1 greeting *.%id%_GoAnnounce <*LM_JoinFaction&IsPolitical %2%> .%id%_Skills .%id%_Facs Msg%id%01 <%name1% joined %name2%.> end %% %% *LM_WannaJoin_Rejected Content %% %% A lancemate has been rejected by a faction. Yay! %% %% PARAM1: The Lancemate %% PARAM2: The faction %% Content name desc requires <*LM_WannaJoin_Rejected 1:M.pro> % E1 is the lancemate % E2 is the faction % P%id%01 = One time only counter update end sub Persona 1 greeting .%id%_GoSetCompetition .%id%_HeroicSkills .%id%_CompetSkills Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%02_2 end %% %% *LM_WannaJoinFaction Content %% %% A lancemate wants to join a faction. The PC can try to get them in, or not. %% %% PARAM1: The Lancemate %% PARAM2: The faction to be joined %% PARAM3: The NPC to speak with about joining %% Content name desc requires <*LM_WannaJoinFaction> % E1 is the lancemate % E2 is the faction % E3 is the NPC to speak with about joining start .%id%_GoDelete % SubPlot1 is what happens if the LM's application is accepted. % SubPlot2 is what happens if the LM's application is rejected. SubPlot1 <*LM_WannaJoin_Accepted 1 2> SubPlot2 <*LM_WannaJoin_Rejected 1 2> sub Persona 3 % V%id%01 = LM's score % V%id%02 = PC's fac point gain, based on honesty of answers % V%id%11 = Answered P04 % V%id%12 = Answered P05 greeting .%id%_GoReportProblems .%id%_GoContinueReport % Upon starting interview, clear all counters to prevent scumming. result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 .%id%_GoReject Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%04_1 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 <%name1% has a history of petty crime.> CPrompt%id%05 Prompt%id%06 Prompt%id%07 <[Continue]> end %% %% *LM_SecretPlace Content %% %% A lancemate will bring the PC to a secret place. %% %% PARAM1: The Lancemate %% PARAM2: The outdoors scene where the secret place is %% Content name desc requires <*LM_SecretPlace 1:A.tha ~1:CRAFT ~1:LABOR ~1:CORPO ~"Industrial"> % Element1 is the lancemate % Element2 is the outdoors scene % Element3 is the junkyard Element3 Place3 <2> sub Persona 1 greeting .%id%_GoNotHere .%id%_GoExplain Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 requires <*:CS_ItemCozy ~&Stolen> Size 5 % E1 is the item to be found % E2 is an outdoors scene % E3 is the thief's hideout % E4 is a local public scene, for NeverFail % E5 is a thief Place1 <5> Element2 Element3 Place3 <2> Element4 Element5 NeverFail5 Place5 <3 (Guards) sd> HINT_%id% <:%id1%> % P%id%01 = Initialization Counter % P%id%02 = Item has been lost % FAIL CONDITIONS: % - The NPC Has Item subplot fails % - The Reveal NPC Base subplot fails % - E1 is destroyed update end .%id%_GoCheckE1 .%id%_GoFail Get%1% .%id%_%plotid%_GoItemGone Msg%id%01 % SubPlot1 is where the PC learns that the corporate character has the item. % SubPlot2 is the PC's attempt to locate the mansion. SubPlot1 <*:CS_DiscoverNPCHasItem 5 1 2 3> sub Persona 5 special % V%id%01 = NPC was known beforehand % V%id%02 = First time counter &HiPrice &LowPrice greeting GoCheckSurrender GoItemDestroyed GoCheckKnown *GoGreet <*IHearYouAreLookingForItem %1% GoMakeOffer> GoMakeOffer result1 result2 *result3 <*HurryBackWithMoney> result4 result5 GoR5Fail result6 result7 result8 GoR8NoAmore *result9 <*GoodBye> Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Prompt1 CPrompt1 Prompt2 CPrompt2 Prompt3 Prompt4 CPrompt4 Prompt5 CPrompt5 Prompt6 CPrompt6 Prompt7 CPrompt7 Prompt8 Prompt9 STC MS_EmptyBuilding SetID 3 end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name GoStartCombat Msg1 end Content name desc requires <*:CS_ItemCozy ~&Abandoned> Size 5 % E1 is the item to be held % E2 is the endless void of space % E3 is the derelict itself % E4 is a crate for the item on board the ship Place1 <4> Element2 Element3 Place3 <2> Element4 Place4 <3> HINT_%id% <:%id1%> % P%id%01 = Initialization Counter % FAIL CONDITIONS: % - The RevealDungeon subplot fails % - E1 is destroyed update end .%id%_GoFail Get%1% Msg%id%01 % SubPlot1 is the RevealDungeon task SubPlot1 <*:CS_RevealEncounter_ItemDungeon&Derelict 2 3 1> sub MetaScene 3 rumor%id% type terrain mapwidth 50 mapheight 50 MonkeyMap % Suffocation effect... Vacuum LockedDoorChance 10 SecretDoorChance 10 IndustrialTiles Ceiling NeededCells 3 % L1 = Initialization Counter % L2 = Have been given item % L3 = Have been threatened once start Msg1 content1 content2 content3 sub Team 1 SetEnemy 2 Team 2 name type Deploy SetEnemy 1 room special minimap <............2............> sub TrapDoor Destination -1 MiniMapComponent 2 use end room desig minimap <......###..#1=..###......> end end inv STC CORE-ACTIVATABLE name STC Safe end Content name desc requires <*:CS_ItemCozy ~&Stolen -&Abandoned> Size 7 % E1 is the item to be held % E2 is a place for the pirate ship % E3 is the pirate ship itself % E4 is a local scene % E5 is a pirate leader % E6 is a crate for the item on board the ship Place1 <6> Element2 Element3 Place3 <2> Element4 Element5 NeverFail5 Place5 <3 (Pirates)> Element6 Place6 <3> HINT_%id% % FAIL CONDITIONS: % - The RevealShip subplot fails % - E1 is destroyed % P%id%01 = Initialization Counter % P%id%02 = Item Gone Counter % P%id%03 = Peaceful Entry Counter update .%id%_GoCheckFail .%id%_GoCheckDestruction Get%1% .%id%_%plotid%_GoItemGone .%id2%_%plotid2%_GoPeacefulEntry Msg%id%01 % SubPlot1 is the RevealShip task % SubPlot2 is the ability to gain peaceful entry to the ship SubPlot1 <*:CS_RevealEncounter_NPCBase&PirateShip 2 3 5> SubPlot2 <*:CS_PeacefulEntry_NPCBase&PirateShip 3 5> sub MetaScene 3 rumor%id% mapwidth 35 mapheight 35 MonkeyMap LockedDoorChance 30 SecretDoorChance 5 IndustrialTiles NeededCells 3 % L1 = Initialization Counter % L2 = Have been given item % L3 = Have been threatened once start GoCheckGone GoCheckPeace GoCheckThreat PCAttack Get%1% GoTripAlarm Msg1 Msg2 sub Team 1 SetEnemy 2 3 Team 2 name SetEnemy 1 SetAlly 3 Team 3 name type SetEnemy 1 SetAlly 2 Deploy room special desig minimap <1...........2............> sub TrapDoor Destination -1 MiniMapComponent 2 use end room desig minimap <......###..#1=..###......> end Persona 5 special &HighOffer &MidOffer &LowOffer greeting GoCheckSurrender *GoGreet <*IHearYouAreLookingForItem %1% GoExplain> GoChat GoExplain GoStartFight GoBuyItem GoBuyFail GoWinContest GoLoseContest result1 GoR1Fail result2 GoR2Fail result3 result4 result5 result6 *result7 <*GoodBye> result8 *result9 <*InsultContest GoWinContest GoLoseContest StoryDL> Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Prompt1 Prompt2 Prompt3 Prompt4 CPrompt4 Prompt5 CPrompt5 Prompt6 CPrompt6 Prompt7 Prompt8 Prompt9 end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name GoStartCombat Msg1 STC Safe name end GH2/series/UNICON_BuildingFiller.txt0000644000175000017500000000042311326004536016074 0ustar kaolkaol%% %% Contains all kind of rooms to add to buildings. Many of these rooms %% will contain things that unlawful characters can steal. %% %% *UNIVERSITY_X Content %% Filler for universities. Professor's offices, student lounges, etc. %% Minimaps open on one side. GH2/series/PLOT_SPECIAL_Promotion.txt0000644000175000017500000000211611326004535016112 0ustar kaolkaolPlot name desc requires <*SPECIAL_Promotion> % E1 is the faction that triggered this plot. % E2 is a local scene. % E3 is a member of E1 who is near E2 Element2 <.> Element3 % V1 = Email indicator start GoLoseReward 5min Msg1_1 <%name3%@ \SCENE NPCScene %3% :// You've earned a promotion. Come see me to accept your reward.> Msg1_2 <%name3%@ \SCENE NPCScene %3% :// I have urgent business which requires your attention at once.> Msg1_3 <%name3%@ \SCENE NPCScene %3% :// Report here for debriefing at once, \RANK .> Msg1_4 <%name3%@ \SCENE NPCScene %3% :// You have been promoted. Report here to receive your comission.> Msg1_5 <%name3%@ \SCENE NPCScene %3% :// Your presence is required over here immediately.> % SubPlot1 is the reward. Actually, it's the subplot that handles everything. SubPlot1 <*FACTION_PROMOTION_REWARD 3> GH2/series/ANPCdefault.txt0000644000175000017500000001253311341163445014223 0ustar kaolkaol% DEFAULT ARCHETYPES FILE % This file defines the character archetypes that can be used to % populate a campaign world. % Most NPCs should get ~80 skill points. PCs start with 88. % STATS REF BOD SPD PER CFT EGO KNO CHA Arch Assassin statline 14 11 12 10 9 10 9 5 Combatant job_desig Chardesc Shy Villainous Villainous Criminal Criminal SpotWeakness Stealth Intimidation Talent 16 Arch Spy statline 11 9 11 11 11 11 11 13 Combatant job_desig Chardesc Shy Shy Shy Awareness Stealth Conversation CodeBreaking Arch Athlete statline 11 14 13 8 7 10 7 11 job_desig Athletics Vitality Toughness Arch Miner statline 10 13 9 9 11 10 9 9 job_desig Sealed Dodge CloseCombat Toughness Arch Explorer statline 10 10 10 12 10 10 10 10 Combatant job_desig CharDesc Shy Awareness Survival Repair 5 Arch Martial Artist statline 12 15 14 11 12 11 12 8 chardesc Spiritual Passionate Combatant job_desig Talent 1 Talent 2 Awareness Initiative Arch Ticket Agent statline 8 8 10 10 11 10 11 12 job_desig Shopping Arch THIEF statline 12 7 13 12 13 10 8 10 chardesc criminal Shy job_desig Stealth CodeBreaking Arch Cook statline 9 9 9 10 12 9 9 10 job_desig CloseCombat Shopping Arch Journalist statline 8 7 9 13 10 12 11 13 chardesc Sociable Renowned job_desig Awareness Conversation Insight Arch Bartender statline 9 12 8 10 10 9 12 13 chardesc Sociable job_desig Conversation Arch Priest statline 8 10 9 13 9 12 12 10 chardesc Spiritual Spiritual Spiritual Spiritual Spiritual Sociable job_desig Conversation Mysticism Arch Warrior Monk statline 12 15 14 11 12 11 12 8 chardesc Spiritual Spiritual Spiritual Passionate Combatant job_desig Talent 1 Talent 2 Awareness Initiative Mysticism Arch Monk statline 10 12 12 10 10 11 13 10 chardesc Spiritual Spiritual Spiritual Shy Heroic job_desig Talent 2 CloseCombat 6 Dodge 8 Conversation Mysticism Arch Construction Worker statline 9 14 9 8 14 8 9 9 job_desig Repair Toughness Arch Trucker statline 10 12 10 8 13 9 8 10 Combatant job_desig Repair Conversation Arch Bounty Hunter statline 11 11 11 11 11 11 11 11 chardesc Melancholy Pragmatic Shy Combatant job_desig Awareness Intimidation ARCH Corporate Executive statline 8 9 10 8 13 11 11 10 job_desig Conversation Shopping ARCH Celebrity statline 9 10 10 9 9 15 9 16 chardesc Renowned job_desig Conversation Taunt ARCH Singer statline 9 9 8 9 9 17 9 18 chardesc Renowned job_desig Conversation Performance Arch Politician statline 9 9 10 10 9 13 14 14 chardesc Renowned job_desig Conversation Arch Bureaucrat statline 9 9 9 9 10 10 10 10 chardesc Renowned job_desig Conversation Arch Mechanic statline 9 10 8 9 14 12 9 9 job_desig Shopping 5 Repair 10 Arch Engineer statline 9 10 8 9 14 12 9 9 job_desig Shopping 5 Repair 10 MechaEngineering 10 Arch Professor statline 8 7 9 10 10 10 18 8 job_desig Awareness 10 Science 5 Arch Scientist statline 7 8 9 10 12 9 17 8 job_desig Awareness 5 Science 10 Arch Nurse statline 8 9 8 9 14 8 11 13 chardesc Heroic Cheerful job_desig Medicine 5 Arch Cyberdoc statline 10 7 8 10 15 10 18 10 chardesc Pragmatic job_desig Medicine 15 Science 10 Arch Doctor statline 10 8 9 10 13 11 17 12 chardesc Heroic job_desig Medicine 10 Arch Citizen statline 9 9 9 9 9 9 9 9 Arch Student statline 9 9 9 9 9 9 9 9 Arch Shopkeeper statline 7 9 8 10 13 11 12 10 % Shopkeepers generally have a great distrust of thieves... chardesc Lawful Lawful Lawful Lawful Lawful Lawful Lawful Lawful job_desig Shopping 10 Repair 5 Arch Mercenary statline 11 11 11 11 9 9 9 9 Combatant job_desig Toughness Arch Soldier statline 12 12 12 12 8 8 8 8 Combatant job_desig Toughness Arch Captain statline 11 11 11 11 10 15 13 14 Combatant job_desig ElectronicWarfare Repair Conversation Arch Pirate statline 11 10 12 10 9 10 9 9 chardesc Criminal Combatant job_desig Taunt Arch Rocket Star statline 13 10 12 12 10 10 10 11 Combatant job_desig chardesc Passionate Heroic SetFaction 14 ElectronicWarfare Initiative Arch Privateer statline 11 10 12 10 9 10 9 9 chardesc Criminal Combatant job_desig Taunt ElectronicWarfare Arch Knight %% One of the defenders of the L5 Alliance statline 12 12 12 12 10 10 10 10 chardesc lawful heroic SetFaction 3 Combatant job_desig SpotWeakness ElectronicWarfare Arch Arena Pilot statline 11 11 11 11 11 11 11 11 Combatant job_desig SetFaction 5 Intimidation Arch Mecha Pilot statline 13 10 10 10 9 9 9 10 Combatant job_desig Initiative Arch Forensic Investigator statline 9 8 9 13 11 8 14 8 chardesc Lawful Heroic Pragmatic Combatant job_desig Awareness Insight Science Arch Police Officer % This is the city defense NPC. Really really tough. statline 13 13 12 13 10 13 10 10 chardesc Lawful Heroic Combatant job_desig Awareness SpotWeakness Insight Arch Bandit statline 10 10 10 10 9 9 9 9 chardesc Villainous criminal Combatant job_desig Toughness GH2/series/QUEST_OddJobs.txt0000644000175000017500000014565511341163445014456 0ustar kaolkaol%% %% This file contains random tasks which may be assigned to the PC, but which don't yet have %% enough components to really warrant their own files. %% %% %% *:Q_PersonalIntervention Content %% %% E1 is a character who's into a lot of bad things. E2 sent the PC to %% locate him and hopefully reform/return him. %% %% This quest can set a place string for E1. %% %% PARAM1: The intervention target %% PARAM2: The mission-giving NPC %% Content name desc requires <*:Q_PersonalIntervention> % E1 is the prodigal target % E2 is the mission-giver % E3 is the character that E1 is in debt to % E4 is E3's "office" % E5 is the town scene % E6 is a law enforcement faction loyal to E5, needed for "gotcha" victory % E7 is E3's black market Place1 <7 (Citizens) pass> Element3 Place3 <7 (Guards) sd ally> Element4 Place4 <7> Element5 <.> Element6 Element7 % P%id%01 = Initialization Counter %% FAIL CONDITIONS: %% - E1 dies end update % SubPlot1 is an entrance for the black market. SubPlot1 <*:Q_ENT_BlackMarket 7> sub % Scene %4% is going to hold all the main variables for this quest. Persona 1 rumor%id% <%name1% has been paying off a debt to %name3%.> greeting .%id%_GoQuestIntro .%id%_GoWinQuest Result%id%01 .%id%_GoTrivial Result%id%02 Result%id%03 Result%id%04 Result%id%05 Result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 <%name1% owes a debt to %name3%, and cannot return to %name2% until this debt is repaid.> Msg%id%04 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 <%name2% sent me to check up on you.> Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 <%name3% has agreed to release you.> CPrompt%id%05 Prompt%id%06 <%name3% is dead.> CPrompt%id%06 Persona 3 % V1 = Debt Amount special greeting GoMaybeFight GoStartFight *GoPreFight <*IWantToTalkAboutNPC&YouBadNPC %1% GoExplain> GoChat GoExplain result1 result2 result3 result4 result5 result6 result7 Msg1 <%name1% had a lot of expensive habits, and because of these habits became very deeply indebted to me. I can't let \OPR %1% go until these debts are paid off. Since %name1% has no money, the debt must be paid in work.> Msg2 Msg3 Msg4 <%name3% invited you downstairs to \PPR %3% office to discuss %name1%'s freedom.> Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Prompt1 Prompt2 Prompt3 Prompt4 CPrompt4 Prompt5 Prompt6 CPrompt6 Prompt7 MetaScene 4 type special BoxMap MapWidth 12 MapHeight 12 entrance <*GoDown> % L1 = E1 has explained about E3 (1), E3 has released E1 (2) % L2 = E3 has moved to this scene for possible fight (1) % L3 = Fight has started % L4 = Conversation Counter start % If the PC has started the fight, leaving the room results in a loss. end Surrender%3% Msg1 sub Team 1 Team 2 name Passive Room sub StairsUp Destination -1 end end STC QS_BlackMarket SetID 7 name <%name3%'s Den of Sin> end inv NPC Pirate job job_desig end Content name desc requires <*:Q_PersonalIntervention ~"City" ~"Dangerous"> % E1 is the prodigal target % E2 is the mission-giver % E3 is the NPC who can tell the PC where E1 went % E4 is the trapdoor leading to the pit % E5 is the pit itself % E6 is the space under the pit where E1 is hiding % E7 is a sleazy building for the pit Place1 <6 (Citizens) pass ally> Element3 Place3 <7 (Citizens) pass ally> Element4 Place4 <7> Element5 Place5 <7> Element6 Place6 <5> Element7 %% FAIL CONDITIONS: %% - E1 dies % P%id%01 = E1 has joined lance % if E3 dies, automatically reveal the pit. faint%3% start end Msg%id%01 sub Persona 1 greeting .%id%_GoAskHelp *.%id%_GoBusy <*BrushOff> .%id%_GoJoinLance result%id%01 result%id%02 result%id%03 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Prompt%id%01 <%name2% sent me to rescue you.> Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona 3 rumor%id% <%name3% knows where %name1% is.> % V1 = Have given information greeting GoCheckInfo *GoGiveInfo <*LookingForNPC %1% GoMoreInfo GoLessInfo> GoMoreInfo GoLessInfo GoActivateDungeon result1 Msg1 Msg2 <%name3% told you that %name1% was thrown into a pit at %name7%.> Msg3 Msg4 <%name3% implied that %name1% had run afoul of some loan sharks at %name7%.> Msg5 Prompt1 MetaScene 4 sub room minimap <#...##.1.##...###+###...#> special desig end STC QS_Dungeon_Sewer name SetID 5 MetaScene 6 name type special BoxMap entrance <*GoDown> MapWidth 12 MapHeight 17 NeededCells 2 content Start Surrender%1% Msg1 Msg2 <%name1% passes out from exhaustion and dies.> sub Team 1 Team 2 name Room minimap <......###..#1#...........> inv Elevator Destination -1 MiniMapComponent 1 end end end inv TrapDoor name desig % V1 = if nonzero, reveal trapdoor UPDATE GoHide Hide end %% %% *:Q_StolenMaterials Content %% &ConstructionSite = The materials stolen from a construction site %% %% Several tons of crap have been stolen from E1. The PC will be sent to get %% them back, or may possibly collude with whoever stole them, or may even %% steal a bit himself... %% %% Param1: The boss NPC who sent the PC to find the problem %% Content name desc requires <*:Q_StolenMaterials "L5PAT"> % E1 is the NPC who stole the materials % E2 is the pirate who took the materials % E3 is the black market % E4 is the pirate's office % E5 is L5Law, needed for "Gotcha" Element2 Place2 <4 (Pirate) sd> Element3 Element4 Place4 <3> Element5 %% Hear a rumor that this pirate has been selling stolen goods. %% Locate the black market. Ask the pirate nicely to release the %% goods, perform a service to get the goods, or simply fight for %% them. Alternatively you could take a bribe and let the pirate %% keep the goods. Doing the service and taking the bribe both %% result in the pirate opening a contraband store which you can %% use later. % P%id%01 = Initialization Counter % P%id%02 = Have fought E2 update %% SubPlot1 = Locate the black market %% SubPlot2 = E2's task SubPlot1 <*:Q_ENT_BlackMarket 3> SubPlot2 <*:Q_MinorMission 2> sub Persona 2 rumor%id% special &MaterialsCost &DiscountPrice &BribeAmount % V1 = Have opened shop to PC % V2 = Melancholy Counter % V3 = Easygoing Counter % V4 = Insult Counter greeting GoCheckQuest GoCheckLoss GoChat *GoGreet <*NiceToMeetYou GoNegotiate> GoNegotiate result1 result2 result3 result4 result5 result6 result7 result8 result9 result10 result11 *result12 <*InsultContest GoR12Win result11 %threat%> GoR12Win result13 result14 result15 result16 result17 .wares *result18 <*GoodBye> Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Msg15 Msg16 Msg17 Msg18 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 CPrompt9 Prompt10 CPrompt10 Prompt11 Prompt12 CPrompt12 Prompt13 CPrompt13 Prompt14 Prompt15 Prompt16 CPrompt16 Prompt17 Prompt18 STC QS_BlackMarket name <%name2%'s Black Market> SetID 3 IndustrialTiles start GoDeserted Msg1 Msg2 sub Room minimap <#...##.1.##...###+###...#> inv StairsDown desig MiniMapComponent 1 end end MetaScene 4 name <%name2%'s Office> type special Tolerance 25 BoxMap MapWidth 12 MapHeight 12 NeededCells 2 IndustrialTiles content Start Faint%2% Surrender%2% end Msg1 Msg2 Msg3 sub Team 1 Team 2 name SetAlly 3 Team 3 name SetAlly 2 type deploy Room minimap <............1............> sub StairsUp Destination -1 MiniMapComponent 1 end end end inv NPC Pirate Shopping 10 Knowledge 13 chardesc sociable cheerful easygoing end Content name desc requires <*:Q_StolenMaterials (1:CRAFT|1:LABOR|1:TRADE)> % E1 is the NPC who lost the materials % E2 is the assistant % E3 is a local outdoors scene % E4 is E2's hideout % E5 is a local public scene for E2 Element2 Place2 <5 (Citizens) pass ally> Element3 Element4 Place4 <3> Element5 %% Confront E2 in a public place, causing him to flee. Locate his hideout. %% Once there, learn the truth: E2 has been stealing the parts so he can %% build mecha and open a customization shop. The PC then has several %% choices: return the parts to E1, take the mecha, or let E2 keep them; %% also, turn E2 in to the authorities or keep his crime a secret. % P%id%01 = Have moved E2 %% FAIL CONDITIONS: %% - E2 dies before hideout is revealed end % SubPlot1 : Locate E2's hideout SubPlot1 <*:Q_RevealEncounter 4 3> sub Persona 2 rumor%id% <%name2% has been acting suspicious lately.> %% V1 = Have opened shop %% V2 = Have decided fate %% V3 = Reputation Counter greeting GoOpenStore GoDecideFate GoHangUp GoCheckQuest *GoGreet <*IWantToTalkAboutNPC&YouBadNPC %1% GoAvoid> GoAvoid GoChat result1 result2 result3 result4 result5 result6 result7 result8 .mektype result9 result10 result11 result12 result13 .wares *result14 <*GoodBye> result15 result16 Msg1 <...[click]> Msg2 Msg3 CPrompt6 Prompt7 CPrompt7 Prompt8 CPrompt8 Prompt9 CPrompt9 Prompt10 Prompt11 Prompt12 Prompt13 Prompt14 Prompt15 Prompt16 MetaScene 4 name <%name2%'s Workshop> type special BoxMap entrance <*QUEST-INACTIVE> MapWidth 12 MapHeight 17 NeededCells 2 %% L1 = First time entry counter content Start Msg1 sub Team 1 Team 2 name Team 3 name SetAlly 2 Room minimap <......###..#1#...........> sub Elevator name MiniMapComponent 1 Use GoQuestOver GoStealMecha .mektype Msg1 Msg2 Msg3 Msg4 Msg5 end Room minimap <............1............> sub TrapDoor Destination -1 MiniMapComponent 1 end end end inv NPC Mechanic job chardesc Criminal Easygoing MechaEngineering 10 Shopping 10 Combatant end Content name desc requires <*:Q_StolenMaterials -"EARTH"> % Element1 is the NPC who lost the materials % Element2 is the NPC who runs the shipping service % Element3 is the wonky robot that's been collecting stuff % Element4 is the Sentinel that's behind it all % Element5 is an artifact that came from Earth with the Sentinel % Element6 is the scene for E2 % Element7 is the scene for E3 % Element8 is the scene for E4,E5 Element2 Place2 <6 (Citizens) Ally Pass> Element3 Place3 <7 (Sparky) SD Enemy> Element4 Place4 <8 (Enemies) SD Enemy> Element5 Place5 <8> Element6 Element7 Place7 <6> Element8 Place8 <7> % P%id%01 = Have deactivated the robots %% FAIL CONDITIONS: %% - E2 Dies end sub Persona 2 rumor%id% <%name2% at the spaceport handles all deliveries for %name1%.> % V1 = Have explained situation and opened first lock greeting *GoNonQuest <*NiceToMeetYou GoExpo> GoExpo GoFirstTime GoOpenFirstDoor Result1 Result2 Result3 Result4 Result5 Result6 Result7 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg3 Msg4 <%name2% revealed that %name1%'s supplies have been diverted by a rogue droid named Sparky. You can find this robot in the basement of %name6%.> Msg5 Msg6 Msg7 Msg8 Msg9 Msg10_1 CMsg10_1 Msg10_2 CMsg10_2 Msg11 Msg12 Prompt1 Prompt2 <%name1% sent me to find \PPR %1% missing parts.> Prompt3 Prompt4 Prompt5 Prompt6 CPrompt6 Prompt7 MetaScene 2 sub room minimap <2...####+#3.1.......##+##> special desig inv TrapDoor MiniMapComponent 2 desig use CLUE_CODEBREAKING Msg1 Msg101 STC Computer-1 MiniMapComponent 3 PDir 1 Use CLUE_CODEBREAKING CLUE_INSIGHT CLUE_SCIENCE Msg1 Msg2 Msg3 end end MetaScene 5 sub room desig special minimap <......#.#...1...#.#......> end MetaScene 7 name <%name2%'s warehouse> type terrain special MonkeyMap MapWidth 35 MapHeight 35 RockyTiles LockedDoorChance 15 NeededCells 2 content % L1 = E2 has opened the door to this level % L2 = Monster generation counter Start nu3 .%id%_%plotid%_GoCure .%id%_GoNothing Msg1 Msg2 Msg3 Msg4 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 SetAlly 3 type Team 3 name SetEnemy 1 SetAlly 2 Room sub StairsUp Destination -1 end Room minimap <........1...2............> desig special sub Trapdoor desig MiniMapComponent 2 use CLUE_CODEBREAKING Msg1 Msg101 end end MetaScene 8 name <%name2%'s sub-basement> type terrain special BoxMap MapWidth 17 MapHeight 17 RockyTiles NeededCells 3 content % L1 = E2 has opened the door to this level % L2 = have been here before Start GoBeenBefore Msg1 Msg2 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Room sub StairsUp Destination -1 end end end inv NPC Trucker Monster GuardBot name CLUE_SCIENCE CLUE_CODEBREAKING CLUE_REPAIR GoDestroyed GoFail GoCure Msg1 Msg2 Monster Sentinel end %% %% *:Q_MechaHeld Content %% %% E1's mecha is currently being held by someone else. In this subquest, %% the PC will be able to find that someone and release the mecha... Or %% maybe have it locked up perminantly. The NPC has a semi-legitimate %% claim to holding the mecha... it may be in hock due to debts, or maybe %% its ownership is disputed. The presence of this subquests implies a %% certain amount of inexperience or irresponsibility on the part of the %% pilot. %% %% PARAM1: The NPC who lost his mecha. %% Content name desc requires <*:Q_MechaHeld> % E1 is the character who owns the mecha. % E2 is the storekeeper holding the mecha. % E3 is the building where the storekeeper operates Element2 Place2 <3 (Citizens) pass ally> Element3 %% FAIL CONDITIONS: %% - if E2 dies, the quest is a failure. end sub Persona 1 % This conversation thread will activate after the PC asks E2 about the mecha. greeting .%id%_GoE2Died result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Persona 2 % V1 = Amount owed % NPCVar %2% 1 = Communication variable. When =1, PC has talked to E2. WHen =2, E1 agreed to pay installments. rumor%id% <%name2% is holding %name1%'s mecha until all debts are paid.> greeting GoE1Dead result1 result2 result3 result4 GoR4Fail result5 result6 result7 GoR7Fail result8 result9 GoR9Fail result10 result11 *GoNoQuest <*NiceToMeetYou GoShop> *GoShop <*SHOP_Mecha GoBye> *GoBye <*GoodBye> Msg1 Msg2 Msg3 <%name1% owes me money, quite a bit of it in fact... I'll be holding onto \PPR %1% mecha until that money is repaid in full.> Msg4 <%name2% is holding %name1%'s mecha until \SPR %1% can pay off \PPR %1% debt.> Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Msg15 Msg16 Prompt1 Prompt2 Prompt3 Prompt4 <%name1% can't earn money without a mecha.> Prompt5 <%name1% has agreed to pay the money.> CPrompt5 Prompt6 CPrompt6 Prompt7 CPrompt7 Prompt8 Prompt9 Prompt10 Prompt11 Metascene 2 sub room minimap <2#...+#.......1.....&---&> special desig inv TrapDoor MiniMapComponent 2 % V1 = Reputation Counter use GoFail GoNoUse Msg101 Msg102 Msg103 Msg104 Msg105 Msg106 end end end inv NPC Shopkeeper end Content name desc requires <*:Q_MechaHeld L5PAT ~"Safe"> % E1 is the character who owns the mecha. % E2 is the police officer holding the mecha. % E3 is a scene for the police officer. Element2 Place2 <3 (Guards) SD Ally> Element3 % FAIL CONDITIONS: % - E2 dies end sub Persona 1 greeting result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Persona 2 % NPCVar %2% 1 = State Counter, if =1 have learned of test, if =2 have taught E1 % V2 = Intimidation counter &Bribe rumor%id% <%name2% impounded %name1%'s mecha due to multiple parking tickets.> greeting GoCheckWin GoMainQuest GoFirstTime *GoNoQuest <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> result1 *result2 <*MISC_CHATTER> result3 result4 result5 GoR5NoLaw result6 GoR6Fail result7 result8 result9 result10 Msg1 Msg2 Msg3 Msg4 <%name2% confiscated %name1%'s mecha, and won't return it until \SPR %1% completes a safety course.> Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 CPrompt6 Prompt7 Prompt8 CPrompt8 Prompt9 CPrompt9 Prompt10 end inv NPC Police Officer SetFaction 9 end Content name desc requires <*:Q_MechaHeld Spinner (!Ne|!Lo)> % E1 is the character that owns the mecha % E2 is the encounter representing the mecha % E3 is the environs scene Element2 Place2 <3> Element3 inv STC QUEST-ENCOUNTER-AUTO-WANDER name rumor%id% <%name1% was forced to eject outside the station.> EncounterMove 20 ENCOUNTER_NonCombat ATTACK USE GoTakeMecha .mekdesc Msg1 Msg2 Msg3 Msg4 end %% %% Debugging Frames %% Content requires <*:Q_Debug> desc % E1 is the person who will be giving the mission. % E2 is the CavClub Element1 Place1 <2 (Citizens) ally pass> Element2 % SubQuest1 is the mission. SubPlot1 <*:Q_DStolenMaterials 1> sub Persona 1 rumor0 <%name1% needs a cavalier for a mission of some kind.> greeting GoCheckLoss *GoEndInLoss <*MissionWasFailure na na na> GoCheckFirst *GoOfferQuest <*DoYouWantAJob GoStartQuest> GoStartQuest GoChat Msg1 Msg2 MetaScene 1 sub room desig minimap <#&&&#&...&&.1.&&...&&&-&&> end end GH2/series/PLOT_MOOD_Funfestation.txt0000644000175000017500000001137211326004535016253 0ustar kaolkaol%% %% Fungal Infestation Plots %% %% The colony has been infested with fungus; plenty of SF:0 jobs to go root them out. %% %% Mood Spec: %% - No Elements %% Plot name desc requires <*FUNFESTATION> PayRate 300 % E1 is the character offering the mission. Element1 % V1 = Timer % FAIL CONDITIONS- % - E1 dies % - Time runs out before the scene is entered end start GoCheckWin GoCheckLoss update Msg1 Msg2 % SubPlot1 is the fungus-cleaning task. % SubPlot2 is the victory condition % SubPlot3 is the failure condition SubPlot1 <*:TASK_KillFungus 1> SubPlot2 <*UTIL_WinMission&Chara 1> SubPlot3 <*UTIL_LoseMission&Chara 1> sub Persona 1 rumor0 <%name1% has a bit of a fungus problem.> % if PlotStatus = PlotID then the PC has been informed of the job, and can get it % automatically until timeout. greeting *GoRemind <*GenericMissionReminder> *GoCheckEnemy <*ENEMY_CHECK GoCheckAuto ChatNPCFac GoEnd> GoEnd *GoCheckAuto <*AutoMissionTest&Chara GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> GoRejectMission *GoCheckSkill <*GenericMissionTest&Chara&Extermination GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> GoMissionBriefing GoNotHere result%id%01 *result%id%02 <*RejectMission GoEnd> result%id%03 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <%name1% asked you to help clear out a fungal infestation beneath \SCENE NPCScene %1% .> Msg%id%03 Msg%id%03_1 Msg%id%04_1 Msg%id%04_2 Msg%id%04_3 Msg%id%04_4 Msg%id%04_5 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 end GH2/series/MEGA_CORE_GOAL_Default.txt0000644000175000017500000006751711374513723015744 0ustar kaolkaol%% %% *CS_WinFetchItem Content %% %% The PC was sent to fetch an item and has apparently gotten it. %% Note that between getting the item and returning to the mission %% giver the PC might have lost the item, destroyed the item, sold %% the item, eaten the item, abused the item, or otherwise decided %% to keep the item. If the PC gives the item to the NPC, a reward %% is in order. %% %% Param1: The Mission-Giver %% Param2: The Item %% Content name desc requires <*CS_WinFetchItem> % E1 is the NPC % E2 is the item start HINT_%id% <:%id1%> % SubPlot1 transfers to the actual reward... SubPlot1 <*CS_WinGoal 1> sub Persona 1 greeting *.%id%_GoComeHere <*NotByPhone> result%id%01 % Success: Delete the item, activate the WINGOAL subplot, and go back to the GREETING. result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 end %% %% *CS_WinGoal Content %% &NoCash The PC will not get any cash for this mission %% NOTE: Don't assign this type for CORE_R_ missions!!! %% %% The PC has completed the MAIN course of this episode and is about to be %% rewarded. The exact reward is, of course, going to depend upon the current %% goal. %% %% Remember to include a HINT_%id% and to debrief the PC. %% %% PARAM1: The NPC giving the reward %% Content name desc requires <*CS_WinGoal *CORE_S_PEA E:good_ (1:evil_|F:REDMA|F:AEGIS) -E:FAMILY -E:LOVER -E:A.hat -E:A.pch -E:A.mut -E:M.nih> % E1 is the NPC % E2 is the Enemy NPC Element2 Place2 HINT_%id% start .%id%_%plotid%_GoEnd Msg%id%01 <%name1%'s guards move to surround you. Before they can act, %name2% steps forward.> Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 <%name1% tried to double cross you, but you were saved by %name2%.> sub Persona 1 greeting *.%id%_NotHere <*NotByPhone> .%id%_GoEnd Msg%id%01 end Content name desc requires <*CS_WinGoal *CORE_S_PEA> % E1 is the NPC start HINT_%id% sub Persona 1 greeting .%id%_GoEnd result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%04 Msg%id%04_1 CMsg%id%04_1 Prompt%id%01 Prompt%id%02 end Content name desc requires <*CS_WinGoal *CORE_S_PCF -&NoCash> changes % E1 is the NPC. start HINT_%id% sub Persona 1 greeting .%id%_GoReportSuccess *.%id%_DefaultWin <*MissionWasSuccess&Reward .%id%_GoEnd &AllyFac &EnemyFac> .%id%_GoEnd result%id%01 result%id%02 .%id%_fac Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name desc requires <*CS_WinGoal *CORE_OFF_5 -&NoCash> % E1 is the NPC. start HINT_%id% sub Persona 1 greeting Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 end Content name desc requires <*CS_WinGoal *CORE_DEF_5 -&NoCash> % E1 is the NPC. % E2 is the Enemy Faction % E3 is the Allied Faction Element2 Element3 start HINT_%id% sub Persona 1 %% Winning the DEF_5 episode will start a side story, probably. greeting .%id%_sidestory <*CSPAWN_Def5 %e2% %e3%> Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 end Content name desc requires <*CS_WinGoal *CORE_R_LAN> % E1 is the NPC % E2 is a local public scene % E3 is the new NPC Element2 Element3 Place3 start HINT_%id% sub Persona 1 greeting result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 <%name1%'s friend %name3% joined your lance.> Msg%id%05 Msg%id%06 <%name3% at %name2% offered to join your lance.> Prompt%id%01 Prompt%id%02 end Content name desc requires <*CS_WinGoal *CORE_R_EQP> % E1 is the NPC start HINT_%id% sub Persona 1 % V%id%01 = Mecha Renown, StoryDL+20 greeting Msg%id%01 end Content name desc requires <*CS_WinGoal *CORE_R_MON> % E1 is the NPC. start HINT_%id% sub Persona 1 greeting *.%id%_GoReportSuccess <*MissionWasSuccess&Reward .%id%_GoEnd &AllyFac &EnemyFac> .%id%_GoEnd end Content name desc requires <*CS_WinGoal *CORE_R_MEK> % E1 is the NPC start HINT_%id% sub Persona 1 % V%id%01 = Mecha Renown, StoryDL+20 greeting .%id%_fac Msg%id%01 end Content name desc requires <*CS_WinGoal *CORE_R_EXP (1:MILIT|1:ADVEN)> % E1 is the NPC. start HINT_%id% sub Persona 1 % V%id%01 = XP Award greeting result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 end Content name desc requires <*CS_WinGoal *CORE_R_EXP -1:MILIT -1:ADVEN ~1:ACADE ~1:TRADE> % E1 is the NPC % E2 is the book Element2 start HINT_%id% sub Persona 1 greeting Msg%id%01 end inv Treasure name desc Fudge 450000 % V1 = Have read book already use GoReadLater GoAlreadyRead Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 end Content name desc requires <*CS_WinGoal &NoCash> % E1 is the NPC start HINT_%id% sub Persona 1 greeting Msg%id%01 end Content name desc requires <*CS_WinGoal 1:FRIEND ~R:-- &NoCash> % E1 is the NPC start HINT_%id% sub Persona 1 greeting .%id%_GoComeHere .%id%_GoBecomeAlly result%id%01 *.%id%_GoJoinLance <*WelcomeToLance> result%id%02 .%id%_GoEndR2 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 end Content name desc requires <*CS_WinGoal ~P:-- -P:PCFAC -1:NOFAC (*CORE_Intro|*CORE_DEF_|*CORE_OFF_) -&NoCash> changes % E1 is the NPC. start HINT_%id% sub Persona 1 greeting .%id%_GoReportSuccess *.%id%_DefaultWin <*MissionWasSuccess&Reward .%id%_GoEnd &AllyFac &EnemyFac> .%id%_GoEnd result%id%01 result%id%02 .%id%_fac Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name desc requires <*CS_WinGoal (*CORE_Intro|*CORE_DEF_1|*CORE_DEF_2|*CORE_DEF_3|*CORE_DEF_4|*CORE_OFF_1|*CORE_OFF_2|*CORE_OFF_3|*CORE_OFF_4) -&NoCash> % E1 is the NPC. start HINT_%id% sub Persona 1 greeting *.%id%_GoReportSuccess <*MissionWasSuccess&Reward .%id%_GoEnd &AllyFac &EnemyFac> .%id%_GoEnd end %% %% *CS_LoseGoal Content %% %% The PC has failed. Bummer. Remember to DEBRIEF the PC. %% %% Param1: The Task-giver. %% Content name desc requires <*CS_LoseGoal *CORE_S_PCF P:++ -P:PCFAC Common ~1:HERO_> % E1 is the NPC. start sub Persona 1 greeting .%id%_GoMakeOffer *.%id%_GoReportLoss <*MissionWasFailure .%id%_GoEnd &AllyFac &EnemyFac> .%id%_GoEnd result%id%01 .%id%_facs result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 prompt%id%01 prompt%id%02 prompt%id%03 prompt%id%04 prompt%id%05 end Content name desc requires <*CS_LoseGoal ~1:FRIEND ~1:LOVER> % E1 is the NPC. start sub Persona 1 greeting Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 end Content name desc requires <*CS_LoseGoal> % E1 is the NPC. start sub Persona 1 greeting *.%id%_GoReportLoss <*MissionWasFailure .%id%_GoEnd &AllyFac &EnemyFac> .%id%_GoEnd end %% %% *CS_MootGoal Content %% %% The mission has become unwinnable, somehow. This counts as a loss, but at least %% the NPC involved will be kind enough to explain what's happening. %% %% Param1: The Mission-giver %% Content name desc requires <*CS_MootGoal> % E1 is the NPC. % p%id%01 = Email Counter start update Msg%id%01_1 <%name1% @ \SCENE NPCScene %1% :// Your mission has been cancelled.> Msg%id%01_2 <%name1% @ \SCENE NPCScene %1% :// Your mission has been aborted.> sub Persona 1 greeting Msg%id%01 end %% %% *CS_FriendFired_Win Content %% %% The PC's friend has just been fired from a faction, and for whatever reason this %% is a win for the PC. %% %% The parent plot sets the plot status to this layer's ID when ready. %% %% Param1: The NPC who has been fired %% Param2: The faction he's been fired from %% Content name desc requires <*CS_FriendFired_Win> % E1 is the friend % E2 is the faction he's been fired from % E3 is a public scene for the meeting Element3 % p%id%01 = Initialization Counter start update Msg%id%01_1 <%name1%@ \NARRATIVE 7 :// I've received bad news from %name2%... come meet me in %name3%.> Msg%id%01_2 <%name1%@ \NARRATIVE 7 :// I've got some bad news. Come see me in %name3%.> Msg%id%01_3 <%name1%@ \NARRATIVE 7 :// This has been a terrible day... I'd like to talk with you in %name3%.> sub Persona 1 greeting .%id%_next <+T-- +F--> result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Msg%id%03 <%name1% was fired from %name2%, much to your relief.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end GH2/series/STC_default.txt0000644000175000017500000007544211376734116014311 0ustar kaolkaol% ********************************************* % *** STC Standard Item Descriptions *** % ********************************************* Gun 5 name desig type caliber <50mm cannister> Range 4 Scale 2 Magazine 5 sub Ammo 5 caliber <50mm cannister> end Gun 2 name desig caliber <20mm caseless> Range 3 BV 3 scale 2 Magazine 40 sub Ammo 2 caliber <20mm caseless> end Gun 4 name desig caliber <45mm caseless> Range 6 BV 3 scale 2 Magazine 60 sub Ammo 4 caliber <45mm caseless> end Gun 5 name desig caliber <45mm caseless> type mass -7 scale 2 range 3 acc -2 BV 7 magazine 80 sub ammo 5 caliber <45mm caseless> end Gun 8 Scale 2 name desig caliber <30mm ferrous> Range 6 BV 4 Magazine 100 mass -4 sub Ammo 8 caliber <30mm ferrous> mass -6 end Gun 16 Scale 2 name desig caliber <50mm ferrous> Range 7 BV 3 Magazine 60 sub Ammo 16 caliber <50mm ferrous> mass -8 end Gun 12 Scale 2 name desig caliber <70mm ferrous> Range 7 Recharge 1 Magazine 24 sub Ammo 12 caliber <70mm ferrous> end Gun 9 scale 2 Name desig Caliber <150mm Shells> range 5 Magazine 15 sub Ammo 9 Caliber <150mm Shells> end Gun 6 scale 2 Name desig Caliber <99mm Shells> range 5 BV 1 Magazine 30 sub Ammo 6 Caliber <99mm Shells> end Gun 7 name desig caliber Scale 2 type range 5 Acc 1 Speed 2 BV 1 Magazine 50 sub Ammo 7 caliber end Gun 17 scale 2 name desig caliber <70cm self-propelled> range 7 scale 2 mass -4 recharge 1 Magazine 8 sub Ammo 17 caliber <70cm self-propelled> type end Gun 17 name desig caliber <70cm self-propelled> range 7 scale 2 mass -4 Magazine 12 sub Ammo 17 caliber <70cm self-propelled> type end Gun 5 Scale 2 name caliber <65mm caseless> desig Speed 2 Range 6 mass -4 Magazine 120 sub Ammo 5 caliber <65mm caseless> mass -3 end Gun 4 Scale 2 name desig caliber <15mm ferrous needle> desc range 6 Acc 2 Speed 3 Mass -1 Magazine 20 sub Ammo 4 caliber <15mm ferrous needle> type end Gun 24 Scale 2 name desig caliber <120mm ferrous> Range 8 Recharge 1 Magazine 16 sub Ammo 24 caliber <120mm ferrous> end BeamGun 3 Scale 2 name DESIG Range 2 Recharge 3 Acc 1 BeamGun 5 Scale 2 name DESIG Range 3 Recharge 3 ACC 1 BeamGun 10 Scale 2 name DESIG Range 5 Recharge 3 ACC 1 BeamGun 2 Scale 2 name desig type Range 7 ACC 1 Recharge 3 BeamGun 6 Scale 2 name desig type Range 8 ACC 1 BeamGun 13 Scale 2 name desig type Range 9 Recharge 1 BeamGun 8 name desig type scale 2 Range 7 BeamGun 25 name desig type Scale 2 Range 8 Recharge 1 rockets 5 name SCALE 2 desig type Range 7 Magazine 1 Rockets 1 name desig Scale 2 type range 6 Magazine 1 Rockets 18 name desig Scale 2 Acc 2 type range 8 Magazine 1 Rockets 4 name desig Scale 2 type range 7 acc 2 Magazine 1 Rockets 20 Name Desig Scale 2 Type Magazine 1 range 8 Rockets 9 name desig Scale 2 type Range 6 Magazine 1 Rockets 3 name Scale 2 desig type Acc 1 Range 7 Magazine 1 Rockets 1 name desig Scale 2 Range 5 ACC -2 Magazine 1 Rockets 8 name desig type Scale 2 Range 6 Magazine 1 Rockets 10 name desig Scale 2 Range 6 ACC -2 Magazine 1 Rockets 10 name desig type Scale 2 Range 7 ACC 2 Magazine 1 Rockets 15 name desig Scale 2 Range 8 Magazine 1 Rockets 20 name type desig Scale 2 Range 8 Magazine 1 Rockets 3 Name desig Scale 2 Type Range 6 Acc -1 Magazine 1 Rockets 2 scale 2 name desig Range 7 Magazine 1 type Rockets 5 scale 2 name desig Range 7 Magazine 1 type Rockets 6 scale 2 name desig Range 7 Magazine 1 type Rockets 8 scale 2 name desig Range 7 Magazine 1 type Rockets 15 scale 2 name desig Range 8 Acc -1 Magazine 1 type Rockets 12 scale 2 name Desig range 6 Magazine 1 type % ******************** % *** TREASURE *** % ******************** Grenade 15 name desig meat Scale 0 type mass 1 gear 10 0 6 name desig scale 0 stat 1 2 sub gear 4 0 12 name type scale 0 Recharge 3 end RepairFuel 45 name scale 0 desig RepairFuel 250 name scale 0 desig mass 1 SkillMechaSys name desig CyberSlot SkillModMechaGunnery SkillModAmount 2 SkillMechaSys name desig CyberSlot SkillModMechaFighting SkillModAmount 1 Food 30 name desig FoodFX:Regeneration FoodMod 16 FoodMorale -5 FoodXP:Athletics FoodXPAmount 50 Food 20 name desig FoodFX:Healing FoodMod 9 FoodMorale -2 FoodXP:Vitality FoodXPAmount 100 Treasure name desig mass 1 Treasure name desig Food 30 name desig Food 20 name desig Food 25 name desig FoodMorale -1 Food 20 name desig FoodMorale 1 Food 15 name desig Food 20 name desig Food 25 name desig FoodMorale -3 FoodFX:CauseStatus FoodMod:Sickness Food 20 name desig FoodMorale -3 FoodFX:CauseStatus FoodMod:Poison % ********************** % *** METASCENES *** % ********************** MetaScene desig %% This is a press conference or similar media event ClubMap MapWidth 25 MapHeight 25 type special nu1 sub Team 1 Team 2 name setally 1 Passive Team 3 name setally 2 room name desig Content Content1 Content2 end MetaScene desig %% This metascene is used in core story components based on task +Tsh. %% It included opportunities for the PC to earn "party points". ClubMap MapWidth 25 MapHeight 25 type special nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 room name desig Content Content1 Content2 end MetaScene desig %% This is a generic place of business. If the calling plot sets its faction, %% its content types will be improved. ClubMap MapWidth 20 MapHeight 20 type special nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 room name desig Content Content1 Content2 end MetaScene ClubMap desig %% A generic cafe for meeting friends and talking. MapWidth 25 MapHeight 25 type special nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 room name desig Content Content1 Content2 end MetaScene ClubMap desig %% A generic club for meeting friends and dancing. MapWidth 25 MapHeight 25 type special Content nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 room name desig Content Content1 Content2 end MetaScene ClubMap desig %% A generic school for consulting with students and professors. MapWidth 25 MapHeight 25 type special nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 room name desig Content Content2 end MetaScene BoxMap desig MapWidth 13 MapHeight 13 special nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 end % ********************** % *** ENCOUNTERS *** % ********************** Encounter desig % Sets this encounter's orders to Seek & Destroy... very slowly. GoSetOrders EncounterMove 2 update ATTACK < > Encounter desig % Sets this encounter's orders to Passive GoSetOrders EncounterMove 50 Encounter desig name % This encounter is used for those cases where the PC goes directly to a metascene % after activating a mission. It tries to always be in the same tile as the PC, and % is never visible. In most cases, obviously, actual teleportation isn't involved... update EncounterMove 0 Encounter desig % Sets this encounter's orders to Passive % E1 must be the mission-giver GoSetOrders name <%name1%'s Mission> update EncounterMove 50 Encounter desig % Sets this encounter's orders to Passive % A lancemate(E1)'s secret spot. % This encounter will activate during its subplot if the lancemate is alive % and in the party. GoSetOrders name <%name1%'s Destination> update Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % Sets this encounter's orders to Passive % A lancemate(E1)'s personal job. % This encounter will activate during its subplot if the lancemate is alive % and in the party. GoSetOrders name <%name1%'s Mission> update Attack < > EncounterMove 0 Encounter desig % Sets this encounter's orders to Passive GoSetOrders name <%name1%'s Mission> update EncounterMove 50 Encounter desig update % Sets this encounter's orders to Passive GoSetOrders attack < > EncounterMove 0 Encounter desig % Sets this encounter's orders to Passive GoSetOrders name update EncounterMove 50 Encounter desig % Sets this encounter's orders to Seek & Destroy GoSetOrders name update EncounterMove 50 Encounter desig % This encounter attacks as soon as the PC enters the scene. GoSetOrders name update ATTACK EncounterMove 100 Encounter desig % This encounter attacks as soon as the PC enters the scene. GoSetOrders name update EncounterMove 100 Encounter desig % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. update % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 Encounter desig % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. update % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 Encounter desig % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. % % The first time the PC tries to enter, he will be attacked. Afterwards he may enter % the location normally. The fight uses %threat% for its difficulty rating. % % The attacking faction will be the core story enemy faction, and the encounter % threat level will be the StoryDL. %% %% V1 = Have defeated mecha counter %% %% % Dynamic Combat Info %% % V2 = Initialization Counter %% % V3 = Destination Scene %% update % Sets this encounter's orders to Passive GoSetOrders Attack < > use GoStartCombat .nu1 .nu2 .msg1 Msg1 EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % The first time the PC tries to enter, he will be attacked. Afterwards he may enter % the location normally. % % The attacking faction will be the core story enemy faction, and the encounter % threat level will be the StoryDL. % This encounter appears when PlotStatus %plotid% = %id% %% %% V1 = Have defeated mecha counter %% %% % Dynamic Combat Info %% % V2 = Initialization Counter %% % V3 = Destination Scene %% update % Sets this encounter's orders to Passive GoSetOrders Attack < > use GoMaybeCombat GoAutoCombat GoStartCombat .nu1 .nu2 .msg1 Msg1 Msg2 Msg3 Msg4 Msg5 EncounterMove 30 Encounter desig % Sets this encounter's orders to Seek & Destroy GoSetOrders EncounterMove 50 Encounter desig % This encounter will try to go to the spot specified. GoSetOrders EncounterMove 50 Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. Special update % Sets this encounter's orders to Passive GoSetOrders EncounterMove 50 Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % It becomes active when QuestStatus %qid% = %id% and remains visible % forever after that. % V-1 = Have become visible counter Special update GoSetVisible % Sets this encounter's orders to Passive GoSetOrders EncounterMove 50 Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. Special update % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % It becomes active when QuestStatus %qid% = %id% and remains % active forever after. % V-1 = Have become visible counter Special update GoSetVisible % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. Special update % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % A Quest MapMarker doesn't have a MetaScene ID. % It becomes active when QuestStatus %qid% = %id% and remains % active forever after. % V-1 = Have become visible counter Special update GoSetVisible % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % A Quest MapMarker doesn't have a MetaScene ID. % It becomes active when QuestStatus %qid% = %id% and remains % active forever after. % V-1 = Have become visible counter Special update GoSetVisible % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 50 ENCOUNTER_NonCombat Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % This one is always active. Special update < > % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % This one is always active. Special update % Sets this encounter's orders to Passive GoSetOrders Attack < > EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % A Quest Encounter doesn't have a MetaScene ID. % This one does, however, require a NID % It becomes active when its NAG_Narrative/NAS_EncounterActive attribute is nonzero. % % The first time the PC tries to enter, he will be attacked. Afterwards he may enter % the location normally. The fight uses %threat% for its difficulty rating. % %% %% V1 = Have defeated mecha counter %% %% % Dynamic Combat Info %% % V1 = Entrance NID %% % V2 = Initialization Counter %% % V3 = Destination Scene %% Special update % Sets this encounter's orders to Passive GoSetOrders Attack < > use GoStartCombat .nu1 .nu2 .msg1 Msg1 EncounterMove 0 ENCOUNTER_NonCombat Encounter desig % % A Dynamic Encounter doesn't lead to a scene; instead, it triggers a % dynamic mecha fight. % Once activated, the DAILY encounter will attack the PC once per day % until it is defeated. % % USAGE: Assign a name and faction for the encounter. When you want it to % become active, set its clock (V1) and difficulcy rating (V2). When the % encounter has been defeated, V3 will be nonzero. % % Alter .msg1 to change the message printed when the fight is won. % % Once the fight has been won, this battle won't appear again unless it % is reset by an outside source. If you want the encounter to be deleted % after it is finished, this must be done by an outside source. % Special % V1 = Visibility Clock. If ComTime > V1 and V1 <> 0, encounter is active % V2 = Difficulcy Rating. % V3 = Victory Counter. Increases by one each time the PC defeats the pirates. update GoSetOrders use ATTACK GoAutoAttack GoAvoidAttack % In the dynamic scene, L1 = This encounter's NID GoStartCombat .nu1 .nu2 .msg1 EncounterMove 50 % ****************************** % *** SUPERWEAPON PARTS *** % ****************************** CombatProp 10 name Desig SDL_SKIN Mesh 7 Scale 2 RangedCombat 8 Initiative 8 ElectronicWarfare 10 sub Turret size 3 armor 3 sub BeamGun 2 Scale 2 name type Range 6 ACC 1 Recharge 4 end end CombatProp 15 name Desig SDL_SKIN Mesh 7 Scale 2 RangedCombat 10 Initiative 10 ElectronicWarfare 14 SpotWeakness 9 sub Turret size 3 armor 3 sub BeamGun 4 Scale 2 name type Range 8 ACC 1 Recharge 4 end end CombatProp 20 name Desig SDL_SKIN Mesh 7 Scale 2 RangedCombat 12 Initiative 12 ElectronicWarfare 18 SpotWeakness 12 sub Turret size 3 armor 3 sub BeamGun 4 Scale 2 name type Range 8 ACC 1 Recharge 4 end end CombatProp 25 name Desig SDL_SKIN Mesh 8 Scale 2 RangedCombat 16 Initiative 10 ElectronicWarfare 18 sub Turret size 5 armor 5 sub BeamGun 6 Scale 2 name type Range 6 Recharge 1 end end CombatProp 20 name Desig SDL_SKIN Mesh 6 Scale 2 RangedCombat 14 Initiative 6 ElectronicWarfare 18 sub Turret size 5 armor 5 sub MLauncher 16 sub Rockets 2 name Range 8 Magazine 80 type end end end CombatProp 20 name Desig SDL_SKIN Mesh 5 Scale 2 RangedCombat 14 Initiative 6 ElectronicWarfare 18 sub Turret size 5 armor 5 sub BeamGun 4 Scale 2 name type Range 8 ACC 1 Recharge 4 end end % ************************* % *** SPACESHIP PARTS *** % ************************* CombatProp 10 name Desig SDL_SKIN Mesh 7 Scale 2 RangedCombat 10 Initiative 9 ElectronicWarfare 15 SpotWeakness 10 sub Turret size 3 armor 3 sub BeamGun 2 Scale 2 name type Range 7 ACC 1 Recharge 4 end end CombatProp 15 name Desig SDL_SKIN Mesh 7 Scale 2 RangedCombat 12 Initiative 11 ElectronicWarfare 24 SpotWeakness 12 sub Turret size 3 armor 3 sub BeamGun 4 Scale 2 name type Range 8 ACC 1 Recharge 4 end end % ********************** % *** COMBAT PROPS *** % ********************** CombatProp 10 name Desig sdl_sprite roguechar <&> Scale 2 RangedCombat 8 Initiative 10 sub Turret size 3 armor 3 sub Gun 4 name desig caliber <20mm caseless> Range 6 BV 3 scale 2 Magazine 60 sub Ammo 4 caliber <20mm caseless> end end end CombatProp 10 name Desig roguechar <&> Scale 2 RangedCombat 8 Initiative 10 Mesh 2 sdl_SKIN sub Turret size 3 armor 3 sub Gun 4 name desig caliber <20mm caseless> Range 6 BV 3 scale 2 Magazine 60 sub Ammo 4 caliber <20mm caseless> end end end Prop 20 name Desig roguechar <&> Scale 2 Mesh 1 sdl_SKIN Frame 1 % ****************** % *** PERSONAS *** % ****************** Persona %% A blank persona. desig greeting Persona %% A persona which should revert back to the original. desig greeting % Etc... Scene BoxMap desig MapWidth 32 MapHeight 32 nu1 sub Team 1 Team 2 name setally 1 3 Passive Team 3 name SetAlly 1 2 end GH2/series/MAP_RocketArena.txt0000644000175000017500000000315111326004537015023 0ustar kaolkaol40 40 *** GearHead Location Record *** 45 13 2 14 37 13 4 14 35 13 12 14 7 13 3 14 5 13 3 14 9 13 14 14 5 13 6 14 2 13 5 14 7 13 16 14 3 13 15 14 6 13 11 14 2 13 3 14 3 13 3 14 2 13 11 14 6 13 10 14 2 13 3 14 4 13 3 14 2 13 11 14 6 13 8 14 3 13 3 14 4 13 3 14 2 13 11 14 7 13 6 14 4 13 5 14 2 13 3 14 2 13 11 14 7 13 5 14 6 13 8 14 4 13 9 14 8 13 4 14 8 13 1 14 1 13 4 14 6 13 7 14 9 13 3 14 12 13 3 14 7 13 6 14 9 13 3 14 12 13 2 14 9 13 5 14 9 13 7 14 7 13 3 14 11 13 3 14 10 13 7 14 6 13 2 14 9 13 6 14 11 13 6 14 5 13 3 14 8 13 7 14 14 13 2 14 5 13 4 14 8 13 6 14 15 13 3 14 3 13 6 14 3 13 1 14 2 13 3 14 18 13 9 14 2 13 3 14 1 13 7 14 17 13 9 14 4 13 14 14 12 13 4 14 11 13 2 14 4 13 8 14 10 13 4 14 12 13 2 14 6 13 7 14 8 13 5 14 12 13 3 14 10 13 3 14 7 13 3 14 15 13 2 14 10 13 3 14 7 13 3 14 14 13 3 14 10 13 3 14 7 13 6 14 10 13 3 14 11 13 3 14 8 13 6 14 8 13 3 14 11 13 3 14 9 13 7 14 6 13 4 14 10 13 4 14 8 13 9 14 5 13 3 14 10 13 5 14 7 13 11 14 3 13 4 14 9 13 7 14 6 13 11 14 2 13 6 14 7 13 9 14 5 13 11 14 2 13 2 14 2 13 3 14 6 13 10 14 5 13 14 14 2 13 7 14 2 13 10 14 6 13 13 14 3 13 18 14 7 13 5 14 2 13 4 14 7 13 14 14 9 13 3 14 20 13 7 14 34 13 5 14 36 13 3 14 45 13 *** 1600 -1 GH2/series/ARENAMISSION_REWARD_SkillTrainer.txt0000644000175000017500000002501411326004537017551 0ustar kaolkaol%% %% *REWARD missions %% %% Don't let the name fool you- these missions aren't rewards in themselves; %% rather, they present an opportunity for the PC to earn a significant reward. %% The reward missions are typically more difficult than regular missions. %% %% The SkillTrainer rewards usually need a SKILL_TRAIN_MISSION coupon. Check to %% make sure that the appropriate skill tag is present. Minor skills, which do %% not require a coupon, will be indicated in the section headers. %% %% The skill trainer missions are arranged by Skill ID. %% %% ******************************** %% *** [s1] MECHA GUNNERY *** %% ******************************** ArenaMission name requires <*REWARD SKILL_TRAIN_MISSION [s1]> desc MapWidth 50 MapHeight 50 PayRate 500 SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop Element1 % L1 = Initialization/Timer % L2 = Victory counter start % Every 5 minutes, and every player move, check to see if the time is up. TMove1 TMove2 5min GoCheckTime nu1 nu3 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 sub Team 1 SetEnemy 2 3 ParaX 5 ParaY 5 Team 2 SetEnemy 1 SetAlly 3 Deploy ParaX 30 ParaY 30 Team 3 SetEnemy 1 SetAlly 2 end inv Prop 5 name Scale 2 SDL_SPRITE SDL_COLORS <201 205 229 122 130 130 208 34 51> update Hardened SetTeam 3 XPos 45 YPos 45 end %% ********************************* %% *** [s2] MECHA FIGHTING *** %% ********************************* ArenaMission name requires <*REWARD SKILL_TRAIN_MISSION [s2] -!Ne> desc MapWidth 60 MapHeight 60 PayRate 500 CityBlockMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Bases Counter- if all four are destroyed, earn Mecha Fighting training % L4 = Reinforcement calculator % L11 = B1 Destroyed % L12 = B2 Destroyed % L13 = B3 Destroyed % L14 = B4 Destroyed Start nu1 %% 1 2 %% PC <--- Map! %% 3 4 nu11 nu12 nu13 nu14 GoWinMission GoLoseMission Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 sub Team 1 SetEnemy 11 12 13 14 21 22 23 24 ParaX 30 ParaY 30 Team 11 SetEnemy 1 SetAlly 12 13 14 21 22 23 24 home Team 21 SetEnemy 1 SetAlly 11 12 13 14 22 23 24 Deploy home rect name Desig sub SuperProp requires <*Fortress> SetTeam 11 end Team 12 SetEnemy 1 SetAlly 11 13 14 21 22 23 24 home Team 22 SetEnemy 1 SetAlly 11 12 13 14 21 23 24 Deploy home rect name Desig sub SuperProp requires <*Fortress> SetTeam 12 end Team 13 SetEnemy 1 SetAlly 11 12 14 21 22 23 24 home Team 23 SetEnemy 1 SetAlly 11 12 13 14 21 22 24 Deploy home rect name Desig sub SuperProp requires <*Fortress> SetTeam 13 end Team 14 SetEnemy 1 SetAlly 11 13 12 21 22 23 24 home Team 24 SetEnemy 1 SetAlly 11 12 13 14 21 23 22 Deploy home rect name Desig sub SuperProp requires <*Fortress> SetTeam 14 end end %% ********************************* %% *** [s3] MECHA PILOTING *** %% ********************************* %% NOTE: Mecha Piloting should not be made available at !Ne or !Lo levels. ArenaMission name requires <*REWARD SKILL_TRAIN_MISSION [s3] -!Ne -!Lo> desc MapWidth 50 MapHeight 50 PayRate 500 ForestMap terrain Element1 Element2 Element3 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 GoBadWin Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 sub Team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 Team 2 SetEnemy 1 3 Deploy ParaX 20 ParaY 30 Team 3 SetEnemy 2 SetAlly 1 ParaX 45 ParaY 45 end inv NPC Mecha Pilot job SetFaction -2 SetTeam 3 end %% ***************************** %% *** [s11] INITIATIVE *** %% ***************************** ArenaMission name requires <*REWARD SKILL_TRAIN_MISSION [s11]> desc MapWidth 50 MapHeight 50 PayRate 500 WildMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Time Limit Start nu1 nu2 GoFailRendezvous Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 ParaX 10 ParaY 10 Team 2 SetEnemy 1 Deploy ParaX 40 ParaY 40 end %% ******************************** %% *** [s13] MECHA REPAIR *** %% ******************************** %% NOTE: Mecha Repair should not be made available at !Ne level. %% *************************** %% *** [s14] MEDICINE *** %% *************************** %% NOTE: Medicine should not be made available at !Ne level. %% Minor Skill %% ************************************** %% *** [s15] ELECTRONIC WARFARE *** %% ************************************** %% ********************************* %% *** [s16] SPOT WEAKNESS *** %% ********************************* %% ******************************* %% *** [s17] CONVERSATION *** %% ******************************* %% Minor Skill ArenaMission name requires <*REWARD [s17]> desc MapWidth 50 MapHeight 50 PayRate 500 CityBlockMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Time Limit Start nu1 nu2 Msg1 Msg2 Msg3 Msg4 sub Team 1 SetEnemy 2 ParaX 10 ParaY 10 Team 2 SetEnemy 1 Deploy ParaX 40 ParaY 40 end GH2/series/ARENAMISSION_Criminal.txt0000644000175000017500000000130511326004535015633 0ustar kaolkaol%% ************************************** %% *** [!Ne+] BEGINNER MISSIONS *** %% ************************************** %% **************************************** %% *** [!Lo+] LOW LEVEL MISSIONS *** %% **************************************** %% ******************************************* %% *** [!Md+] MEDIUM LEVEL MISSIONS *** %% ******************************************* %% ***************************************** %% *** [!Hi+] HIGH LEVEL MISSIONS *** %% ***************************************** %% *************************************** %% *** [!Ex] ACE LEVEL MISSIONS *** %% *************************************** GH2/series/PLOT_ENC_PolicePatrol.txt0000644000175000017500000002050611365256063016061 0ustar kaolkaolPlot name requires <*GENERAL LAWFUL> % This police patrol is tougher, gets reinforcements, and there's no way % to talk your way out of it. Ouch. % E1 is the Town scene % E2 is the outdoors scene for the patrol. % E3 is the encounter for the patrol % E4 is the local police faction % E5 is the police officer to be used Element1 Element2 Element3 Place3 <2> Element4 Element5 Place5 <3 (Guards) sd enemy> % P1 = Timer % P2 = Have entered combat start update GoCheckTime GoDelete sub MetaScene 3 2 MapWidth 30 MapHeight 30 % L1 = Reinforcements level start end 5min nu1 nu2 % The PC has won this encounter. GoVictory % The PC has lost this encounter... GoLoss Msg1 sub Team 1 SetEnemy 2 ParaX 5 ParaY 5 Team 2 name SetEnemy 1 Deploy ParaX 25 ParaY 25 end Persona 5 special rumor <%name5% is out on patrol.> greeting *GoDoWin <*GetYouNextTime> GoCheckLoss *GoDoLoss <*YouLose> *GoFightPC <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy GoStartCombat> end inv STC ENCOUNTER-WANDER name <%name5%'s Patrol> &ifShouldAttack use GoTooSmall GoNoAttack Attack GoMakeChoice Msg1 Msg2 Msg3 Msg4 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg5_3 CMsg5_3 Msg5_4 CMsg5_4 Msg5_5 CMsg5_5 Msg5_6 CMsg5_6 Msg5_7 CMsg5_7 Msg5_8 CMsg5_8 Msg5_9 CMsg5_9 Msg5_10 CMsg5_10 Msg5_11 Msg6 Msg7 ENCOUNTER_Defense end Plot name requires <*GENERAL -LAWFUL -LAWLESS> % E1 is the Town scene % E2 is the outdoors scene for the patrol. % E3 is the encounter for the patrol % E4 is the local police faction % E5 is the police officer to be used Element1 Element2 Element3 Place3 <2> Element4 Element5 Place5 <3 (Guards) sd enemy> % P1 = Timer % P2 = Have entered combat start update GoCheckTime GoDelete sub MetaScene 3 2 MapWidth 30 MapHeight 30 % L2 = Encounter Over Indicator start end 5min nu1 nu2 % The PC has won this encounter. GoVictory % The PC has lost this encounter... GoLoss sub Team 1 SetEnemy 2 ParaX 5 ParaY 5 Team 2 name SetEnemy 1 Deploy ParaX 25 ParaY 25 end Persona 5 special rumor <%name5% is out on patrol.> % If the PC isn't of sufficient level to handle this NPC, the battle % will be left up to the lancemates. greeting *GoDoWin <*GetYouNextTime> GoCheckLoss *GoDoLoss <*YouLose> *GoFightPC <*BattleChallenge&Avoid GoCheckRenown GoExit> GoCheckRenown *GoThemeExpo <*THEME_EXPO&Enemy GoStartCombat> GoExit Msg1 Msg1_1 Msg1_2 end inv STC ENCOUNTER-WANDER name <%name5%'s Patrol> &ifShouldAttack use GoTooSmall GoNoAttack Attack GoMakeChoice Msg1 Msg2 Msg3 Msg4 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg5_3 CMsg5_3 Msg5_4 CMsg5_4 Msg5_5 CMsg5_5 Msg5_6 CMsg5_6 Msg5_7 CMsg5_7 Msg5_8 CMsg5_8 Msg5_9 CMsg5_9 Msg5_10 CMsg5_10 Msg5_11 Msg6 Msg7 ENCOUNTER_Defense end GH2/series/RANCON_Combat.txt0000644000175000017500000000616011326004537014400 0ustar kaolkaol%% %% Combat Random Scene Content- Random scene content that's all out of bubblegum! %% %% This file contains random scene combat that's meant to be used in combat %% missions. In order to use any of these things, your scene may need to meet %% certain criteria: %% - The faction of the scene is set to the faction being fought. %% - There's a hostile team named "Enemies" %% - There's a friendly team named "Allies" %% %% *CRC_Nemesis Content %% %% One of your many enemies will show up to oppose you. This content %% will not appear at !Ne renown, but will start showing up thereafter. %% Content name requires <*CRC_Nemesis -!Ne> Element1 <.> Element2 Team2 TeamData2 start sub Persona 2 special *greeting <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> end Content name requires <*CRC_Nemesis -!Ne -!Lo> Element1 <.> Element2 Team2 TeamData2 start sub Persona 2 special *greeting <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> end Content name % The relations are getting a bit thin by now... requires <*CRC_Nemesis -!Ne -!Lo -!Md> Element1 <.> Element2 Team2 TeamData2 start sub Persona 2 special *greeting <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> end %% %% *CRC_Backup Content %% %% One of your friends or allies will show up to provide backup. %% Content name requires <*CRC_Backup Common> Element1 <.> Element2 Team2 TeamData2 start sub Persona 2 special greeting GoFirstTime *GoGreet <*HereToHelpFight GoThemeExpo GoRejectAid na> *GoThemeExpo <*THEME_EXPO&Ally na> *GoRejectAid <*RejectAid GoRunAway> GoRunAway end Content name requires <*CRC_Backup -!Ne> Element1 <.> Element2 Team2 TeamData2 start sub Persona 2 special greeting GoFirstTime *GoGreet <*HereToHelpFight GoThemeExpo GoRejectAid na> *GoThemeExpo <*THEME_EXPO&Ally na> *GoRejectAid <*RejectAid GoRunAway> GoRunAway end GH2/series/ARENAMISSION_Military.txt0000644000175000017500000001230011326004535015664 0ustar kaolkaol%% ************************************** %% *** [!Ne+] BEGINNER MISSIONS *** %% ************************************** %% **************************************** %% *** [!Lo+] LOW LEVEL MISSIONS *** %% **************************************** ArenaMission name requires <*MISSION -!Ne MILITARY> desc MapWidth 50 MapHeight 50 PayRate 1000 WildMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Base Counter Start nu1 nu2 nu3 GoN3SomeLeft GoWinMission GoLoseMission 5Min Msg1 Msg2 Msg3 Msg4_1 CMsg4_1 Msg4_2 CMsg4_2 Msg5 sub Team 1 SetEnemy 2 3 4 ParaX 5 ParaY 5 Team 2 SetEnemy 1 SetAlly 3 4 Deploy home Team 3 SetEnemy 1 SetAlly 2 4 home Team 4 SetEnemy 1 SetAlly 2 3 home Deploy rect width 5 height 5 name rect name MFX 35 MFY 35 width 10 height 10 sub SuperProp requires <*Fortress> SetTeam 3 end end %% ******************************************* %% *** [!Md+] MEDIUM LEVEL MISSIONS *** %% ******************************************* ArenaMission name requires <*MISSION Military -!Ne -!Lo> desc MapWidth 50 MapHeight 50 PayRate 1600 CityBlockMap terrain % E1 is an allied government % E2 is an allied police force % E3 is the criminal faction % E4 is the police officer Element1 Element2 Element3 Element4 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 Team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 ParaX 7 ParaY 7 rect name desig rect name desig end inv NPC Police Officer SetFaction -2 SetTeam 3 end ArenaMission name requires <*MISSION Military -!Ne -!Lo> desc <%name1% has deployed an orbital laser satellite. It must be destroyed before it can be used against us.> MapWidth 50 MapHeight 50 PayRate 1200 SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Base Counter Start nu1 nu2 nu3 GoWinMission GoLoseMission 5Min Msg1 Msg2 Msg3 Msg4 sub Team 1 SetEnemy 2 3 4 ParaX 5 ParaY 5 Team 2 SetEnemy 1 SetAlly 3 4 Deploy ParaX 40 ParaY 40 Team 3 SetEnemy 1 SetAlly 2 4 ParaX 45 ParaY 45 end inv CombatProp 30 SetTeam 3 name sdl_sprite roguechar <&> Scale 2 RangedCombat 8 Dodge 8 Initiative 2 sub Turret size 8 armor 8 sub BeamGun 8 name Range 12 type end PowerSource 10 end end %% ***************************************** %% *** [!Hi+] HIGH LEVEL MISSIONS *** %% ***************************************** %% *************************************** %% *** [!Ex] ACE LEVEL MISSIONS *** %% *************************************** GH2/series/CHOICE_Default.txt0000644000175000017500000004263511401151245014531 0ustar kaolkaol%% %% DRAMATIC CHOICES %% %% At the end of each episode, the PC gets to make a dramatic choice. %% %% DESIG: Must have a unique designation. %% %% REQUIRES: The pre-requsites for this choice to appear. %% Previous choices can be added with a ":" plus their designation. %% %% PROMPT: The menu text the player will select. Add various options %% to account for the different possible story states. %% REPLY: What the NPC you're speaking to says after you make the choice. %% ALERT: What the alert message says after you make the choice. %% %% CHOICE: The script label to be called when this choice is made. %% This script should set the StoryNote. %% %% %% ************************* %% ** DEFENSE TRACK *** %% ************************* %% %% Complete this track to get more allies in the final battle. %% Choice 1 name desc desig requires <-!Ex> % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 2 name desc desig requires <:DEF_1 -!Ex> % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 3 name desc desig requires <:DEF_2 -!Ex> % E1 is the city for the next episode. Element1 Prompt <%name1% must be protected.> Reply Alert Choice Msg%id%01 Choice 4 name desc desig requires <:DEF_3 -!Ex P:++> % E1 is the city for the next episode. Element1 Prompt Prompt_1 <%name1% is probably \PERSONA &EnemyNPC 's next target.> CPrompt_1 Prompt_2 <%name1% must be protected from \FACTION &EnemyFac .> CPrompt_2 Reply Alert Choice Msg%id%01 Choice 5 name desc desig requires <:DEF_4 -!Ex P:++ F:++> % E1 is the city for the next episode. Element1 Prompt Prompt_1 <%name1% is probably \PERSONA &EnemyNPC 's next target.> CPrompt_1 Prompt_2 <%name1% must be protected from \FACTION &EnemyFac .> Reply Alert Choice Msg%id%01 %% %% ************************* %% ** OFFENSE TRACK *** %% ************************* %% %% Complete this track to weaken the enemies in the final battle. %% Choice 6 name desc desig requires <-!Ex> % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 7 name desc desig requires <:OFF_1 -!Ex> % E1 is the city for the next episode. Element1 Prompt Prompt_1 CPrompt_1 Reply Alert Choice Msg%id%01 Choice 8 name desc desig requires <:OFF_2 -!Ex> % E1 is the city for the next episode. Element1 Prompt Prompt_1 <\PERSONA &EnemyNPC must be stopped.> CPrompt_1 Prompt_2 CPrompt_2 Reply Alert Choice Msg%id%01 Choice 9 name desc desig requires <:OFF_3 -!Ex F:++> % E1 is the city for the next episode. Element1 Prompt Prompt_1 <\PERSONA &EnemyNPC needs a proper arse-kicking.> CPrompt_1 Prompt_2 CPrompt_2 Reply Alert Choice Msg%id%01 Choice 10 name desc desig requires <:OFF_4 -!Ex F:++> % E1 is the city for the next episode. Element1 Prompt Prompt_1 CPrompt_1 Prompt_2 CPrompt_2 Reply Alert Choice Msg%id%01 %% %% *************************** %% *** REWARD CHOICES *** %% *************************** %% %% Choices 11 through 20 are the Reward Choices. Each of these will result %% in an immediate reward for the PC. Note that only one RC will appear in %% the choice menu; this is hardcoded into the menu generator, so these %% choice indicies should not be reassigned. Also, they don't need a "requires" %% field. %% Choice 11 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 12 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 13 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 14 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 15 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 16 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 17 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 18 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 19 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 20 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 %% %% ****************************** %% *** EASY MONEY TRACK *** %% ****************************** %% %% You've heard that there's no such thing as a free lunch? There's no such %% thing as easy money, either... These plots allow you to indulge your criminal %% tendencies at the cost of increased peril. %% %% Complete all 5 choices to unlock the Pirate God ending. %% Choice 21 name desc desig requires <(P:--|P:CRIME|P:PRIVA) -!Ex> % E1 is the city for the next episode. Element1 Prompt Reply_1 CReply_1 Reply_2 CReply_2 Alert Choice Msg%id%01 %% %% ************************************** %% *** SPECIALS (BUT I WANNA!) *** %% ************************************** %% %% Choices the PC can make which steer the direction of the core story, but %% which don't neccessarily lead to any direct benefit. These options may be %% useful if you want to achieve one of the special endings, or if you just %% want to move the campaign in that particular direction. %% %% %% Choice S_PCF %% %% The PC is going to select an ally faction. %% Choice 101 name desc desig requires % E1 is the city for the next episode. % E2 is the faction to inform Element1 Element2 Prompt_1 <%name2% must be told about this.> Prompt_2 Reply <%name1%'s the place you want to go for that.> Alert Choice Msg%id%01 Choice 102 name desc desig % This choice is needed for the Peace & Love ending. Note the faction % restrictions below- these will match the faction restrictions of the % ending. requires <(P:MILIT|P:POLIT|P:POLIC|P:CRIHN) (F:MILIT|F:POLIT|F:CRIHN|F:REDMA) :DEF_4 -:OFF_3 -!Ex -!Ne -!Lo> % E1 is the city for the next episode. Element1 Prompt_1 Prompt_2 Reply Alert Choice Msg%id%01 %% %% ******************* %% *** ENDINGS *** %% ******************* %% %% At the end of the game you get to make one final choice. Choice 2001 name desc desig requires % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 Choice 2002 name desc desig %% Note the faction restrictions below- these must match the "Negotiate %% for Peace" requirements above. requires % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 GH2/series/MEGA_LearnSecret.txt0000644000175000017500000000653311365256063015145 0ustar kaolkaol%% %% *:LearnSecretAboutNPC Content %% &CommonKnowledge = The secret you're looking for isn't much of a secret. %% &Incriminating = The secret will incriminate E2 %% %% The PC will have a chance to learn something about this particular NPC. %% There's no guarantee that this will work. %% %% If this subplot is won, the following trigger must be set: %% .%id%_%plotid%_GoLearnSecret %% %% PARAM1: The Secret %% PARAM2: The NPC %% PARAM3: The city where this subplot is to happen %% Content name desc requires <*:LearnSecretAboutNPC ~&CommonKnowledge> Size 1 % E1 is the secret % E2 is the NPC whom it involves % E3 is the city % E4 is a local public scene % E5 is a person who can tell the secret Element4 Element5 NeverFail5 % FAIL CONDITIONS: % - E5 dies end sub Persona 5 rumor%id% <%name5% knows something about %name2%.> greeting *GoGreet <*IWantToTalkAboutNPC %2% GoInform> GoInform Msg1 end Content name desc requires <*:LearnSecretAboutNPC> Size 2 % E1 is the secret % E2 is the NPC whom it involves % E3 is the city % E4 is a local public scene % E5 is a person who can tell the secret Element4 Element5 NeverFail5 % FAIL CONDITIONS: % - E5 dies start sub Persona 5 rumor%id% <%name5% can tell you something about %name2%.> % V1 = Information Cost % V2 = Got the Discount greeting GoChat *GoGreet <*IHaveInformation&AboutNPC GoCalc %2%> GoCalc GoInform GoFFFail result1 *result2 <*HurryBackWithMoney> result3 *result4 <*YourLossSucker GoCancelSubplot> GoCancelSubplot Msg1 Msg2 Msg3 Msg4 Prompt1 CPrompt1 Prompt2 Prompt3 CPrompt3 Prompt4 end GH2/series/QUEST_Unique_L5PAT.txt0000644000175000017500000033274711365256063015312 0ustar kaolkaol%% %% *:Q_UNIQUE_[city designation]_[identifier] Content %% *Q_UNIQUE_[city designation]_[identifier] Content %% %% For guaranteed quests. Some of these will have their own PlotIDs, while %% others will use the parent PlotID. %% Content name desc requires <*Q_UNIQUE_THESP_Rung4> % For the final level, the PC will meet the gardener behind Theles Spinner and % be given a choice that could end the game. % E1 is the third level. This is needed to open up the pathways after finishing % the fourth level. % E2 is the fourth level itself. % E3 is the gardener. % E4 is the meme Element3 Place3 <2 (Citizens) SD> Element4 sub Persona 3 special % V1 = Have been told to get in the pod. greeting GoFirstTime result1 result2 result3 result4 result5 result6 result7 result8 result9 result10 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 <%name3% offered you a position as technocrat of Theles Spinner, as long as you don't mind spending the rest of your life in a tube.> Msg11 Msg12 Msg13 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 Prompt10 MetaScene 2 % L1 = Initialization Counter % L2 = Can use elevator % L3 = Can use biotank % L4 = Have killed Gardener Faint%3% Surrender%3% start GoBeenBefore Msg1 Msg2 Msg3 Msg4 <%name4% crumples over and dies.> Msg5 sub rect minimap < ... .2. ... > inv trapdoor MiniMapComponent 2 use GoAccessDenied Msg101 end end end inv NPC Soldier Biotech job job_desig Age -20 chardesc male CHAT_ATTACK_1 CHAT_ATTACK_2 CHAT_SAFE_1 CHAT_SAFE_2 Meme MaxMemeViews 10 Msg_1 Msg_2 Msg_10 CMsg_10 Msg_11 CMsg_11 Msg_12 CMsg_12 Msg_13 Meme MaxMemeViews 3 Msg Msg_1 CMsg_1 Msg_2 CMsg_2 Msg_3 CMsg_3 Msg_4 CMsg_4 Msg_5 <> CMsg_5 Msg_6 <> CMsg_6 Msg_7 CMsg_7 Msg_8 CMsg_8 NPC Cook chardesc young SetFaction 2 Food 50 name desc FoodMorale 5 Mass -8 FoodXP:Vitality FoodXPAmount 100 end Content name requires <*Q_UNIQUE_GAOSP_NewResort> %% %% Parshall's plan is to build a hot springs resort and casino on an %% asteroid just outside the station. Unfortunately, the construction %% has stalled. %% % Param1 is Parshall himself % E2 is the construction site % E3 is Parshall's sister, Ritzi % E4 is the casino % E5 is the spa "bubble" % E6 is the exterior scene % E7 is the success meme Element2 Place2 <6> Element3 Place3 <2 (Guards) sd> Element4 Place4 <2> Element5 Place5 <4> Element6 Element7 % P%id%01 = Initialization Counter update end % SubPlot1 is the construction problem proper. SubPlot1 <*:Q_ConstructionProblem 1 2> sub Persona 1 greeting .%id%_GoBigPrize .%id%_mecha .%id%_GoPassBuck result%id%01 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Persona 3 % V1 = Have spoken before greeting GoCheckProgress GoChat GoFirstTime result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 result%id%09 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%03 <[sigh] It's about bloody time that idiot sent someone. They're having some kind of problem on the upper levels. It was supposed to be finished by now, but it's not, and someone deserves to get fired.> Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 <%name1% sent me to check up on things.> Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%08 Prompt%id%09 STC QS_ConstructionSite name <%name1%'s Project> entrance <*QUEST-INACTIVE> SetID 2 RockyTiles sub room minimap <......&&&..&1&...........> inv Elevator name MiniMapComponent 1 use GoNotYet Msg2 desig end end MetaScene 4 name type special Tolerance 10 ClubMap PalaceParkTiles MapWidth 31 MapHeight 26 start Msg1 NeededCells 3 Content1 Content1 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 Boxroom desig Content Content2 Content3 sub Room sub StairsDown Destination -1 end end room minimap <############1###...##...#> inv Elevator MiniMapComponent 1 desig end end MetaScene 5 name type special ArenaMap MapWidth 30 MapHeight 30 PalaceParkTiles WallType 26 GapFill <-4> % V1 = First Time Visit Counter start GoBeenBefore TMOVE1 Msg1 Msg2 Content Content2 sub team 1 team 2 name SetAlly 1 5 Passive team 3 name SetAlly 2 5 team 5 name type SetAlly 2 3 Stat 2 1 Lake Width 15 Height 15 rect Width 5 Height 5 minimap < ... &1& ### > inv Elevator MiniMapComponent 1 destination -1 end end inv %% Steam for the spa STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 STC SMOKE-2 end end inv NPC Shopkeeper name job SDL_Portrait SDL_Colors <67 73 100 255 244 208 113 175 98> Combatant Intimidation Age 8 chardesc female Sociable Melancholy Easygoing Pragmatic statline 9 8 13 11 9 12 12 15 Meme MaxMemeViews 3 MemeTimeLimit Msg_1 CMsg Msg_2 Msg_3 Msg_4 Msg_5 Msg_6 end Content name requires <*:Q_UNIQUE_GAOSP_Parshall> %% %% Gaos Spinner isn't nearly so ritzy as it once was. Parshall will hire %% the PC to go spice up a party. %% % E1 is the Prince Hotel % E2 is Parshall % E3 is the Prince Ballroom Element1 Element2 Place2 <1 (Citizens) pass> Element3 Place3 <1> % P%id%01 = Have accepted mission end % SubPlot1 = Building the Resort SubPlot1 <*Q_UNIQUE_GAOSP_NewResort #70 2> sub Persona 2 rumor%id% % V1 = Have chatted before (original dialog) greeting *.%id%_GoOfferMission <*INeedYourHelp&PCSpecifically .%id%_GoMissionBriefing .%id%_GoHasslePC> .%id%_GoHasslePC .%id%_GoMissionBriefing result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 result%id%09 result%id%10 result%id%11 result%id%12 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 <%name2% asked you to attend a party at the Prince Ballroom. He's trying to rebrand Gaos Spinner as a hip, exciting tourist destination.> Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Msg%id%12 Msg%id%13 Msg%id%14 <%name2% asked you to check up on the construction of his new attraction in Gaos Spinner Exterior.> Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%08 Prompt%id%09 CPrompt%id%09 Prompt%id%10 CPrompt%id%10 Prompt%id%11 CPrompt%id%11 Prompt%id%12 CPrompt%id%12 %% The original persona for Parshall, sequestered off down here. GoChat GoFirstTime result1 Msg1 Msg1_1 Msg1_2 Msg1_3 Msg1_4 Msg1_5 Msg1_6 Msg1_7 Msg1_8 CMsg1_8 Msg2 Msg3 Prompt1 MetaScene 2 sub room MFX 2 MFY 2 Width 5 Height 5 name minimap <...#..1.#.---#...........> desig room minimap <###########&2&##...##...#> inv Elevator name desig use GoNoUse Msg2 MiniMapComponent 2 end end MetaScene 3 name type mapwidth 21 mapheight 21 PalaceParkTiles BoxMap special NeededCells 5 Applause start 5min Msg1 Msg2_1 Msg2_2 Msg2_3 Msg2_4 Msg2_5 Content1 Content2 Content3 sub team 1 team 2 name SetAlly 3 Passive team 3 name SetAlly 2 end end inv NPC Shopkeeper name job SDL_Portrait SDL_Colors <67 73 100 255 244 208 113 175 98> Age 14 chardesc male Sociable Melancholy Easygoing Lawful statline 11 10 11 9 10 13 11 10 end Content name requires <*Q_UNIQUE_GAOSP_Galena> %% %% This subplot is activated the first time the PC speaks to Galena Hodges. %% %% There are a lot of orphans in Gaos Spinner. There really ought to be an %% orphanage, but that's going to take money. %% % Param1 is the hospital % Param2 is Galena Hodges % E3 is one of the orphans % E4 is another of the orphans % E5 is the third, since mecha series orphans only come in threes Element3 Place3 <1 (Guards) sd ally> Element4 Place4 <1 (Citizens) pass ally> Element5 Place5 <1 (Citizens) pass ally> end % SubPlot1 is the search for cash SubPlot1 <*:Q_FindMoney.Charity 2> sub Persona 2 rumor0 greeting *GoCheckFirst <*NiceToMeetYou GoOfferQuest> GoOfferQuest *GoChat <*MISC_CHATTER> result1 result2 result3 result4 result5 result6 Msg1 Msg2 Msg3 <%name2% in %name1% needs money to build a new orphanage.> Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 Prompt6 CPrompt6 Persona 3 %% V1 = Have offered to aid PC greeting GoChat Msg1 Msg2 <%name3% thanked you for helping the orphans of Gaos Spinner.> MetaScene 3 sub Room minimap <5..1.....32........4##+##> desig inv STC BED-1 PDir 0 MiniMapComponent 5 STC BED-1 PDir 0 MiniMapComponent 2 STC BED-1 PDir 4 MiniMapComponent 3 STC BED-1 PDir 4 MiniMapComponent 4 end end end inv NPC Student Combatant Age -2 NPC Student Age -3 NPC Student Age -4 end Content name requires <*:Q_UNIQUE_XIASP_Venus> %% %% There's a Vesuvian trading ship at the spaceport. The ship is actually %% a living being; unfortunately, en route to L5 it developed an autoimmune %% disorder and its antibodies are now wreaking havoc on the engineering %% level. The PC will be hired to enter the ship and deactivate its defenses. %% % E1 is the spaceport % E2 is the first officer % E3 is the engineering level itself % E4 is the "magic pill" reward % E5 is the elevator Element1 Element2 Place2 Element3 Place3 <1> Element4 Place4 Element5 Place5 <1> %% P%id%01 = Initialization Counter %% P%id%02 = Have accepted quest %% P%id%03 = Have shut down defenses %% P%id%04 = Have destroyed neurocore update end sub Persona 2 rumor%id% greeting GoLowPrize GoCheckQuest GoChat *GoFirstTime <*INeedYourHelp&PCSpecifically GoBriefing GoRefuse> GoBriefing GoRefuse result1 result2 result3 result4 result5 result6 Msg1 Msg2 Msg3 Msg4 Msg5 <%name5% asked you to stop the malfunctioning defense system of the Vesuvian spaceship Zuryo Maru; \spr %2% mentioned something about the system being adaptative.> Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 MetaScene 5 sub Room minimap <########1####....+....#&&> desig special sub Door update GoCheckQuest end end MetaScene 3 name special type mapwidth 50 mapheight 50 MonkeyMap Ceiling LockedDoorChance 35 SecretDoorChance 25 OrganicTiles FloorType 1 % L1 = Deactivation counter % L2 = Monster initialization counter % L3 = First time counter start GoBeenBefore GoInitMonsters end 5Min GoMonsterUp nu3 msg1 msg2 msg3 msg4 Category SDL_PORTRAIT Fudge 1000000 Elevator name desig use GoNoUse CLUE_CodeBreaking MiniMapComponent 2 Msg1 Msg2 Msg3 end Content name requires <*Q_UNIQUE_XIASP_Matthias> %% Param1 is Matthias %% E2 is the exterior scene %% E3 is the pirate encounter %% E4 is the black market %% E5 is the pirate captain %% E6 is the pirate faction %% E7 is the Nomos Blade %% E8 is E5's mission %% E9 is the trucker being sought %% E10 is the Demios Blade Element2 Element3 Place3 <2> Element4 Element5 Place5 <4 (Guards) SD> Element6 Element7 Place7 Element8 Place8 <2> Element9 Place9 Element10 Place10 %% %% Matthias warns the player to watch out- there are pirates in the area. %% From this point onwards the PC may get attacked by pirates in Xianzai %% Station Exterior. Each time they are defeated, the PC can return to %% Matthias for a reward. Doing so resets the pirates for another attack %% the next day. %% %% When this quest is activated, a black market is also activated. If the %% player travels there they can meet a pirate captain. Drive away the %% captain and the mecha attacks stop immediately, and the PC can claim a %% special reward from Matthias. Alternatively, the player can do a mission %% for the pirate captain, which earns a different reward. %% %% P%id%01 = Initialization Counter %% P%id%02 = Pirate lances destroyed %% P%id%03 = Have eliminated E5 %% P%id%04 = Have accepted E5's mission %% FAIL CONDITIONS: %% - Matthias dies end %% Killing or forcing the surrender of the pirate captain will cause the %% pirates to retreat. Faint%5% Surrender%5% update Msg%id%01 Msg%id%02 Msg%id%03 %% SubPlot1 is the black market entrance SubPlot1 <*:Q_ENT_BlackMarket #25 4> sub Persona 1 %% V%id%02 = Rewards received greeting result%id%01 .%id%_mecha .%id%_GoSmallReward .%id%_GoSmallEnding result%id%02 %% Winning the quest gives 2 points of lawful, deletes the encounters, and if %% the PC agreed to do E5's quest turns E5 into an enemy. .%id%_GoWinQuest result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 <%name1% in Xianzai Spinner told you that there's a reward for fighting pirates outside of the colony.> Msg%id%04 Msg%id%05 Prompt%id%01 CPrompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Persona 5 special % V%id%01 = Have spoken first time greeting .%id%_mecha .%id%_GoCheckMission .%id%_GoGreet .%id%_GoChat .%id%_GoConfront result%id%01 result%id%02 result%id%03 *result%id%04 <*GoodBye> result%id%05 *result%id%06 <*InsultContest .%id%_GoR6Succeed .%id%_Go678Fail %threat%> .%id%_GoR6Succeed .%id%_Go678Fail result%id%07 result%id%08 result%id%09 result%id%10 result%id%11 result%id%12 .%id%_Go1112Fail .%id%_GoRunAway result%id%13 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Msg%id%12 Msg%id%13 Msg%id%14 Msg%id%15 <%name5% asked you to capture a freighter in Xianzai Spinner exterior in exchange for a share of the loot.> Msg%id%16 Msg%id%17 Msg%id%18 Msg%id%19 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%08 Prompt%id%09 Prompt%id%10 Prompt%id%11 Prompt%id%12 Prompt%id%13 Persona 9 special greeting .%id%_GoChat result1 result2 .%id%_mecha Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Prompt1 CPrompt1 Prompt2 STC QS_BlackMarket SetID 4 %% V11 = Have started fight with guards start GoGone end Msg1 Msg2 end inv Encounter name Special %% This encounter will attack once per day. If the PC defeats the pirates, then this encounter %% needs to be reset by Matthias before it will appear again. % V1 = Visibility Clock. If ComTime > V1 and V1 <> 0, encounter is active % V2 = Difficulcy Rating. Increases by one each time the PC defeats the pirates. update % Sets this encounter's orders to Passive GoSetOrders use ATTACK GoAutoAttack GoAvoidAttack GoStartCombat .nu1 .nu2 EncounterMove 50 Msg-1 Msg-2 .Msg1 NPC Pirate job chardesc Criminal Villainous Melee 10 name desc Speed 4 Acc 1 mass -8 type sub Gun 9 Name caliber <8mm self-propelled> Integral Mass -7 Range 5 Acc 2 Speed 4 magazine 20 sub Ammo 9 name caliber <8mm self-propelled> SDL_PORTRAIT type end end Encounter name <%name5%'s Mission> Special %% This encounter will attack once per day until defeated. % V1 = Visibility Clock. If ComTime > V1 and V1 <> 0, encounter is active update % Sets this encounter's orders to Passive GoSetOrders use ATTACK GoAutoAttack GoAvoidAttack GoStartCombat .nu1 .nu2 EncounterMove 50 Msg1 Msg3 Msg4 Msg5 Msg6 .Msg1 NPC Trucker Combatant Melee 10 name desc Speed 4 Acc 1 mass -8 type sub Gun 9 Name caliber <8mm self-propelled> Integral Mass -7 Range 5 Acc 2 Speed 4 magazine 20 sub Ammo 9 name caliber <8mm self-propelled> SDL_PORTRAIT type end end end Content name requires <*Q_UNIQUE_TOHSP_McCabe1> %% Param1 is McCabe %% Param2 is the new building under construction %% FAIL CONDITIONS: %% - McCabe dies update % SubPlot1 is the construction problem SubPlot1 <*:Q_ConstructionProblem 1 2> sub Persona 1 rumor%id% <%name1%'s reconstruction effort has hit a problem.> %% V%id%01 = Victory counter greeting GoCheckVictory .%id%_mektype GoCheckResult *GoCheckEnemy <*ENEMY_CHECK GoOfferQuest ChatNPCFac GoMakeEnemy> GoMakeEnemy *GoOfferQuest <*NiceToMeetYou GoExplainQuest> GoExplainQuest Result1 Result2 Result3 Result4 Result5 result6 result7 Result8 Msg1 Msg2 <%name1% is in charge of reconstruction in Tohru Spinner. Unfortunately, one of her projects has hit a problem.> Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Prompt1 Prompt2 CPrompt2 Prompt3 Prompt4 CPrompt4 Prompt5 Prompt6 Prompt7 Prompt8 end Content name requires <*:Q_Unique_CAYLE_Caves> %% Param1 is the Cayley Caves dungeon % Element2 is the ghost dog % Element3 is the watcher Element2 Place2 Element3 Place3 <1 (Exorg) SD enemy> % P%id%01 = Ghost dog counter % P%id%02 = Final level message counter % P%id%03 = Ghost dog likes the PC % Get on good terms with the ghost dog, and you get on good terms with the watcher. Not that % it's going to help much if you plan on visiting Cayley Core. start Msg%id%01 sub MetaScene 1 % The encounter with the ghost dog should happen within a half % hour of entering the caves. 5min Msg11 Persona 2 special greeting result1 GoFail result2 result3 Msg1 Msg2 Msg3 Msg4 Msg5 Prompt1 Prompt2 Prompt3 end inv NPC Citizen %% Just use a character blank, and fill it with our needed info name job SDL_PORTRAIT SDL_COLORS <172 225 175 172 225 175 255 255 80> age -20 statline 17 19 16 22 15 20 12 14 Monster Exorg Watcher end Content name requires <*:Q_Unique_CAYLE_Core> %% Param1 is the Cayley Core dungeon % Element2 is the laboratory % Element3 is the data slate % Element4 is a local public scene % Element5 is a specific faction % Element6 is the person searching for the data core % Element7 is a holder for the data core Element2 Place2 <1> Element3 Place3 <7> Element4 Element5 Element6 Place6 Element7 Place7 <2> % P%id%01 = Initialization Counter % P%id%02 = Have accepted E6's mission update end .%id%_GoCheckDeath Msg%id%01 sub Persona 6 rumor%id% <%name6% is looking for something from the bottom of the mine.> greeting GoChat *GoOfferQuest <*DoYouWantAJob&Chara GoBriefing> GoBriefing GoNotHere result1 result2 .fac result3 GoR3Warn result4 result5 result6 result7 result8 result9 result10 result11 result12 result13 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 <%name6% in %name4% asked you to recover a dataslate from a secret research station at the bottom of Old Wynter's Mine.> Msg13 Msg14 Msg15 Msg16 Msg17 Msg18 Msg19 Prompt1 Prompt2 CPrompt2 Prompt3 CPrompt3 Prompt4 Prompt5 <[Continue]> Prompt6 Prompt7 Prompt8 Prompt9 Prompt10 Prompt11 Prompt12 Prompt13 MetaScene 2 name %% Lab originally used by Jane Modo years ago type special BoxMap RockyTiles entrance <*GoDown> MapWidth 17 MapHeight 17 % V1 = First time entry Start GoBeenBefore Msg1 Msg2 content sub Team 1 Team 2 name Team 3 name SetAlly 2 Room minimap <......#&#..&#&..#1#......> inv Elevator name Msg1 Destination -1 MiniMapComponent 1 end room minimap <......&&&..&&&..&&&......> room minimap <......&&&..&&&..&&&......> room minimap <............1............> inv STC BIOTANK name MiniMapComponent 1 use CLUE_SCIENCE CLUE_INSIGHT Msg1 Msg2 end room minimap <............1............> inv STC BIOTANK name MiniMapComponent 1 use GoBeenBefore Msg1 Msg2 Msg3 Msg4 inv BeamGun 14 name desc CLUE_SCIENCE CLUE_INSIGHT Msg1 UsesReflexes Range 8 Acc 1 Recharge 3 mass -9 Biotech inv item Jade Disc end end end room minimap <............1............> inv STC BIOTANK name MiniMapComponent 1 use GoBeenBefore Msg1 Msg2 Msg3 Msg4 inv Shield 8 name desc CLUE_SCIENCE CLUE_INSIGHT Msg1 DefBonus 2 mass -7 Biotech AntiBeam sub EMelee 13 name type Recharge 3 UsesReflexes inv item Jade Disc end end end end end MetaScene 7 % Hold the bedroom, which holds the endtable, which holds the data slate sub Room minimap <...........321..###......> desig inv STC BED-1 MiniMapComponent 2 STC ENDTABLE-1 PDir 6 use MiniMapComponent 3 inv item Antiseptic Ointment item Super Glue item Biotech Lab Set end end end end inv item Mediabook name desc % V1 = Have overcome encryption use .fname CLUE_CODEBREAKING GoAlreadyBroken Msg1 Msg2 Msg3 Msg4 Msg5 STC ENDTABLE-1 PDir 6 use end GH2/series/RANCON_Dungeon.txt0000644000175000017500000010145511347637671014613 0ustar kaolkaol%% *TREASUREROOM_? CONTENT Content requires <*TREASUREROOM_WRECK !Ne> element1 minimap <............1............> inv STC Crate RandomLoot 2000 Loot_Category Loot_Factions end Content requires <*TREASUREROOM_WRECK !Lo> element1 minimap <............1............> inv STC Crate RandomLoot 8000 Loot_Category Loot_Factions end Content requires <*TREASUREROOM_WRECK !Md> element1 minimap <............1............> inv STC Crate RandomLoot 32000 Loot_Category Loot_Factions end Content requires <*TREASUREROOM_WRECK !Hi> element1 minimap <............1............> inv STC Crate RandomLoot 128000 Loot_Category Loot_Factions end Content requires <*TREASUREROOM_WRECK !Ex> element1 minimap <............1............> inv STC Crate RandomLoot 512000 Loot_Category Loot_Factions end %% *DUNGEON_THREAT CONTENT %% Dungeon threats. Little things to make life more dangerous for hapless adventurers. Content name requires <*DUNGEON_THREAT !Ne (Condemned|Sewer)> element1 teamdata1 start msg%id%01 inv Monster Giant Roach % Less dangerous than usual CloseCombat 2 Dodge 1 end Content name requires <*DUNGEON_THREAT !Lo (Condemned|Sewer)> element1 teamdata1 element2 teamdata2 element3 teamdata3 start msg%id%01 inv Monster Giant Roach Monster Giant Roach Monster Giant Roach end Content name requires <*DUNGEON_THREAT !Ne mine> element1 teamdata1 start msg%id%01 inv Monster Mining bot end Content name requires <*DUNGEON_THREAT !Lo mine> element1 teamdata1 element2 teamdata2 element3 teamdata3 start msg%id%01 inv Monster Mining bot Monster Mining bot Monster Guardbot end Content name requires <*DUNGEON_THREAT !Md mine> element1 teamdata1 start msg%id%01 inv Monster Forgebot end Content name requires <*DUNGEON_THREAT !Hi (mine|Factory)> element1 teamdata1 element2 teamdata2 element3 teamdata3 element4 teamdata4 element5 teamdata5 start msg%id%01 inv Monster Murder Machine inv item Golden Crown end Monster Guardbot Monster Guardbot Monster Guardbot Monster Guardbot end Content name requires <*DUNGEON_THREAT !Ex (mine|Factory)> minimap <.......47..213..65.......> element1 teamdata1 element2 teamdata2 element3 teamdata3 element4 teamdata4 element5 teamdata5 element6 teamdata6 element7 teamdata7 start msg%id%01 inv Monster Murder Machine inv item Jeweled Crown end Monster ForgeBot Monster ForgeBot Monster Guardbot Monster Guardbot Monster Androbot Monster Workbot end Content name requires <*DUNGEON_THREAT !Ne Asteroid> minimap <......1.....3.....2......> element1 teamdata1 element2 teamdata2 element3 start msg%id%01 inv Monster Silver Shil Monster Silver Shil STC Victim Use CLUE_MEDICINE CLUE_MYSTICISM Msg1 Msg2 Msg3 end Content name requires <*DUNGEON_THREAT !Lo Asteroid> element1 teamdata1 start msg%id%01 inv Monster Fungal Hunter end Content name requires <*DUNGEON_THREAT !Md Asteroid> minimap <......1.....3.....2......> element1 teamdata1 element2 teamdata2 start msg%id%01 inv Monster Scarlet Shil Monster Scarlet Shil end Content name requires <*DUNGEON_THREAT !Hi Asteroid> element1 teamdata1 element2 teamdata2 element3 teamdata3 start msg%id%01 inv Monster Space Squid end Content name requires <*DUNGEON_THREAT !Ne Cave> element1 teamdata1 element2 teamdata2 element3 teamdata3 start msg%id%01 inv Monster Icky Slime Monster Icky Slime Monster Icky Slime end Content name requires <*DUNGEON_THREAT !Lo Cave> element1 teamdata1 start msg%id%01 inv Monster Green Jelly end Content name requires <*DUNGEON_THREAT !Hi Cave> element1 teamdata1 element2 teamdata2 start msg%id%01 inv Monster Flying Polyp Monster Flying Polyp end Content name requires <*DUNGEON_THREAT !Ne SPACE> element1 teamdata1 start msg%id%01 inv Monster Armored Fungus % This armored fungus is slightly easier than a regular one. Dodge 1 Vitality 0 end Content name requires <*DUNGEON_THREAT !Lo SPACE> element1 teamdata1 element2 teamdata2 element3 teamdata3 start msg%id%01 inv Monster Fungal Sphere Monster Fungal Sphere Monster Fungal Sphere end Content name requires <*DUNGEON_THREAT !Ne (Derelict|Industrial|Ruin)> element1 teamdata1 start msg%id%01 inv Monster Cleanbot name CloseCombat 4 Dodge 4 Vitality 4 SpotWeakness 2 end Content name requires <*DUNGEON_THREAT !Lo (Derelict|Industrial|Laboratory)> element1 teamdata1 start msg%id%01 inv Monster Guardbot end Content name requires <*DUNGEON_THREAT !Lo (Ruin|Cave) SPACE> element1 teamdata1 start msg%id%01 inv Monster Exorg Floater end Content name requires <*DUNGEON_THREAT !Ne GROUND> element1 teamdata1 start msg%id%01 inv Monster Plague Rat end Content name requires <*DUNGEON_THREAT !Lo GROUND> element1 teamdata1 element2 teamdata2 element3 teamdata3 element4 teamdata4 element5 teamdata5 element6 start msg%id%01 inv monster Rat monster Rat monster Rat monster Rat monster Death Rat Food 60 name mass -10 FoodMorale 2 Fudge 3000 desc end Content name requires <*DUNGEON_THREAT !Md ~EARTH> element1 teamdata1 start msg%id%01 inv monster Hunter-Killer end Content name requires <*DUNGEON_THREAT (!Hi|!Ex) ~EARTH> element1 teamdata1 start msg%id%01 inv monster Sentinel end %% *DUNGEON_REWARD CONTENT %% Dungeon treasure. Something that's good for everyone. Content name requires <*DUNGEON_REWARD !Ne (Ruin|Sewer)> element1 minimap <............1............> inv STC Crate RandomLoot 8000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Lo (Ruin|Sewer)> element1 minimap <............1............> inv STC Crate RandomLoot 30000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Md (Ruin|Sewer)> element1 minimap <............1............> inv STC Crate RandomLoot 150000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Hi (Ruin|Sewer)> element1 minimap <............1............> inv STC Crate RandomLoot 750000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Ex (Ruin|Sewer)> element1 minimap <............1............> inv STC Crate RandomLoot 3750000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Ne (Derelict|Building)> element1 minimap <............1............> inv STC Crate RandomLoot 8000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Lo (Derelict|Building)> element1 minimap <............1............> inv STC Crate RandomLoot 30000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Md (Derelict|Building)> element1 minimap <............1............> inv STC Crate RandomLoot 150000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Hi (Derelict|Building)> element1 minimap <............1............> inv STC Crate RandomLoot 750000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Ex (Derelict|Building)> element1 minimap <............1............> inv STC Crate RandomLoot 3750000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Ne (Mine|Cave)> element1 minimap <............1............> inv STC Crate RandomLoot 8000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Lo (Mine|Cave)> element1 minimap <............1............> inv STC Crate RandomLoot 30000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Md (Mine|Cave)> element1 minimap <............1............> inv STC Crate RandomLoot 150000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Hi (Mine|Cave)> element1 minimap <............1............> inv STC Crate RandomLoot 750000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD !Ex (Mine|Cave)> element1 minimap <............1............> inv STC Crate RandomLoot 3750000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_REWARD (Mine|derelict|ruin) Space ~!Md> minimap <......###..#1=..###......> element1 inv STC CRATE inv BodyArmor 2 name desc Mass -3 Sealed ArmArmor 2 name desc Mass -3 Sealed LegArmor 2 name desc Mass -3 Sealed ArmArmor 2 name desc Mass -3 Sealed LegArmor 2 name desc Mass -3 Sealed HeadArmor 2 name desc Mass -1 Sealed Phone PowerSource 8 name end end Content name requires <*DUNGEON_REWARD Mine (!Ne|!Lo)> element1 inv Item Pick end Content name requires <*DUNGEON_REWARD Mine (!Lo|!Md)> element1 inv EMelee 20 Name Type Mass 14 Acc -1 Speed 1 desc inv PowerSource 4 mass -1 name end end %% *DUNGEON_DECOR CONTENT %% Dungeon Extras. Little bits of personality for dungeon levels, or things which aren't %% usable by all characters, or reward/threat combos. Content name requires <*DUNGEON_DECOR (cave|sewer) (!Hi|!Ex)> element1 minimap < ... .......1....... ... > inv STC MUSHROOMPATCH-2 % V1 = Have extracted mushroom CLUE_SURVIVAL .mushroom GoBeenBefore GoFail Msg1 Msg2 Msg3 end Content name requires <*DUNGEON_DECOR (sewer|condemned)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE name use GoMakeSick GoOpen Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 end Content name requires <*DUNGEON_DECOR !Ne (sewer|condemned)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE name % V1 = Have "unlocked" the goodies use GoMakeSick GoOpen Msg1 Msg2 Msg3 Msg4 Msg5 RandomLoot 9000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Lo (sewer|condemned)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE name % V1 = Have "unlocked" the goodies use GoMakeSick GoOpen Msg1 Msg2 Msg3 Msg4 Msg5 RandomLoot 33000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Md (sewer|condemned)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE name % V1 = Have "unlocked" the goodies use GoMakeSick GoOpen Msg1 Msg2 Msg3 Msg4 Msg5 RandomLoot 160000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Hi (sewer|condemned)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE name % V1 = Have "unlocked" the goodies use GoMakeSick GoOpen Msg1 Msg2 Msg3 Msg4 Msg5 RandomLoot 810000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Ex (sewer|condemned)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE name % V1 = Have "unlocked" the goodies use GoMakeSick GoOpen Msg1 Msg2 Msg3 Msg4 Msg5 RandomLoot 4050000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Ne (asteroid|mine|cave)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE % V1 = Have "unlocked" the rock use GoBlocked CLUE_SCIENCE GoOpen GoSciFail Msg1 Msg2 Msg3 RandomLoot 10000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Lo (asteroid|mine|cave)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE % V1 = Have "unlocked" the rock use GoBlocked CLUE_SCIENCE GoOpen GoSciFail Msg1 Msg2 Msg3 RandomLoot 36000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Md (asteroid|mine|cave)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE % V1 = Have "unlocked" the rock use GoBlocked CLUE_SCIENCE GoOpen GoSciFail Msg1 Msg2 Msg3 RandomLoot 180000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Hi (asteroid|mine|cave)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE % V1 = Have "unlocked" the rock use GoBlocked CLUE_SCIENCE GoOpen GoSciFail Msg1 Msg2 Msg3 RandomLoot 900000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Ex (asteroid|mine|cave)> element1 minimap < ... .......1....... ... > inv STC RUBBLE-INDESTRUCTIBLE % V1 = Have "unlocked" the rock use GoBlocked CLUE_SCIENCE GoOpen GoSciFail Msg1 Msg2 Msg3 RandomLoot 4500000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Lo (Building|Sewer) -SPACE> element1 teamdata1 element2 element3 element4 minimap <......12....34..###......> start msg%id%01 inv NPC Soldier job Renown 20 update STC CRATE-AMMO PDir 5 use RandomLoot 25000 Loot_Category Loot_Factions STC BED-1 PDir 6 STC ENDTABLE-1 PDir 6 use RandomLoot 12000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Md (Building|Sewer) -SPACE> element1 teamdata1 element2 element3 element4 minimap <......12....34..###......> start msg%id%01 inv NPC Soldier job Renown 45 update STC CRATE-AMMO PDir 5 use RandomLoot 90000 Loot_Category Loot_Factions STC BED-1 PDir 6 STC ENDTABLE-1 PDir 6 use RandomLoot 10000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR !Hi (Building|Sewer) -SPACE> element1 teamdata1 element2 element3 element4 minimap <......12....34..###......> start msg%id%01 inv NPC Soldier job Renown 70 update STC CRATE-AMMO PDir 5 use RandomLoot 250000 Loot_Category Loot_Factions STC BED-1 PDir 6 STC ENDTABLE-1 PDir 6 use RandomLoot 100000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_DECOR (Derelict|Laboratory)> minimap <............1............> element1 inv STC COMPUTER-1 use CLUE_CODEBREAKING CLUE_REPAIR CLUE_SCIENCE .parts GoBeenBefore msg1 msg2 msg3 end Content name requires <*DUNGEON_DECOR (Ruin|Laboratory|Factory) ~!Lo ~!Md> minimap <......#1#..#2#..###......> element1 element2 Teamdata2 inv Door use GoRefuse GoUsed NPCOpenDoor < > CLUE_SCIENCE CLUE_INSIGHT GoSkillFailed Msg101 <[DEFENSE ROBOT RECHARGE STATION] Lift bar to activate defense robot.> Msg102 Msg103 Msg104 Msg105 Msg106 Msg107 Msg108 Monster Androbot end Content name requires <*DUNGEON_DECOR (Ruin|Laboratory|Factory) -!Ne ~!Hi ~!Ex> minimap <......#1#..#2#..###......> element1 element2 Teamdata2 inv Door use GoRefuse GoUsed NPCOpenDoor < > CLUE_SCIENCE CLUE_INSIGHT GoSkillFailed Msg101 <[DEFENSE ROBOT RECHARGE STATION] Lift bar to activate defense robot.> Msg102 Msg103 Msg104 Msg105 Msg106 Msg107 Msg108 Monster Androbot end Content name requires <*DUNGEON_DECOR !Lo (mine|cave)> element1 teamdata1 element2 teamdata2 element3 inv Monster Mining bot Monster Mining bot Treasure name Fudge 32000 Mass 1 end Content name requires <*DUNGEON_DECOR !Hi (mine|cave)> element1 teamdata1 element2 teamdata2 element3 inv Monster Forgebot Monster Forgebot Item Platinum Ingot mass 1 fudge 600000 end Content name requires <*DUNGEON_DECOR (Ruin|Derelict|Laboratory)> minimap < ... .###..#1+..###. ... > element1 start msg%id%01 inv STC COMPUTER-1 use CLUE_CODEBREAKING CLUE_SCIENCE CLUE_INSIGHT GoBeenBefore GoFail msg1 Msg2 Msg3 Msg4 end Content name requires <*DUNGEON_DECOR ~!Ne> minimap <.......#...###...#.......> Content name requires <*DUNGEON_DECOR> element1 start msg%id%01 inv STC SHRINE-1 end %% *DUNGEON_GOAL CONTENT %% - When sold, the dungeon goal content should be enough to buy a cheap level-appropriate mecha. %% !Ne = 1,000,000 %% !Lo = 2,000,000 %% !Md = 4,000,000 %% !Hi = 8,000,000 %% !Ex = 16,000,000 Content requires <*DUNGEON_GOAL !Ne> element1 minimap <............1............> inv STC Crate RandomLoot 1000000 Loot_Category Loot_Factions end Content requires <*DUNGEON_GOAL !Lo> element1 minimap <............1............> inv STC Crate RandomLoot 2000000 Loot_Category Loot_Factions end Content requires <*DUNGEON_GOAL !Md> element1 minimap <............1............> inv STC Crate RandomLoot 4000000 Loot_Category Loot_Factions end Content requires <*DUNGEON_GOAL !Hi> element1 minimap <............1............> inv STC Crate RandomLoot 8000000 Loot_Category Loot_Factions end Content requires <*DUNGEON_GOAL !Ex> element1 minimap <............1............> inv STC Crate RandomLoot 16000000 Loot_Category Loot_Factions end Content name requires <*DUNGEON_GOAL (Mine|Cave) !Ne> minimap < ... .......1....... ... > % E1 is the nugget. element1 inv Treasure name desc Fudge 1200000 mass 7 end Content name requires <*DUNGEON_GOAL (Mine|Cave) !Lo> minimap < ... .......1....... ... > % E1 is the nugget. element1 inv Treasure name desc Fudge 2500000 mass 18 end GH2/series/QUEST_MissionResults.txt0000644000175000017500000004722411326004537016125 0ustar kaolkaol%% %% ******************************** %% *** A NOTE ON REWARDS *** %% ******************************** %% %% Quest rewards should be much greater than regular plot rewards. Here %% are some suggestions: %% - 500% cash prize %% - A new mecha %% - A new lancemate %% - NPC becomes a skill trainer, opens a custom shop, or otherwise provides %% a permanent benefit to the PC %% %% If providing a permanent bonus, also leave a history note to remind the %% player of what's available. %% %% %% *Q_Win_OneShotReward %% .nocash This will not be a cash reward. %% %% The PC will be given a one-time reward. %% This component will end its quest thread. %% %% PARAM1: The NPC who provided the task %% Persona 1 should include a .%id%_GoInit script. %% Content name desc requires <*Q_Win_OneShotReward (.nocash|1:ACADE) ~1:ACADE ~1:MEDIA ~1:FAITH ~1:LABOR Spinner> % E1 is the NPC % E2 is Aigaion % E3 is the dungeon % E4 is Aigaion's home scene % E5 is the environs scene % E6 is the dungeon entrance Element2 Place2 <4 (Monsters) sd enemy> Element3 Place3 <5> Element4 Place4 <3> Element5 Element6 Place6 <5> %% FAIL CONDITIONS: %% - E1 dies start sub Persona 1 greeting .%id%_GoInit result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 <%name1% in \SCENE RootSceneID told you about the space monster Aigaion.> Msg%id%03 Msg%id%04 Msg%id%05 Prompt%id%01 Prompt%id%02 Prompt%id%03 STC QS_Dungeon_TreasureWreck entrance <*QUEST-INACTIVE> SetID 3 end sub room ForGoalLevel minimap <......###..###..#2#......> inv STC AIRLOCK-1 desig use MiniMapComponent 2 Msg2 Msg3 Msg4 end end Metascene 4 2 name SpaceMap SpaceScroll Microgravity Vacuum SpaceBackdrop MapWidth 50 MapHeight 50 % L1 = Init % L2 = Dungeon entrance % L3 = Victory Counter % L4 = Have fought before Start GoBeenBefore nu1 nu2 .fac Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 ParaX 7 ParaY 7 Team 2 name SetEnemy 1 ParaX 23 ParaY 23 end end inv Arch Aigaion % Aigaion is a composite robot- it collects parts from the spaceships and mecha it captures, % then adds them to itself. In this way it's not too much different from the average GearHead PC. Scale 2 rumor0 job Metal roguechar sdl_sprite sdl_colors <201 205 229 157 172 183 208 34 51> Statline 10 32 11 13 19 14 17 2 sealed CloseCombat RangedCombat Dodge Vitality Initiative ElectronicWarfare Toughness sub torso armor 5 sub ECM 5 PowerSource 10 mass -10 SpaceFlight 5 end turret armor 4 sub BeamGun 16 name Recharge 1 type range 5 PowerSource 5 mass -5 SpaceFlight 5 end arm armor 5 sub hand inv STC PAR-6 end SpaceFlight 5 end arm armor 5 sub hand inv STC GR-12 end SpaceFlight 5 end arm armor 5 sub hand inv STC MAC-4 end SpaceFlight 5 end arm armor 5 sub hand inv melee 18 name Scale 2 type mass -6 end SpaceFlight 5 end end STC QUEST-MAPMARKER-STATIONARY name <%name3%> desig end Content name desc requires <*Q_Win_OneShotReward ~1:TRADE -.nocash> special % E1 is the NPC. %% FAIL CONDITIONS: %% - E1 dies start sub Persona 1 greeting .%id%_GoInit Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 end Content name requires <*Q_Win_OneShotReward ~1:MILIT ~1:ADVEN ~1:CRAFT> special % E1 is the NPC %% FAIL CONDITIONS: %% - E1 dies start sub Persona 1 greeting .%id%_GoInit .%id%_meks Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 end %% %% *Q_WinTask %% %% The PC has completed some task (not a formal mission) for this NPC. %% Give a reward. %% %% PARAM1: The NPC who provided the task. %% Content name desc requires <*Q_WinTask> % E1 is the NPC update SubPlot1 <*Q_Win_OneShotReward 1> Content name desc requires <*Q_WinTask 1:ACADE> % E1 is the NPC %% FAIL CONDITIONS: %% - E1 dies start sub Persona 1 Greeting .%id%_GoStudy *.%id%_GoBlowOff <*BrushOff> .%id%_GoNoEnemy *.%id%_GoBye <*GOODBYE> result%id%01 .%id%_skills <9 13 14 21 22> result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 end Content name desc requires <*Q_WinTask 1:FAITH> % E1 is the NPC %% FAIL CONDITIONS: %% - E1 dies start sub Persona 1 Greeting .%id%_GoStudy *.%id%_GoBlowOff <*BrushOff> .%id%_GoNoEnemy *.%id%_GoBye <*GOODBYE> result%id%01 result%id%02 .%id%_mecha result%id%03 .%id%_skills <2 5 8 9 24> result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 <%name1% in \SCENE RootSceneID offered to teach you many things.> Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 end %% %% *Q_Win_CatchNPC %% %% The PC has captured an NPC, and is now due for a reward. %% %% PARAM1: The NPC who provided the task. %% PARAM2: The item being fetched %% Content name %% The PC will be given a reward and that's it. requires <*Q_Win_CatchNPC> special %% E1 is the character to give the reward. %% E2 is the character who's been defeated. sub Persona 1 greeting Msg%id%01_10 <%name2% is now in custody. Thanks, \PC ... This wouldn't have been possible without you.> CMsg%id%01_10 Msg%id%01_20 <%name2% won't be a threat any more. Thanks, \PC ... This wouldn't have been possible without you.> end %% %% *Q_Win_FetchItem %% SubTypes: %% .secret The item contains a secret that must be preserved %% %% The PC was sent to fetch an item, and succeeded. %% Of course the item might not make it back to this NPC... %% %% PARAM1: The NPC who provided the task. %% PARAM2: The item being fetched %% Content name %% The PC will gain a new teacher. requires <*Q_Win_FetchItem (1:MILIT|1:ADVEN) -.secret> special %% E1 is the character to give the reward. %% E2 is the item that was given. start sub Persona 1 Greeting .%id%_GoFail .%id%_GoStudy *.%id%_GoBlowOff <*BrushOff> .%id%_GoNoEnemy *.%id%_GoComeHere <*NotByPhone> *.%id%_GoBye <*GOODBYE> result%id%01 .%id%_skills <1 2 3 4 5 6> result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 <%name1% offered to become your trainer after you recovered the %name2%.> Msg%id%07 Msg%id%08 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 end Content name desc requires <*Q_Win_FetchItem> special % E1 is the NPC % E2 is the item start % SubPlot1 is the reward SubPlot1 <*Q_Win_OneShotReward 1> sub Persona 1 greeting *.%id%_GoComeHere <*NotByPhone> result%id%01 result%id%02 result%id%03 *.%id%_GoLose <*YouLostItem> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 end Content name desc requires <*Q_Win_FetchItem> special % E1 is the NPC % E2 is the item start sub Persona 1 greeting *.%id%_GoComeHere <*NotByPhone> result%id%01 *result%id%02 <*MissionWasSuccess&Reward .%id%_GoEnd na na> .%id%_GoEnd result%id%03 *.%id%_GoLose <*YouLostItem> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 end %% %% *Q_FetchWantedItem %% %% The PC has obtained an item that someone else wants. %% This subplot will allow the PC to hand over said item %% for a reward. %% %% PARAM1: The NPC who provided the task. %% PARAM2: The item being fetched %% Content name desc requires <*Q_FetchWantedItem> special % E1 is the NPC % E2 is the item start .%id%_GoCheckItem sub Persona 1 rumor%id% <%name1% really wants the %name2%.> greeting *.%id%_GoComeHere <*NotByPhone> *result%id%01 <*MissionWasSuccess&Reward .%id%_GoEnd na na> .%id%_GoEnd result%id%02 *.%id%_GoKeep <*PCWillKeepItem %4%> result%id%03 *.%id%_GoLose <*YouLostItem> Msg%id%01 Msg%id%02 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 end %% %% *Q_LoseTask %% %% The PC was sent to do something, but failed. Call them a big %% stupid loser and end the plot thread. %% %% PARAM1: The NPC who provided the task. %% Content name desc requires <*Q_LoseTask> special % E1 is the NPC. start sub Persona 1 greeting *.%id%_GoReportLoss <*MissionWasFailure .%id%_GoEnd na na> .%id%_GoEnd end %% DEBUGGING FRAMES Content name requires <*:Q_Debug> desc % E1 is the person who will be giving the mission. % E2 is the CavClub Element1 Place1 <2 (Citizens) ally pass> Element2 % SubQuest1 is the mission. SubPlot1 <*Q_DWin_OneShotReward 1> sub Persona 1 rumor0 <%name1% needs a cavalier for a mission of some kind.> greeting GoChat MetaScene 1 sub room desig minimap <#&&&#&...&&.1.&&...&&&-&&> end end GH2/series/PLOT_CORE_Defense.txt0000644000175000017500000000555411333736445015170 0ustar kaolkaol%% %% Lead-ins for *CORE_DEF_ missions. %% Plot name desc requires <*CORE_DEF_ ~C:MEDIC (L:ALLY|P:--) -L:ENEMY> % E1 is the metascene where this will take place % E2 is the character who recommends the PC for the mission % E3 is the scene for the real mission-giver % E4 is the real mission-giver Element1 Element2 Place2 <1 (Guards) SD ally> element3 element4 Place4 % If E2 dies before giving the mission, just move straight on to the % conversation. end Msg1 <%name4%@ \SCENE &EpisodeScene :// I have some work for you. Come see me at %name3%.> % SubPlot1 is the conversation with E4 SubPlot1 <*CS_Conversation 4> sub MetaScene 1 WarzoneMap MapWidth 50 MapHeight 50 special start GoBeenBefore Msg1 Msg2 Msg3 content sub Team 1 SetAlly 2 3 ParaX 5 ParaY 5 Team 2 name SetAlly 1 3 Passive Team 3 name home SetAlly 1 2 Team 4 name SetEnemy 1 rect name desig end Persona 2 rumor0 <%name2% is leading the relief efforts at %name1%.> special greeting GoChat *GoGreet <*CS_WarZone_Greeting GoOfferHelp GoNoOffer> GoOfferHelp GoNoOffer Msg1 Msg1_10 <> CMsg1_10 Msg1_20 <> CMsg1_20 Msg2 <%name2% at the %name1% cleanup zone suggested that you go to %name3% and ask %name4% about a mission.> Msg3 <%name2% suggested that you ask %name4% for a mission.> Msg4 Msg4_10 <> CMsg4_10 Msg4_20 <> CMsg4_20 end GH2/series/WMON_people.txt0000644000175000017500000001515611365256063014272 0ustar kaolkaol% ******************************* % *** PEOPLE MONSTER LIST *** % ******************************* % These are humans who will show up as wandering monsters. % There are two broad types- CRIMINALs and GUARDs. % STATS REF BOD SPD PER CFT EGO KNO CHA Arch HW Soldier statline 11 11 11 11 9 9 9 9 MonsterTV 70 roguechar sdl_sprite sdl_colors <120 90 90 255 212 195 244 216 28> type habitat <> RangedCombat 7 CloseCombat 7 Dodge 10 Sealed sub head armor 3 torso armor 3 arm armor 3 sub hand inv item Raketen Mk6 Bazooka end end arm armor 3 sub hand end inv item Riot Shield end leg armor 3 leg armor 3 end Arch Soldier statline 11 11 11 11 9 9 9 9 MonsterTV 62 roguechar sdl_sprite sdl_colors <90 120 90 255 212 195 244 216 28> type habitat <> RangedCombat 5 CloseCombat 5 Dodge 9 Sealed sub head armor 3 torso armor 3 arm armor 3 sub hand inv item FS Vaken Assault Rifle end end arm armor 3 sub hand end inv item Riot Shield end leg armor 3 leg armor 3 end Arch Security Guard statline 9 9 9 9 9 9 9 9 MonsterTV 48 roguechar sdl_sprite sdl_colors <90 90 100 255 212 195 244 216 28> type habitat <> CHAT_ATTACK1 CHAT_ATTACK2 RangedCombat 3 CloseCombat 3 Dodge 6 Sealed sub head torso arm sub hand inv item Laver-2 Combat Rifle end end arm sub hand end inv item Riot Shield end leg leg end Arch Watchman statline 9 9 9 9 9 9 9 9 MonsterTV 21 roguechar sdl_sprite sdl_colors <90 90 90 255 212 195 150 120 90> type habitat <> CHAT_ATTACK1 CHAT_ATTACK2 CHAT_ATTACK3 RangedCombat 2 CloseCombat 2 Dodge 3 Sealed sub head torso arm sub hand inv item Brash G1 Light Laser Pistol end end arm sub hand end leg leg end ARCH Assassin Lord roguechar MonsterTV 105 NoCorpse type habitat <> statline 15 14 14 15 11 13 12 10 SDL_SPRITE SDL_COLORS <36 46 30 200 159 180 70 30 81> explosion <10 CREATESTC GAS-1 Blast 1> explosion_desc <# dissolves in a cloud of toxic gas!> RangedCombat 6 CloseCombat 9 Dodge 15 Vitality 2 SpotWeakness 7 Sealed Talent 16 sub head armor 3 torso armor 3 arm armor 3 sub hand inv gun 5 name type range 4 mass -3 recharge 6 caliber <8mm pistol> sub ammo 5 magazine 10 caliber <8mm pistol> end end end arm armor 3 sub hand inv melee 22 name type scale 0 mass -15 end end leg armor 3 leg armor 3 end ARCH Assassin roguechar MonsterTV 80 NoCorpse type habitat <> SDL_SPRITE SDL_COLORS <34 37 30 200 159 180 80 28 28> statline 14 13 13 14 10 11 11 7 explosion <10 CREATESTC GAS-1 Blast 1> explosion_desc <# dissolves in a cloud of toxic gas!> RangedCombat 6 CloseCombat 8 Dodge 11 SpotWeakness 4 Talent 16 Sealed sub head armor 3 torso armor 3 arm armor 3 sub hand inv Gun 5 Name desig desc category factions caliber <5mm rifle> Mass -1 Range 10 Acc 1 Speed 3 sub Ammo 8 name <5mm Rifle Clip [Killer]> caliber <5mm rifle> type magazine 30 end end end arm armor 3 sub hand inv Melee 6 name type scale 0 mass -5 recharge 1 end end leg armor 3 leg armor 3 end ARCH Ravager statline 12 14 10 12 7 9 8 8 MonsterTV 52 roguechar type habitat <> CHAT_ATTACK RangedCombat 4 CloseCombat 6 Dodge 7 Vitality 2 sealed sub head armor 2 inv item Steel Mask end torso armor 2 arm armor 1 sub Hand inv item Shotgun end end arm armor 1 sub Hand end leg armor 1 leg armor 1 end Arch Street Punk statline 9 9 9 9 8 8 8 8 MonsterTV 41 roguechar

    type habitat <> SDL_SPRITE SDL_COLORS <56 26 81 170 112 90 168 133 230> CHAT_ATTACK1 CHAT_ATTACK2 CHAT_ATTACK3 RangedCombat 1 CloseCombat 1 Dodge 4 sub head torso arm sub hand inv item Shotgun end end arm sub hand end leg leg end Arch Street Punk statline 9 9 9 9 8 8 8 8 MonsterTV 25 roguechar

    SDL_SPRITE SDL_COLORS <56 26 81 170 112 90 66 121 80> type habitat <> CHAT_ATTACK1 CHAT_ATTACK2 CHAT_ATTACK3 RangedCombat 1 CloseCombat 1 Dodge 2 sub head torso arm sub hand inv Item Steel Pipe end end arm sub hand end leg leg end Arch Street Punk statline 9 9 9 9 8 8 8 8 MonsterTV 21 roguechar

    genepool SDL_SPRITE SDL_COLORS <208 34 51 234 180 88 244 216 28> CHAT_ATTACK MonsterTV 105 DomTarget 25 RangedCombat 8 CloseCombat 8 Dodge 15 Initiative 8 sub head sub melee 12 name type end torso sub BeamGun 8 name type range 4 PowerSource 20 name end arm name sub MLauncher 2 name type range 9 sub rockets 1 magazine 20 end end arm name sub MLauncher 2 name type range 9 sub rockets 1 magazine 20 end end leg leg end Arch Fire Penguin statline 13 8 14 14 5 7 2 12 type habitat <> SDL_SPRITE SDL_COLORS <166 47 32 230 230 230 244 215 28> roguechar

    genepool EvolveAt 12500 MonsterTV 40 DomTarget 15 RangedCombat 5 CloseCombat 5 Dodge 5 sub head sub melee 3 name type end torso arm name arm name leg leg end ARCH Fire Toad SDL_SPRITE SDL_COLORS <172 61 97 228 34 51 50 200 0> roguechar type habitat <> statline 10 17 9 10 2 2 2 2 *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrFlameGland 56 toad> MonsterTV 56 DomTarget 19 RangedCombat 3 CloseCombat 5 Dodge 5 SpotWeakness 6 Vitality 5 sub head armor 1 sub gear 4 0 7 name beamgun 6 name type range 6 Recharge 1 end torso armor 2 sub PowerSource 20 name end leg armor 1 name leg armor 1 name leg armor 1 name leg armor 1 name end ARCH Spike Toad SDL_SPRITE SDL_COLORS <101 72 42 245 200 170 34 216 244> roguechar type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 72 toad> statline 10 16 10 7 2 2 2 2 MonsterTV 72 DomTarget 23 CloseCombat 5 Dodge 6 SpotWeakness 5 Vitality 5 sub head armor 1 sub gear 4 0 6 name end torso armor 4 leg armor 3 name leg armor 3 name leg armor 4 name leg armor 4 name end ARCH Giant Frog SDL_SPRITE SDL_COLORS <45 85 80 112 127 98 244 216 35> roguechar type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 35 frog> statline 8 11 10 9 2 2 2 2 MonsterTV 35 DomTarget 14 CloseCombat 4 Dodge 3 SpotWeakness 1 Vitality 5 sub head sub gear 4 0 6 name end torso leg name leg name leg name leg name end ARCH Giant Leech SDL_SPRITE SDL_COLORS <140 105 81 136 141 101 130 143 114> roguechar type *CLUE_SURVIVAL <*SURVIVAL_GetMeatMaybeTainted 39 leech> habitat <> statline 7 8 3 4 1 1 1 1 MonsterTV 21 CloseCombat 2 Initiative 15 sub torso sub melee 1 name ACC 2 recharge 7 end end ARCH Marsh Lobster roguechar SDL_SPRITE SDL_COLORS <134 141 101 144 161 111 168 153 230> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetSashimi 35 lobster> statline 12 14 8 11 5 7 4 6 MonsterTV 35 DomTarget 15 CloseCombat 4 Dodge 3 Vitality 4 sub torso armor 2 arm armor 1 name arm armor 1 name end ARCH Rock Lobster roguechar SDL_SPRITE SDL_COLORS <150 112 89 80 80 85 234 180 88> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetSashimiOrLobsterShield 68 lobster> statline 13 20 9 11 5 7 4 5 MonsterTV 52 DomTarget 18 CloseCombat 4 Dodge 4 Vitality 8 sub torso armor 4 arm armor 4 name arm armor 4 name end ARCH Deep Lobster roguechar SDL_SPRITE SDL_COLORS <45 26 91 6 42 120 77 121 119> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetSashimiOrLobsterShield 85 lobster> statline 17 29 6 13 5 8 8 3 MonsterTV 85 DomTarget 21 RangedCombat 4 CloseCombat 4 Dodge 7 Vitality 12 sub torso armor 5 sub BeamGun 3 name Range 5 type PowerSource 20 name end arm armor 5 name type arm armor 5 name type end ARCH Archaeopteryx roguechar type habitat <> sdl_sprite sdl_colors <170 50 32 70 121 120 0 200 150> statline 10 2 11 7 2 3 2 2 *CLUE_SURVIVAL <*SURVIVAL_GetMeat 33 bird-thing> MonsterTV 22 CloseCombat 1 Dodge 4 DomTarget 16 sub head sub gear 3 0 1 name end torso wing wing leg leg tail end ARCH Velociraptor roguechar type habitat <> SDL_SPRITE SDL_COLORS <170 120 89 0 0 0 0 200 100> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 52 raptor> statline 14 13 16 11 8 7 2 5 DomTarget 23 MonsterTV 52 CloseCombat 4 Dodge 8 Vitality 2 sub head armor 1 torso armor 1 arm armor 1 arm armor 1 leg armor 1 sub gear 4 0 5 name end leg armor 1 sub gear 4 0 5 name end end ARCH Oviraptor roguechar type habitat <> SDL_SPRITE SDL_COLORS <146 161 101 0 0 0 214 216 28> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 25 raptor> statline 10 6 11 13 8 7 2 5 DomTarget 17 MonsterTV 25 CloseCombat 2 Initiative 2 Dodge 2 sub head armor 1 torso armor 1 arm armor 1 arm armor 1 leg armor 1 sub gear 4 0 3 name end leg armor 1 sub gear 4 0 3 name end end ARCH Iron Crab SDL_SPRITE SDL_COLORS <136 191 225 170 178 187 179 0 210> roguechar type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetSashimi 44 crab> statline 10 15 5 9 1 1 1 1 DomTarget 21 MonsterTV 44 CloseCombat 6 Dodge 2 Initiative 7 Vitality 6 SpotWeakness 5 sub torso armor 5 arm armor 5 sub gear 4 0 5 name end arm armor 5 sub gear 4 0 5 name end leg armor 5 leg armor 5 leg armor 5 leg armor 5 end ARCH Crab SDL_SPRITE SDL_COLORS <204 51 85 200 144 64 208 93 8> roguechar type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetSashimi 30 crab> statline 9 9 6 8 1 1 1 1 DomTarget 13 MonsterTV 30 CloseCombat 3 Initiative 4 Vitality 1 SpotWeakness 2 sub torso armor 3 arm armor 2 sub gear 4 0 3 name end arm armor 2 sub gear 4 0 3 name end leg armor 2 leg armor 2 leg armor 2 leg armor 2 end ARCH Dragon Viper SDL_SPRITE roguechar type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrDragonHeart 85 serpent> statline 15 16 16 11 4 19 2 2 MonsterTV 85 DomTarget 22 RangedCombat 6 CloseCombat 7 Dodge 10 Initiative 3 Toughness 7 sub head armor 4 sub gear 4 0 7 name type Recharge 5 Acc 2 BeamGun 8 name type BV 1 recharge 3 range 6 end torso armor 4 sub PowerSource 10 name end tail armor 4 end ARCH Swamp Viper SDL_SPRITE SDL_COLORS <84 97 85 191 205 229 244 206 48> roguechar type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatMaybeTainted 44 viper> statline 13 5 12 7 2 3 2 2 MonsterTV 44 DomTarget 21 CloseCombat 3 Dodge 5 sub head armor 2 sub Melee 6 name type Acc 1 end torso armor 2 tail armor 2 end ARCH Dragon Serpent SDL_SPRITE SDL_COLORS <36 46 22 200 50 0 56 28 81> genepool EvolveAt 20000 *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrDragonHeart 45 serpent> roguechar type habitat <> statline 12 13 12 5 2 16 2 2 MonsterTV 36 DomTarget 18 RangedCombat 1 CloseCombat 3 Dodge 1 Vitality 2 Toughness 3 sub head armor 1 sub BeamGun 4 name stat 1 4 stat 3 1 stat 4 2 Melee 5 name stat 3 5 end torso armor 1 sub PowerSource 20 name end tail armor 1 end ARCH Stinger roguechar SDL_SPRITE SDL_COLORS <170 150 235 122 88 199 0 0 0> statline 11 7 10 1 1 1 1 1 MonsterTV 64 CloseCombat 6 Dodge 6 Vitality 8 Toughness 4 type habitat <> sub torso sub melee 7 name Type melee 7 name Type Melee 7 name Type end end ARCH Dragon Rat roguechar SDL_SPRITE SDL_COLORS <55 153 93 174 68 42 221 0 132> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrDragonHeart 76 rat> statline 13 12 15 9 9 7 5 5 MonsterTV 76 DomTarget 20 CloseCombat 5 Dodge 11 Initiative 4 Vitality 10 Toughness 7 sub head armor 3 sub Melee 5 name stat 3 1 BeamGun 2 name type stat 1 2 stat 3 1 stat 4 4 end torso armor 3 sub PowerSource 20 name end leg armor 3 name sub gear 4 0 4 name end leg armor 3 name sub gear 4 0 4 name end leg armor 3 name leg armor 3 name tail armor 3 sub gear 4 0 5 name end end ARCH Death Rat roguechar SDL_SPRITE SDL_COLORS <119 123 178 234 195 0 217 103 126> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 56 rat> statline 11 7 14 8 8 6 2 2 MonsterTV 56 DomTarget 18 CloseCombat 5 Dodge 6 Initiative 2 sub head armor 1 sub gear 4 0 6 name end torso armor 1 leg armor 1 name sub gear 4 0 3 name stat 3 1 end leg armor 1 name sub gear 4 0 3 name stat 3 1 end leg armor 1 name leg armor 1 name tail armor 1 end ARCH Two Headed Rat roguechar SDL_SPRITE SDL_COLORS <174 68 42 234 195 0 217 123 166> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 30 rat> statline 10 4 10 8 8 6 2 2 MonsterTV 40 DomTarget 15 CloseCombat 3 Dodge 5 Initiative 6 Vitality 5 sub head sub gear 4 0 6 name end head sub gear 4 0 6 name end torso leg name leg name leg name leg name tail end ARCH Plague Rat roguechar SDL_SPRITE SDL_COLORS <167 144 59 110 133 62 217 132 217> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatMaybeTainted 35 rat> statline 10 3 10 8 8 6 2 2 MonsterTV 48 CloseCombat 4 Dodge 2 Initiative 1 Vitality 5 sub head sub Melee 6 name type recharge 1 end torso leg name type leg name type leg name type leg name type tail end ARCH Radioactive Rat roguechar SDL_SPRITE SDL_COLORS <65 174 42 65 174 42 220 250 26> type habitat <> explosion <8 DAMAGE 20 0 0 0 ArmorPiercing Brutal Haywire Blast 2> statline 8 1 9 8 6 5 2 2 MonsterTV 27 CloseCombat 3 Dodge 1 sub head sub Melee 3 name Acc 1 recharge 3 end torso leg name leg name leg name leg name tail end ARCH Thunder Rat roguechar SDL_SPRITE SDL_COLORS <234 195 0 125 120 110 217 123 166> CHAT_SAFE CHAT_SAFE2 genepool habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrSmallPelt 30 rat> statline 14 10 16 10 10 9 7 13 MonsterTV 68 DomTarget 30 RangedCombat 4 CloseCombat 3 Dodge 10 Initiative 5 Vitality 3 sub head armor 1 sub Melee 5 name end torso armor 1 sub BeamGun 9 name Range 8 Recharge 1 BV 5 type PowerSource 20 name end leg armor 1 name leg armor 1 name leg armor 1 name leg armor 1 name tail armor 1 sub BeamGun 7 name Range 6 Recharge 3 type end end ARCH Electric Rat roguechar genepool EvolveAt 3000 SDL_SPRITE SDL_COLORS <234 195 0 125 120 110 217 123 166> CHAT_SAFE CHAT_SAFE2 type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrSmallPelt 30 rat> statline 8 4 14 8 8 6 3 9 MonsterTV 35 DomTarget 10 RangedCombat 1 CloseCombat 2 Dodge 5 Vitality 1 sub head sub gear 4 0 1 name stat 3 1 end torso sub PowerSource 10 name end leg name leg name leg name leg name tail sub BeamGun 2 name Range 2 Recharge 3 type end end ARCH Rat roguechar SDL_SPRITE SDL_COLORS <174 68 42 174 68 42 217 123 166> *CLUE_SURVIVAL <*SURVIVAL_GetMeat 20 rat> type habitat <> statline 8 1 10 8 8 6 2 2 MonsterTV 20 DomTarget 12 CloseCombat 2 Dodge 3 Initiative 1 sub head sub gear 4 0 1 name end torso leg name leg name leg name leg name tail end GH2/series/PLOT_MOOD_Epidemic.txt0000644000175000017500000001403511401151245015317 0ustar kaolkaol%% %% *Epidemic Plots %% %% A virus/bacteria is on the loose and for whatever reason the health %% authorities are having trouble with it. Just by being in town the PC risks %% getting sick (maybe). %% %% The following script will add PC-infection to your epidemic: %% halfhour %% GoNotSealed %% GoGetSick %% Msg1 %% %% Mood Spec: %% - No Elements %% Plot name desc requires <*Epidemic> % E1 is the doctor. Element1 % V1 = Timer % FAIL CONDITIONS- % - E1 dies end start update sub Persona 1 rumor <%name1% could use some help dealing with the epidemic.> &PledgeAmount greeting result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 end Plot name desc requires <*Epidemic> % E1 is the character who's ill. Element1 % V1 = Timer % V2 = NPC's scene % V3 = Sneeze counter, you get one free warning % FAIL CONDITIONS- % - E1 dies end start GoCheckAwareness update % Check for a sneeze every 5 minutes. 5min GoSealed GoNotSealed GoGetSick Msg1_1 <%name1% coughs.> Msg1_2 <%name1% sneezes.> Msg1_3 <%name1% blows \PPR %1% nose.> Msg2 Msg2_1 Msg3 sub Persona 1 greeting result%id%01 result%id%02 result%id%03 .%id%_GoR3Fail Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 Msg%id%01_9 CMsg%id%01_9 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 Msg%id%05_1 Msg%id%05_2 Msg%id%06 <%name1% goes home.> Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 <[Treat Illness]> end GH2/series/PLOT_MOOD_Occupation.txt0000644000175000017500000000043311365256063015716 0ustar kaolkaol%% %% *Occupation Plots %% %% Mecha from a foreign power are in town to resist the actions of some other %% local faction. This is going to be unpopular with the locals. %% %% Mood Spec: %% E1 = The city itself %% E2 = The occupying power %% E3 = The local faction %% GH2/series/MEGA_SPAWN_CoreStoryEvents.txt0000644000175000017500000003247711365256063017032 0ustar kaolkaol%% %% Subplots needed by various core story events. These are plots which are %% spawned by the core story, but which are not part of the core story %% themselves. %% %% %% *CSE_DEF5_Occupation Content %% %% Some military force or another is occupying a city. Help the military to %% achieve their objective for an IronDefense merit badge, or resolve the %% situation peacefully for a MoralHighGround merit badge. %% %% The mood should already be deployed, and the commander should be placed %% somewhere accessible. %% %% PARAM1: The occupying faction %% PARAM2: The enemy faction %% PARAM3: The place being occupied %% PARAM4: The occupation commander %% PARAM5: The prevailing mood, to be terminated with the plot %% PARAM6: The scene where E4 is set up %% Content name desc requires <*CSE_DEF5_Occupation (2:REDMA|2:CRIHN|2:BOHEM)> % E1: The occupying faction % E2: The enemy faction % E3: The place being occupied % E4: The occupation commander % E5: The prevailing mood, to be terminated with the plot % E6: Peacekeeper HQ % E7: E4's assistant, to handle the beefgate % E8: The outdoor scene where the pirate is % E9: The Pirate % E10: The Pirate's encounter % E11: A corporate faction for the industrialist % E12: A local public scene % E13: The crooked corpo % E14: The secret that activates the Criminal Investigation subplot % E15: a bodyguard for E4 % E16: another bodyguard for E4 Element7 Place7 <6 (Guards) sd ally> Element8 Element9 Place9 <10 (Pirates) sd> Element10 Place10 <8> Element11 Element12 Element13 Place13 <12 (Citizens) pass> Element14 Element15 Place15 <6 (Guards) sd ally> Element16 Place16 <6 (Guards) sd ally> % P%id%01 = E9 has been defeated ( 1 = in fight, -1 = voluntarily ) % P%id%02 = Have made deal with E9 to remove E13 % P%id%03 = Initialization Counter % P%id%04 = E13 has been arrested %% FAIL CONDITIONS: %% - E4 dies end update %% Upon learning the secret, activate SubPlot3. .%id2%_%plotid2%_GoLearnSecret %% Upon destroying the factory, become an enemy of E11 .%id4%_%plotid4%_GoWin Msg%id%01 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 % SubPlot1 is the beefgate, activated by E4 % SubPlot2 is the secret about the pirates, also activated by E4 % SubPlot3 is the crooked corpo, activated by SubPlot2 % SubPlot4 is the destroy factory task, activated by E9 SubPlot1 <*:ReconBeefgate 7 8 10 2> SubPlot2 <*:LearnSecretAboutNPC&CommonKnowledge 14 13 3> SubPlot3 <*:CriminalInvestigation 13 3> SubPlot4 <*:TASK_DefeatFactory 8> sub Persona 4 % The peacekeeper commander % v%id%01 = Have given initial spiel. % v%id%02 = Have gotten reward for CriminalInvestigation greeting .%id%_GoGreet *result%id%01 <*GenericMissionReminder> result%id%02 .%id%_GoBestEnding .%id%_GoSoSoEnding .%id%_GoLeaveVoluntarily result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 .%id%_GoPirateEnding result%id%08 result%id%09 result%id%10 result%id%11 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09_1 CMsg%id%09_1 Msg%id%09_2 CMsg%id%09_2 Msg%id%10 Msg%id%11 Msg%id%12 Msg%id%13 Msg%id%14 Msg%id%15 Msg%id%16 Msg%id%17 <%name7% is the one in charge of finding %name9%'s base. Good luck, and keep me up to date on your progress.> Msg%id%18 Msg%id%19 <%name4% told you that %name1% are in %name3% on a peacekeeping mission. %name7% has been tasked with finding the pirate captain %name9%.> Prompt%id%01 Prompt%id%02 <%name9% has been defeated.> CPrompt%id%02 Prompt%id%03 <%name13% has broken the law.> CPrompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 CPrompt%id%07 Prompt%id%08 <%name9% is an honorable pilot, as are you.> CPrompt%id%08 Prompt%id%09 Prompt%id%10 Prompt%id%11 Persona 9 % The pirate captain special greeting .%id%_GoMakeADeal .%id%_GoStartCombat *result%id%01 <*THEME_EXPO&Enemy .%id%_GoStartCombat> result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 result%id%09 result%id%10 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 <%name13% won't bother you any more.> CPrompt%id%04 Prompt%id%05 CPrompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%08 <%name13%'s factory is toast.> CPrompt%id%08 Prompt%id%09 Prompt%id%10 MetaScene 10 2 % The pirate captain home scene. % L1 = Combat has finished % L2 = Combat has started MapWidth 50 MapHeight 50 Start nu1 nu2 end GoCheckE9Dead Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv NPC Mecha Pilot chardesc Young bio update NPC Pirate chardesc Heroic bio STC CORE-ACTIVATABLE name <%name9%'s lance> NPC Corporate Executive chardesc Villainous update Secret Msg <%name9% is loved in %name3% because \SPR %9% stands up to %name13%.> NPC Mecha Pilot update NPC Mecha Pilot update end GH2/series/MAP_test.txt0000644000175000017500000000176611326004535013614 0ustar kaolkaol30 20 *** GearHead Location Record *** 32 13 5 14 25 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 12 13 6 14 1 13 5 14 1 13 5 14 12 13 5 14 2 13 13 14 1 13 4 14 7 13 18 14 1 15 6 14 4 13 19 14 1 15 6 14 5 13 18 14 1 15 6 14 3 13 5 14 2 13 13 14 1 13 4 14 5 13 6 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 1 13 5 14 1 13 5 14 13 13 5 14 83 13 *** 600 0 0 0 0 0 Stats 1 3 2 2 3 5 4 5 -1 Z -1 -1 0 0 0 0 0 Stats 1 15 2 3 3 5 4 5 -1 Z -1 -1 0 0 0 0 0 Stats 1 3 2 14 3 5 4 5 5 2 -1 Z -1 -1 0 0 0 0 0 Stats 1 9 2 13 3 5 4 5 5 2 -1 Z -1 -1 0 0 0 0 0 Stats 1 15 2 13 3 5 4 5 5 2 -1 Z -1 -1 0 0 0 0 0 Stats 1 9 2 3 3 5 4 5 -1 Z -1 -1 -1 GH2/series/WMON_space.txt0000644000175000017500000002003511365256063014071 0ustar kaolkaol% ****************************** % *** SPACE MONSTER LIST *** % ****************************** % Space monsters all have the "SPACE" terrain type and the SEALED intrinsic. % STATS REF BOD SPD PER CFT EGO KNO CHA ARCH Exorg Watcher MonsterTV 130 NoCorpse Sealed Biotech roguechar sdl_sprite <> sdl_colors <28 52 38 45 45 45 194 16 38> type habitat statline 17 19 16 22 15 20 12 14 CloseCombat 9 RangedCombat 12 Dodge 14 Vitality 5 Awareness 15 SpotWeakness 8 ElectronicWarfare 8 Toughness 8 sub torso armor 5 AntiBeam sub BeamGun 8 range 7 name type PowerSource 6 name EMelee 12 name ECM 6 end leg armor 5 AntiBeam sub Arcjet 5 end leg armor 5 AntiBeam sub Arcjet 5 end leg armor 5 AntiBeam sub Arcjet 5 end leg armor 5 AntiBeam sub Arcjet 5 end end ARCH Exorg Floater MonsterTV 60 NoCorpse Sealed roguechar sdl_sprite sdl_colors <255 230 210 255 157 123 200 0 100> type habitat statline 15 7 15 16 5 19 10 7 CloseCombat 7 RangedCombat 6 Dodge 9 Awareness 5 SpotWeakness 5 sub torso sub BeamGun 3 range 5 name type PowerSource 3 name Melee 1 name end wing sub HoverJet 3 end wing sub HoverJet 3 end wing sub HoverJet 3 end wing sub HoverJet 3 end end ARCH Space Squid roguechar SDL_SPRITE SDL_COLORS <180 36 70 135 30 17 200 200 0> MonsterTV 92 type habitat statline 13 16 12 9 3 6 1 5 *CLUE_SURVIVAL <*SURVIVAL_GetPearl 80 squid> RangedCombat 5 CloseCombat 6 Dodge 13 Stealth 4 sub torso armor 4 sub Melee 14 name acc -3 type Gun 5 name caliber range 5 type Magazine 16 sub Ammo 5 caliber end end tail name armor 4 sub Melee 5 name type end tail name armor 4 sub Melee 5 name type end end ARCH Scarlet Shil SDL_SPRITE SDL_COLORS <180 36 70 0 0 0 200 244 40> roguechar type habitat statline 13 5 13 2 1 1 1 1 MonsterTV 70 CloseCombat 6 Dodge 10 Vitality 4 sealed sub torso sub Melee 8 name type HoverJet 7 end end ARCH Silver Shil SDL_SPRITE SDL_COLORS <199 188 162 0 0 0 244 216 28> roguechar type habitat statline 13 2 13 2 1 1 1 1 MonsterTV 30 CloseCombat 3 Dodge 6 sealed sub torso sub Melee 2 name type HoverJet 7 end end ARCH Strobe Sphere SDL_SPRITE SDL_COLORS <94 96 125 174 238 251 174 238 251> SDL_PORTRAIT roguechar <'> type habitat statline 11 8 10 5 1 1 1 1 *CLUE_SURVIVAL <*SURVIVAL_GetPearl 70 fungus> MonsterTV 70 RangedCombat 5 CloseCombat 5 Dodge 10 Vitality 5 Stealth 5 sealed sub torso sub Melee 5 name type Speed 3 BeamGun 6 name type range 4 PowerSource 5 name HoverJet 8 end end ARCH Fungal Sphere SDL_SPRITE SDL_PORTRAIT SDL_COLORS <199 188 162 135 141 101 234 180 0> roguechar <'> type habitat statline 10 8 10 1 1 1 1 1 *CLUE_SURVIVAL <*SURVIVAL_GetPearl 40 fungus> MonsterTV 40 RangedCombat 1 CloseCombat 4 Dodge 5 Vitality 4 sealed sub torso sub Melee 4 name type Gun 3 name caliber range 4 BV 3 Magazine 80 sub Ammo 3 type caliber end HoverJet 8 end end ARCH Toxic Fungus SDL_SPRITE SDL_PORTRAIT SDL_COLORS <250 200 49 175 26 10 17 78 200> roguechar type habitat statline 9 16 8 9 1 1 1 1 *CLUE_SURVIVAL <*SURVIVAL_LootGizzard 75 fungus> MonsterTV 75 CloseCombat 6 RangedCombat 6 Dodge 8 Initiative 7 Vitality 7 sealed sub torso armor 5 sub Gun 4 name caliber range 5 BV 4 Magazine 80 sub Ammo 4 type caliber end end leg armor 4 sub melee 5 name type end leg armor 4 sub melee 5 name type end leg armor 4 sub melee 5 name type end leg armor 4 sub melee 5 name type end leg armor 4 sub melee 5 name type end leg armor 4 sub melee 5 name type end end ARCH Armored Fungus SDL_SPRITE SDL_COLORS <49 91 159 170 153 230 244 210 30> roguechar type habitat statline 7 20 7 8 1 1 1 1 *CLUE_SURVIVAL <*SURVIVAL_LootGizzard 44 fungus> MonsterTV 44 CloseCombat 4 Dodge 4 Initiative 7 Vitality 5 sealed sub torso armor 4 leg armor 3 sub melee 5 name type end leg armor 3 sub melee 5 name type end leg armor 3 sub melee 5 name type end leg armor 3 sub melee 5 name type end leg armor 3 sub melee 5 name type end leg armor 3 sub melee 5 name type end end ARCH Fungal Scavenger SDL_SPRITE SDL_COLORS <135 141 101 199 188 162 244 210 30> roguechar type habitat statline 7 5 8 5 1 1 1 1 *CLUE_SURVIVAL <*SURVIVAL_LootGizzard 25 fungus> MonsterTV 25 CloseCombat 3 Dodge 5 sealed sub torso leg sub melee 2 name end leg sub melee 2 name end leg sub melee 2 name end leg sub melee 2 name end leg sub melee 2 name end leg sub melee 2 name end end ARCH Fungal Hunter SDL_SPRITE SDL_COLORS <199 188 162 135 141 101 250 50 150> roguechar type habitat statline 9 13 13 9 1 1 1 1 *CLUE_SURVIVAL <*SURVIVAL_GetPearl 65 fungus> MonsterTV 60 CloseCombat 7 Dodge 8 Initiative 5 SpotWeakness 5 sealed sub torso armor 4 arm armor 3 sub melee 9 name type end arm armor 3 sub melee 9 name type end leg armor 3 sub melee 2 name end leg armor 3 sub melee 2 name end leg armor 3 sub melee 2 name end leg armor 3 sub melee 2 name end end arch armored worm roguechar SDL_SPRITE SDL_PORTRAIT SDL_COLORS <49 91 159 170 153 230 244 210 30> type habitat statline 9 12 6 5 4 5 1 1 MonsterTV 65 sealed CloseCombat 8 Dodge 7 Vitality 10 Toughness 10 %% The armored worm is tough as nails Talent 4 sub head armor 5 Hardened sub Melee 8 name type UsesBody end torso armor 5 sub Melee 4 name type end tail armor 5 end arch spaceworm roguechar SDL_SPRITE SDL_COLORS <245 213 160 245 213 160 255 230 195> type habitat statline 7 3 6 5 4 5 1 1 MonsterTV 15 sealed CloseCombat 3 Vitality 1 sub head armor 2 sub Melee 4 name end torso tail end GH2/series/CG_FAMILY_Default.txt0000644000175000017500000000356311326004537015135 0ustar kaolkaol% FAMILY HISTORY FILE % The "V" of a family gear tells how many "Jobs" it contributes to the % PC's creation. Each job should be indicated in the family desc as a #. % It will be replaced with the job name at runtime. % The family context is applied first, so even if no changes are intended % it includes a "blank" personal context. Family 1 desc requires context <+B-- +G--> inv Chara PCMentor PCFamily role chardesc male Renown 50 Quest <*EGG_Primary> Age 25 Chara PCMentor PCFamily role chardesc female Renown 50 Quest <*EGG_Secondary> Age 25 end Family 1 desc requires context <+B-- +G--> inv Chara PCMentor PCFamily role chardesc female Renown 50 Quest <*EGG_Primary> Age 25 Chara PCMentor PCFamily role chardesc male Renown 50 Quest <*EGG_Secondary> Age 25 end Family 1 desc requires context <+B-- +G--> inv Chara PCMentor PCFamily role chardesc female Renown 50 Quest <*EGG_Primary> Age 25 end Family 1 desc requires context <+B-- +G--> inv Chara PCMentor PCFamily role chardesc male Renown 50 Quest <*EGG_Primary> Age 25 end Family 0 desc requires context <+Bor +G--> Family 0 desc requires context <+Bor +G--> Family 0 desc requires context <+Bpd +G--> Family 0 desc requires context <+Bpd +G--> GH2/series/MEGA_CORE_MIX_Confrontation.txt0000644000175000017500000011072711376734116017111 0ustar kaolkaol%% %% *:CS_MIX_Confrontation Content %% &NoDerailment May not derail the plot; just straight combat please! %% %% Y'know how to get more randomness in your random story generator? Mix things %% up a bit. %% %% The core story enemy is doing something. The PC is trying to prevent it. %% Now, the two of them are gonna fight. It doesn't really matter what kind of %% fight we're dealing with- whatever's going on, we can work in some character %% development. %% %% At the start of combat, this subplot will be activated. %% The combat encounter can then send the following triggers, both of which %% must have scripts in this subplot: %% %% .%id%_%plotid%_GoCombatWin %% .%id%_%plotid%_GoCombatLoss %% %% The confrontation will send back the following triggers: %% %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% %% The combat encounter requires the following teams: %% %% Enemies %% EnemyNPC %% %% Note that this subplot may completely derail combat by issuing a RETURN %% command. The parent subplot must wait for one of the above responses before %% concluding. %% %% An encounter to which a confrontation has been attached automatically %% becomes a MAIN COURSE. %% %% PARAM1: The Enemy NPC, who should be in the encounter scene + ready to fight %% PARAM2: The outdoors scene where the encounter is %% Content name requires <*:CS_MIX_Confrontation 1:M.mer (1:A.tha|1:A.equ|1:A.adm|1:A.obs) ~E:TRADE ~E:CORPO ~E:LABOR ~E:FAITH ~F:CORPO> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%02 sub Persona 1 special greeting .%id%_GoThemeExpo result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 <> CMsg%id%01_12 Msg%id%01_13 <\PC ... You and I are yin and yang, two sides of the same coin. Just as I get paid to stop you, you get paid to stop me, and in this way our eternal battle profits us both...> CMsg%id%01_13 Msg%id%01_14 CMsg%id%01_14 Msg%id%02 <%name1% opined that your battle is a source of profit to you both.> Msg%id%03 Msg%id%03_1 Msg%id%03_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name requires <*:CS_MIX_Confrontation 1:A.obs 1:M.com -!Ne -!Lo ~1:MILIT ~1:FAITH -1:nemesis -1:evil_ ~C:FAITH> desc % Stop Three on the Disciple Path changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % E3 is a public scene in case the PC wins this fight Element3 % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%03 Msg%id%03_1 <> CMsg%id%03_1 Msg%id%04 Msg%id%05 <%name1% was originally your enemy, but after failing to defeat you gave up to become your disciple.> Msg%id%06 <%name1% quit being your enemy and pledged to be your disciple.> Msg%id%07 sub Persona 1 %% A mostly generic battle challenge. special greeting *.%id%_GoGreet <*BattleChallenge .%id%_GoThemeExpo na> .%id%_GoThemeExpo Msg%id%01 end Content name requires <*:CS_MIX_Confrontation 1:A.hat 1:M.com -!Ne -!Lo ~1:MILIT ~1:FAITH -1:nemesis -1:evil_ ~C:FAITH> desc % Stop Two on the Disciple Path changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%03 Msg%id%03_1 <> CMsg%id%03_1 Msg%id%04 sub Persona 1 %% An entirely generic battle challenge. special greeting *.%id%_GoGreet <*BattleChallenge .%id%_GoThemeExpo na> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end Content name requires <*:CS_MIX_Confrontation 1:A.env 1:M.com -!Ne ~1:MILIT ~1:FAITH ~C:FAITH> desc % Stop One on the Disciple Path changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%03 Msg%id%03_1 <> CMsg%id%03_1 Msg%id%04 sub Persona 1 %% An entirely generic battle challenge. special greeting *.%id%_GoGreet <*BattleChallenge .%id%_GoThemeExpo na> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end Content name requires <*:CS_MIX_Confrontation 1:M.--- (1:A.---|1:A.jr_) F:++ ~1:MILIT ~1:POLIC ~F:MILIT ~F:POLIC ~F:CORPO> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%02 sub Persona 1 special greeting *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_11 <> CMsg%id%01_11 <> Msg%id%02 <%name1% fought you for the glory of \FACTION &EnemyFac .> Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 end Content name requires <*:CS_MIX_Confrontation 1:A.tha> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%03 sub Persona 1 special greeting end Content name requires <*:CS_MIX_Confrontation 1:A.sr_ (1:M.pro|1:M.com) -!Ne ~1:MILIT ~1:ADVEN ~1:FAITH ~1:LABOR ~1:GOOD_> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter % P%id%02 = E1 retreated % P%id%03 = Pissed off E1 update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%04_1 <> CMsg%id%04_1 Msg%id%04_2 CMsg%id%04_2 sub Persona 1 special greeting *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 result%id%03 .%id%_GoR3Fail result%id%04 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 CPrompt%id%03 Prompt%id%03_1 Prompt%id%04 <[Continue]> end Content name requires <*:CS_MIX_Confrontation 1:A.sr_ 1:M.--- ~1:MILIT ~1:ACADE ~1:POLIT ~1:MEDIC ~1:GOOD_> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%04_1 <> CMsg%id%04_1 Msg%id%04_2 <> CMsg%id%04_2 sub Persona 1 special % This NPC will flee the board, and instead send extra henchmen after you. % V%id%01 = Renown Thingie greeting result%id%01 result%id%02 ifNPCOK %1% Monologue %1% %id%01 .%id%_GoR2Fail Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%02_1 <> CMsg%id%02_1 Msg%id%02_2 <> CMsg%id%02_2 Msg%id%03 Msg%id%03_1 <> CMsg%id%03_1 Msg%id%03_2 <> CMsg%id%03_2 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 end Content name requires <*:CS_MIX_Confrontation (1:A.jr_|1:A.ant) 1:M.com -!Ne ~1:MILIT ~1:FAITH> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%04 sub Persona 1 special greeting *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 CMsg%id%01_12 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 end Content name requires <*:CS_MIX_Confrontation 1:A.ant (1:M.mer|1:M.pro) ~1:ADVEN ~1:THIEF -!Ne> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 Msg%id%02 Msg%id%02_1 <> CMsg%id%02_1 Msg%id%02_2 <> CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%03 <%name1% came on this mission just for a chance to fight you.> Msg%id%04 Msg%id%05 Msg%id%06 <%name1% and \PPR %1% lance are here supporting another team.> Msg%id%07 sub Persona 1 special greeting end Content name requires <*:CS_MIX_Confrontation 1:M.com (1:A.sr_|1:A.equ) -!Ne ~1:LABOR ~1:CRAFT ~1:MEDIA ~1:FAITH -1:ADVEN -1:MILIT> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%03 sub Persona 1 %% An entirely generic battle challenge. special greeting *.%id%_GoGreet <*BattleChallenge .%id%_GoThemeExpo na> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end Content name requires <*:CS_MIX_Confrontation 1:M.mer (1:A.---|1:A.sr_|1:A.ant) ~E:CORPO ~E:TRADE ~E:POLIT ~E:MEDIA ~F:CORPO -!Ne> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%02 sub Persona 1 special % V%id%01 = Enemy Earning Counter greeting *.%id%_GoGreet <*BattleChallenge .%id%_GoAsk na> .%id%_GoAsk *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> result%id%01 .%id%_GoR1Fail result%id%02 .%id%_GoR2Fail result%id%03 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_12 <> CMsg%id%01_12 Msg%id%01_13 <> CMsg%id%01_13 Msg%id%02 Msg%id%03 Msg%id%04 <%name1% was jealous over how much you make.> Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 <[Continue]> end Content name requires <*:CS_MIX_Confrontation 1:M.mer (1:A.---|1:A.nme|1:A.jr_) ~E:CORPO ~E:TRADE ~F:CRIME ~F:CORPO> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter % P%id%02 = E1's proposal update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%02 sub Persona 1 special greeting .%id%_GoNoCheer *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_11 <> CMsg%id%01_11 Msg%id%01_12 <> CMsg%id%01_12 Msg%id%02 <%name1% blamed you for \PPR %1% current lack of money.> Msg%id%03 Msg%id%03_1 <> CMsg%id%03_1 Msg%id%03_2 <> CMsg%id%03_2 Msg%id%03_11 <> CMsg%id%03_11 Msg%id%03_12 <> CMsg%id%03_12 Msg%id%04 <%name1% offered you a bribe to just go away.> Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%07_1 <> CMsg%id%07_1 Msg%id%07_2 <> CMsg%id%07_2 Msg%id%07_11 <> CMsg%id%07_11 Msg%id%07_12 <> CMsg%id%07_12 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 <[Continue]> end Content name requires <*:CS_MIX_Confrontation 1:M.--- (1:A.---|1:A.jr_|1:A.sr_|1:A.ant|1:A.equ|1:A.pch) ~E:THIEF ~E:CORPO ~E:TRADE ~F:CRIME ~F:CORPO -F:POLIC> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%02 sub Persona 1 special greeting *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 CMsg%id%01_12 Msg%id%01_13 <\PC , why do you always show up at the worst possible times!? I won't let you ruin this mission... your presence here is cutting into my profit.> CMsg%id%01_13 Msg%id%01_14 CMsg%id%01_14 Msg%id%02 <%name1% accused you of cutting into \PPR %1% profits.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name requires <*:CS_MIX_Confrontation 1:A.nme (1:M.---|1:M.pro|1:M.com|1:M.nih) ~1:MILIT ~1:ADVEN ~F:POLIT> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02 Msg%id%03 sub Persona 1 special % This NPC will flee the board, and instead send extra henchmen after you. % V%id%01 = First time counter greeting result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%02 Msg%id%02_1 <> CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 end Content name requires <*:CS_MIX_Confrontation (1:A.---|1:A.nme)> desc changes results % E1 is the Enemy NPC % E2 is the outdoors scene where this encounter is % P%id%01 = ForceChat Counter update .%id%_%plotid%_GoCombatWin .%id%_%plotid%_GoCombatLoss Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%04 sub Persona 1 special greeting *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end GH2/series/ROBOTS_default.txt0000644000175000017500000000316411326004537014650 0ustar kaolkaol% ****************************************** % *** DEFAULT ROBOT BODY FORM LIST *** % ****************************************** % % STATS REF BOD SPD PER CFT EGO KNO CHA % % These stats represent the basic scores that this robot type % can have- on a good skill roll bonuses will be applied. If % CHARM is greater than 1 the robot will be sentient and % cannot be built by a character with a robotics skill lower % than 11. % % Don't include skills or armor ratings with the robot bodies, % but do include a power source in the body. % Arch Humanoid (Sentient) statline 9 9 9 9 10 9 9 10 desc sdl_sprite sdl_portrait Metal Sealed sub head torso sub PowerSource 10 end arm name sub Hand name end arm name sub Hand name end leg name leg name end ARCH Arachnoid SDL_SPRITE SDL_COLORS <72 104 136 150 150 150 220 0 0> statline 5 5 5 5 5 1 1 1 metal Sealed sub torso sub mount PowerSource 5 end leg leg leg leg end Arch Ball statline 1 1 1 1 1 1 1 1 desc <> sdl_sprite <> sdl_portrait <> Metal Sealed sub torso sub mount PowerSource 4 Tracks 8 end end Arch Androbot statline 8 10 8 9 8 8 8 1 desc sdl_sprite sdl_portrait Metal Sealed sub head torso sub PowerSource 10 end arm sub Hand end arm sub Hand end leg leg end GH2/series/TEXT_CCLJMD.txt0000644000175000017500000000337111326004540013726 0ustar kaolkaol%% Cayley Core lab, Jane Modo's diary -- They didn't see this coming. HAZ! Ash-filthy idiots. You'd think they would have known that their own technology could be used against them. They probably didn't even think of using it in this way. HAZ, indeed. Still, this is no time to relax. If they find me again I will be in no condition to resist, and my research is yet unfinished. I only hope I live long enough to see it through. -- Crossing the featureless void between stars must be incredibly lonely. Each adult Exorg carries within itself a number of symbiotes which act as companions, guardians, and servitors. They feed on solar energy and are for all practical purposes immortal. Forever is a long time. Among the Exorg there is no distinction between their tools and their selves; they represent the pinnacle of cybernetic co-evolution. Perhaps they would see things differently. Maybe this is as far as a mere human is capable of imagining, even in my current state. I wonder how they will feel as they watch the human race go extinct. -- With each day I feel my knowledge growing and sanity slipping away. It's best not to dwell on the sanity; I am turning into a machine. Soon I will be a creature of the earth, like a rabbit or a doorknob. The work must go on but after I cease to exist they must still not find my brain. Concentrate on what is important. Maybe I should up my dosage. HAZ! -- I believe that it is time to move on. This cave is too small. The children are starting to complain, and they've grown so big while I've been here. Soon it will be necessary to take the risk. REMEMBER: bring the weapons and the records, and leave the field on in case we ever need to come back. -- GH2/series/QUEST_LeadershipSeries.txt0000644000175000017500000011515111401151245016341 0ustar kaolkaol%% %% *********************************** %% *** THE LEADERSHIP SERIES *** %% *********************************** %% %% In order to gain lancemate slots 2 and 3, the PC must demonstrate leadership. %% These quests are linked to the Cavalier's Club, meaning that they'll always %% appear in the PC's hometown. There are three rungs on the leadership ladder: %% %% 1) LM1: Gain an ally, available from start %% 2) LM2: Gain a second lancemate slot, available from Renown 30 %% 3) LM3: Gain the third lancemate slot, available from Renown 50 %% %% %% *:Q_LeadershipSeries %% %% This is the root quest; it will activate the subquests as needed. %% Content name desc requires <*:Q_LeadershipSeries> % Element1 is the cavalier's club. Since everything starts here, might as well % hand it out as an element. % Element2 is the quest villain, to be kept frozen until needed. % Element3 is the veteran in the CavClub, who gives advice and maybe a reward. % Element4 is the VIP Room, which is needed in tasks 2 and 3 % Element5 is the entrance (of the VIP Room) Element1 Element2 Place2 Element3 Place3 <1 (Guards) sd ally> Element4 Place4 <1> Element5 Place5 <1> % P%id%01 = Email Timer % P%id%02 = Part 3 timer; after SP2 complete, wait a while before starting SP3 update pumpnews GoCheckSP3 % At the ending, clear the emails as appropriate end Msg%id%01 Msg%id%02 <%name2%@ \SCENE PCHomeTown :// I've tried doing this the nice way, now it's time to get serious. Your stupid Cavalier's Club is off-limits. Anyone who tries to open it back up again is gonna have to answer to me.> % SubPlot1 is the first lancemate % SubPlot2 is the LM2 badge % SubPlot3 is the LM3 badge % SubPlot4 is the ultimate dungeon SubPlot1 <*:Q_LS_Task1 #10 1 2> SubPlot2 <*:Q_LS_Task2 #40 1 2 4> SubPlot3 <*:Q_LS_Task3 #65 1 2 4> SubPlot4 <*Q_LS_Ultima #100 3> sub Persona 3 *greeting <*NiceToMeetYou GoCheckWin3> % V1 = Have given final quest GoCheckWin3 GoTeaser GoCheckOver3 GoMainMenu GoMission GoCheckPro3 GoCheckWin2 GoCheckPro2 GoCheckWin1 GoCheckPro1 GoNoAdvice result1 result2 *result3 <*MENTOR_WhatDoFirst 1 2 4> *result4 <*MENTOR_WhatEquipment 1 2 3> *result5 <*GoodBye> result6 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 Msg2 Msg3 Msg4 Msg5 <%name2% is gone for now, but \SPR %2% 's sure to be back. Train hard so you're ready when that happens.> Msg6 Msg7 Msg8 Msg9 Msg10 Prompt1 CPrompt1 Prompt2 CPrompt2 Prompt3 Prompt4 Prompt5 Prompt6 MetaScene 4 name type mapwidth 15 mapheight 15 MallMap Ceiling Tolerance 15 NeededCells 3 Content1 Content2 Content3 Start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 sub StairsDown Destination -1 end end MetaScene 5 sub room desig minimap <#######1###...##...##...#> end end inv NPC Mecha Pilot chardesc old old old renowned StairsUp desig use GoNotYet Msg99 end %% %% *Q_LS_Ultima %% %% The PC has completed the Leadership Series; time for the ultimate dungeon. %% %% PARAM1: The veteran pilot from the Cavalier's Club %% Content name requires <*Q_LS_Ultima> % E1 is the veteran % E2 is an outdoors scene in which to place the dungeon % E3 is the dungeon itself Element2 Element3 Place3 <2> start Msg%id%01 Msg%id%02 % SubQuest1 is a DungeonTreasure. SubPlot1 <*:Q_DungeonSecret #120 3> sub Persona 1 .%id%_GoInit Msg%id%01 <%name3% wasn't shut down, exactly- it had to be abandoned because of all the shroom-beasts. People say there's loads of valuable stuff down there, but no-one has ever returned from its lower depths alive.> Msg%id%02 <%name1% told you about %name3% in %name2%.> STC QS_Dungeon_AsteroidMine SetID 3 entrance <*QUEST-INACTIVE> MapWidth 40 MapHeight 40 end Content name requires <*Q_LS_Ultima> % E1 is the veteran % E2 is an outdoors scene in which to place the dungeon % E3 is the dungeon itself Element2 Element3 Place3 <2> start Msg%id%01 Msg%id%02 % SubQuest1 is a DungeonTreasure. SubPlot1 <*:Q_DungeonSecret #120 3> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% told you about %name3% in %name2%.> STC QS_Dungeon_AsteroidCave SetID 3 entrance <*QUEST-INACTIVE> MapWidth 40 MapHeight 40 end Content name requires <*Q_LS_Ultima> % E1 is the veteran % E2 is an outdoors scene in which to place the dungeon % E3 is the dungeon itself Element2 Element3 Place3 <2> start Msg%id%01 Msg%id%02 % SubQuest1 is a DungeonTreasure. SubPlot1 <*:Q_DungeonSecret #120 3> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% told you about %name3% in %name2%.> STC QS_Dungeon_Derelict SetID 3 entrance <*QUEST-INACTIVE> MapWidth 50 MapHeight 50 end Content name requires <*Q_LS_Ultima> % E1 is the veteran % E2 is an outdoors scene in which to place the dungeon % E3 is the dungeon itself Element2 Element3 Place3 <2> start Msg%id%01 Msg%id%02 % SubQuest1 is a DungeonTreasure. SubPlot1 <*:Q_DungeonSecret #120 3> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% told you about %name3% in %name2%.> STC QS_Dungeon_UrbanHellHole SetID 3 entrance <*QUEST-INACTIVE> MapWidth 40 MapHeight 40 end Content name requires <*Q_LS_Ultima> % E1 is the veteran % E2 is an outdoors scene in which to place the dungeon % E3 is the dungeon itself Element2 Element3 Place3 <2> start Msg%id%01 Msg%id%02 % SubQuest1 is a DungeonTreasure. SubPlot1 <*:Q_DungeonSecret #120 3> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% told you about %name3% in %name2%.> STC QS_Dungeon_Sewer SetID 3 entrance <*QUEST-INACTIVE> MapWidth 50 MapHeight 50 end %% %% *:Q_LS_Task3 %% %% It's time for the final confrontation with the series villain. Things get %% started when the PC enters the Cavalier's Club and finds it deserted. After %% that, activate a MISCHIEF mood and let the PC discover the NPC's secret %% base. %% %% PARAM1: The Cavalier's Club %% PARAM2: The series villain %% PARAM3: The VIP Room %% Content name desc requires <*:Q_LS_Task3> % E1 is the Cavalier's Club % E2 is the series villain, who may very well get defeated this time around % E3 is the VIP Lounge % E4 is an outdoors scene % E5 is E2's private club % E6 is the MischiefMaker mood % E7 is the Master Token, which prevents the Sentinel from attacking E2 Place2 <5 (Guards) sd> Element4 Element5 Place5 <4> Element6 Element7 Place7 <2> % P%id%01 = Have entered CavClub first time/deployed mood % P%id%02 = Have picked up the Master Token % VICTORY: % The PC must locate E2 and stop this bullying. This can be achieved % in several ways: E2 can be killed, E2 can surrender, or the PC can % talk E2 down. The sentinel may be made to switch sides by either % physically hacking it or by obtaining E2's Master Token. Faint%2% Surrender%2% Get%7% % The mood gets deployed the first time the PC enters the CavClub or % the VIP Room after quest activation. While this plot is active, both % buildings will be empty. start .%id%_GoCheckVIP .%id%_GoEmptyRoom Msg%id%01 Msg%id%02 Msg%id%03 <%name2% scared everybody away from the Cavalier's Club.> % SubPlot1 = The victory condition; moving it to a subplot because it's % complicated and otherwise I'd have to copy the same script all over % the place. SubPlot1 <*Q_LS_Win3 2 1> sub Persona 2 special greeting GoChat *GoGreet <*YouWentThroughALotOfTrouble&IsEnemy GoChallenge> GoChallenge result1 result2 result3 result4 result5 result6 result7 result8 result9 result10 result11 result12 result13 result14 result15 result16 result17 result18 result19 result20 Msg1 Msg2 Msg3 Msg4_1 Msg4_2 Msg4_3 Msg5 Msg6 Msg7 Msg8 Msg9 <%name2% has left this area.> Msg10 Msg11_1 Msg11_2 Msg11_3 Msg12 Msg13 Msg14_1 Msg14_2 Msg14_3 Msg15 Msg16 Msg17 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 CPrompt5 Prompt6 Prompt7 <[Continue]> % E2's origin story... Prompt8 CPrompt8 Prompt9 Prompt10 Prompt11 Prompt12 Prompt13 Prompt14 Prompt15 <[Continue]> % E2's origin story, part 2... Prompt16 CPrompt16 Prompt17 Prompt18 CPrompt18 Prompt19 CPrompt19 Prompt20 CPrompt20 MetaScene 2 %% Contains E2's office and "Enforcer". %% Note that at this point in the game, a Sentinel is serious OOD, %% which should encourage the player to seek a nonviolent resolution. sub room name <%name2%'s Office> desig minimap <......1.2...........#---#> inv monster SENTINEL MiniMapComponent 2 % Improved ranged combat score; really, you don't want to fight this thing. RangedCombat 9 SetTeam 4 %% It's possible to hack this particular Sentinel %% and make it attack E2, which should greatly even %% the odds in a fight. CLUE_CODEBREAKING CLUE_REPAIR CLUE_SCIENCE GoHack GoNoHack GoBorked Msg11 Msg12 Msg13 <%name2%'s modifications have messed up the fire control system.> end end MetaScene 5 name <%name2%'s Private Club> entrance <*QUEST-INACTIVE> type special MallMap MapWidth 15 MapHeight 15 Ceiling % L1 = Initialization Counter start Msg1 neededcells 2 Content1 Content2 Content3 sub Team 1 Team 2 name type deploy SetAlly 3 Stat 2 1 team 3 name SetAlly 2 4 team 4 name SetAlly 3 room name desig end end inv MinorMood 1 name <%name2%'s Mischief> plot_type <*Mischief> Element1 Element2 Element3 MeleeAddOn 2 Name desc Category Acc 2 Speed 2 end %% %% *Q_LS_Win3 %% %% Conclusion for above. %% %% PARAM1: The Enemy NPC. %% PARAM2: The Cavalier's Club. %% Content requires <*Q_LS_Win3> % E1 = the series enemy, who may or may not be dead by now. % E2 = the cavalier's club % E3 = a victory meme element3 update .%id%_GoRegular Msg%id%01 inv Meme MaxMemeViews 3 MemeTimeLimit msg_1 CMsg_1 msg_2 <> CMsg_2 msg_3 <> CMsg_3 msg_11 <> CMsg_11 msg_12 CMsg_12 msg_13 CMsg_13 msg_14 CMsg_14 msg_15 <> CMsg_15 msg_16 CMsg_16 end %% %% *:Q_LS_Task2 %% %% In this subquest, the PC has a first encounter with the series villain, and must act to %% save the Cavalier's Club itself. %% %% PARAM1: The Cavalier's Club %% PARAM2: The series villain %% PARAM3: The VIP Room %% Content name desc requires <*:Q_LS_Task2> % E1 is the Cavalier's Club % E2 is the series villain, met here for the first time % E3 is the VIP Lounge % E4 is the manager, who will run this quest Element4 Place4 <3 (Guards) ally sd> % P%id%01 = First time entering the VIP Room. %% FAIL CONDITIONS: %% - E4 dies end % The first time you enter the VIP Room, you'll see E2 and E4 arguing. start Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 <%name2% leaves the area.> % SubPlot1 is the FindMoney task. SubPlot1 <*:Q_FindMoney 4> sub Persona 4 % This character will handle the FindMoney plot. greeting GoChat *GoFirstTime <*INeedYourHelp GoListen GoRefuse> GoListen GoRefuse GoWinQuest .meks GoBasicPrize result1 result2 *result3 <*HurryBackWithMoney> result4 result5 result6 result7 Msg1 Msg2 <%name2% inherited a controlling share of the Cavalier's Club, and has threatened to shut us down unless we buy his stock at ridiculously inflated prices.> Msg3 Msg4 <%name4% needs a lot of money to save the Cavalier's Club from %name2%.> Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Prompt1 Prompt2 CPrompt2 Prompt3 Prompt4 Prompt5 Prompt6 CPrompt6 Prompt7 CPrompt7 end inv NPC Soldier chardesc old sociable heroic MOTIVATION:GreaterGood end %% %% *:Q_LS_Task1 %% %% This subquest will allow the PC to gain a first lancemate, and should also foreshadow the %% later parts of the series. %% %% PARAM1: The Cavalier's Club %% PARAM2: The series villain %% Content name desc requires <*:Q_LS_Task1> % E1 is the Cavalier's Club % E2 is the series villain, needed for foreshadowing % E3 is the character who lost the artifact % E4 is the character who stole the artifact % E5 is the artifact Element3 Place3 <1 (Guards) sd ally> Element4 Element5 Place5 <4> % P%id%01 = Have started quest % P%id%02 = Have accepted E4's offer to intimidate E3 % P%id%03 = Have silenced E3 % P%id%04 = Have obtained E5 %% Speaking with E3 will set P%id%01 and activate the quest. Locate E4 and maybe get the item %% back through negotiation. Alternatively, accept E4's offer to convince E3 to back off. %% FAIL CONDITIONS: %% - E3 and E4 both die. %% - PC has silenced E3, and E4 dies %% - PC has obtained E5, and E3 dies %% - PC has obtained E5 and silenced E3 end .%id%_GoCheckF2 .%id%_GoCheckF3 .%id%_GoCheckF4 get%5% faint%3% Msg%id%01 % SubPlot1 is E4's hideout SubPlot1 <*:Q_NPCHideout 4> sub Persona 3 rumor%id% <%name3% has lost the %name5%.> &BribeAmount greeting GoChat *GoFirstTime <*NiceToMeetYou GoJobOffer> GoJobOffer GoSilenceE3 *result1 <*ComeBackWhenYouKnowSomething> result2 result3 *GoR3End <*PCWillKeepItem %5%> result4 *GoR4End <*YouLostItem> result5 result6 result7 result8 result9 result10 result11 result12 result13 result14 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 <%name3% asked you to help retrieve the %name5% from %name4%.> Msg8 Msg9 <\ITEM_DESC %5% \ITEM_HISTORY %5% So, can you help me recover it?> Msg10 Msg11 Msg12 Msg13 Prompt1 Prompt2 CPrompt2 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt5 CPrompt5 Prompt6 Prompt7 Prompt8 Prompt9 CPrompt9 Prompt10 <%name4% must have already sold it; it's gone.> CPrompt10 Prompt11 CPrompt11 Prompt12 Prompt13 Prompt14 Persona 4 % V1 = Have asked who E2 is % V2 = Have attempted to make a deal &BribeAmount greeting GoBrushOff GoChat *GoGreet <*WhatDoYouWantWithMe&YouHaveSomething GoBargain> GoBargain GoReport Result1 result2 result3 result4 result5 result6 result7 result8 result9 result10 result11 result12 result13 result14 result15 Msg1 <...> Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Msg15 Msg16 Msg17 Msg18 Prompt1 CPrompt1 Prompt2 CPrompt2 Prompt3 Prompt4 CPrompt4 Prompt5 Prompt6 <%name2% is dead.> CPrompt6 Prompt7 CPrompt7 Prompt8 CPrompt8 Prompt9 Prompt10 CPrompt10 Prompt11 Prompt12 CPrompt12 Prompt13 Prompt14 <%name3% won't be bothering you again.> CPrompt14 Prompt15 end inv NPC Thief Combatant end GH2/series/MEGA_BountyHunt.txt0000644000175000017500000001666611401151245015050 0ustar kaolkaol%% %% This file contains components needed for the bounty hunting missions. %% %% %% *:BH_CaptureTarget %% %% The PC has been asked to aprehend a criminal. %% %% This subplot should level and move the target upon activation. Upon %% completion, it will set one of the following triggers: %% %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% .%id%_%plotid%_GoTimeOver %% %% Upon winning, the target will either be frozen or dead. It's up to the %% parent plot what to do next. %% %% PARAM1: The NPC to be caught %% Content name % The NPC is in his mecha outside of town. In order to capture him, the % PC will have to fight in a mecha. requires <*:BH_CaptureTarget> % E1 is the target being sought % E2 is an environs scene, for placing the encounter % E3 is the encounter/metascene itself Element2 Element3 Place3 <2> % P%id%01 = Time Limit/Initialization Counter % P%id%02 = Have entered combat % At startup, move the target to the encounter and get them update start sub Persona 1 Special rumor%id% <%name1% is out driving around in %name2%.> Greeting *.%id%_GoGreet <*BattleIntroduction&SentToFight .%id%_GoThemeExpo> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> MetaScene 3 2 % L1 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 GoLoseMission GoWinMission Msg1_1 <%name1% has been captured.> CMsg1_1 Msg1_2 <%name1% has been defeated.> CMsg1_2 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name <%name1%'s Mecha> end Content name requires <*:BH_CaptureTarget> % E1 is the target being sought % E2 is the RevealEncounter mood % E3 is an outdoors scene, for placing the encounter % E4 is the encounter/metascene itself Element2 Element3 Element4 Place4 <3> % P%id%01 = Time Limit/Initialization Counter % P%id%02 = Have entered combat % At startup, move the target to the encounter and get them update start sub Persona 1 Special Greeting .%id%_GoNotHere .%id%_GoGreet *.%id%_GoFirstTime <*IntruderGreeting&Arrest .%id%_GoBargain> *.%id%_GoBargain <*YoureUnderArrest %threat% .%id%_GoWillSurrender .%id%_GoWillResist na> *.%id%_GoWillSurrender <*ISurrender&Arrest .%id%_GoSurrender> *.%id%_GoWillResist <*IDisagreeYouDie&Arrest .%id%_GoAttack> .%id%_GoAttack <> .%id%_GoSurrender Msg%id%01 <...> MetaScene 4 special % L1 = Initialization Counter % L2 = Have defeated all guards BoxMap MapWidth 17 MapHeight 17 content Start Surrender%1% nu1 nu2 % If you eliminate all the guards, you get an easy capture roll. nu3 GoFailCapture GoLoseMission GoWinMission Msg1_1 Msg2 Msg3 Msg3_1 Msg3_2 <> Msg4 Msg4_1 <> Msg4_2 <> Msg5 Msg5_1 <> Msg5_2 <> Msg6 <%name6% surrenders.> sub Team 1 SetEnemy 2 3 Team 2 name SetEnemy 1 SetAlly 3 Team 3 name Deploy type SetEnemy 1 SetAlly 2 Room special minimap <......###..#1#...........> sub Elevator Destination -1 MiniMapComponent 1 use end end end inv MinorMood 1 name plot_type <*FindEncounter> Element1 Element2 STC CORE-ACTIVATABLE name <%name1%'s Hideout> end %% %% *BH_CatchMinorNPC %% %% The PC has won a bounty hunting mission. If the NPC is alive, earn three %% times the nominal pay rate. Delete the target and end the plot. %% %% The master plot must have a pay rate set. Remember this should be one %% third of the optimum pay rate. %% %% PARAM1: The mission giver %% PARAM2: The target %% Content name requires <*BH_CatchMinorNPC Common> % E1 is the mission-giving NPC. % E2 is the target start sub Persona 1 greeting .%id%_GoSmallWin *.%id%_GoAnnounceSmall <*HaveKilledTarget .%id%_GoEnd ChatNPCFac> .%id%_GoBigWin *.%id%_GoAnnounceBig <*HaveCapturedTarget .%id%_GoEnd ChatNPCFac> .%id%_GoEnd end %% %% *BH_LoseMinorNPC %% %% The player has lost this mission. Delete the target and end the plot. %% %% PARAM1: The mission-giving NPC %% PARAM2: The target %% Content name requires <*BH_LoseMinorNPC> % E1 is the mission-giving NPC. % E2 is the target % P%id%01 = Time Limit start .%id%_GoEnd update sub Persona 1 greeting *.%id%_GoLostMission <*TargetEscaped .%id%_GoEnd ChatNPCFac> .%id%_GoEnd end GH2/series/PLOT_MOOD_Skirmishing.txt0000644000175000017500000001515011326004537016075 0ustar kaolkaol%% %% *Skirmishing Plots %% %% Mecha from a foreign power are in town skirmishing with the local security forces, %% but it's nothing serious. There will be a lot of mecha missions until everything %% calms down. %% %% Mood Spec: %% E1 = The city itself %% E2 = The enemy faction %% Plot name desc requires <*Skirmishing> % E1 is an outdoors scene % E2 is an encounter to place there % E3 is the faction that's rampaging % E4 is an ally of that faction Element1 Element2 Place2 <1> Element3 Element4 Place4 <2 (Enemies)> % P1 = Time Limit/Initialization Counter % P2 = Mode; if P2=1 then the player joins the battle on E4's side start update sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 SetFaction 3 start nu1 nu2 GoAltEnding Msg2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoAltDeploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 3 Deploy ParaX 10 ParaY 10 end Persona 4 special greeting GoCheckEnemy GoCheckAlly GoCheckSides *GoBattleChallenge <*BattleChallenge&Avoid GoThemeEnemy GoEnd> *GoJoinPatrol <*JoinOurFacPatrol GoThemeAlly GoEnd %3% RootSceneFac> *GoThemeEnemy <*THEME_EXPO&Enemy GoStartCombat> *GoThemeAlly <*THEME_EXPO&Ally GoStartCombat> GoStartCombat GoEnd Msg1 end inv STC ENCOUNTER-WANDER name <%name4%'s Patrol> EncounterMove 20 use ATTACK GoMaybeAttack GoAutoAttack Msg3 Msg4 Msg5 end Plot name % This job gives salvage. requires <*Skirmishing> PayRate 125 % E1 is the faction to be fought % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place Element1 Element2 Element3 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Versus 2 3 1> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \SCENE NPCSCene %2% :// \RANK \PC , report to \SCENE EScene 2 for a mission.> Msg1_1 <%name2%@ \SCENE NPCSCene %2% :// I have a job for you at \SCENE EScene 2 .> Msg1_2 <%name2%@ \SCENE NPCSCene %2% :// %name1% needs your help. Come see me at \SCENE EScene 2 .> Msg1_3 <%name2%@ \SCENE NPCSCene %2% :// Call when you can; I've got a mission for you.> sub Persona 2 rumor0 <%name2% needs a pilot to fight the infiltrators from %name1%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*CivilDefenseTest&Mecha GoMissionBriefing GoEnd GoRejectMission GoReducePay ChatNPCFac %1% %threat%> GoReducePay *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to fight %name1% in %name3%.> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end GH2/series/arenastub.txt0000644000175000017500000000012111326004537014106 0ustar kaolkaol% Arena Stub ArenaUnit Cash 2000000 name UPDATE GH2/series/UNICON_Dungeon.txt0000644000175000017500000002036211333736445014615 0ustar kaolkaol%% *DUNGEON_THREAT CONTENT %% Dungeon threats. Little things to make life more dangerous for hapless adventurers. %% *DUNGEON_REWARD CONTENT %% Dungeon treasure. Something that's good for everyone. Content name requires <*DUNGEON_REWARD GROUND> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMain> GoMain result1 result2 Msg1 Msg2 Msg3 Msg4 Prompt1 Prompt2 end inv NPC Nurse end %% *DUNGEON_DECOR CONTENT %% Dungeon Extras. Little bits of personality for dungeon levels. Content name requires <*DUNGEON_DECOR (Mine|derelict) !Ex ~Asteroid> element1 element2 teamdata2 start msg%id%01 inv Treasure name Fudge 12000000 mass 2 desc CLUE_SCIENCE CLUE_INSIGHT Msg1 Msg2 monster murder machine % This is a worse than usual murder machine, in that it has a % dodge skill of 10 and an attack skill of 15. Killer. CloseCombat 15 Dodge 10 end Content name requires <*DUNGEON_DECOR Mine Space Ruin> element1 element2 place2 <1> % A dead miner and his harmonica, v2.1 % a GH2 RANCON_Dungeon content entry written for the purpose of learning ASL. % -by Yendaa % -edited by Joe inv % v%id%1 - how the body was treated % 0 nothing yet % 1 rummaged through % 2 left alone % v%id%02 - Mysticism counter MetaTerrain 0 0 name desc SDL_Sprite roguechar mass 170 CLUE_MEDICINE clue_REPAIR clue_SCIENCE clue_MYSTICISM clue_INSIGHT use GoLeave GoRummage GoLeft GoRummaged Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Instrument 2 name desc end %% *DUNGEON_GOAL CONTENT Content name requires <*DUNGEON_GOAL Mine !Ne> minimap < ... .......1....... ... > % E1 is the treasure. It's heavy. element1 inv Treasure name desc CLUE_SCIENCE CLUE_SURVIVAL CLUE_INSIGHT Msg1 Fudge 1255000 mass 24 end Content name requires <*DUNGEON_GOAL Mine !Lo> minimap < ... .......1....... ... > % E1 is the treasure. It's heavy. element1 inv Treasure name desc Fudge 2550000 mass 21 end Content name requires <*DUNGEON_GOAL Mine !Md> minimap < ... .......1....... ... > % E1 is the nugget. It's a doozy. element1 inv Treasure name desc Fudge 5500000 mass 32 end Content name requires <*DUNGEON_GOAL Mine !Hi> minimap < ... .......1....... ... > % E1 is the treasure. element1 inv Treasure name desc Fudge 9000000 mass 14 end Content name requires <*DUNGEON_GOAL Mine !Ex> minimap < ... .......1....... ... > % E1 is the treasure. element1 inv Treasure name desc Fudge 20000000 mass 45 end Content name requires <*DUNGEON_GOAL> minimap < ... .......1....... ... > % Element1 is the katana % E2 is the current scene being built. % E3 is a public building somewhere in the same city. % E4 is the samurai whom the PC can recruit. Element1 Element2 <.> Element3 Element4 Place4 <3 SD Ally> get%1% sub Persona 4 % Kind of a kludgey thing- after resolving this event, one way or another, % manually delete the following rumor. rumor <%name4% lost something precious in %name2%.> % V1 = Have resolved issue. % V2 = Original Team Value greeting GoSurrender GoResolve GoAskAbout *GoSolvedCase <*MISC_CHATTER> *GoNotHere <*IAmBusy&OnPhone> result1 result2 GoR2Fail result3 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Prompt1 CPrompt1 Prompt2 CPrompt2 Prompt3 CPrompt3 end inv Melee 9 Name desc SDL_PORTRAIT type legality 5 Acc 3 Speed 5 Mass -5 NPC Warrior Monk statline 14 12 13 11 9 14 9 7 job MOTIVATION:Professional end GH2/series/MEGA_CORE_LearnSecret.txt0000644000175000017500000001531211365256063015750 0ustar kaolkaol%% %% *:CS_TellSecretAboutNPC Content %% &LearnPlans The PC is trying to learn the NPC's plans %% &IsEnemy The NPC is an enemy of the PC %% %% The PC must learn a secret about this NPC in order to continue with %% the adventure. %% %% If this subplot is won, the following trigger must be set: %% .%id%_%plotid%_GoLearnSecret %% %% PARAM1: The Secret %% PARAM2: The NPC %% Content name desc requires <*:CS_TellSecretAboutNPC -!Ne &IsEnemy ~&LearnPlans ~+Gpo ~+Gre> Size 5 % E1 is the secret % E2 is the NPC whom it involves % E3 is an environs scene % E4 is the encounter involved Element3 Element4 Place4 <3> sub MetaScene 4 2 % Defeat all but one of the raiders, and the last guy standing % will reveal the secret. This mission had so many bugs in GH1 I % must be insane for trying it again... rumor%id% <%name2%'s lancemates have been hanging out in %name3%.> % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start nu1 nu2 GoCheckComplete end Msg1 Msg2 Msg3_1 Msg3_2 Msg3_3 Msg4_1 Msg4_2 Msg4_3 <\SECRET %1% That's all I know, I swear!> Msg5 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 %% This plot will fail if only one enemy mecha is generated. How to solve this? By doing two %% separtate generations, so we'll always have at least two! Why didn't I think of this for %% GearHead1? Deploy ParaX 45 ParaY 45 end end inv Encounter name <%name2%'s Lance> % V1 = Recharge Counter % This encounter will appear once every 3 hours, as long as this is the active subplot. update GoSetOrders use GoAutoAttack GoAvoidAttack end Content name desc requires <*:CS_TellSecretAboutNPC ~!Ne -!Hi -!Ex> Size 1 % E1 is the secret % E2 is the NPC whom it involves % E3 is a person who can tell the secret Element3 % FAIL CONDITIONS: % - E3 dies start sub Persona 3 rumor%id% <%name3% knows something about %name2%.> greeting *GoGreet <*IWantToTalkAboutNPC %2% GoInform> GoInform Msg1 end %% %% *:CS_LearnSecretAboutNPC Content %% %% The PC will have a chance to learn something about this particular NPC. %% Unlike the previous subplot type, this secret is purely optional. %% %% If this subplot is won, the following trigger must be set: %% .%id%_%plotid%_GoLearnSecret %% %% PARAM1: The Secret %% PARAM2: The NPC %% Content name desc requires <*:CS_LearnSecretAboutNPC> Size 2 % E1 is the secret % E2 is the NPC whom it involves % E3 is a public scene % E4 is the journalist Element3 Element4 NeverFail4 start sub Persona 4 rumor%id% <%name4% has been doing a story on %name2%.> % V1 = Information Cost % V2 = Got the Discount greeting GoChat *GoGreet <*IHaveInformation&AboutNPC GoCalc %2%> GoCalc GoInform GoFFFail GoLFail result1 *result2 <*HurryBackWithMoney> result3 result4 result5 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Prompt1 CPrompt1 Prompt2 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt5 CPrompt5 end GH2/series/UNICON_Default.txt0000644000175000017500000002570711326004535014600 0ustar kaolkaol%% *URBAN_X Content %% Things you might find in a city. Generic content for cafes, parks, %% shopping malls, waiting rooms, and so on. Mostly just NPCs. Content name requires <*URBAN_X Public Static (Garage|Arena) ~Meeting> %% A mechanic/engineer who might be recruitable as a lancemate. element1 team1 teamdata1 sub Persona 1 rumor <%name1% plans to earn some money to open a garage.> % V1 = Have made offer % V2 = Hiring Price *Greeting <*NiceToMeetYou GoCheckMember> GoCheckMember GoCheckEnemy GoOffer *GoPine <*GodImBored> result1 *GoWelcome <*WelcomeToLance> result2 *result3 <*HurryBackWithMoney> Msg1 Msg2 Msg3 Prompt1 CPrompt1 Prompt2 Prompt3 end inv NPC Mechanic Combatant MechaEngineering 10 end Content name requires <*URBAN_X Public Static ~Meeting -Legit> %% A thief who might be recruitable as a lancemate. element1 team1 teamdata1 sub Persona 1 rumor <%name1% is a freelance countersecurity consultant. That's a fancy way to say thief.> % V1 = Have made offer % V2 = Hiring Price *Greeting <*NiceToMeetYou GoCheckMember> GoCheckMember GoCheckEnemy GoOffer *GoPine <*GodImBored> result1 *GoWelcome <*WelcomeToLance> result2 result3 result4 result5 Msg1 Msg2 Msg3 Msg4 Msg5 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 CPrompt5 end inv NPC Thief Combatant chardesc sociable pragmatic end Content name requires <*URBAN_X Public Static> %% A completely random NPC who might be recruitable as a lancemate. element1 <.> element2 element3 team3 teamdata3 sub Persona 3 rumor <%name3% dreams of leaving town and becoming a cavalier.> % V1 = Have made offer *Greeting <*NiceToMeetYou GoCheckMember> GoCheckMember *GoChat <*MISC_CHATTER> GoCheckEnemy GoOffer *GoPine <*GodImBored> result1 *GoWelcome <*WelcomeToLance> result2 result3 result4 Msg2 Msg2_1 Msg2_2 Msg3 Msg3_1 Msg4 Msg4_1 CMsg4_1 Msg4_2 CMsg4_2 Msg4_3 CMsg4_3 Msg4_4 CMsg4_4 Msg4_5 CMsg4_5 Msg4_6 CMsg4_6 Msg5 Msg5_1 Msg6 <%name3% joined your lance.> Prompt1 Prompt1_1 Prompt2 Prompt2_1 Prompt3 Prompt4 end Content requires <*URBAN_X Mall Static> % A student to mooch money from the PC... previously a PFrag. name element1 team1 teamdata1 sub Persona 1 % v%id%01 = Timer; if -1, quest is completed. *Greeting <*NiceToMeetYou GoMooch> GoMooch .%id%_GoCheckTime .%id%_GoWait .%id%_GoNothing .%id%_GoChaos .%id%_GoLaw result%id%01 result%id%02 result%id%03 result%id%04 .%id%_GoR4Fail result%id%05 .%id%_GoR5Fail result%id%06 result%id%07 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 end inv NPC Student chardesc Young Sociable end Content requires <*URBAN_X Sleazy Static ~Dangerous> % This bandit will attack the PC. If the PC manages to subdue him, % he'll join as a lancemate. name element1 team1 teamdata1 % L%id%01 = Victory Counter % L%id%02 = Initial Team of Bandit SURRENDER%1% sub Persona 1 rumor <%name1% really likes to fight.> special % V1 = Has been recruited or otherwise placated greeting GoCheckSurrender GoGainLM GoPlacate GoFirstTime result1 GoAttack result2 Msg1 Msg2 Msg3 Msg4 Msg5 <%name1% joined your lance after you defeated \OPR %1% in combat.> Prompt1 Prompt2 end inv NPC Bandit end GH2/series/UNICON_LocalContent.txt0000644000175000017500000016211011365256063015577 0ustar kaolkaol%% Contains things that only show up at one place in the ATLAS, but %% are included as content fragments so as to make organization cleaner %% and to take advantage of the content formatting junk. So here they %% are. %% %% In most cases, this content can be added as a VARIETY. %% %% NAME: *[city designation]_[building name abbreviated]_[content type] %% *KOSSP_CrTo_Content Content %% Kosaka Spinner, Crystal Tower Content name requires <*KOSSP_CrTo_Content> minimap <............1.......#&+&#> element1 team1 teamdata1 sub Persona 1 greeting *GoTrain <*SKILL_TRAINING GoGoodbye> *GoCheckEligibility <*ENEMY_CHECK GoCheckFaction ChatNPCFac GoNil> GoCheckFaction *GoCheckSkills <*FACTION_ENTRANCE_EXAM GoJobOffer ChatNPCFac> *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> *GoJobOffer <*FACTION_JOIN_OFFER ChatNPCFac GoJoin> *GoJoin <*FACTION_JOIN_ACCEPT ChatNPCFac> end inv NPC Politician job name chardesc male age 5 SDL_COLORS <239 198 58 150 112 89 222 222 156> SetFaction 10 end %% *CLUND_MLP2_Content %% Content for Memorial Land. Each game won will add one to L2; %% once all four games are won, the property manager will let the %% PC into the Ruins of Terror. Content name % This ride will test the PC's Vitality requires <*CLUND_MLP2_Content> minimap < 1 &#& & > Element1 inv Elevator name % V1 = Have completed ride use GoWinRoll GoFailRoll GoCompleted Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 end Content name % This ride will test the PC's Athletics requires <*CLUND_MLP2_Content> minimap < 1 &#& & > Element1 inv Elevator name % V1 = Have completed ride use GoWinRoll GoFailRoll GoCompleted Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 end Content name % This ride will test the PC's Mecha Piloting requires <*CLUND_MLP2_Content> minimap < 1 &#& & > Element1 inv Elevator name % V1 = Have completed ride use GoWinRoll GoFailRoll GoCompleted Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 end Content name % This ride will test the PC's Concentration requires <*CLUND_MLP2_Content> minimap < 1 &#& & > Element1 inv Elevator name % V1 = Have completed ride use GoWinRoll .bear GoFailRoll GoCompleted Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 end %% *CLUND_MLP_Content %% Content for the entry dome to Memorial Land. Content %% Mecha Display 1 requires <*CLUND_MLP_Content> minimap <####################1#2#3> element1 element2 element3 inv STC MECHAMODEL-1 sdl_colors <211 195 82 222 222 156 211 195 82> SDL_SPRITE PDir 2 name use Msg1 STC MECHAMODEL-1 sdl_colors <211 195 82 222 222 156 211 195 82> SDL_SPRITE PDir 2 use name Msg1 STC MECHAMODEL-1 sdl_colors <211 195 82 222 222 156 211 195 82> SDL_SPRITE PDir 2 name use Msg1 end Content %% Mecha Display 2 requires <*CLUND_MLP_Content> minimap <####################1#2#3> element1 element2 element3 inv STC MECHAMODEL-1 sdl_colors <211 195 82 222 222 156 211 195 82> SDL_SPRITE PDir 2 name use Msg1 STC MECHAMODEL-1 sdl_colors <211 195 82 222 222 156 211 195 82> PDir 2 name use Msg1 STC MECHAMODEL-1 sdl_colors <211 195 82 222 222 156 211 195 82> PDir 2 name use Msg1 end Content %% Hoblen's gift shop. requires <*CLUND_MLP_Content> minimap <12345........6......&---&LUCRE> element1 element2 element3 element4 element5 element6 team6 teamdata6 sub Persona 6 *greeting <*NiceToMeetYou GoShop> GoShop *GoBye <*GOODBYE> result1 .wares result2 Msg1 Msg2 Prompt1 Prompt2 end inv STC GAMBLING-1 PDir 1 STC MANNEKIN-M PDir 2 STC MANNEKIN-F PDir 2 STC SHOPDISPLAY-WEAPONS-3 PDir 2 STC SHOPDISPLAY-ARMOR-2 PDir 2 NPC Shopkeeper name chardesc female easygoing melancholy sociable Age -4 end %% *WAGSP_L5LA2_Content %% Content for the 2nd level of the L5Law Academy in Wagner Spinner. Content %% The master of forensics requires <*WAGSP_L5LA2_Content> minimap <............1.......#-&-#> element1 team1 teamdata1 sub Persona 1 special *greeting <*NiceToMeetYou GoCheckFac> GoCheckFac *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> result1 .skills <1 2 3 4 5 6 15 21 27> result2 Msg1 Msg2 Prompt1 Prompt2 end inv NPC Forensic Investigator NonCombatant SetFaction 9 end Content %% The master of mecha requires <*WAGSP_L5LA2_Content> minimap <........1...........&---&> element1 team1 teamdata1 sub Persona 1 special *greeting <*NiceToMeetYou GoCheckFac> GoCheckFac *GoChat <*MISC_CHATTER> *GoBye <*GOODBYE> result1 .wares result2 Msg1 Msg2 Msg2_1 Msg2_2 Prompt1 Prompt2 end inv NPC Police Officer name job SDL_PORTRAIT SDL_COLORS <30 30 90 241 254 223 122 88 193> SetFaction 9 NonCombatant Shopping 12 Age 17 chardesc easygoing pragmatic heroic female end Content %% The master of weapons requires <*WAGSP_L5LA2_Content> content element1 team1 teamdata1 sub Persona 1 special *greeting <*NiceToMeetYou GoCheckFac> GoCheckFac *GoChat <*MISC_CHATTER> *GoBye <*GOODBYE> result1 .wares result2 Msg1 Msg2 Prompt1 Prompt2 end inv NPC Police Officer job SetFaction 9 NonCombatant Shopping 12 end %% *WAGSP_L5LA_Content %% Content for the L5Law Academy in Wagner Spinner. Content requires <*WAGSP_L5LA_Content> minimap <............1.......#-&-#> element1 team1 teamdata1 sub Persona 1 special greeting *GoTrain <*SKILL_TRAINING GoGoodbye> *GoCheckEligibility <*ENEMY_CHECK GoCheckFaction ChatNPCFac GoNil> GoCheckFaction *GoCheckSkills <*FACTION_ENTRANCE_EXAM GoJobOffer ChatNPCFac> *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> *GoJobOffer <*FACTION_JOIN_OFFER ChatNPCFac GoJoin> *GoJoin <*FACTION_JOIN_ACCEPT ChatNPCFac> end inv NPC Police Officer job SetFaction 9 end %% *RISSP_SFHQ_Content %% Content for the Space Force HQ. Includes a recruiter for the Rishiri Dominion and an %% Aegis mecha shop. Content requires <*RISSP_SFHQ_Content> minimap <............1.......#&+&#> element1 team1 teamdata1 sub Persona 1 special greeting *GoTrain <*SKILL_TRAINING GoGoodbye> *GoCheckEligibility <*ENEMY_CHECK GoCheckFaction ChatNPCFac GoNil> GoCheckFaction *GoJoin <*FACTION_JOIN_ACCEPT ChatNPCFac> result1 GoR1Fail result2 *result3 <*GoodBye> result4 result5 Msg1 Msg2 Msg3 Msg4 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 end inv NPC Mecha Pilot SetFaction 11 end Content requires <*RISSP_SFHQ_Content> minimap <............1.......#---#> element1 team1 teamdata1 sub Persona 1 special greeting GoOpenShop result1 .wares *result2 <*GoodBye> Msg1 Msg2 Msg3 Prompt1 Prompt2 end inv NPC Shopkeeper SetFaction 4 Repair 10 end %% *RISSP_L5AE_Content %% The ambassador for the Aegis embassy in Rishiri Spinner Content requires <*RISSP_L5AE_Content> minimap <............1.......#&+&#> element1 team1 teamdata1 sub Persona 1 special *greeting <*NiceToMeetYou GoTalk> GoTalk result1 result2 result3 result4 result5 result6 result7 result8 result9 *result10 <*GoodBye> result11 result12 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt2 Prompt3 Prompt4 Prompt4_1 Prompt5 Prompt6 Prompt7 Prompt7_1 Prompt8 Prompt9 Prompt10 Prompt11 Prompt12 end inv NPC Politician job SetFaction 4 end %% *GALSP_NI_Content %% NPCs and stuff for the Nova Initiative in Galconde Spinner Content %% Technology Professor - free skill trainer requires <*GALSP_NI_Content> minimap <............1.......##+##> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoSchool> GoSchool Result1 .skills <13 21 22> *Result2 <*GoodBye> Msg1 Msg2 Prompt1 Prompt2 end inv NPC Scientist job end Content %% Laboratory - guaranteed science toy requires <*GALSP_NI_Content> minimap <#.2.#.......1.......##+##> element1 team1 teamdata1 Element2 sub Persona 1 *greeting <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> end inv NPC Scientist Prop 10 name SDL_SPRITE roguechar <+> use CLUE_SCIENCE GoNoDice GoBeenBefore Msg1 Msg2 Msg3 Msg4 end %% *GALSP_TO_Content %% NPCs for the tech outlet in Galconde Spinner Content %% Mechanic at the Tech Outlet- may become skill trainer requires <*GALSP_TO_Content> content element1 team1 teamdata1 sub Persona 1 % V1 = Have accepted/denied skill training *greeting <*NiceToMeetYou GoCheckGift> GoCheckGift GoOpenShop Result1 Result2 .facs Result3 .wares *Result4 <*GoodBye> Result5 .skills <13 16 22> Result6 Result7 Msg1 Msg2 Msg3 Msg4 <%name1% in Galconde Spinner agreed to teach you about mecha design.> Msg5 Msg6 <%name1% in Galconde Spinner gave you a free mecha.> Msg7 Msg8 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 CPrompt5 Prompt6 Prompt7 end inv NPC Mechanic name CharDesc Female Heroic Lawful Sociable Age 12 Shopping 15 SDL_Portrait SDL_Colors <80 80 85 245 213 160 168 133 230> end %% *GALSP_Guyatt_Content %% NPCs for Guyatt Medical in Galconde Spinner Content requires <*GALSP_Guyatt_Content> minimap <............1.......##+##> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoShop> GoShop result1 .wares Keywords *result2 <*GOODBYE> Msg1 Msg2_1 Msg2_2 Msg2_3 Prompt1 Prompt2 end inv NPC CyberDoc Shopping 15 name Age 21 chardesc Male Pragmatic Heroic Melancholy Sociable SDL_Portrait SDL_Colors <201 205 229 255 230 210 255 120 80> end %% *YATSP_RB2_Content %% NPCs for Rocket Penthouse, aka Rocket Base 2 Content requires <*YATSP_RB2_Content> element1 team1 teamdata1 element2 team2 teamdata2 minimap <#...#..2....1.......#---#> sub Persona 1 greeting GoTeach GoChat result1 .skills <10 11 15> *result2 <*GoodBye> result3 Msg1 Msg2 Msg3 <\PC . I have been watching your advancement... Remember this, that legends are written of the bold. Someday you may become legend, but for right now I suggest you concentrate on simply being.> Prompt1 Prompt2 Prompt3 <[Continue]> Persona 2 greeting GoChat *GoEnd <*GoodBye> result1 .skills <17 25> result2 Msg1 Msg2 Msg3 Prompt1 Prompt2 end inv NPC Rocket Star name job SetFaction 14 Age 42 Chardesc Male Sociable Passionate Spiritual Heroic SDL_PORTRAIT SDL_COLORS <166 47 32 255 212 195 115 100 13> Renown 65 NonCombatant NPC Singer name job SetFaction 14 Age -3 Chardesc Female Passionate Sociable Melancholy Heroic Combatant end Content requires <*YATSP_RB2_Content> element1 team1 teamdata1 minimap <............1.......#---#> sub Persona 1 special Greeting GoChat result1 .wares *result2 <*GoodBye> Msg1 Msg2 Prompt1 Prompt2 end inv NPC Rocket Star name job SetFaction 14 Shopping 13 Age 8 Chardesc Male Sociable Passionate Villainous NonCombatant end Content requires <*YATSP_RB2_Content> element1 team1 teamdata1 minimap <............1.......##+##> sub Persona 1 greeting end inv NPC Rocket Star end %% *YATSP_RB1_Content %% NPCs for Rocket Base 1 Content requires <*YATSP_RB1_Content> element1 team1 teamdata1 minimap <............1.......#&+&#> sub Persona 1 Greeting result1 GoBadWares .wares1 .wares2 *result2 <*GoodBye> Msg1 Msg2 Prompt1 Prompt2 end inv NPC Cyberdoc name SetFaction 14 Shopping 10 Age 8 Chardesc Female sociable passionate cheerful heroic end Content requires <*YATSP_RB1_Content> content element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoCheckEnemy> *GoCheckEnemy <*ENEMY_CHECK GoShop ChatNPCFac GoIsEnemy> GoIsEnemy GoShop GoMinorShop *GoBye <*GOODBYE> result1 result2 result3 .wares Msg1 Msg2 Msg3 Msg4 Msg4_1 Msg4_2 CMsg4_2 Msg4_3 CMsg4_3 Prompt1 Prompt2 Prompt3 end inv NPC Mechanic name SetFaction 14 Shopping 10 SDL_PORTRAIT <> SDL_COLORS <> CharDesc Male Sociable Passionate Passionate Cheerful Pragmatic Age 2 end %% *THESP_HM_Content %% NPCs for Hovel Manor in Theles Spinner Content requires <*THESP_HM_Content> element1 team1 teamdata1 % L%id%01 = Monologue Counter start Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 sub Persona 1 special greeting Msg1 end inv NPC Citizen chardesc old melancholy passionate spiritual end %% *THESP_SH_Content %% NPCs for Salvation Hospital in Theles Spinner Content requires <*THESP_SH_Content> minimap <............1.......#&+&#> element1 team1 teamdata1 sub Persona 1 special % V1 = Have been told about offer. % V2 = Have sold some body tissue already. % V3 = Sell tissue timer % V6 .. V9 = Have sold appropriate body part. &MuscleCost <150000> &EyeCost <200000> &SpineCost <250000> &BrainCost <300000> *greeting <*NiceToMeetYou GoOpenShop> GoOpenShop *GoBye <*GOODBYE> GoOperate result1 GoR1Base .wares result2 result3 result4 GoR4Fail result5 result6 GoR6Fail result7 GoR7Fail result8 GoR8Fail result9 GoR9Fail result10 result11 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt5 Prompt6 CPrompt6 Prompt7 CPrompt7 Prompt8 CPrompt8 Prompt9 CPrompt9 Prompt10 Prompt11 <[Continue]> end inv NPC Cyberdoc name SDL_PORTRAIT SDL_COLORS <136 141 101 255 233 225 244 216 28> chardesc Male Criminal Melancholy Easygoinge Age 14 Shopping 15 end %% *THESP_FR_Content %% NPCs for Factory Rejects in Theles Spinner Content requires <*THESP_FR_Content> minimap <............1.......#&+&#> element1 team1 teamdata1 sub Persona 1 greeting *GoTrain <*SKILL_TRAINING GoGoodbye> *GoCheckEligibility <*ENEMY_CHECK GoCheckFaction ChatNPCFac GoNil> GoCheckFaction *GoCheckSkills <*FACTION_ENTRANCE_EXAM GoJobOffer ChatNPCFac> *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> *GoJobOffer <*FACTION_JOIN_OFFER ChatNPCFac GoJoin> *GoJoin <*FACTION_JOIN_ACCEPT ChatNPCFac> end inv NPC Privateer SetFaction 6 end %% *THESP_TheSpPo_Content %% NPCs for Theles Spinner's spaceport. Content requires <*THESP_TheSpPo_Content> minimap <............1.......##+##> element1 team1 teamdata1 sub Persona 1 special % V1 = Have spoken counter *Greeting <*NiceToMeetYou GoChat> GoChat GoChaos result1 result2 result3 result4 result5 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 end inv NPC Police Officer name Age 7 chardesc female heroic passionate SDL_PORTRAIT SDL_COLORS <30 30 90 150 112 89 172 225 175> SetFaction 9 end %% *MAQSP_ComTow_Content %% NPCs for Comet Tower in Maquise Spinner Content requires <*MAQSP_ComTow_Content> minimap <............1.......##+##> element1 team1 teamdata1 content1 sub Persona 1 greeting *GoTrain <*SKILL_TRAINING GoGoodbye> *GoCheckEligibility <*ENEMY_CHECK GoCheckFaction ChatNPCFac GoNil> GoCheckFaction *GoCheckSkills <*FACTION_ENTRANCE_EXAM GoJobOffer ChatNPCFac> *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> *GoJobOffer <*FACTION_JOIN_OFFER ChatNPCFac GoJoin> *GoJoin <*FACTION_JOIN_ACCEPT ChatNPCFac> end inv NPC Corporate Executive name job SDL_PORTRAIT SDL_COLORS <234 180 88 255 212 195 50 50 50> Chardesc Male Villainous Sociable Melancholy SetFaction 2 Age 12 end %% *ATHSP_TA_MusicShop %% At the Atrium in Athera Spinner, there is a guaranteed music shop. Content requires <*ATHSP_TA_MusicShop> element1 team1 teamdata1 minimap <..1.................#---#> sub Persona 1 % V1 = Have become skill trainer (1) or PC has declined the offer (-1) *greeting <*NiceToMeetYou GoCheckOFfer> GoCheckOffer GoOpenStore result1 .wares *result2 <*GoodBye> result3 .skills <8 17 18 25 28> result4 result5 result6 result7 result8 Msg1 Msg2 Msg3 Msg4 <\PC , I'd like a word with you, if I could. I told you once that Athera Spinner is the place where dreams are born- far too often, it is also the place where dreams come to die. When I came here first I dreamt of becoming a star.> Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 <[Continue]> Prompt5 Prompt6 Prompt7 Prompt8 end inv NPC Singer name Combatant Age 5 CharDesc Lawful Sociable Easygoing Female Shopping 12 SDL_PORTRAIT SDL_COLORS <144 166 195 250 183 139 103 3 45> end %% *EMESP_EB_Investment %% At the Emerald Bank, there's a place to invest money. The success or failure of the %% PC's investment will be determined by the Shopping skill. Content requires <*EMESP_EB_Investment> element1 team1 teamdata1 minimap <..1............##+###...#> sub Persona 1 rumor % This is going to be a complicated one. Investing here is pretty much like % gambling- if your Shopping skill exceeds a target number, you get a return % on your investment. Otherwise you lose your money. % Every time the PC successfully gains money, the difficulcy number for the % next transaction increases. % % V1 = Cash units invested % V2 = Renown at last withdrawl % V3 = Initialization Counter % V4 = Cash units requested; doesn't get stored until transaction complete % V5 = Risk level; 0=Low, 1=Medium, 2=High % V6 = Renown at last deposit % V7 = Shopping roll at last deposit % V8 = Investment Timer % V9 = Number of successful investments % V10 = Investment target % % InvestmentCost = the cost of investing X cash units. &InvestmentCost <* ? 25000> % *Greeting <*NiceToMeetYou GoOpenBank> GoOpenBank GoNoClients GoSelectStock GoConfirm GoNotYet GoInvestSucceed GoCheckMedium GoLowMoney GoInvestFailed result1 result2 result3 result4 *result5 <*GoodBye> result6 result7 result8 result9 result10 result11 result12 result13 result14 result15 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7_1 CMsg7_1 Msg7_2 CMsg7_2 Msg7_3 CMsg7_3 Msg8_1 CMsg8_1 Msg8_2 CMsg8_2 Msg8_3 CMsg8_3 Msg9 Msg10 Msg11 Prompt1 CPrompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 Prompt6 CPrompt6 Prompt7 CPrompt7 Prompt8 CPrompt8 Prompt9 CPrompt9 Prompt10 CPrompt10 Prompt11 CPrompt11 Prompt12 Prompt13_1 Prompt13_2 Prompt13_3 Prompt13_4 Prompt13_5 Prompt14_1 Prompt14_2 Prompt14_3 Prompt14_4 Prompt14_5 Prompt15_1 Prompt15_2 Prompt15_3 Prompt15_4 Prompt15_5 end inv NPC Corporate Executive job end %% *MAQSP_PG_PrivateerShop %% One shop at the Privateer's Guild sells privateer weapons, armor, and %% mecha. Content requires <*MAQSP_PG_PrivateerShop> element1 team1 teamdata1 minimap <..1.................#---#> sub Persona 1 % V1 = Stupid question counter *greeting <*NiceToMeetYou GoOpenStore> GoOpenStore result1 .wares *result2 <*GoodBye> result3 Msg1 Msg2 Msg3 Prompt1 Prompt2 Prompt3 CPrompt3 end inv NPC Privateer name NonCombatant SetFaction 6 Age 23 % He isn't lying about the pistols, check out that Lawful rep CharDesc Lawful Pragmatic Passionate Male Shopping 12 SDL_PORTRAIT SDL_COLORS <20 90 130 150 112 89 168 153 230> end %% *CSNSP_SilSan_Content %% The content of the silver fortress private level. Content %% Weaponmaster requires <*CSNSP_SilSan_Content> element1 team1 teamdata1 minimap <..1.................#---#> sub Persona 1 *greeting <*NiceToMeetYou GoCheckFac> GoCheckFac *GoChat <*MISC_CHATTER> *GoBye <*GOODBYE> result1 .wares result2 .skills <1 2 3 4 5 6 15 16 22> result3 Msg1 Msg2 Msg3 Prompt1 Prompt2 Prompt3 end inv NPC Knight name NonCombatant Age 19 SetFaction 3 chardesc Passionate Shy Cheerful Lawful Heroic Female Shopping 15 job SDL_PORTRAIT SDL_COLORS <150 205 229 150 112 89 103 3 45> end Content %% Cybersurgeon requires <*CSNSP_SilSan_Content> element1 team1 teamdata1 minimap <..1.................#---#> sub Persona 1 *Greeting <*NiceToMeetYou GoCheckFac> GoCheckFac *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> result1 .wares result2 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg3 Prompt1 Prompt2 end inv NPC Cyberdoc name SDL_PORTRAIT SDL_COLORS <150 205 229 123 63 0 55 25 81> Age 16 Shopping 15 SetFaction 3 chardesc Pragmatic Melancholy Lawful Heroic Male Combatant end Content %% Chaplain requires <*CSNSP_SilSan_Content> element1 team1 teamdata1 element2 minimap <..1....2............#---#> sub Persona 1 *Greeting <*NiceToMeetYou GoCheckFac> GoCheckFac *GoChat <*MISC_CHATTER> *GoBye <*GOODBYE> result1 .skills <14 21 27> result2 Msg1 Msg2 Prompt1 Prompt2 end inv NPC Knight name SDL_PORTRAIT SDL_COLORS <150 205 229 255 212 195 65 121 119> Age 22 SetFaction 3 chardesc Spiritual Easygoing Lawful Heroic Male Mysticism 10 job job_desig STC SHRINE-1 end %% *CSNSP_SilFor_Content %% The content of the silver fortress public level. Content %% Museum requires <*CSNSP_SilFor_Content> element1 element2 place2 <1> element3 element4 minimap <3...4.......1.......&---&> Get%2% .%id%_GoAttack inv STC DISPLAYCASE-1 name <%name2% Display> % V1 = Lock Picked % V2 = Code Breaking Counter use GoNotAllowed GoLocked CLUE_CODEBREAKING GoCheckStealth GoAlarm GoUnlocked GoCBFail Msg1 Msg2 Msg3 Msg4 Msg5 STC COMPUTER-1 USE Msg1 STC COMPUTER-1 USE Msg1 end Content %% Keefa's Garage requires <*CSNSP_SilFor_Content> content element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoCheckEnemy> *GoCheckEnemy <*ENEMY_CHECK GoShop 3 GoIsEnemy> GoIsEnemy GoShop GoMinorShop *GoBye <*GOODBYE> result1 result2 result3 result4 result5 .wares Msg1 Msg2 Msg3 Msg3_1 CMsg3_1 Msg3_2 CMsg3_2 Msg4 Msg5 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 end inv NPC Mechanic name SetFaction 3 Shopping 10 SDL_PORTRAIT SDL_COLORS <150 205 229 255 215 195 152 61 97> CharDesc Female Lawful Shy Easygoing Pragmatic Age 5 end GH2/series/PFRAG_MechaArena.txt0000644000175000017500000004631111326004535015036 0ustar kaolkaol%% %% Arena Persona Fragments %% %% This file contains persona fragments needed by the mecha arenas. %% Mostly this is for the managers. %% TYPE: *RejectChallenge %% The PC has told the NPC that he isn't ready to fight yet. The NPC will reply. Persona requires <*RejectChallenge> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *ChallengerStatusReport %% The PC is supposed to fight NPC1; the arena manager will tell him the current status %% of things. This procedure checks for lost battles, proceeding battles, and the death %% of the NPC. %% PARAM1: CID of the challenger %% PARAM2: ID of the arena %% PARAM3: Challenger died exit label Persona requires <*ChallengerStatusReport> START .%id%_GoCheckAnything .%id%_GoCheckEligibility Msg%id%01 <\PERSONA %1% defeated you in the arena. Try challenging \OPR %1% again later; you can't advance in the competition until you defeat \OPR %1% .> Msg%id%02 Msg%id%03 %% TYPE: *YourChallengerDied %% The challenger the PC was supposed to fight has died, making that fight impossible. %% This pfrag will instead set up a generic encounter at the arena, and cancel the %% challenger quest. The PC will be jumped directly to the arena. %% PARAM1: ID of the arena %% PARAM2: QID of subquest Persona requires <*YourChallengerDied> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *MeetMeAtArena %% The NPC will meet the PC at the arena for a big fight. %% PARAM1: ID of the arena %% PARAM2: Exit label Persona requires <*MeetMeAtArena Easygoing> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Persona requires <*MeetMeAtArena> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *ChallengeMeLater %% The PC can't challenge this NPC again so soon; maybe in 24 hours. Persona requires <*ChallengeMeLater> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *ChallengeArenaWin %% The PC has won against a NPC challenger. Give some money %% PARAM1: CID of the challenger. Be careful, he may be dead. %% PARAM2: ID of the arena, needed for calculating the cash prize %% PARAM3: Arena reset exit label; called when the prize is given Persona requires <*ChallengeArenaWin> START Msg%id%01_10 CMsg%id%01_10 Msg%id%01_20 CMsg%id%01_20 Persona requires <*ChallengeArenaWin> START .%id%_MekFac Msg%id%01 Msg%id%01_20 CMsg%id%01_20 Persona requires <*ChallengeArenaWin L5PAT> START .%id%_MekFac Msg%id%01 Msg%id%01_20 CMsg%id%01_20 %% TYPE: *BasicArenaWin %% The PC has won a non-championship match. Give some money %% PARAM1: ID of the arena, needed for calculating the cash prize %% PARAM2: Arena reset exit label; called when the prize is given Persona requires <*BasicArenaWin> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *BasicArenaLoss %% The PC has lost a match. Basically do nothing. %% PARAM1: ID of the arena; might be needed, better include it anyway %% PARAM2: Arena reset exit label Persona requires <*BasicArenaLoss Melancholy> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Persona requires <*BasicArenaLoss> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *CharaArenaWin %% The PC has won a non-championship match at a personal scale %% arena. Give some money %% PARAM1: ID of the arena, needed for calculating the cash prize %% PARAM2: Arena reset exit label; called when the prize is given Persona requires <*CharaArenaWin> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *CharaArenaLoss %% The PC has lost a match at a personal scale arena. The arena manager will %% be surprised that the PC is still alive. %% PARAM1: ID of the arena; might be needed, better include it anyway %% PARAM2: Arena reset exit label Persona requires <*CharaArenaLoss> START Msg%id%01 Msg%id%01_1 Msg%id%01_2 %% TYPE: *ArenaIsBusy %% There's no fight right now, but later there will be. %% PARAM1: ID of the arena; might be needed, better include it anyway Persona requires <*ArenaIsBusy> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *ArenaIsFull %% The PC should be in the arena fighting right now. %% PARAM1: ID of the arena; might be needed, better include it anyway Persona requires <*ArenaIsFull> START Msg%id%01 %% TYPE: *RefuseArenaFight %% The PC doesn't want to fight right now. %% PARAM1: ID of the arena; might be needed, better include it anyway Persona requires <*RefuseArenaFight> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *StartArenaBattle %% The PC has agreed to fight. Do something. %% PARAM1: ID of the arena; might be needed, better include it anyway %% PARAM2: Arena fight prep exit label Persona requires <*StartArenaBattle Cheerful> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Persona requires <*StartArenaBattle Melancholy> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Persona requires <*StartArenaBattle> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 %% TYPE: *StartCharaBattle %% The PC has agreed to fight in a personal-scale arena. %% PARAM1: ID of the arena; might be needed, better include it anyway %% PARAM2: Arena fight prep exit label Persona requires <*StartCharaBattle> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 GH2/series/PLOT_TYPE_Resort.txt0000644000175000017500000000734311374513723015121 0ustar kaolkaol%% %% Plots for resorts and vacation spots. %% Plot name desc requires <*GENERAL Resort> % E1 is the city itself % E2 is a local meeting or hotel scene % E3 is the NPC to duel % E4 is a location for the duel % E5 is the NPC's home town Element1 Element2 Element3 Place3 <2 (Guards) sd ally> Element4 Element5 % P%id%01 = Time Limit update start .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLose .%id%_GoEnd %% If E3 dies, end the plot. end Msg%id%01 Msg%id%02 % SubPlot1 = The Duel % SubPlot2 = Win the Duel % SubPlot3 = Lose the Duel SubPlot1 <*NC_Duel 3 2 4> SubPlot2 <*NC_WinDuelReturn 3 5> SubPlot3 <*NC_LoseDuelReturn 3 5> sub Persona 3 rumor%id% <%name3% is enjoying an exciting vacation at %name2%.> special % v%id%01 = Renown Calculator greeting .%id%_GoChat *.%id%_GoUnknownGreeting <*NiceToMeetYou .%id%_GoChallenge> *.%id%_GoKnownGreeting <*HowAreYou .%id%_GoChallenge> .%id%_GoChallenge .%id%_GoStartDuel *.%id%_GoAcceptChallenge <*IAcceptYourChallenge %4%> .%id%_GoFail .%id%_GoEndPlot *.%id%_GoBye <*GoodBye> result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 <[Continue]> end GH2/series/MEGA_CORE_MinorMission.txt0000644000175000017500000001753111374513723016173 0ustar kaolkaol%% %% *:CS_SideMission Content %% %% An NPC will ask the PC to do something bigger than a minor mission below. %% In fact, this will probably be as big as a MAIN subplot. Woo-hoo! %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% %% The persona for E1 requires a .%id%_GoInit script to explain the mission. %% %% PARAM1: The NPC who will be giving the mission. %% Content name requires <*:CS_SideMission ~1:ENEMY -1:NOFAC> desc % E1 is the mission-giver % E2 is an environs scene % E3 is the encounter % E4 is the mutual enemy faction % E5 is the enemy to defeat Element2 Element3 Place3 <2> Element4 Element5 Place5 <3 (Enemies) sd enemy> HINT_%id% <:%id1%> % P%id%01 = Initialization Counter % P%id%02 = Decimation Counter % P%id%03 = Reinforcements Counter %% FAIL CONDITIONS %% - SubPlot1 (DiscoverNPCMission) Lost update end .%id2%_%plotid2%_GoDecimation .%id2%_%plotid2%_GoReinforcements %% SubPlot1 = Discover the NPC's mission %% SubPlot2 = Gain advantage vs NPC SubPlot1 <*:CS_DiscoverNPCMission&GatheringForces 2 3 5> SubPlot2 <*:CS_GainAdvantageVsNPC&Decimation&Reinforcements 5> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% asked you to defeat %name5% of %name4%.> Persona 5 special *greeting <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> MetaScene 3 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoBigDeploy ParaX 45 ParaY 45 team 3 name setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end inv stc CORE-ACTIVATABLE name <%name5%'s Lance> NPC Mecha Pilot end %% %% *:CS_MinorMission Content %% %% An NPC will give the PC a minor task; usually, this will be done %% in exchange for some help or information that the NPC might provide. %% Note that because the mission description is given at initialization, %% these missions shouldn't involve anything that a player might want %% to opt out of, such as making enemies. %% %% If the mission is completed, this subplot will be won. If it is failed, %% this subplot will be lost. %% %% The persona for E1 requires a .%id%_GoInit script to explain the mission. %% %% PARAM1: The NPC who will be giving the mission. %% Content name requires <*:CS_MinorMission> desc Size 4 % E1 is the mission-giver % E2 is a space or asteroid environs scene % E3 is the encounter Element2 Element3 Place3 <2> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% asked you to clear out an agricultural microstation.> MetaScene 3 special boxmap MapWidth 17 MapHeight 17 special FloorType 1 nu2 Msg1 Msg2 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Deploy type Forest Forest Forest Forest Forest end end inv STC CORE-STATIONARY name end Content name requires <*:CS_MinorMission (1:ADVEN|1:CORPO|1:CRAFT|1:LABOR|1:MILIT|1:POLIC|1:POLIT|1:TRADE|1:MOOK_)> desc Size 4 % E1 is the mission-giver % E2 is an outdoors scene % E3 is the encounter Element2 Element3 Place3 <2> sub Persona 1 .%id%_GoInit Msg%id%01 Msg%id%02 <%name1% asked you to defeat some raiders in %name2%.> MetaScene 3 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 nu1 nu2 Msg1 Msg2 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-MECHAENCOUNTER end %% %% *:CS_CaptureBuilding Content %% %% The PC will have to capture a building. This is a pretty minor mission, so I'm %% sticking it here. %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% Winning and losing the mission doesn't produce any alerts here; do that in the %% parent plot scripts. %% %% PARAM1: The outdoors scene where the building will be located. %% Content name requires <*:CS_CaptureBuilding> desc Size 5 % E1 is the outdoors scene % E2 is the building exterior encounter Element2 Place2 <1> sub MetaScene 2 2 % L1 = Encounter Over Counter MapWidth 35 MapHeight 35 nu1 nu2 nu3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy home team 3 name rect name desig Height 5 Width 5 sub SuperProp requires <*Fortress> SetTeam 3 end end end inv STC CORE-STATIONARY name end GH2/series/RANCON_Default.txt0000644000175000017500000006541511341163445014570 0ustar kaolkaol% RANDOM CONTENT FILE %% %% *JUNKYARD_SALVAGE Content %% Some SF:2 items that the PC can pick up and lug away. %% Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv STC MAC-4 end Content requires <*JUNKYARD_SALVAGE (!Lo|!Md)> Element1 minimap < ... .1. ... > inv STC VC-5 end Content requires <*JUNKYARD_SALVAGE (!Lo|!Md)> Element1 minimap < ... .1. ... > inv STC RG-8 end Content requires <*JUNKYARD_SALVAGE (!Hi|!Ex)> Element1 minimap < ... .1. ... > inv STC RG-16 end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv STC GR-12 end Content requires <*JUNKYARD_SALVAGE (!Md|!Hi)> Element1 minimap < ... .1. ... > inv STC GR-24 end Content requires <*JUNKYARD_SALVAGE !Ne> Element1 minimap < ... .1. ... > inv STC SC-9 end Content requires <*JUNKYARD_SALVAGE !Lo> Element1 minimap < ... .1. ... > inv STC MRIF-5 end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv STC LAS-5 end Content requires <*JUNKYARD_SALVAGE (!Md|!Hi)> Element1 minimap < ... .1. ... > inv STC LAS-10 end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo|!Md)> Element1 minimap < ... .1. ... > inv STC PAR-2 end Content requires <*JUNKYARD_SALVAGE (!Lo|!Md|!Hi)> Element1 minimap < ... .1. ... > inv STC PAR-6 end Content requires <*JUNKYARD_SALVAGE (!Hi|!Ex)> Element1 minimap < ... .1. ... > inv STC PAR-13 end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv STC SML-5 end Content requires <*JUNKYARD_SALVAGE (!Hi|!Ex)> Element1 minimap < ... .1. ... > inv MLauncher 2 sub STC NUKE-20 end end Content requires <*JUNKYARD_SALVAGE !Ex> Element1 minimap < ... .1. ... > inv MLauncher 4 sub STC NUKE-20 Magazine 2 end end Content requires <*JUNKYARD_SALVAGE !Ne> Element1 minimap < ... .1. ... > inv Gun 1 Name Scale 2 Range 3 caliber Speed 4 BV 4 Acc -1 mass -2 Magazine 280 sub Ammo 1 caliber end end Content requires <*JUNKYARD_SALVAGE !Ne> Element1 minimap < ... .1. ... > inv melee 5 name desc Scale 2 end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv Gun 3 Name Scale 2 Range 3 caliber Speed 4 BV 4 Acc -1 Magazine 120 sub Ammo 3 caliber end end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv Gun 12 Name Scale 2 Range 7 caliber Speed 1 Acc -1 Magazine 20 sub Ammo 12 caliber end end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo)> Element1 minimap < ... .1. ... > inv melee 14 name type Scale 2 acc 1 end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo|!Md)> Element1 minimap < ... .1. ... > inv melee 12 name Scale 2 acc 1 type end Content requires <*JUNKYARD_SALVAGE (!Lo|!Md)> Element1 minimap < ... .1. ... > inv melee 16 name Scale 2 acc 1 speed 3 mass -4 type end Content requires <*JUNKYARD_SALVAGE (!Md|!Hi)> Element1 minimap < ... .1. ... > inv melee 18 name Scale 2 type end Content requires <*JUNKYARD_SALVAGE (!Hi|!Ex)> Element1 minimap < ... .1. ... > inv EMelee 23 Name Scale 2 Type Mass 3 inv PowerSource 5 mass -4 name end end Content requires <*JUNKYARD_SALVAGE !Ex> Element1 minimap < ... .1. ... > inv gun 10 name range 6 Scale 2 type BV 2 Speed 3 caliber <90mm caseless> mass -2 Magazine 90 sub Ammo 10 caliber <90mm caseless> end end Content requires <*JUNKYARD_SALVAGE !Ne> Element1 minimap < ... .1. ... > inv Gun 14 Scale 2 name caliber <90mm shell> Range 5 Acc -1 Recharge 1 Magazine 20 sub Ammo 14 caliber <90mm shell> end end Content requires <*JUNKYARD_SALVAGE (!Md|!Hi)> Element1 minimap < ... .1. ... > inv Gun 20 Scale 2 name caliber <210mm shell> Range 8 Acc 1 Speed 3 Magazine 20 sub Ammo 20 caliber <210mm shell> end end Content requires <*JUNKYARD_SALVAGE !Md> Element1 minimap < ... .1. ... > inv Gun 20 Scale 2 name caliber <500mm ferrous ball> Range 7 Recharge 1 type Mass -2 Magazine 15 sub Ammo 20 caliber <500mm ferrous ball> end end Content requires <*JUNKYARD_SALVAGE (!Lo|!Md|!Hi)> Element1 minimap < ... .1. ... > inv BeamGun 6 name desc UsesKnowledge scale 2 Range 6 Acc -1 type end Content requires <*JUNKYARD_SALVAGE (!Ne|!Lo|!Md)> Element1 minimap < ... .1. ... > inv Melee 7 name type speed 3 mass -4 scale 2 end Content requires <*JUNKYARD_SALVAGE (!Hi|!Ex)> Element1 minimap < ... .1. ... > inv MLauncher 16 Scale 2 sub STC SWM-2 name type magazine 80 end end Content requires <*JUNKYARD_SALVAGE (!Lo|!Md)> Element1 minimap < ... .1. ... > inv Gun 15 Scale 2 name desc caliber Range 6 Speed 1 type Magazine 36 sub Ammo 15 caliber end end Content requires <*JUNKYARD_SALVAGE (!Md|!Hi)> Element1 minimap < ... .1. ... > inv Gun 11 name caliber Scale 2 type range 5 Speed 2 BV 4 Magazine 150 sub Ammo 11 caliber end end Content requires <*JUNKYARD_SALVAGE (!Hi|!Ex)> Element1 minimap < ... .1. ... > inv BeamGun 20 name acc 1 range 6 Speed 1 scale 2 type end %% %% *WARZONE_CLEANUP Content %% There's just been a battle right here. The PC can walk around and maybe %% help with the recovery efforts. %% %% A WarZone scene needs three teams: Citizens, Guards, and Monsters. %% Content % A wounded person, Mk 1 requires <*WARZONE_CLEANUP> Element1 minimap < %%% %1% %%% > inv STC RECOVERY-VICTIM end Content % A wounded person, Mk 2 requires <*WARZONE_CLEANUP> Element1 minimap <%% 1%% %^% .% > inv STC RECOVERY-VICTIM end Content % A wounded person, Mk 3 requires <*WARZONE_CLEANUP> Element1 minimap < .%. ^.% ^1% > inv STC RECOVERY-VICTIM end %% *RANDOM_SHAPES Content %% Some random obstacles to break up LOS. Content requires <*RANDOM_SHAPES> minimap <.......#...###...#.......> Content requires <*RANDOM_SHAPES> minimap <...........###...........> Content requires <*RANDOM_SHAPES> minimap <.......#....#....#.......> Content requires <*RANDOM_SHAPES> minimap <......#.#.......#.#......> Content requires <*RANDOM_SHAPES> minimap <......###..###..###......> %% *ARMORY Content %% Weapon lockers. Content requires <*ARMORY -!Md -!Hi -!Ex> MiniMap <...........1#............> element1 inv STC SHOPDISPLAY-WEAPONS-2 PDir 4 use RandomLoot 50000 Loot_Category Loot_Factions end Content requires <*ARMORY -!Ne -!Hi -!Ex> MiniMap <...........1#............> element1 inv STC SHOPDISPLAY-WEAPONS-3 PDir 4 use RandomLoot 100000 Loot_Category Loot_Factions end Content requires <*ARMORY (!Md|!Hi)> MiniMap <...........1#2...........> element1 element2 inv STC SHOPDISPLAY-WEAPONS-1 PDir 4 use RandomLoot 100000 Loot_Category Loot_Factions STC SHOPDISPLAY-WEAPONS-2 PDir 0 use RandomLoot 100000 Loot_Category Loot_Factions end Content requires <*ARMORY (!Hi|!Ex)> MiniMap <.......3...1#2...4.......> element1 element2 element3 element4 inv STC SHOPDISPLAY-WEAPONS-3 PDir 4 use RandomLoot 100000 Loot_Category Loot_Factions STC SHOPDISPLAY-WEAPONS-1 PDir 0 use RandomLoot 100000 Loot_Category Loot_Factions STC SHOPDISPLAY-ARMOR-1 PDir 6 use RandomLoot 100000 Loot_Category Loot_Factions STC SHOPDISPLAY-ARMOR-2 PDir 2 use RandomLoot 100000 Loot_Category Loot_Factions end Content requires <*ARMORY !Ex> MiniMap <.......3...1#2...4.......> element1 element2 element3 element4 inv STC SHOPDISPLAY-WEAPONS-3 PDir 4 use RandomLoot 250000 Loot_Category Loot_Factions STC SHOPDISPLAY-WEAPONS-1 PDir 0 use RandomLoot 250000 Loot_Category Loot_Factions STC SHOPDISPLAY-ARMOR-1 PDir 6 use RandomLoot 200000 Loot_Category Loot_Factions STC SHOPDISPLAY-ARMOR-2 PDir 2 use RandomLoot 200000 Loot_Category Loot_Factions end %% *HIDEOUT_X Content %% Filler for hideouts and strongholds. %% Minimaps should be open on all four sides. Content requires <*HIDEOUT_X> Minimap <.......1....2............> element1 element2 inv STC BOX-1 STC BOX-1 end Content requires <*HIDEOUT_X> Minimap <............1............> element1 inv STC BOX-1 end Content requires <*HIDEOUT_X> Minimap <............12...........> element1 element2 inv STC BOX-1 STC CRATE Lock 10 inv Treasure name Fudge 100000 end end %% *ANTI_ATTACK_SECURITY Content %% A standard script to prevent the PC from firing weapons in public places. %% This one isn't so random, but it's a complicated script that I'm going to %% want to re-use in a whole lot of places, so it's easiest to put it here. %% PARAM: The identity of the team being protected, usually the citizens. %% Note that the alarm won't be sounded if the PC attacks while being %% attacked himself. Content requires <*ANTI_ATTACK_SECURITY> content1 content2 % V%id%01 = Initial number of civilians in scene. % V%id%02 = PC has been given warning START PCAttack % The map is not safe. Set the warning value to -1 to remind the script that the PC is fighting .%id%_GoNotSafe % The map is currently safe, only because the PC's attack supposedly dispatched the last enemy. .%id%_LastHit .%id%_GoWarn .%id%_GoAttack Msg%id%01 Msg%id%02 %% *NPC_GUARD Content %% A security guard or whatnot. Belongs to the "Guards" team. Content requires <*NPC_GUARD ~Public> element1 team1 teamdesc1 inv NPC Soldier job end Content requires <*NPC_GUARD Public ~Government ~Mall (Maqui|FCOMS|RISHI) ~Legit Common> element1 team1 teamdesc1 inv NPC Police Officer SetFaction 9 end Content requires <*NPC_GUARD ~Private> element1 team1 teamdesc1 inv Monster Guard Dog end Content requires <*NPC_GUARD Corp> element1 team1 teamdesc1 inv Monster Androbot end Content requires <*NPC_GUARD Private> element1 team1 teamdesc1 inv Monster Guardbot end Content requires <*NPC_GUARD Static (Maqui|FCOMS) ~Sleazy -Legit Public> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Privateer SetFaction 6 end Content requires <*NPC_GUARD Static FCOMS Public> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Mecha Pilot SetFaction 10 end Content requires <*NPC_GUARD Static RISHI Public> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Rocket Star end Content requires <*NPC_GUARD Static Maqui ~Legit -Sleazy Public ~Government> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Knight SetFaction 3 end Content requires <*NPC_GUARD Static BOHEM Public Common ~Government ~Habitation> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Pirate SetFaction 8 end Content requires <*NPC_GUARD Static BOHEM Public ~Sleazy> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Pirate SetFaction 15 end %% *SECURITY Content %% There's some sort of guard here, as a member of the GUARD team. %% Traps and checkpoints would also be good. Content requires <*SECURITY> Content1 Content2 Content3 Content4 Content5 Content requires <*SECURITY> minimap <#...#...............#...#> Content1 Content2 Content3 Content4 %% *ALARMSYS Content %% There's a security system that must be disabled before something else %% can take place. %% PARAM: Number of local variable to set when alarm has been deactivated. Content requires <*ALARMSYS> element1 content Content2 minimap <......#?#..#1#..###......> inv STC Computer-1 use Msg1 end %% *REDHERRING Content %% It looks like something important, but it isn't. Content requires <*REDHERRING> minimap <......#?#..#.#..###......> Content requires <*REDHERRING> minimap <......###..###..###......> %% *URBAN_X Content %% Things you might find in a city. Generic content for cafes, parks, %% shopping malls, waiting rooms, and so on. Mostly just NPCs. Content requires <*URBAN_X ~Public Legit> Content Content requires <*URBAN_X Static> element1 <.> element2 element3 team3 teamdata3 sub Persona 3 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end Content requires <*URBAN_X Public Static> element1 <.> element2 element3 team3 teamdata3 sub Persona 3 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end Content requires <*URBAN_X Public Resort> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Citizen job end Content requires <*URBAN_X Club Dynamic> element1 sub Persona 1 *Greeting <*HowAreYou GoChat> *GoChat <*MISC_CHATTER> end Content requires <*URBAN_X Public ~Legit ~Static> element1 team1 teamdata1 inv NPC Citizen end Content requires <*URBAN_X Mine> element1 team1 teamdata1 inv NPC Miner end Content requires <*URBAN_X Public> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Citizen end Content requires <*URBAN_X Laboratory> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Scientist end Content requires <*URBAN_X University> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Professor end Content requires <*URBAN_X Public ~University ~Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Student chardesc Young end Content requires <*URBAN_X ~Garage ~Public Static> element1 team1 teamdata1 inv NPC Trucker end Content requires <*URBAN_X Static (Maqui|FCOMS) Sleazy> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Privateer SetFaction 6 end Content requires <*URBAN_X RISHI Public> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Rocket Star end Content requires <*URBAN_X L5PAT Sleazy> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Pirate end Content requires <*URBAN_X L5PAT Sleazy ~Lawless> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Pirate SetFaction 8 end Content requires <*URBAN_X L5PAT Sleazy ~Dangerous> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Pirate SetFaction 15 end Content requires <*URBAN_X Static Maqui Legit> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Knight SetFaction 3 end Content requires <*URBAN_X Public Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Arena Pilot end Content requires <*URBAN_X Garage Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Mecha Pilot end Content requires <*URBAN_X Public ~Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Journalist end Content requires <*URBAN_X ~Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Monk end Content requires <*URBAN_X ~Sleazy Static> element1 team1 teamdata1 inv NPC Bounty Hunter end Content requires <*URBAN_X ~Sleazy Static> element1 team1 teamdata1 sub Persona 1 % V1 = Has joined lance/has been snubbed counter % V2 = Recruitment Cost greeting *GoNiceToMeetYou <*NiceToMeetYou GoCheckOffer> GoCheckOffer GoMakeOffer *GoMiscTalk <*MISC_CHATTER> RESULT1 result2 *GoWelcome <*WelcomeToLance> *result3 <*Goodbye> result4 result5 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 Msg2 Msg2_1 Msg3 Msg3_1 Msg4 Prompt1 Prompt2 CPrompt2 Prompt3 Prompt4 Prompt5 end inv NPC Mercenary update GoLevel MOTIVATION:Mercenary end Content requires <*URBAN_X Public Corp ~Static> element1 team1 teamdata1 inv NPC Corporate Executive update end Content requires <*URBAN_X Hospital ~Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Nurse end Content requires <*URBAN_X Hospital ~Static> element1 team1 teamdata1 inv NPC Nurse end Content requires <*URBAN_X Sleazy Static> element1 team1 teamdata1 inv NPC Thief end Content requires <*URBAN_X Sleazy ~Static> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoMiscTalk> *GoMiscTalk <*MISC_CHATTER> end inv NPC Bandit end %% *BATTLE_X CONTENT %% Used to add a little something special to otherwise normal battles. Content name % This area has some things lying around from a previous battle. requires <*BATTLE_X GROUND> start Msg%id%01 CONTENT_1 CONTENT_2 CONTENT_3 Content name requires <*MEKTREASURE> Element1 inv STC MAC-4 end Content name requires <*MEKTREASURE> Element1 inv STC PAR-6 end Content name requires <*MEKTREASURE> Element1 inv STC RG-8 end Content name requires <*MEKTREASURE> Element1 inv STC RG-16 end Content name requires <*MEKTREASURE> Element1 inv STC GR-12 end Content name % The area is about to be pounded with meteors. requires <*BATTLE_X ASTEROID> start 5min Msg%id%01 Msg%id%02 Content name % A trucker is caught in the middle of the battle zone. If he makes it to the % edge without getting blasted, the PC will get 50XP. % V%id%01 = Has requested help % V%id%02 = Has escaped map requires <*BATTLE_X> element1 TEAMData1 start MOVE%1% sub Persona 1 greeting GoFirstTime Msg1 Msg2 end GH2/series/RANCON_CoreStory.txt0000644000175000017500000003471611365256063015142 0ustar kaolkaol%% %% CORE STORY RANDOM CONTENT %% %% These fragments are used by the core story. Therefore, they can use all the story %% context descriptors in their REQUIRES blocks, and can access the XRan palette freely. %% %% In addition, some of these fragments are tied to specific components, and may not %% be readily reusable in other components. %% %% %% *CS_SHIP_DISASTER %% %% The PC wanted to take this ship from the inside. Well, there's a pretty %% good chance of encountering something dangerous on board. %% Content requires <*CS_SHIP_DISASTER (!Ne|!Lo)> Element1 Team1 minimap < ... .1. ... > inv Monster Androbot end Content requires <*CS_SHIP_DISASTER -!Ne -!Lo> Element1 Team1 minimap < ... .1. ... > inv Monster Sentinel end %% %% *CS_SHIP_SYSTEMS %% %% Some control systems for a spaceship. You need two teams to go %% along with these: GUARDS and SYSTEMS. %% %% Three types: Weapons Control, Engine Control, and Main Computer. %% Use Variety to get one of each. %% Content requires <*CS_SHIP_SYSTEMS> Element1 Team1 minimap < ... .1. ... > inv STC MACHINE-1 name % V1 = Have disabled device use %% Science and CodeBreaking can disable the device easily; %% Repair takes a bit more doing. CLUE_REPAIR CLUE_CODEBREAKING CLUE_SCIENCE GoFail GoDone GoWin Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg3 Msg4 Msg5 Msg6 end Content requires <*CS_SHIP_SYSTEMS> Element1 Team1 minimap < ... .1. ... > inv STC MACHINE-1 name % V1 = Have disabled device use %% Science and CodeBreaking can disable the device easily; %% Repair takes a bit more doing. CLUE_REPAIR CLUE_CODEBREAKING CLUE_SCIENCE GoFail GoDone GoWin Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg3 Msg4 Msg5 Msg6 end Content requires <*CS_SHIP_SYSTEMS> Element1 Team1 minimap < ... .1. ... > inv STC MACHINE-1 name

    % V1 = Have disabled device use %% Science and CodeBreaking can disable the device easily; %% Repair takes a bit more doing. CLUE_REPAIR CLUE_CODEBREAKING CLUE_SCIENCE GoFail GoDone GoWin Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg3 Msg4 Msg5 Msg6 end %% %% *CS_END_Final_Allies Content %% %% The final battle is starting. Load some allies to help the PC. All of these %% NPCs should say something upon receiving the RollCall trigger. %% Content name requires <*CS_END_Final_Allies P:++> Element1 Team1 TeamData1 RollCall Msg%id%01 <%name1% , reporting for \FACTION &AllyFac !> Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <%name1% reporting for duty.> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Content name requires <*CS_END_Final_Allies ~P:PCFAC> Element1 Element2 Team2 TeamData2 RollCall Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <%name2% reporting for %name1%.> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <\RANK , \FACTION %1% sent me to help. Let's kick some ass!> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Content name requires <*CS_END_Final_Allies Common> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Content name requires <*CS_END_Final_Allies Common> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Content name requires <*CS_END_Final_Allies Common> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Content name requires <*CS_END_Final_Allies> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 %% %% *CS_END_Final_Enemies Content %% %% The final battle is starting. Load some enemy NPCs to fight. All of these %% NPCs should say something upon receiving the RollCall trigger. %% Content name requires <*CS_END_Final_Enemies F:++> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <%name1% reporting for \FACTION &EnemyFac .> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Content name requires <*CS_END_Final_Enemies F:++> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Content name requires <*CS_END_Final_Enemies> Element1 Team1 TeamData1 RollCall Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 GH2/series/PFRAG_MiscChatter.txt0000644000175000017500000002556511326004535015270 0ustar kaolkaol% TYPE: *MISC_CHATTER % No Params Persona requires <*MISC_CHATTER> START Persona requires <*MISC_CHATTER Village> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*MISC_CHATTER Rishi> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*MISC_CHATTER Maqui> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_101 CMsg%id%01_101 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 Msg%id%01_9 CMsg%id%01_9 Msg%id%01_10 Persona requires <*MISC_CHATTER CAYLE> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Persona requires <*MISC_CHATTER XIASP> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Persona requires <*MISC_CHATTER CSNSP> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*MISC_CHATTER AthSp> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*MISC_CHATTER MaqSp> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 Persona requires <*MISC_CHATTER FucSp> START .%id%_GoSpecial Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*MISC_CHATTER Mall> START Msg%id%01 Msg%id%01_7 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 GH2/series/PFRAG_Intro.txt0000644000175000017500000001550711326004536014151 0ustar kaolkaol% Persona Fragment File % All the persona fragments in this file deal with the beginning of the game. % There are two primary characters to be concerned with: the INTRO and the MENTOR. % The INTRO is the character who speaks to the PC first, and usually explains % the core story. % TYPE: *SHOP_INTRO Persona requires <*SHOP_INTRO> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *INTRO_Introduction % The INTRO will greet the PC % Param1: Exit script label Persona requires <*INTRO_Introduction> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*INTRO_Introduction> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*INTRO_Introduction> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *INTRO_Question % The Intro will ask a question of the PC, setting some initial traits % Param1: Exit script label % TYPE: *INTRO_Advice % The Intro character will provide the PC with some basic advice. Usually this % will just consist of "Talk to the other people around here before heading out". Persona requires <*INTRO_Advice> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *MENTOR_WhatDoFirst % The mentor will tell the PC what he should do first. % Param1,2,3: Exit prompt numbers Persona requires <*MENTOR_WhatDoFirst> start Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *MENTOR_WhatEquipment % The mentor will tell the PC to buy a handphone. % Param1,2,3: Exit prompt numbers Persona requires <*MENTOR_WhatEquipment> start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 GH2/series/PFRAG_Default.txt0000644000175000017500000026473211374513723014456 0ustar kaolkaol% Persona Fragment File % TYPE: *SmallFryChallenge % % The PC has just challenged the NPC, but the NPC thinks that the PC % isn't in the same league, so the PC can either pressure the NPC to % accept them or just give up. % Difficulty rating taken as ChatNPCRenown. % PARAM1: Accept Challenge Exit Label % PARAM2: PC Gives Up Exit Label % PARAM3: PC Fails Exit Label Persona requires <*SmallFryChallenge> START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 % TYPE: *F2Dest_Greeting % % The PC wants to go somewhere, but has to fight this NPC first. There's % a chance that the PC will be able to find out where the destination is % from this NPC. % PARAM1: Difficulty Rating % PARAM2: Location being sought % PARAM3: Reveal location exit label (no message printed here) % PARAM4: Fight exit label (no message printed here) Persona requires <*F2Dest_Greeting @:A.jr_> %% Pretty easy to get the info if the NPC is your junior START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 Msg%id%02 Msg%id%02_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 <[Continue]> Persona requires <*F2Dest_Greeting ("Friend"|"Lover"|"@:A.tha") Common> %% Instawin if the NPC is a friend, a lover, or just thankful to the PC. START result%id%01 Msg%id%01 Prompt%id%01 <[Continue]> Persona requires <*F2Dest_Greeting> START result%id%01 result%id%02 result%id%03 .%id%_GoR4Success result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%04 Msg%id%04_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 CPrompt%id%03 Prompt%id%03_1 Prompt%id%04 <[Continue]> Win Prompt%id%05 <[Continue]> Fail Prompt%id%06 <[Continue]> Bribe % TYPE: *ShowMeYourWorkplace % &NothingToHide The NPC has nothing to hide (or claims such, at least) % &Investigation This is part of a criminal investigation % The PC wants to gain entry to the NPC's workplace. % PARAM1: Success Exit Label % PARAM2: Failure Exit Label % PARAM3: Difficulty Rating Persona requires <*ShowMeYourWorkplace &NothingToHide> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 <[Continue]> % TYPE: *SuspectGreeting % The NPC is suspected of a crime which the PC is investigating. % PARAM1: Exit Label Persona requires <*SuspectGreeting> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 % TYPE: *LM_JoinFaction % &IsPolice = It's a police faction % &IsMilitary = It's a military faction % &IsCorporate = It's a corporation % &IsPolitical = It's a government % The Lancemate has just joined a faction. % PARAM1: The faction in question Persona requires <*LM_JoinFaction> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 <> CMsg%id%01_12 Msg%id%01_13 CMsg%id%01_13 Msg%id%01_14 <> CMsg%id%01_14 Msg%id%01_15 CMsg%id%01_15 Msg%id%01_16 CMsg%id%01_16 % TYPE: *YourLossSucker % The PC has rejected an offer, the NPC will respond thusly. % PARAM1: Exit Label Persona requires <*YourLossSucker> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%01_7 <> CMsg%id%01_7 Msg%id%01_8 <> CMsg%id%01_8 % % TYPE: *BasicMissionBriefing % % The PC is being offered a basic mecha mission. Explain what's going % on, and allow the PC to either accept or reject it. % % Pay is "Reward %4% PayRate" plus salvage. % % PARAM1: Scene where the mission will take place % PARAM2: Accept mission exit label % PARAM3: Refuse mission exit label % PARAM4: The renown value, to calculate "Reward %4% PayRate" % Persona requires <*BasicMissionBriefing> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % % TYPE: *LM_WhyNotMission % % The lancemate has referred the PC to a mission, but the PC refused. % Note that this pfrag might jump straight to the DENY exit, or the NPC % might try arguing with the PC. % % PARAM1: Accept Mission exit label % PARAM2: Deny Mission exit label % PARAM3: The mission-giving NPC % Persona requires <*LM_WhyNotMission (Easygoing|Cheerful|Shy)> START Msg%id%01 Msg%id%01_1 Msg%id%01_2 Persona requires <*LM_WhyNotMission -Easygoing -Cheerful -Shy> START Msg%id%01 Msg%id%01_1 Msg%id%01_2 Persona requires <*LM_WhyNotMission @:M.pro> START Msg%id%01 Msg%id%01_1 Msg%id%01_2 Persona requires <*LM_WhyNotMission @:M.see> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%03_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*LM_WhyNotMission @:M.mer> START result%id%01 .%id%_GoR1Fail result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%04 Msg%id%05 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 % % TYPE: *LM_RefuseRequest % % The PC has just refused a request from the lancemate. The lancemate will % probably take it well. % % PARAM1: Exit label % Persona requires <*LM_RefuseRequest @:M.pro> % A professional won't take the refusal personally at all. START Msg%id%01 Msg%id%01_1 Persona requires <*LM_RefuseRequest> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % % TYPE: *LM_LetsDoIt % &SeekRumor The PC should try to find a rumor % % The PC has just agreed to some inane request from the lancemate. % Vagueness is perfectly acceptable. % % PARAM1: Exit label % Persona requires <*LM_LetsDoIt &SeekRumor> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*LM_LetsDoIt> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % % TYPE: *LetsGoToMission % % The NPC has referred the PC to a mission; let's go there now! % % PARAM1: Exit Label % PARAM2: NPC offering mission % PARAM3: Scene where mission is % Persona requires <*LetsGoToMission> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % % TYPE: *BetterBeGoing % % The NPC has to go do something. % % PARAM1: Exit Label % Persona requires <*BetterBeGoing> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % % TYPE: *IWillDestroyYou % % This NPC is swearing to destroy the PC. This is not a normal run of the mill death threat, either- % no, this NPC will literally go to the ends of the universe in their quest for revenge. % Persona requires <*IWillDestroyYou> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *YouWentThroughALotOfTrouble % &IsEnemy The NPC is an enemy of the PC. % The PC has gone through a lot of trouble to meet this NPC. % % PARAM1: Exit Label Persona requires <*YouWentThroughALotOfTrouble> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 <[Continue]> % TYPE: *TheySayYoureTough % The NPC has heard that the PC is tough. Maybe a challenge is going to take % place, or some kind of toughness-deflating advice will be given. % % PARAM1: Exit Label Persona requires <*TheySayYoureTough> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%02 % TYPE: *HurryBackWithMoney % The NPC is offering some kind of service. The PC can't afford it right now. Persona requires <*HurryBackWithMoney> START Msg%id%01 Msg%id%01_4 CMsg%id%01_4 % TYPE: *YouCouldUseHelp % The NPC has heard that the PC can use some help; usually followed by % either an offer of help or taunting. % % Param1: Exit Label Persona requires <*YouCouldUseHelp> % v%id%01 = One time use counter START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *ComeHereForIt % The NPC can't give the PC something over the phone; the PC should visit in person. Persona requires <*ComeHereForIt> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *NotByPhone % &TooDangerous It's dangerous to speak over the phone. % The NPC can't speak on the phone; the PC should visit in person. Persona requires <*NotByPhone> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *IHearYouAreLookingForItem % The NPC has heard about the PC's search for an item. % PARAM1: Item ID % PARAM2: Exit label Persona requires <*IHearYouAreLookingForItem> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 % TYPE: *IHearYouAreFightingNPC % The NPC has heard about the PC's fight with NPC. % PARAM1: NPC identity % PARAM2: Exit label Persona requires <*IHearYouAreFightingNPC> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 % TYPE: *WhatDoYouWantWithMe % &Information We want information... information... % &YouHaveSomething The NPC has something the PC needs/wants % The PC has barged in on someone, who is feeling understandably defensive. % PARAM1: Exit Label Persona requires <*WhatDoYouWantWithMe> % V%id%01 = One time only counter START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 <> CPrompt%id%01_1 Prompt%id%01_2 <> CPrompt%id%01_2 Prompt%id%01_3 <> CPrompt%id%01_3 Prompt%id%01_4 <> CPrompt%id%01_4 Prompt%id%01_5 <> CPrompt%id%01_5 Prompt%id%01_6 <> CPrompt%id%01_6 % TYPE: *PROMOTION_Minor_Reward % The PC doesn't have enough renown (or some other reputation) for the good % promotion reward, so instead he's going to get a relatively minor cash % prize. How awful. Persona requires <*PROMOTION_Minor_Reward> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *RevealSecret % The NPC will reveal a secret to the PC, usually after the PC has done something. % PARAM1: Exit label Persona requires <*RevealSecret> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 % TYPE: *TalkAgainWhenYouHaveTime % The PC doesn't want to talk now, so the NPC will advise them to come % back later. % Persona requires <*TalkAgainWhenYouHaveTime> START Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *LancemateWantsToTalk % One of the PC's lancemates wants to talk about something. The PC will either be able to % accept the conversation or delay it until later. % PARAM1: Accept exit label % PARAM2: Deny exit label Persona requires <*LancemateWantsToTalk> START result%id%01 result%id%02 Msg%id%01 <\PC , there's something I have to tell you.> Msg%id%01_1 <\PC , there's something that I really think we ought to discuss.> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <\PC , there's a matter which we urgently need to discuss.> CMsg%id%01_4 Msg%id%01_5 <\PC , there's something I want to talk about. This seems like a pretty good time.> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *SomeoneSentPCToTalk % &ForJob The PC is here to do a job for the NPC % The PC will tell the NPC that someone sent him here to talk. % PARAM1: NPC who sent PC % PARAM2: Exit script label Persona requires <*SomeoneSentPCToTalk> START result%id%01 Msg%id%01 Prompt%id%01_10 <\PERSONA %1% sent me to talk.> CPrompt%id%01_10 Prompt%id%01_20 CPrompt%id%01_20 % TYPE: *INeedYourHelp % &SomethingPersonal The NPC needs help of a sensitive nature % &PCSpecifically The NPC wants the PC's help, not just anyone % &Spiritual This is a spiritual matter of some kind % The NPC wants the PC to help him. % PARAM1: Will listen exit Label % PARAM2: Won't listen exit label Persona requires <*INeedYourHelp &PCSpecifically> START result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 <\PC , at last I've found you. I need your help with something.> Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <\PC , I need your help.> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <\PC , I have a terrible problem, and you're the only one in the universe who can help!> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <\PC , I've come to seek your aid in an important matter. You are quite literally my last hope.> CMsg%id%01_6 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Prompt%id%05_1 Persona requires <*INeedYourHelp> START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 <> CPrompt%id%01_1 Prompt%id%01_2 <> CPrompt%id%01_2 Prompt%id%01_3 <> CPrompt%id%01_3 Prompt%id%01_4 <> CPrompt%id%01_4 Prompt%id%01_5 <> CPrompt%id%01_5 Prompt%id%01_6 <> CPrompt%id%01_6 % TYPE: *IDisagreeYouDie % &Arrest The NPC will be taken to jail. % The NPC won't do as the PC says, but will fight instead. % PARAM1: Exit Label Persona requires <*IDisagreeYouDie &Arrest> START Msg%id%01 Persona requires <*IDisagreeYouDie> START Msg%id%01 % TYPE: *ISurrender % &Arrest The NPC will be taken to jail. % The NPC will surrender and go with the PC. % PARAM1: Exit Label Persona requires <*ISurrender &Arrest> START Msg%id%01 Persona requires <*ISurrender> START Msg%id%01 % TYPE: *ComeBackWhenYouKnowSomething % The NPC will remind the PC to go learn something... I dunno what. Persona requires <*ComeBackWhenYouKnowSomething> START Msg%id%01 % TYPE: *GenericMissionReminder % The NPC will remind the PC to go do something... I dunno what. Persona requires <*GenericMissionReminder> START Msg%id%01 % TYPE: *MechaMissionReminder % The NPC will remind the PC to go fight/investigate some mecha % in Scene %1%. % PARAM1: The scene where the mecha are Persona requires <*MechaMissionReminder> START Msg%id%01 % TYPE: *IWantToTalkAboutNPC % &YouBadNPC ChatNPC has done something bad to P1 NPC, supposedly % The PC wants to ask the NPC about a second NPC. % PARAM1: NPC to be discussed % PARAM2: Exit label Persona requires <*IWantToTalkAboutNPC> START result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *GodImBored % The NPC will express a state of boredom or ennui. Persona requires <*GodImBored> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%01_7 <> CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 % TYPE: *IAcceptYourChallenge % The PC has challenged the NPC to a duel; the NPC has accepted. % PARAM1: Duel Scene (referenced as \SCENE %1% here) Persona requires <*IAcceptYourChallenge> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *WelcomeToLance % The NPC has just joined the PC's team, and will give an opening % speech of some type. Persona requires <*WelcomeToLance> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *YoureUnderArrest % &CanMakeDeal Can make a deal whereby the NPC avoids arrest % The PC is trying to arrest a criminal. The criminal might go peacefully, % or may choose to resist... it mostly depends on how the PC plays % things here. % PARAM1: Difficulcy rating % PARAM2: Go peacefully exit label (no message printed here) % PARAM3: Resist arrest exit label (no message printed here) % PARAM4: Make deal exit label (message printed here) Persona requires <*YoureUnderArrest> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *IntruderGreeting % &Arrest The PC is here to arrest the NPC % The PC has intruded into the NPC's personal space. The NPC will therefore % confront the PC. % PARAM1: Exit label Persona requires <*IntruderGreeting &Arrest> % V%id%01 = One time use counter START result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*IntruderGreeting> % V%id%01 = One time use counter START result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *PCChallengeNPC % The PC has a chance to challenge the NPC to combat... if he doesn't wuss out. % PARAM1: Issue challenge exit label % PARAM2: Wuss out exit label Persona requires <*PCChallengeNPC> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%02 Persona requires <*PCChallengeNPC Shy> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%02 Persona requires <*PCChallengeNPC "PDASS"> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%02 % TYPE: *IAmBusy % &OnPhone This conversation is definitely via telephone % The NPC is too busy to talk. Persona requires <*IAmBusy &OnPhone> START Msg%id%01 Persona requires <*IAmBusy> START Msg%id%01 % TYPE: *IMustProveMyself % The NPC feels that he/she must prove him/herself. % This frament does not exit. Persona requires <*IMustProveMyself> START Msg%id%01 % TYPE: *IConcedeDefeat % &Battle The victory was in battle % The PC has defeated the NPC somehow, in battle or otherwise. The NPC % will now concede. Persona requires <*IConcedeDefeat> START Msg%id%01 Persona requires <*IConcedeDefeat &Battle> START Msg%id%01 % TYPE: *InsultContest % The NPC will insult the PC. The PC then gets a chance to respond. % If the insult is sufficiently virulent, the PC wins. This PFrag % should include a check so that the PC can't attempt to do it % twice without improving his TAUNT skill (42). % Every insult should have a snappy comeback, a lame reply, and maybe % an additional orthogonal reply that probably also counts as a win % though sometimes a loss. % PARAM1: Success Exit Label (no message printed here) % PARAM2: Failure Exit Label (no message printed here) % PARAM3: Difficulcy rating of skill roll, expressed as renown Persona requires <*InsultContest Melancholy> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*InsultContest> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Persona requires <*InsultContest> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*InsultContest> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*InsultContest> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 % TYPE: *YouDanceTerribly % The PC has just danced with the NPC, possibly breaking toes. % Let it all out. Tell him how you really feel. Persona requires <*YouDanceTerribly> START Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 % TYPE: *YouDanceWell % The PC has just danced with the NPC without breaking any toes. This complement will % end the conversation. Persona requires <*YouDanceWell> START Msg%id%01 % TYPE: *LetMeIn % The PC has encountered a bouncer or some other security type. This is his % chance to intimidate, flirt, or otherwise talk his way in. % If the attempt fails, this pfrag can terminate the conversation. % PARAM1: Difficulcy Rating (expressed as renown) % PARAM2: Success exit label (no message printed here) % PARAM3: Enraged exit label (no message printed here) % Typically this will happen if the PC challenges the guard to combat, or % otherwise bugs him too much. Persona requires <*LetMeIn> % V%id%01 = Have tried bluff % V%id%02 = Have tried flirt start result%id%01 result%id%02 .%id%_GoR2Fail result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 .%id%_GoR8Win result%id%09 .%id%_GoR9Fail result%id%10 result%id%11 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Prompt%id%05 Prompt%id%05_1 Prompt%id%06 Prompt%id%07 Prompt%id%08 CPrompt%id%08 Prompt%id%09 CPrompt%id%09 Prompt%id%10 Prompt%id%11 % TYPE: *YouLostItem % The PC lost an item that he was supposed to deliver, protect, or recover. Oops. % The NPC will now heap out some abuse, may become the PC's enemy, or other fun % things. Persona requires <*YouLostItem> START Msg%id%01 % TYPE: *PCWillKeepItem % The PC will keep an item that the NPC sent him to get. This is the NPC's % reaction to that. The reaction may include setting the PC as an enemy % of other nastiness, depending on the NPC's whim. % PARAM1: Item ID Persona requires <*PCWillKeepItem> START Msg%id%01 % TYPE: *BrushOff % &Questioning % &GetOutOfHere % The NPC doesn't want to talk with the PC any more. Persona requires <*BrushOff> START Msg%id%01 Persona requires <*BrushOff &GetOutOfHere> START Msg%id%01 Persona requires <*BrushOff &Questioning> START Msg%id%01 % TYPE: *ProtectCheckIn % The PC is here to protect this NPC. % PARAM1: Exit label (to mission briefing, usually) Persona requires <*ProtectCheckIn> START result%id%01 Msg%id%01_1 <\PC ? What are you doing here?> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Prompt%id%01 Prompt%id%01_1 % TYPE: *DefenseCheckIn % The PC is checking in to defend a building. % PARAM1: Exit label (to mission briefing, usually) Persona requires <*DefenseCheckIn> START result%id%01 result%id%02 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*DefenseCheckIn> START result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *PCinviteNPC % The PC is asking the NPC to meet somewhere. % PARAM1: Ask exit label % PARAM2: PC chickens out or otherwise doesn't manage to ask exit label Persona requires <*PCinviteNPC> START result%id%01 result%id%02 Msg%id%01 prompt%id%01 prompt%id%02 % TYPE: *DestructionGreeting % The NPC is standing in a site of wanton destruction and is understandably % upset about it. The PC will have a chance to console the NPC or request % further information. The exit is to information/a job offer. % PARAM1: Exit script label % PARAM2: Faction responsible (optional) Persona requires <*DestructionGreeting (Passionate|HasMecha)> START result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*DestructionGreeting ~Melancholy> START .%id%_GoSecond result%id%01 msg%id%01 msg%id%02 prompt%id%01 Persona requires <*DestructionGreeting> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%02 % TYPE: *ThanksForRescue % The NPC has been rescued by the PC and will now say thanks. % PARAM1: Exit script label Persona requires <*ThanksForRescue> START result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Prompt%id%01_7 CPrompt%id%01_7 % TYPE: *Shimli_Chatter % The NPC will chat meaninglessly with the PC, giving him the chance to % alter his personality traits. % PARAM1: Exit script label Persona requires <*SHIMLI_CHATTER Sociable Charm ~City> START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Persona requires <*SHIMLI_CHATTER ~Sociable> START Result%id%01 Result%id%02 Result%id%03 Msg%id%01 Msg%id%01_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 % TYPE: *GoodLuckPlusHint % The NPC will wish the PC luck on the mission, and also provide a hint. % PARAM1: Exit script label % PARAM2: Hint PlotID Persona requires <*GoodLuckPlusHint> Start Msg%id%01 <\HINT_MEMO %2% Good luck on the mission.> Msg%id%01_1 <\HINT_MEMO %2% Report back here once you've completed everything.> CMsg%id%01_1 Msg%id%01_2 <\HINT_MEMO %2% Good luck.> CMsg%id%01_2 Msg%id%01_3 <\HINT_MEMO %2% Have fun out there.> CMsg%id%01_3 Msg%id%01_4 <\HINT_MEMO %2% Try not to get yourself killed.> CMsg%id%01_4 Msg%id%01_5 <\HINT_MEMO %2% Hopefully this won't be too hard of a job...> CMsg%id%01_5 Msg%id%01_6 <\HINT_MEMO %2% Go kick some ass!> CMsg%id%01_6 % TYPE: *GoodLuckOnMission % &Advance PC will get an advance payment for this mission % &NoEnemyFac There's no enemy faction to worry about % The NPC has just given the PC a mission, the PC has accepted, so the NPC is % seeing the PC off. % PARAM1: Exit script label % PARAM2: Allied Faction [optional] % PARAM3: Enemy Faction [optional] Persona requires <*GoodLuckOnMission =MIL_DEFAULT_DEFENSE> % Because this is a MIL_DEFAULT_DEFENSE mission, we know that %2% and %3% exist. Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 Msg%id%01_8 Persona requires <*GoodLuckOnMission &Advance> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*GoodLuckOnMission> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *DidYouGetEmail % The NPC sent an email to the PC. % PARAM1: Exit script label Persona requires <*DidYouGetEmail ~Shy> Start result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 Msg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%01_3 Prompt%id%01_4 Persona requires <*DidYouGetEmail> Start result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <\PC , I've been waiting to speak with you. Did you get the email I sent?> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *IHaveSomethingToTellYou % The NPC has something delicate to tell the PC. % PARAM1: Exit script label Persona requires <*IHaveSomethingToTellYou ~Sociable> Start result%id%01 result%id%02 Msg%id%01 <\PC , I have something difficult to tell you. I guess the easiest thing to do is come right out and say it.> Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*IHaveSomethingToTellYou Friend> Start result%id%01 Msg%id%01 <\PC , you should know that I have a secret. I'm a bit nervous about telling anyone...> Prompt%id%01 % TYPE: *IHaveAJobForYou % The NPC has a job for the PC, % PARAM1: Exit script label Persona requires <*IHaveAJobForYou ~Shy> START result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 % TYPE: *DoYouWantAJob % &Mecha it's a mecha mission % &Chara it's a personal scale combat mission % &Investigation it's an investigative mission % &NonCombat It's a noncombat mission % The NPC will offer the PC a job. No former knowledge on behaf of the PC is % expected. % PARAM1: Exit script label Persona requires <*DoYouWantAJob> START Result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Persona requires <*DoYouWantAJob THIEF> START Result%id%01 Result%id%02 Result%id%03 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 % TYPE: *AreYouHereAboutJob % &Mecha it's a mecha mission % &Chara it's a personal scale combat mission % &Investigation it's an investigative mission % &Recruit it's some kind of recruitment drive % The NPC has a job to be done, has supposedly announced it via news or email, % and will now greet the PC. % PARAM1: Exit script label Persona requires <*AreYouHereAboutJob &Recruit> Start result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Persona requires <*AreYouHereAboutJob &Chara> Start result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Persona requires <*AreYouHereAboutJob "POLIT"> Start Result%id%01 Result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_7 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*AreYouHereAboutJob> Start result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Persona requires <*AreYouHereAboutJob Shy> Start result%id%01 Msg%id%01 Msg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Persona requires <*AreYouHereAboutJob &Mecha -FRIEND> Start .%id%_GoNoRenown .%id%_GoNoLike result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 <\PC ! I could really use your help right about now. I have a job available for a mecha pilot, and it has to be done as soon as possible.> Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 % TYPE: *HowAreYou % Similar to the *NiceToMeetYou fragments, but here we're assuming that the % PC and NPC may have met before. % PARAM1: Exit script label Persona requires <*HowAreYou> START result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 <> CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 <> CPrompt%id%01_6 Persona requires <*HowAreYou> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 % TYPE: *FACTION_JOIN_ACCEPT % The PC has agreed to join this faction. Give a mecha, maybe some goodies, and % send him on his way. % PARAM1: Faction ID Persona requires <*FACTION_JOIN_ACCEPT> START .%id%_mecha Msg%id%01 Msg%id%02 Persona requires <*FACTION_JOIN_ACCEPT SILKN> START .%id%_mecha Msg%id%01 Msg%id%02 % TYPE: *FACTION_JOIN_OFFER % The faction will offer to hire PC as a permanent member. % PARAM1: Faction to maybe join % PARAM2: PC has decided to join exit Persona requires <*FACTION_JOIN_OFFER> START result%id%01 result%id%02 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *FACTION_ENTRANCE_EXAM % Test the PC's applicability for entrance into this faction. The generic case % is that the PC must have a renown score of 20 and a positive reaction score. % PARAM1: Exit label on success % PARAM2: Faction to maybe join Persona requires <*FACTION_ENTRANCE_EXAM> START .%id%_GoFail Msg%id%01 Persona requires <*FACTION_ENTRANCE_EXAM SILKN> START .%id%_GoFail Msg%id%01 Persona requires <*FACTION_ENTRANCE_EXAM POLIC> START .%id%_GoFail Msg%id%01 Persona requires <*FACTION_ENTRANCE_EXAM CORPO> START .%id%_GoFail Msg%id%01 % TYPE: *ENEMY_CHECK % Check to see if the PC is an enemy of this NPC. If so, give a "buzz off" message. % Otherwise exit to the provided script label. % PARAM1: Exit script label if not enemy % PARAM2: Faction to check enemity of % PARAM3: Exit script label if faction enemy Persona requires <*ENEMY_CHECK (friend|family|lover)> START Msg%id%01 <\PC , this isn't a good time for you to be seen here...> Msg%id%01_1 Msg%id%01_2 Persona requires <*ENEMY_CHECK -friend -family -lover> START Msg%id%01 Msg%id%01_1 Msg%id%01_2 Persona requires <*ENEMY_CHECK -friend -family -lover> START Msg%id%01 Persona requires <*ENEMY_CHECK Sociable -friend -family -lover> START Msg%id%01 % TYPE: *GOODBYE % The NPC will say good-bye to the PC. % No parameters. This fragment terminates the conversation. Persona requires <*GoodBye "SILKN"> Start Msg%id%01 Msg%id%01_1 Persona requires <*GoodBye CORP> Start .%id%_gonofac Msg%id%01 Msg%id%02 Persona requires <*GOODBYE Melancholy> START .%id%_GoFail msg%id%01 msg%id%02 Persona requires <*GOODBYE Easygoing> START msg%id%01 Persona requires <*GOODBYE Shy> START msg%id%01 Persona requires <*GOODBYE Mechanic> START msg%id%01 Persona requires <*GOODBYE Trucker> START msg%id%01 Persona requires <*GOODBYE Doctor> START msg%id%01 Persona requires <*GOODBYE Shopkeeper> START msg%id%01 Persona requires <*GOODBYE ~Shopkeeper Maqui> START msg%id%01 Persona requires <*GOODBYE Passionate> START .%id%GoNoFriend .%id%GoNoLike msg%id%01 msg%id%02 msg%id%03 Persona requires <*GOODBYE Spiritual> START msg%id%01 Persona requires <*GOODBYE> START msg%id%01 Persona requires <*GOODBYE Sociable> START msg%id%01 Persona requires <*GOODBYE Cheerful> START .%id%_GoFail msg%id%01 msg%id%02 Persona requires <*GOODBYE Sociable ~Pragmatic> START .%id%_End result%id%01 result%id%02 result%id%03 msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 Prompt%id%03 % TYPE: *GreetThenDiscuss % The NPC will greet the PC before moving on to other business. % PARAM1: Exit script label Persona requires <*GreetThenDiscuss ~Shy ~PCAlly> % V%id%01 = Counter so you won't get the same chatter twice. START result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*GreetThenDiscuss ~Sociable> % V1 = Counter so you won't get the same chatter twice. START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 Persona requires <*GreetThenDiscuss ~+Fmi> % V1 = Counter so you won't get the same chatter twice. START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 GH2/series/MEGA_Tasks.txt0000644000175000017500000001336011365256063014017 0ustar kaolkaol%% %% A task is a minor mission that the PC may need to complete, often just as %% the prerequisite for something else. There are many kinds of tasks, divided %% by category. %% %% When a task is completed, its plotid will be set to WIN. If it is lost, %% its plotid will be set to LOSS. %% %% *:TASK_Defeat[X] %% %% The PC is sent to defeat some mecha. The mecha involved have no faction, %% or at least no faction as set by the calling plot. %% %% PARAM1: The outdoors scene where the mecha will be found. %% Content name requires <*:TASK_DefeatFactory> desc % E1 is the outdoors scene % E2 is the building exterior encounter Element2 Place2 <1> sub MetaScene 2 2 % L1 = Encounter Over Counter MapWidth 35 MapHeight 35 nu1 nu2 nu3 Msg1 sub team 1 SetEnemy 2 3 ParaX 5 ParaY 5 team 2 SetEnemy 1 SetAlly 3 Deploy home team 3 name SetEnemy 1 SetAlly 2 rect name desig Height 5 Width 5 sub SuperProp requires <*Fortress> SetTeam 3 end end end inv STC CORE-STATIONARY name end Content name requires <*:TASK_DefeatLooters> desc % E1 is the target scene % E2 is the encounter Element2 Place2 <1> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 nu1 nu2 Msg1 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-MECHAENCOUNTER name end %% %% *:TASK_KillFungus %% %% The PC is sent to clean out a fungal infestation from somewhere. As soon as %% this subplot is activated the PC will be whisked away to the combat zone. %% %% PARAM1: The anchor; metascene entrance placed in same location as this. %% Content name requires <*:TASK_KillFungus (!Ne|!Lo)> % E1 is the anchor. % E2 is the combat metascene. Element2 Place2 <~1> % P%id%01 = Initialization Counter % Upon activation, move the PC to E2. update sub MetaScene 2 special boxmap MapWidth 17 MapHeight 12 IndustrialTiles % L1 = Victory Counter end nu2 Msg1 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Deploy type room special minimap <............2............> sub StairsUp Destination -1 MiniMapComponent 2 use end end end inv STC ENCOUNTER-TELEPORT-MISSION end Content name requires <*:TASK_KillFungus (!Lo|!Md)> % E1 is the anchor. % E2 is the combat metascene. Element2 Place2 <~1> % P%id%01 = Initialization Counter % Upon activation, move the PC to E2. update sub MetaScene 2 special boxmap MapWidth 17 MapHeight 17 IndustrialTiles % L1 = Victory Counter end nu2 Msg1 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Deploy type room special minimap <............2............> sub StairsUp Destination -1 MiniMapComponent 2 use end end end inv STC ENCOUNTER-TELEPORT-MISSION end Content name requires <*:TASK_KillFungus (!Md|!Hi|!Ex)> % E1 is the anchor. % E2 is the combat metascene. Element2 Place2 <~1> % P%id%01 = Initialization Counter % Upon activation, move the PC to E2. update sub MetaScene 2 special monkeymap MapWidth 35 MapHeight 35 IndustrialTiles % L1 = Victory Counter end nu2 Msg1 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Deploy type room special minimap <............2............> sub StairsUp Destination -1 MiniMapComponent 2 use end end end inv STC ENCOUNTER-TELEPORT-MISSION end GH2/series/PFRAG_CORE_Briefing.txt0000644000175000017500000005401111365256063015413 0ustar kaolkaol% TYPE: *CS_BasicStopEnemyMission % The PC is being given a basic stop enemy mission. In this mission, the PC will be % told to go stop the enemy NPC from doing whatever the enemy NPC is doing. % If the PC is a member of the mission-giver's faction, refusal is impossible. % NOTE: This component is responsible for setting the StoryNote % Generally, a note will be stored for accepting the mission, but not for rejecting % the mission. % PARAM1: Accept Mission Exit Label % PARAM2: Reject Mission Exit Label (Note: Not all missions may be rejected!) % (Also note: If this component features another ending, neither exit may % be called) % PARAM3: Reward Hint (the layerid of the reward subplot) Persona requires <*CS_BasicStopEnemyMission "MEDIC" ~E:EVIL_ ~"NOFAC" ~*CORE_DEF_ ~*CORE_OFF_> START result%id%01 result%id%02 Msg%id%01_1 Msg%id%01_2 Msg%id%01_10 <> CMsg%id%01_10 Msg%id%01_20 <> CMsg%id%01_20 Msg%id%01_30 <> CMsg%id%01_30 Msg%id%01_40 <> CMsg%id%01_40 Msg%id%01_50 <> CMsg%id%01_50 Msg%id%01_60 <> CMsg%id%01_60 Msg%id%02 <\CHATNPC hired you to stop \PERSONA &EnemyNPC from completing \PPR &EnemyNPC mission.> Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Persona requires <*CS_BasicStopEnemyMission P:++ P:PCFAC -"PCFAC"> %% The PC is here on behaf of their faction. START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to stop \PERSONA &EnemyNPC from completing \PPR &EnemyNPC mission.> Prompt%id%01 Prompt%id%01_1 <> Prompt%id%01_2 <> Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*CS_BasicStopEnemyMission ~"POLIC" (E:THIEF|F:CRIME)> START result%id%01 result%id%02 Msg%id%01 <\PERSONA &EnemyNPC is planning some kind of heist in \SCENE RootSceneID . I need you to stop \PPR &EnemyNPC plans before \SPR &EnemyNPC gets a chance to succeed. \HINT %3%> Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to prevent \PERSONA &EnemyNPC 's latest heist.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_BasicStopEnemyMission> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to locate and stop \PERSONA &EnemyNPC .> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *CS_BasicFightingMission % The PC is being given a basic fighting mission. In this mission, the PC will be % told to go fight some raiders- mecha aligned with the enemy PC/faction. % If the PC is a member of the mission-giver's faction, refusal is impossible. % NOTE: This component is responsible for setting the StoryNote % Generally, a note will be stored for accepting the mission, but not for rejecting % the mission. % PARAM1: Accept Mission Exit Label % PARAM2: Reject Mission Exit Label (Note: Not all missions may be rejected!) % (Also note: If this component features another ending, neither exit may % be called) % PARAM3: Reward Hint (the plotid of the reward subplot) Persona requires <*CS_BasicFightingMission ("TRADE"|"CORPO"|"CRAFT") "NOFAC" ~Friend ~Family ~!Ne ~!Lo> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you for a mecha combat mission; this is a new business that \SPR ChatNPCID 's trying.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_BasicFightingMission "MEDIC" ~"Doctor" ~C:MEDIC ~"NOFAC" ~*CORE_DEF_ ~*CORE_R_MON ~E:EVIL_> START result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you drive off some raiders as a public health measure.> Msg%id%03 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%03_6 CMsg%id%03_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 <> Prompt%id%03 Prompt%id%04 CPrompt%id%04 Persona requires <*CS_BasicFightingMission (*CORE_OFF_2|*CORE_OFF_3|*CORE_OFF_4|*CORE_OFF_5) F:++ ~"PCFAC"> % No way to refuse the mission, since this is exactly what the PC % asked for. START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to fight some mecha from \FACTION &EnemyFac .> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Persona requires <*CS_BasicFightingMission ("CORPO"|"CRAFT"|"LABOR"|"TRADE")> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to locate and defeat some convoy raiders.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_BasicFightingMission "ACADE"> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to investigate a sensor reading.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_BasicFightingMission "POLIC" (E:THIEF|E:EVIL_|F:CRIME) ~*CORE_OFF_ ~*CORE_DEF_> START result%id%01 result%id%02 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 Msg%id%03_1 <\ChatNPC asked you to find and defeat \PERSONA &EnemyNPC .> CMsg%id%03_1 Msg%id%03_2 <\ChatNPC asked you to find and defeat raiders from \FACTION &EnemyFac .> CMsg%id%03_2 Msg%id%03_3 <\ChatNPC asked you to find and defeat some criminals.> CMsg%id%03_3 Msg%id%04_1 <\ChatNPC mentioned that \PERSONA &EnemyNPC is in town. There's a reward if you can locate and defeat \OPR &EnemyNPC .> CMsg%id%04_1 Msg%id%04_2 <\ChatNPC mentioned that mecha from \FACTION &EnemyFac are in town. There's a reward if you can locate and defeat them.> CMsg%id%04_2 Msg%id%04_3 <\ChatNPC mentioned that your enemies are in town. There's a reward if you can locate and defeat them.> CMsg%id%04_3 Prompt%id%01 Prompt%id%02 Persona requires <*CS_BasicFightingMission F:++ (+P--|+Pun)> START Result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC sent you to intercept a group of mecha from \FACTION &EnemyFac .> Prompt%id%01 Prompt%id%02 CPrompt%id%02 Persona requires <*CS_BasicFightingMission E:++ -E:A.nme -E:Friend> START result%id%01 result%id%02 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_11 <> CMsg%id%01_11 Msg%id%01_12 <> CMsg%id%01_12 Msg%id%01_13 <> CMsg%id%01_13 Msg%id%01_14 <> CMsg%id%01_14 Msg%id%01_15 <> CMsg%id%01_15 Msg%id%01_16 <> CMsg%id%01_16 Msg%id%02_1 <\CHATNPC sent you to intercept mecha affiliated with \PERSONA &EnemyNPC .> CMsg%id%02_1 Msg%id%02_2 <\CHATNPC sent you to intercept some mecha.> CMsg%id%02_2 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 <> CPrompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 CPrompt%id%02_1 Prompt%id%02_2 <> CPrompt%id%02_2 Persona requires <*CS_BasicFightingMission +P--> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC sent you to investigate an odd sensor reading.> Prompt%id%01 Prompt%id%01_1 <> Prompt%id%01_2 <> Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_BasicFightingMission F:++ F:CRIME> START Result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC sent you to locate and intercept a group of mecha from \FACTION &EnemyFac .> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_BasicFightingMission> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to locate and engage a group of raiders.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 GH2/series/MEGA_default.txt0000644000175000017500000003104011376734116014353 0ustar kaolkaol%% %% MegaPlot Components %% %% Similar to quests, but work for plots. %% %% *:CriminalInvestigation Content %% %% A person of some type is breaking the law. Your task is to uncover the %% evidence which will prove this... or, maybe accept a bribe to keep the %% whole thing quiet. %% %% If the evidence is uncovered, this subplot will be won. If the PC accepts %% a bribe or fails in the mission, it will be lost. %% %% Upon reporting the crime, the parent plot should either end or cancel this %% subplot to prevent the PC from collecting bribes after the fact. %% %% PARAM1: The character under investigation %% PARAM2: The city where the investigation is happening %% Content name desc requires <*:CriminalInvestigation 1:EVIL_ (1:CORPO|1:TRADE|1:ACADE)> %% E1 is the NPC under investigation %% E2 is the city where this investigation will take place %% E3 is an urban scene, for placing the factory %% E4 is the factory scene, full of monsters %% E5 is a secret- reveal this to corner E1 right away. Element3 Element4 Place4 <3> Element5 % P%id%01 = Learned secret about factory end .%id1%_%plotid1%_GoLearnSecret % SubPlot1 is the LearnSecret bit. SubPlot1 <*:LearnSecretAboutNPC&Incriminating 5 1 2> sub Persona 1 % V%id%01 = Have opened factory greeting .%id%_GoShocked *.%id%_GoGreeting <*SuspectGreeting .%id%_GoCheckFactory> .%id%_GoCheckFactory .%id%_GoAskQuestions *result%id%01 <*ComeBackWhenYouKnowSomething> result%id%02 result%id%03 *result%id%04 <*ShowMeYourWorkplace&NothingToHide&Investigation .%id%_GoOpenFactory .%id%_GoRefuse %threat%> .%id%_GoOpenFactory *.%id%_GoRefuse <*BrushOff&Questioning> result%id%05 result%id%06 result%id%07 result%id%08 result%id%09 result%id%10 result%id%11 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 <%name1% told you where to find \PPR %1% factory in \EXACT_SCENE %3% .> Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%08 CPrompt%id%08 Prompt%id%09 Prompt%id%10 Prompt%id%11 MetaScene 4 MonkeyMap MapWidth 35 MapHeight 35 IndustrialTiles % L1 = Victory Counter start GoBeenBefore End Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Deploy type room special minimap <3...........2...........4> inv Trapdoor Destination -1 MiniMapComponent 2 use CLUE_Insight CLUE_CodeBreaking Msg101 Msg102 STC BIOTANK CLUE_Insight CLUE_Science Msg1 Msg2 MiniMapComponent 3 STC BIOTANK CLUE_Insight CLUE_Science Msg1 Msg2 MiniMapComponent 4 end room special minimap <............2............> inv Trapdoor Destination -1 MiniMapComponent 2 use end end end inv STC CORE-ACTIVATABLE name <%name1%'s Factory> ENCOUNTER_NonCombat Secret Msg end %% %% *:ReconBeefgate Content %% %% You're searching for a team of mecha, but the final encounter is probably %% too difficult for the PC as of yet. What to do? The answer, of course, is to %% install a beefgate. The PC will be sent to fight successively harder waves %% of enemies until the final battle is unveiled. %% %% Note that %name3% will be used as the title of what you're looking for, so %% name your encounter accordingly. %% %% PARAM1: The Mission-Giver %% PARAM2: The outdoor scene where scene-to-reveal is %% PARAM3: Scene-to-reveal %% PARAM4: The faction of the beef Content name requires <*:ReconBeefgate> desc % E1 is the mission-giver % E2 is the outdoors scene % E3 is the scene to be revealed % E4 is the enemy faction % E5 is the beefgate itself Element5 Place5 <2> % P%id%01 = BeefGate Appearance Timer % P%id%02 = BeefGate Difficulty Rating % P%id%03 = Number of wins against the beefgate % P%id%04 = Number of wins rewarded % This script handles the recharge if E1 is dead. hour sub Persona 1 % V%id%01 = Next Mission Counter greeting .%id%_GoMajorVictory .%id%_GoMinorVictory .%id%_GoCheckNext .%id%_GoActivateBeefgate .%id%_GoWaitNextMission .%id%_GoCheckProgress *.%id%_GoFirstTime <*IHaveAJobForYou .%id%_GoBriefing> .%id%_GoBriefing result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 <%name1% sent you to fight some mecha in \EXACT_SCENE %2% so you can locate %name3%.> Msg%id%09 Prompt%id%01 Prompt%id%02 MetaScene 5 2 special MapWidth 50 MapHeight 50 % L1 = Encounter Over CounterifUStealth HardSkillTar P%id%02 else GoAutoAttack ifYesNo -3 -4 -5 else GoAvoidAttack Goto GoAutoAttack % Losing the encounter will set the recharge for about twelve hours. nu1 % Winning the encounter will increase the difficulty + number of wins. % If the mission-giving NPC is dead, it may also reveal the search scene. nu2 end Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg2_1 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv Encounter name <%name1%'s Mission> %% The BeefGate itself. %% This encounter will attack once per day. If the PC defeats it, then this encounter %% needs to be reset by E1 before it will appear again. If E1 is dead, it will reset %% by itself. update % Sets this encounter's orders to Passive GoSetOrders use ATTACK GoAutoAttack GoAvoidAttack GoStartCombat EncounterMove 50 Msg1 Msg2 Msg3 Msg4 end GH2/series/QUEST_Default.txt0000644000175000017500000003754411326004537014512 0ustar kaolkaol%% %% *Q_LocalTough Content %% %% The PC has just met a tough guy (or gal); find out what makes him (or her) %% so tough. %% %% PARAM1: The locale of the tough %% PARAM2: The tough NPC %% %% NOTE: Persona 2 must include a .%id%_GoInit script to initialize this %% subquest. %% Content name desc requires <*Q_LocalTough> % E1 is the scene where the tough NPC is located % E2 is the tough NPC % E3 is an arena entrance % E4 is the arena itself % E5 is the reward, an artifact Element3 Place3 <1> Element4 Place4 <1> Element5 Place5 sub Persona 2 % V%id%01 = Have accepted arena % V%id%02 = Have accepted the initial duel .%id%_GoInit Greeting .%id%_GoOpenArena .%id%_GoCheckQ2Loss .%id%_GoCheckQ2Ready .%id%_GoArenaWin *.%id%_GoMinorWin <*BasicArenaWin %4% .%id%_GoResetArena> *.%id%_GoArenaLoss <*BasicArenaLoss %4% .%id%_GoResetArena> .%id%_GoResetArena *.%id%_GoArenaFull <*ArenaIsFull %4%> *.%id%_GoArenaBusy <*ArenaIsBusy %4%> .%id%_GoBigWin .%id%_GoOfferFight .%id%_GoPartOne *.%id%_GoTrashTalk <*ArenaChallenge .%id%_GoThemeInfo> *.%id%_GoThemeInfo <*THEME_EXPO&Enemy NA> .%id%_GoCheckStatus *.%id%_GoWaiting <*WaitingForDuel %4%> .%id%_GoCheckWin .%id%_GoCheckChallenge *.%id%_GoLater <*ChallengeMeLater> .%id%_GoBriefing .%id%_GoStartCombat *.%id%_GoChooseBattle <*StartArenaBattle %4% .%id%_GoStartFight> .%id%_GoStartFight result%id%01 *result%id%02 <*RejectChallenge> *result%id%03 <*MeetMeAtArena %4% .%id%_GoStartCombat> result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 MetaScene 4 2 name <%name2%'s Homebrew Arena> BoxMap RockyTiles MapWidth 32 MapHeight 32 Ceiling terrain special start GoCheckBattle GoBoringStart nu1 nu2 GoEndBattle Msg1 Msg2 Msg3 Msg5 Msg6 Content3 sub Team 1 name SetEnemy 2 ParaX 4 ParaY 4 Team 2 name SetEnemy 1 Deploy ParaX 29 ParaY 29 end MetaScene 3 sub room desig minimap < ... .......1....... ... > end end inv Trapdoor desig end %% %% *:Q_RevealEncounter Content %% %% There's an encounter. Reveal it. %% %% PARAM1: The scene being sought %% PARAM2: The outdoors scene where it is. %% Content name %% Reveal the scene using a FindEncounter mood. requires <*:Q_RevealEncounter> special % E1 is the scene being sought % E2 is the outdoors scene % E3 is a mood to be deployed to locate the hideout Element3 % P%id%01 = Initialization Counter update inv MinorMood 1 name plot_type <*FindEncounter> Element1 Element2 end %% %% *:Q_ENT_BlackMarket Content %% %% There's a black market in town and the PC needs to find it. %% %% This subplot should set a PLACE string for the black market %% in question. %% %% PARAM1: The scene being sought %% Content name %% Reveal the scene using a FindEncounter mood. requires <*:Q_ENT_BlackMarket> special % E1 is the black market being sought % E2 is a local urban scene % E3 is a mood to be deployed to locate the hideout % E4 is the encounter to be used as the entrance Place1 <2> Element2 Element3 Element4 Place4 <2> % P%id%01 = Initialization Counter update inv MinorMood 1 name plot_type <*FindEncounter> Element1 Element2 STC QUEST-MAPMARKER-STATIONARY name desig end %% %% *:Q_NPCHideout Content %% %% An NPC has a hideout, somewhere in town. The PC has to go find it. %% %% This subplot should set a PLACE string for the NPC in question. %% %% PARAM1: The NPC who needs a hideout. %% Content name %% The NPC has a hideout in the urban scene of this location. %% Reveal the hideout using a FindEncounter mood. requires <*:Q_NPCHideout> special % E1 is the NPC being sought % E2 is a local urban scene % E3 is the hideout proper % E4 is a mood to be deployed to locate the hideout Place1 <3 (Guards) sd ally> Element2 Element3 Place3 <2> Element4 % P%id%01 = Initialization Counter update sub MetaScene 3 name <%name1%'s Hideout> type special BoxMap entrance <*QUEST-INACTIVE> MapWidth 12 MapHeight 17 NeededCells 2 content Start Msg1 sub Team 1 Team 2 name Team 3 name SetAlly 2 Room minimap <......###..#1#...........> sub Elevator Destination -1 MiniMapComponent 1 end end end inv MinorMood 1 name plot_type <*FindEncounter> Element1 Element2 end %% %% *Q_ProveMyself Content %% %% The key NPC needs to prove himself, somehow. This may be to make up for %% some past deed, as a rite of passage, or whatever. In any case the deed %% done will be risky. %% %% At the first UPDATE, move the NPC to the proving ground. %% %% PARAM1: The NPC who will be doing the proving. %% Content name % A derelict spaceship has been found. A local professor has offered a reward % for the first person to locate its cargo hold. The PC can take part in the mission, % but will be beaten there by E1... at which point in time the PC can allow him to % claim the prize, or fight him and take the prize himself. requires <*Q_ProveMyself L5PAT ~"Safe"> % E1 is the character who must prove himself % E2 is the professor offering the quest % E3 is the environs scene % E4 is the spaceship dungeon % E5 is the final goal room % E6 is the professor's home Element2 Place2 <6 (Citizens) Pass Ally> Element3 Element4 Place4 <3> Element5 Place5 <4> Element6 %% FAIL CONDITIONS: %% - E2 dies start % P%id%01 = Initialization Counter % P%id%02 = Original Home of E1 Update sub Persona 1 % v%id%01 = Have gotten win speech already greeting .%id%_GoWinQuest .%id%_GoInDungeon .%id%_GoFirstDungeon *.%id%_GoBusy <*IAmBusy&OnPhone> *.%id%_GoChat <*MISC_CHATTER> result%id%01 result%id%02 .%id%_GoSurrender .%id%_GoStartFight Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 <%name1% leaves the area.> Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Persona 2 rumor%id% <%name2% has discovered an old wrecked spaceship, and is offering a reward to whoever explores it.> % V1 = Have collected reward % V2 = Have initialized quest % V3 = E1 dead at start of mission greeting GoCheckQuest GoCheckBegin GoRemindQuest GoStartQuest *GoNoQuest <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> result1 result2 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Prompt1 Prompt2 <%name1% found the cargo hold.> CPrompt2 STC QS_Dungeon_TreasureWreck SetID 4 entrance <*QUEST-INACTIVE> MetaScene 5 name <# Hold> type special BoxMap IndustrialTiles entrance <*GoDown> MapWidth 22 MapHeight 32 NeededCells 2 content % V11 = Quest initialization counter % V12 = SubQuest Status; if V12=1, quest has been initialized. % V13 = If nonzero, PC counts as first to cargo hold start GoEntry GoStartNPCDead SURRENDER%1% FAINT%1% % If the quest status hasn't been set by the time the scene is exited, do it then. End GoNoHostile GoNPCDead Msg1 Msg11 sub Team 1 Team 2 name SetAlly 1 Team 3 name SetAlly 2 Room minimap <......###..#1#...........> sub Elevator Destination -1 MiniMapComponent 1 end end end inv NPC Professor end GH2/series/ARENAMISSION_Police.txt0000644000175000017500000000130511326004535015310 0ustar kaolkaol%% ************************************** %% *** [!Ne+] BEGINNER MISSIONS *** %% ************************************** %% **************************************** %% *** [!Lo+] LOW LEVEL MISSIONS *** %% **************************************** %% ******************************************* %% *** [!Md+] MEDIUM LEVEL MISSIONS *** %% ******************************************* %% ***************************************** %% *** [!Hi+] HIGH LEVEL MISSIONS *** %% ***************************************** %% *************************************** %% *** [!Ex] ACE LEVEL MISSIONS *** %% *************************************** GH2/series/MAP_Theles3F.txt0000644000175000017500000000137511326004535014246 0ustar kaolkaol40 20 *** GearHead Location Record *** 43 13 5 14 34 13 6 14 20 13 7 14 8 13 5 14 20 13 8 14 9 13 3 14 20 13 7 14 10 13 3 14 20 13 3 14 12 13 30 14 9 13 32 14 7 13 33 14 8 13 32 14 9 13 30 14 12 13 3 14 20 13 3 14 6 13 1 14 7 13 3 14 20 13 3 14 5 13 3 14 6 13 3 14 21 13 1 15 6 13 3 14 4 13 5 14 20 13 3 14 5 13 3 14 3 13 6 14 20 13 3 14 5 13 3 14 4 13 5 14 20 13 11 14 29 13 11 14 29 13 11 14 41 13 *** 800 0 0 0 0 0 Stats 1 13 2 2 3 5 4 5 -1 Z -1 -1 0 0 0 0 0 Stats 1 20 2 2 3 5 4 5 -1 Z -1 -1 0 0 0 0 0 Stats 1 13 2 12 3 5 4 5 5 2 -1 Z -1 -1 0 0 0 0 0 Stats 1 20 2 12 3 5 4 5 5 2 -1 Z -1 -1 -1 GH2/series/WMON_antibody.txt0000644000175000017500000000440111365256063014606 0ustar kaolkaolARCH Immunokiller SDL_SPRITE SDL_COLORS <200 35 200 30 30 30 255 233 2> roguechar type habitat <> statline 13 13 13 13 1 1 1 1 MonsterTV 90 RangedCombat 9 Dodge 10 Awareness 30 NoCorpse sealed biotech sub torso sub BeamGun 5 name type range 8 PowerSource 5 name HoverJet 8 end end ARCH Immunosentry SDL_SPRITE SDL_COLORS <75 135 218 30 30 30 255 233 2> roguechar type habitat <> statline 8 8 8 8 1 1 1 1 MonsterTV 60 RangedCombat 5 Dodge 7 Awareness 20 NoCorpse sealed biotech sub torso sub BeamGun 4 name type range 6 PowerSource 5 name HoverJet 8 end end ARCH Immunoseeker SDL_SPRITE SDL_COLORS <11 218 81 30 30 30 255 233 2> roguechar type habitat <> statline 4 4 4 4 1 1 1 1 MonsterTV 30 RangedCombat 1 Dodge 4 Awareness 10 NoCorpse sealed biotech sub torso sub BeamGun 1 name range 4 PowerSource 5 name HoverJet 8 end end ARCH Teraphage SDL_SPRITE SDL_COLORS <180 36 70 180 36 70 77 156 131> roguechar

    type habitat <> statline 11 11 11 11 1 1 1 1 MonsterTV 75 CloseCombat 9 Dodge 8 Toughness 30 NoCorpse Sealed Biotech sub torso armor 5 sub Melee 8 name type Speed 3 end tail name armor 5 end ARCH Gigaphage SDL_SPRITE SDL_COLORS <255 107 83 255 107 83 77 156 131> roguechar

    type habitat <> statline 7 7 7 7 1 1 1 1 MonsterTV 45 CloseCombat 5 Dodge 5 Toughness 20 NoCorpse Sealed Biotech sub torso armor 2 sub Melee 5 name type end tail name armor 2 end ARCH Megaphage SDL_SPRITE SDL_COLORS <250 200 49 250 200 49 77 156 131> roguechar

    element4 palette_entry_code4 element5 palette_entry_code5 element6 palette_entry_code6 element7 palette_entry_code7 element8 palette_entry_code8 element9 palette_entry_code9 element10 palette_entry_code10 GH2/series/PLOT_CORE_Reward.txt0000644000175000017500000000557511333736445015046 0ustar kaolkaol% % Plots based on the *CORE_R_ goals. % Plot name desc requires <*CORE_R_MEK ~P:++ Common> % Element1 is the NPC to meet % Element2 is the martial artist % Element3 is the meeting scene element1 Element2 Element3 start .%id%_GoCheckDead Msg%id%01 <%name1%@ \SCENE &EpisodeScene :// If you're in town I'd love to speak with you.> % SubPlot1 is the conversation you're going to have. subplot1 <*CS_Conversation 2> sub Persona 1 greeting rumor%id% <%name1% has been looking for you.> GoChat *.%id%_GoGreet <*HowAreYou .%id%_GoAdvise> .%id%_GoAdvise Msg%id%01 Msg%id%02 <%name1% advised you to ask %name2% at %name3% for a mission.> Msg%id%03 <%name1% told you about %name2%'s mission.> end inv NPC Engineer chardesc old renowned end Plot name desc requires <*CORE_R_EXP ~P:++ Common> % Element1 is the NPC to meet % Element2 is the martial artist % Element3 is the meeting scene element1 Element2 Element3 start .%id%_GoCheckDead Msg%id%01 <%name1%@ \SCENE &EpisodeScene :// Give me a call when you get in town.> % SubPlot1 is the conversation you're going to have. subplot1 <*CS_Conversation 2> sub Persona 1 greeting rumor%id% <%name1% knows someone who can help you train.> GoChat *.%id%_GoGreet <*HowAreYou .%id%_GoAdvise> .%id%_GoAdvise Msg%id%01 Msg%id%02 <%name1% told you that %name2% in %name3% might be able to help you train.> Msg%id%03 <%name1% told you to seek training from %name2%.> end inv NPC Martial Artist chardesc old renowned end GH2/series/MEGA_CORE_RevealEncounter.txt0000644000175000017500000023767011376734116016661 0ustar kaolkaol %% %% *:CS_LocateDuel Content %% %% Two NPCs are about to fight. The PC needs to find them; this could be to %% aid one NPC against the other, to stop the fight, or just to watch. Who knows? %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The presumed ally in the fight %% PARAM4: The presumed antagonist in the fight %% Content name desc requires <*:CS_LocateDuel> Size 5 % E1 is the outdoors scene where the duel is to take place % E2 is the metascene being sought % E3 is the presumed ally % E4 is the presumed antagonist % E5 is an encounter for E4's lance Element5 Place5 <1> HINT_%id% sub MetaScene 5 2 rumor%id% <%name4% has been searching for %name3% in %name1%.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time % L3 = Storynote % L4 = Time Limit % L5 = Number of tries MapWidth 50 MapHeight 50 % The time limit is five minutes, plus an additional two minutes for each % previous attempt. Start nu1 nu2 GoTooLate end Msg1_1 Msg1_2 <> Msg1_3 <> Msg1_4 <> Msg1_5 <> Msg2 Msg3 Msg4 Msg5 Msg6 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv Encounter name <%name4%'s Lance> % V1 = Recharge Counter % This encounter will appear once every 5 hours, as long as this is the active subplot. update GoSetOrders use GoAutoAttack GoAvoidAttack end %% %% *:CS_DiscoverNPCMission Content %% &EnemyNPC The NPC in question is the core story enemy NPC %% &DestroyFactory The mission is to destroy a factory %% &GatheringForces The enemy forces are assembling for some big event %% &Searching The enemy is searching for something %% %% An NPC is going on some kind of mission which the PC has to stop. In order %% to do this he's going to have to find out what the mission is, then where %% it is. %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically give the PC 100XP. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The NPC doing the mission %% Content name desc %% No XP award for this component, because it's trivial. requires <*:CS_DiscoverNPCMission !Ne> Size 2 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is the NPC being sought % E4 is a local public scene % E5 is a trucker or adventurer who can tell where the raiders are. Element4 Element5 NeverFail5 % The Hint HINT_%id% %% FAIL CONDITIONS: %% - E5 dies before revealing the secret start sub Persona 5 rumor%id% <%name5% had a run-in with %name3% a little while ago.> greeting *GoGreet <*IHaveInformation&AboutNPC GoSpiel %3%> GoSpiel Msg1 <%name3% stops by here from time to time. Today \SPR %3% said \SPR %3% 's got something to do in \EXACT_SCENE %1% ... I don't know what's up but it sounded ominous.> Msg2 <%name5% told you that %name3% was headed for \EXACT_SCENE %1% .> end Content name desc requires <*:CS_DiscoverNPCMission (&GatheringForces|&Searching)> Size 5 % E1 is the outdoors scene where the encounter is % E2 is the metascene being sought % E3 is the NPC doing the mission % E4 is a local NPC who's an enemy of E3 but not of the player % E5 is the encounter/patrol you must rescue Element4 Element5 Place5 <1> HINT_%id% <%name4% has also been looking into this; you may be able to collaborate.> %% FAIL CONDITIONS: %% - E4 dies before revealing E5 % P%id%01 = E5 has been revealed end % SubPlot1 - The find-your-own-damn-henchmen branch. subplot1 <*TrailHenchmenToNPC 1 2 3> sub Persona 4 rumor%id% <%name4%'s team has been tracking %name3%.> greeting GoChat *GoGreet <*IHaveInformation&AboutNPC GoBrief %3%> GoBrief result1 result2 result3 Msg1 Msg2 Msg3 <%name4% sent you to aid a patrol in %name1% which had been trailing %name3%.> Msg4 Msg5 <%name4% told you that %name3% was in %name1%.> Prompt1 Prompt2 Prompt3 CPrompt3 MetaScene 5 2 % You arrive in time to see a group of enemies fighting the recon patrol. % Defend the scouts to learn the location of E3. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 GoFailRescue end Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name setally 1 setenemy 2 Deploy ParaX 15 ParaY 15 end end inv stc CORE-ACTIVATABLE name end Content name desc requires <*:CS_DiscoverNPCMission &GatheringForces> Size 1 % E1 is the outdoors scene where the encounter is % E2 is the metascene being sought % E3 is the NPC doing the mission % E4 is a public scene % E5 is the recruiter % E6 is a possible new recruit Element4 Element5 NeverFail5 Place5 <4 sd ally (Guards)> Element6 Place6 <4 sd ally (Guards)> % The Hint HINT_%id% %% FAIL CONDITIONS: %% - E5 dies % P%id%01 = Initialization Counter % P%id%02 = Have spoken to E5 already (1)/Have intimidated E6 out of contest (2) end start update % SubPlot1 = The duel with E6 % SubPlot2 = Trail E6 to base SubPlot1 <*:CS_TournamentMatch 6 4> SubPlot2 <*TrailRaiderToBase 1 2 6> sub Persona 5 rumor%id% <%name5% has been recruiting some pilots for %name3%.> % V%id%01 = First time counter % V%id%02 = Tried bluff already... greeting .%id%_GoCheckProgress .%id%_GoGreet .%id%_GoExplain .%id%_GoReveal result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 *result%id%06 <*GoodBye> result%id%07 result%id%08 .%id%_GoR8Fail Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 <%name5% suggested that you challenge %name6% for a chance to get close to %name3%.> Msg%id%06 Msg%id%07 Msg%id%08 <%name6% has left to meet up with %name3%. My work here is done.> Msg%id%09 <%name6% has gone to meet up with %name3%, but with any luck you'll be able to find \OPR %6% first.> Msg%id%10 Msg%id%11 Msg%id%12 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 <%name6% just chickened out. Hire me instead.> CPrompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%08 <%name6% just quit. Look, \SPR %6% 's gone!> CPrompt%id%08 Persona 6 greeting result%id%01 result%id%02 *result%id%03 <*GoodBye> result%id%04 .%id%_GoR4Fail result%id%05 result%id%06 result%id%07 Msg%id%01 Msg%id%02 Msg%id%03 <%name5% is the one to talk to about that. I don't know anything.> Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Prompt%id%06 Prompt%id%07 end Content name desc requires <*:CS_DiscoverNPCMission &EnemyNPC> Size 5 % E1 is the outdoors scene where the encounter is % E2 is the metascene being sought % E3 is the NPC doing the mission % E4 is an environs scene % E5 is a combat encounter against the henchmen % E6 is a clue that can be obtained from the wreckage Element4 Element5 Place5 <4> Element6 Place6 % The Hint HINT_%id% <%name3%'s allies have been seen in %name4%.> % SubPlot1 is the clue identification utility, in case the PC can't do it himself SubPlot1 <*CS_AnalyzeClue_LeadToNPC 1 2 3 6> sub MetaScene 5 2 rumor%id% <%name3%'s henchmen have been spotted in %name4%.> special MapWidth 50 MapHeight 50 % L1 = Encounter Over Counter % L2 = Initialization Counter Start nu1 nu2 end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv Encounter name <%name3%'s Henchmen> % V1 = Recharge Counter % This encounter will appear once every 3 hours, as long as this is the active subplot. update GoSetOrders use GoAutoAttack GoAvoidAttack Treasure name desc use GoNoUse CLUE_SURVIVAL CLUE_SCIENCE CLUE_CODEBREAKING CLUE_MYSTICISM CLUE_INSIGHT GoReveal GoFail Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 end %% %% *CS_AnalyzeClue_LeadToItem Content %% %% The PC has a clue. Maybe he can't figure it out himself. So, he can bring %% it to someone who can. Unfortunately, doing so is going to cost both money %% and time. %% %% In this case, analyzing the clue will lead to the whereabouts of an item. %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The item being sought %% PARAM4: The clue-bearing item %% Content name desc requires <*CS_AnalyzeClue_LeadToItem> size 1 % E1 is the outdoor scene where the encounter is % E2 is the metascene being sought % E3 is the item being sought % E4 is the item to be identified % E5 is a public scene for the meeting % E6 is the investigator element5 element6 NeverFail6 Place6 <5 pass ally> %% FAIL CONDITIONS: %% - If E4 is destroyed end sub Persona 6 rumor%id% <%name6% knows a lot about old artifacts.> % v%id%01 = Clue processing time % v%id%02 = First time counter/Identification Cost greeting GoCheckFirst GoNotHere GoNotYet GoChat GoGreet GoMakeOffer result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 end %% %% *CS_AnalyzeClue_LeadToNPC Content %% %% The PC has a clue. Maybe he can't figure it out himself. So, he can bring %% it to someone who can. Unfortunately, doing so is going to cost both money %% and time. %% %% In this case, analyzing the clue will lead to the whereabouts of a NPC. %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The NPC being sought %% PARAM4: The clue-bearing item %% Content name desc requires <*CS_AnalyzeClue_LeadToNPC> size 1 % E1 is the outdoor scene where the encounter is % E2 is the metascene being sought % E3 is the NPC being sought % E4 is the item to be identified % E5 is a public scene for the meeting % E6 is the investigator element5 element6 NeverFail6 Place6 <5 pass ally> %% FAIL CONDITIONS: %% - If E4 is destroyed end sub Persona 6 rumor%id% <%name6% does a bit of freelance forensic work.> % v%id%01 = Clue processing time % v%id%02 = First time counter/Identification Cost greeting GoCheckFirst GoNotHere GoNotYet GoChat GoGreet GoMakeOffer result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 end %% %% *:CS_RevealEncounter_FactionTarget Content %% &IsEnemy The faction in question is an enemy %% &OfAttack The target is going to be attacked %% %% You're searching for the target of a given faction. They're apparently %% going to attack something, for some reason. %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically give the PC 100XP. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The faction launching the attack %% Content name desc requires <*:CS_RevealEncounter_FactionTarget &IsEnemy &OfAttack -!Ne -!Lo> size 6 % E1 is the outdoor scene where the encounter is % E2 is the metascene being sought % E3 is the faction launching the attack % E4 is a different outdoor scene for this subplot % E5 is the combat encounter % E6 is a member of E3 who will be questioned Element4 Element5 Place5 <4> Element6 Place6 <5 (Enemies) SD enemy> % The Hint HINT_%id% % P%id%01 = Initialization Counter update % SubPlot1 is the TrailRaider subplot, in case the PC doesn't win here. SubPlot1 <*TrailRaiderToBase 1 2 6> sub MetaScene 5 2 rumor%id% % V1 = Encounter Over Counter MapWidth 50 MapHeight 50 % L1 = Encounter Over Counter % L2 = Initialization Counter Start nu1 nu2 Faint%6% GoNobodyLeft GoRevealEncounter Msg1 <%name6% leaves to rendezvous with the attack group. If you hurry, you might still have time to catch them.> Msg2 <%name6% is somewhere out there meeting with the attack group from %name3%. If you hurry, you might still have time to catch them.> Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end Persona 6 special greeting result%id%01 Msg%id%01 <\PC , I was warned that you'd try to interfere in the plans of %name3%.> Msg%id%02 Prompt%id%01 end inv STC CORE-INVISIBLEENCOUNTER name end %% %% *:CS_RevealEncounter_ItemDungeon Content %% &Derelict The dungeon is a wrecked spaceship %% %% You're searching for a dungeon which contains a certain item. The %% name of the location may be expressed as "a %name2%" or "the %name2%". %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically give the PC 100XP. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The item contained within %% Content name desc requires <*:CS_RevealEncounter_ItemDungeon ~&Derelict> Size 5 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is the item being sought % E4 is an encounter % E5 is the treasure map Element4 Place4 <1> Element5 % The Hint HINT_%id% % SubPlot1 is the clue identification utility, in case the PC can't do it himself SubPlot1 <*CS_AnalyzeClue_LeadToItem 1 2 3 5> sub MetaScene 4 2 rumor%id% special % V1 = Encounter Over Counter MapWidth 50 MapHeight 50 % L1 = Encounter Over Counter % L2 = Initialization Counter Start nu1 nu2 end Msg1_1 Msg1_2 <> Msg1_3 <> Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name % V1 = Recharge Counter % This encounter will appear once every 5 hours, as long as this is the active subplot. update GoSetOrders use GoAutoAttack GoAvoidAttack Treasure name desc use GoNoUse CLUE_SURVIVAL CLUE_SCIENCE CLUE_CODEBREAKING CLUE_MYSTICISM CLUE_INSIGHT GoReveal GoFail Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 end %% %% *:CS_DiscoverNPCHasItem %% &NotReally The NPC doesn't actually have it %% %% In this subplot, the PC will learn that an item being sought is in the possession %% of a certain NPC. %% %% When this subplot reveals the NPC, the following trigger must be set: %% .%id%_%plotid%_GoDiscover %% %% PARAM1: The NPC who has it %% PARAM2: The item being sought %% PARAM3: The outdoors scene where the NPC's base may be found %% PARAM4: The NPC's base %% Content name desc requires <*:CS_DiscoverNPCHasItem> Size 1 % E1 is the NPC with the item % E2 is the item % E5 is a local public scene % E6 is an NPC who knows something about it Element5 Element6 NEVERFAIL4 % FAIL CONDITIONS % - E6 dies before revealing the secret % - The RevealNPCBase subplot fails start .%id%_GoCheckWin .%id%_GoCheckLoss HINT_%id% <%name6% might know something about this.> % SubPlot1 is the RevealEncounter bit. SubPlot1 <*:CS_RevealEncounter_NPCBase 3 4 1> sub Persona 6 rumor%id% <%name6% knows something about the %name2%.> greeting GoChat *GoGreet <*LookingForItem %2% GoExplain> GoExplain Msg1_1 Msg1_2 Msg1_3 Msg2 Msg3 end %% %% *:CS_RevealEncounter_NPCBase Content %% &PirateShip The base is a pirate ship. Arr! %% &EnemyNPC The NPC being sought is the core story enemy. %% &Kidnapping The NPC has kidnapped somebody %% %% You're searching for the base/ship/hideout of a particular NPC. %% The name may be expressed here as "%name3%'s %name2%", for instance %% "Magoo's Pirate Ship" or "Bertram's Secret Base". %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically give the PC 100XP. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The NPC in charge of it %% Content name desc %% Exactly like Ranger's Delight below, but here you're tracking a specific person. requires <*:CS_RevealEncounter_NPCBase> Size 5 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is the NPC being sought % E4 is an encounter Element4 Place4 <1> % The Hint HINT_%id% <%name3%'s men have been seen in %name1%.> % P%id%01 = Initialization Counter % P%id%02 = Tracking Difficulcy Number % This particular subplot can't fail. Acers! update &%id%_RevealEncounter sub MetaScene 4 2 rumor%id% <%name3%'s henchmen have been seen in %name1%.> special % V1 = Encounter Over Counter % V2 = Number of failed attempts to track MapWidth 50 MapHeight 50 % Once the PC has defeated all the raiders, they get an Awareness roll to determine % the location of their main force. nu1 nu2 GoTrackFailed end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name %% If the PC manages a stealth roll upon approaching this encounter, the %% raiders may be followed back to their base instead of being fought. %% V1 = Timer; will only attack once every 3 hours update use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 Msg5 end Plot name requires <*:CS_RevealEncounter_NPCBase> desc % This component has no size, even though the PC must perform a task to move on; % the size will be provided by the duel subplot. % E1 is the outdoors scene where it is % E2 is the metascene being sought % E3 is the NPC in charge of it % E4 is the All-Knowing Smuggler % E5 is an outdoors scene for the duel % E6 is a public scene for the NPC Element4 Place4 <6 sd ally> Element5 Element6 % The Hint HINT_%id% % SubPlot1 is the duel itself % SubPlot2 is a blank plot state SubPlot1 <*CS_Duel&NoDerailment 4 6 5> % As long as we're still at this phase, the NPC's death will end the plot. start % Duel Results .%id1%_%plotid%_GoWin .%id1%_%plotid%_GoLose Msg%id%01 Msg%id%02 <%name4% revealed %name3%'s %name2%'s location in %name1%.> Msg%id%03 Msg%id%04 sub Persona 4 rumor%id% <%name4% knows where to find just about everything in this town.> % V1 = Have previously dueled % V2 = Price for revealing location greeting *GoExplain <*LookingForScene %2% GoSetPrice> GoSetPrice GoOffer result1 result2 *result3 <*HurryBackWithMoney> result4 result5 result6 Msg1 Msg2 <%name4% offered to tell you where %name3%'s %name2% is, in exchange for $ \VAL V2 .> Msg3 Msg4 <%name4% revealed the location of %name3%'s %name2% in \EXACT_SCENE %1% .> Msg5 Msg7 Msg8 Msg9 Msg10 Msg11 Prompt1 CPrompt1 Prompt2 CPrompt2 Prompt3 Prompt4 CPrompt4 Prompt5 CPrompt5 Prompt6 end inv NPC Pirate job end Content name desc requires <*:CS_RevealEncounter_NPCBase (&PirateShip|3:THIEF|3:CRIHN|3:REDMA) -!Hi -!Ex> Size 3 % E1 is the outdoors scene % E2 is the encounter to reveal % E3 is the pirate captain % E4 is a shopping area in town % E5 is a junior pirate to rob the joint Element4 Element5 Place5 <4 (Citizens) pass> % The Hint HINT_%id% % p%id%01 = Message Counter start .%id%_%plotid%_GoArrest Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 % SubPlot1 is the tracking subplot. SubPlot1 <*TrailRaiderToBase 1 2 5> sub Persona 5 rumor%id% special greeting GoChat result1 result2 result3 Msg1 Msg2 Msg3 <%name5% leaves the area.> Msg4 <%name5% left before you could discover where %name3%'s %name2% is, but you may still be able to find \OPR %5% nearby.> Msg5 <%name5% stops dead in \PPR %5% tracks.> Msg6 Msg7 Prompt1 <[Get out of the way]> Prompt2 CPrompt2 Prompt3 <[Trip %name5%]> CPrompt3 end inv NPC Pirate end %% %% *:CS_RevealEncounter_Raiders Content %% &ReconMission The enemies are doing reconnaisance of some type %% %% There are some hostile mecha out there which are aligned with the %% enemy faction/enemy NPC. %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically give the PC 100XP. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% Content name desc %% No XP award for this component, because it's trivial. requires <*:CS_RevealEncounter_Raiders !Ne> Size 2 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is a local public scene % E4 is a trucker or adventurer who can tell where the raiders are. Element3 Element4 NeverFail4 % The Hint HINT_%id% %% FAIL CONDITIONS: %% - E4 dies before revealing the secret start sub Persona 4 rumor%id% <%name4% had a run-in with some mysterious mecha a little while ago.> greeting *GoGreet <*IHaveInformation GoSpiel na> GoSpiel 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 Msg2 <%name4% told you that the raiders are in \EXACT_SCENE %1% .> end Content name desc requires <*:CS_RevealEncounter_Raiders E:A.nme> % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is an encounter Element3 Place3 <1> % The Hint HINT_%id% % P%id%01 = Initialization Counter % P%id%02 = Tracking Difficulcy Number % This particular subplot can't fail. Acers! update &%id%_RevealEncounter sub MetaScene 3 2 rumor%id% special % V1 = Encounter Over Counter % V2 = Number of PC defeats... % V3 = Entry message counter MapWidth 50 MapHeight 50 start % Once the PC has defeated all but one of the raiders, the % survivor will spill the beans. nu1 nu2 GoCheckAllGone GoTrackFailed end Msg1 Msg2 Msg3 Msg4_1 CMsg4_1 Msg4_2 CMsg4_2 Msg5 Msg6_1 Msg6_2 Msg7 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 % We need at least two mecha, so do a double generation. Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name %% If the PC manages a stealth roll upon approaching this encounter, the %% raiders may be followed back to their base instead of being fought. %% V1 = Timer; will only attack once every 3 hours update use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 Msg5 end Content name desc %% How you track someone in space I'm not really sure. Minovsky particles? requires <*:CS_RevealEncounter_Raiders> Size 5 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is an encounter Element3 Place3 <1> % The Hint HINT_%id% % P%id%01 = Initialization Counter % P%id%02 = Tracking Difficulcy Number % This particular subplot can't fail. Acers! update &%id%_RevealEncounter sub MetaScene 3 2 rumor%id% special % V1 = Encounter Over Counter % V2 = Number of failed attempts to track MapWidth 50 MapHeight 50 % Once the PC has defeated all the raiders, they get an Awareness roll to determine % the location of their main force. nu1 nu2 GoTrackFailed end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name %% If the PC manages a stealth roll upon approaching this encounter, the %% raiders may be followed back to their base instead of being fought. %% V1 = Timer; will only attack once every 3 hours update use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 Msg5 end Content name desc requires <*:CS_RevealEncounter_Raiders F:++> Size 5 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is an encounter % E4 is the PC's friend Element3 Place3 <1> Element4 Place4 <3 sd ally (Friend)> % The Hint HINT_%id% sub MetaScene 3 2 MapWidth 10 MapHeight 10 % L1 = Initialization Counter Start sub team 1 SetAlly 2 ParaX 4 ParaY 4 team 2 name SetAlly 1 ParaX 6 ParaY 6 end Persona 4 rumor%id% <%name4% has been looking for you in %name1%.> special greeting GoRevealLocation result1 result2 Msg1 Msg2 Msg3 <%name4% betrayed \FACTION &EnemyFac to tell you where the raiders were.> Msg4 <%name4% revealed the location of the raiders in %name1%.> Prompt1 Prompt2 end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name <%name4%'s Mecha> update attack ENCOUNTER_Hostile end Content name desc requires <*:CS_RevealEncounter_Raiders> Size 2 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is a local scene for the rowdy raider % E4 is the confused raider Element3 Element4 Place4 % The Hint HINT_%id% %% FAIL CONDITIONS: %% - If E4 dies, this subplot will fail. % p%id%01 = Initialization Counter % p%id%02 = Message Counter start update .%id%_GoCheckEntrance Surrender%4% Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 <%name4% leaves the area.> Msg%id%05 % SubPlot1 is what happens if E4 leaves the area. SubPlot1 <*TrailRaiderToBase 1 2 4> sub Persona 4 rumor%id% special greeting GoChat GoReveal result1 result2 *GoR2Finish <*BrushOff> result3 result4 result5 GoR5Fail result6 GoR6Fail result7 GoR7Fail result8 result9 GoR9Fail Msg1 Msg2 Msg3 <%name4% leaves the area.> Msg4 <%name4% left before you could learn where the raiders were.> Msg5 <%name4% left before you could learn where the raiders are, but you may still be able to find \OPR %4% nearby.> Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Msg15 Msg16 Msg17 Msg18 Msg19 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 CPrompt9 end inv NPC Mecha Pilot chardesc Young Cheerful job_desig end Content name desc requires <*:CS_RevealEncounter_Raiders> Size 2 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is a local scene for the rowdy raider % E4 is the rowdy raider % E5 is the dataslate that may provide a clue Element3 Element4 Place4 Element5 Place5 <4> % The Hint HINT_%id% %% FAIL CONDITIONS: %% - If Item E5 is destroyed and E4 is dead, this subplot will fail. % p%id%01 = Initialization Counter % p%id%02 = Message Counter start update .%id%_GoCheckEntrance Surrender%4% &%id%_RevealEncounter Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 <%name4% leaves the area.> Msg%id%05 % SubPlot1 is what happens if E4 leaves the area. SubPlot1 <*TrailRaiderToBase 1 2 4> sub Persona 4 rumor%id% special greeting GoChat GoThreat GoJobOffer GoRaiderFlees GoStartFight result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 result%id%09 result%id%10 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%05 Msg%id%05_1 Msg%id%06 <%name4% leaves the area.> Msg%id%07 <%name4% left before you could learn where the raiders were.> Msg%id%08 Msg%id%08_1 Msg%id%09 Msg%id%10 Msg%id%10_1 Msg%id%11 Msg%id%12 Msg%id%12_1 Msg%id%13 Msg%id%13_1 Msg%id%13_2 Msg%id%14 Msg%id%15 Msg%id%15_1 Msg%id%16 <%name4% left before you could learn where the raiders are, but you may still be able to find \OPR %4% nearby.> Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Prompt%id%05_1 Prompt%id%06 Prompt%id%06_1 Prompt%id%07 Prompt%id%07_1 Prompt%id%08 Prompt%id%08_1 Prompt%id%09 Prompt%id%09_1 Prompt%id%10 Prompt%id%10_1 end inv NPC Mecha Pilot job_desig Knowledge 7 Body 15 Computer 1 name desc Category SDL_PORTRAIT Memo Email Mass -1 use CLUE_CODEBREAKING CLUE_INSIGHT GoNoUse Msg1 Msg2 Msg3 end %% %% *:CS_BlockedMeeting %% &EnemyNPC The NPC is the core story enemy %% &EnemyFac The NPC belongs to the enemy faction %% &ForPeace The PC comes in peace %% %% The PC wants to meet an NPC, but the core story enemy is out to prevent %% it. Hillarity ensues. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The metascene being sought %% PARAM3: The NPC to be met %% Content name desc requires <*:CS_BlockedMeeting ~E:++ -&EnemyNPC F:++> % E1 is the outdoors scene where the encounter is % E2 is the metascene being sought % E3 is the NPC to be met % E4 is the fight scene that kicks off everything % E5 is the mood that it spawns Element4 Place4 <1> Element5 %% FAIL CONDITIONS: %% - None. sub MetaScene 4 2 rumor%id% % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start GoHenchmanAnnounce nu1 nu2 %% Upon leaving the encounter, activate the meme end Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg1_3 CMsg1_3 Msg2 Msg3 Msg4_1 <\PERSONA &EnemyNPC swore to stop you from reaching %name3%.> CMsg4_1 Msg4_2 CMsg4_2 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER-SD name MinorMood 1 name <"I Won't Allow It" Mood> plot_type <*FightToDestination> Element1 Element2 Element3 end %% %% *TrailHenchmenToNPC Content %% %% The PC must follow some NPCs to their base. %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically not give the PC any XP. Bummer. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The NPC being sought %% Content name desc requires <*TrailHenchmenToNPC> Size 4 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is the NPC being sought % E4 is an encounter Element4 Place4 <1> % The Hint HINT_%id% <%name3%'s men have been seen in %name1%.> % P%id%01 = Initialization Counter % P%id%02 = Tracking Difficulcy Number % This particular subplot can't fail. Acers! update &%id%_RevealEncounter sub MetaScene 4 2 rumor%id% <%name3%'s henchmen have been seen in %name1%.> special % V1 = Encounter Over Counter % V2 = Number of failed attempts to track MapWidth 50 MapHeight 50 % Once the PC has defeated all the raiders, they get an Awareness roll to determine % the location of their main force. nu1 nu2 GoTrackFailed end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name <%name3%'s Henchmen> %% If the PC manages a stealth roll upon approaching this encounter, the %% raiders may be followed back to their base instead of being fought. %% V1 = Timer; will only attack once every 3 hours update use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 Msg5 end %% %% *TrailRaiderToBase Content %% %% The PC must follow an enemy pilot to their base. %% %% When the PC has located the encounter, this subplot will make it visible %% with SetEncounterActive and conclude with WinSubPlot. Revealing the entrance %% in this subplot will typically not give the PC any XP. Bummer. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The MetaScene being sought %% PARAM3: The raider being trailed %% Content name desc requires <*TrailRaiderToBase> Size 2 % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is the raider being trailed. % E4 is a new encounter for the speed match Element4 Place4 <1> % p%id%01 = Initialization Counter update sub MetaScene 4 2 rumor%id% <%name3% has been spotted in %name1%.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time % L3 = Storynote % L4 = Time Limit % L5 = Number of tries MapWidth 50 MapHeight 50 % The time limit is five minutes, plus an additional two minutes for each % previous attempt. Start nu1 nu2 GoTooLate end Msg1_1 Msg1_2 <> Msg1_3 <> Msg1_4 <> Msg1_5 <> Msg2 Msg3 Msg4 Msg5 Msg6 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv Encounter EncounterMove 50 name <%name3%'s Lance> % V1 = Recharge Counter % This encounter will appear once every 5 hours, as long as this is the active subplot. update GoSetOrders use GoAutoAttack GoAvoidAttack end GH2/series/MEGA_CORE_MEET_Conversation.txt0000644000175000017500000021166311365256063017034 0ustar kaolkaol%% %% *CS_Conversation Content %% %% The PC approaches the NPC. This conversation will lead into the episode's %% MAIN subplot. %% %% Please note that these conversations should not care about the scene where the %% NPC is placed. %% %% The parent plot sets the plot status to this layer's ID when ready. %% %% Param1: The NPC being spoken to %% %% **************************** %% *** THE BASIC FIVE *** %% **************************** %% %% These five conversation subplots cover the five MAIN_COURSE types. %% Content name desc requires <*CS_Conversation ~C:POLIC ~1:POLIC ~P:POLIC E:++ ~1:PDASS ~1:PCFAC ~E:ACADE ~E:ADVEN> % E1 is the NPC that the PC will speak with % E2 is the Enemy NPC element2 % Fail Conditions: % - Mission giver dies before encounter is activated end .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_StopNPCMission&IsEnemyNPC 2> SubPlot2 <*CS_WinGoal 1> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> *.%id%_GoBriefing <*CS_BasicStopEnemyMission .%id%_GoAccept .%id%_GoDeny %id2%> *.%id%_GoAccept <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit *.%id%_GoDeny <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit end Content name desc requires <*CS_Conversation ~C:THIEF ~1:THIEF ~P:CRIME -1:PDASS ~1:PCFAC -1:NOFAC ~E:THIEF ~E:TRADE> % E1 is the NPC that the PC will speak with % E2 is the item to fetch Element2 % Fail Conditions: % - Mission giver dies before encounter is activated % - Fetch subplot fails end .%id%_GoCheckReveal % Upon getting the item, set the win subplot. Get%2% Msg%id%01 Msg%id%02 %% SubPlot1 is Fetch Item %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_FetchItem 2> SubPlot2 <*CS_WinFetchItem 1 2> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing *result%id%01 <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit *Result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit Msg%id%01 Msg%id%02 <%name1% hired you to recover a lost datachip.> Prompt%id%01 Prompt%id%02 CPrompt%id%02 end inv Treasure name desc end Content name desc requires <*CS_Conversation ~C:POLIT ~C:ACADE ~C:MEDIA ~1:POLIT ~1:ACADE ~1:MEDIA ~1:MEDIC ~P:POLIT -1:PDASS ~1:PCFAC ~E:MEDIA ~E:POLIT> % E1 is the NPC that the PC will speak with % Fail Conditions: % - Mission giver dies before mission is activated end .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is Gather Information %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_GatherInformation 1> SubPlot2 <*CS_WinGoal 1> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing Result%id%01 *Result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit Msg%id%01 Msg%id%02 <%name1% hired you to investigate things.> Prompt%id%01 Prompt%id%02 CPrompt%id%02 end Content name desc requires <*CS_Conversation ~C:MILIT ~1:MILIT ~1:ADVEN ~P:MILIT -1:PDASS ~1:PCFAC ~E:MILIT> % E1 is the NPC that the PC will speak with % E2 is the outdoors location where the encounter will be placed % E3 is the encounter to be found element2 element3 place3 <2> % Fail Conditions: % - Mission giver dies before encounter is activated % - Revalation subplot fails end .%id%_GoCheckReveal .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the encounter revealation %% SubPlot3 is the winning condition %% SubPlot4 is the loss condition %% SubPlot5 is the unwinnable condition SubPlot1 <*:CS_MechaEncounter&Raiders 2 3> SubPlot2 <*:CS_RevealEncounter_Raiders 2 3> SubPlot3 <*CS_WinGoal 1> SubPlot4 <*CS_LoseGoal 1> SubPlot5 <*CS_MootGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> *.%id%_GoBriefing <*CS_BasicFightingMission .%id%_GoAccept .%id%_GoDeny %id3%> *.%id%_GoAccept <*GoodLuckPlusHint .%id%_GoR1Exit %id2%> .%id%_GoR1Exit *.%id%_GoDeny <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit end inv stc CORE-ACTIVATABLE name <%name1%'s Mission> end Content name desc requires <*zzzCS_Conversation (1:PCFAC|1:THIEF) P:PCFAC (P:CRIME|P:PRIVA) -*CORE_R_> % E1 is the NPC that the PC will speak with % E2 is the outdoors location where the encounter will be placed element2 % Fail Conditions: % - Mission giver dies before encounter is activated end .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_Plunder 2> SubPlot2 <*CS_WinGoal&NoCash 1> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing *result%id%01 <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit *result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit Msg%id%01 Prompt%id%01 Prompt%id%02 end %% ******************* %% *** DEFAULT *** %% ******************* Content name desc requires <*CS_Conversation ~C:POLIC (1:POLIC|1:ADVEN|1:PDASS) ~P:POLIC E:-- ~1:PCFAC> % E1 is the NPC that the PC will speak with % E2 is the enemy of the week element2 % Fail Conditions: % - Mission giver dies before encounter is activated end .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_StopNPCMission 2> SubPlot2 <*CS_WinGoal 1> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing *result%id%01 <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit *result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit Msg%id%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 end Content name desc requires <*CS_Conversation (1:MEDIC|1:POLIC|1:POLIT) (C:MEDIC|*CORE_DEF_) -!Ne ~1:PCFAC> % E1 is the NPC that the PC will speak with % E2 is the item to fetch % E3 is the epidemic mood Element2 Element3 % Fail Conditions: % - Mission giver dies before encounter is activated % - Fetch subplot fails % Upon either winning or losing the plot, the epidemic's time limit will % be set. end .%id%_GoCheckReveal % Upon getting the item, set the win subplot. Get%2% Msg%id%01 Msg%id%02 %% SubPlot1 is Fetch Item %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_FetchItem&Stolen&Emergency 2> SubPlot2 <*CS_WinFetchItem 1 2> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 % V%id%01 = Set Mood counter % V%id%02 = Synthesis counter greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> .%id%_GoRemind .%id%_GoBriefing *result%id%01 <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 <%name1% asked you to recover the city's supply of vaccine.> Msg%id%03 Msg%id%04 Msg%id%05 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%03 <[Continue]> Prompt%id%04 end inv Treasure name desc mass 7 MinorMood 3 name plot_type <*Epidemic> %% Once per half hour, check to see if the PC gets sick. Wearing %% a sealed suit will cut the risk by a lot, but it still isn't %% perfectly safe. halfhour GoNotSealed GoGetSick Msg1 % V1 = Initialization Counter update Msg2_1 <%city% struck by outbreak of Martian influenza. Residents advised to wear complete environmental sealing at all times.> Msg2_2 % Is it obvious that I've been reading Judge Dredd all weekend? Msg2_3 % Meme Messages Msg_1 Msg_2 CMsg_2 Msg_3 Msg_4 CMsg_4 Msg_5 end Content name desc requires <*CS_Conversation 1:MEDIC ~C:MEDIC ~C:ACADE ~*CORE_R_ -*CORE_OFF_ ~1:NOFAC> % E1 is the NPC that the PC will speak with % E2 is the item to fetch Element2 % Fail Conditions: % - Mission giver dies before encounter is activated % - Fetch subplot fails end .%id%_GoCheckReveal % Upon getting the item, set the win subplot. Get%2% Msg%id%01 Msg%id%02 %% SubPlot1 is Fetch Item %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_FetchItem 2> SubPlot2 <*CS_WinFetchItem 1 2> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing *result%id%01 <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit *Result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit Msg%id%01 Msg%id%02 <%name1% asked you to locate a rare medicinal fungus.> Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 end inv Treasure name desc end Content name desc requires <*CS_Conversation (1:FRIEND|1:FAMILY|1:LOVER) -!Md -!Hi -!Ex> % E1 is the NPC that the PC will speak with % E2 is the item to fetch Element2 % Fail Conditions: % - Mission giver dies before encounter is activated % - Fetch subplot fails end .%id%_GoCheckReveal % Upon getting the item, set the win subplot. Get%2% Msg%id%01 Msg%id%02 %% SubPlot1 is Fetch Item %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_FetchItem 2> SubPlot2 <*CS_WinFetchItem 1 2> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoOfferMission <*CS_C_Greeting .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing .%id%_GoStartMission result%id%01 result%id%02 Msg%id%01 Msg%id%02 <%name1% asked you to help find \PPR %1% watch.> Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 end inv Treasure name desc <%name1%'s watch. It's not even all that nice, really.> end Content name desc requires <*CS_Conversation (1:FRIEND|1:FAMILY|1:LOVER|1:ALLY|1:PCFAC) (1:MILIT|1:ADVEN|1:CORPO|1:LABOR|1:POLIC) -1:ENEMY E:++ ~+Gre -E:GOOD_ ~E:EVIL_> % E1 is the person being spoken to. % E2 is the enemy element2 % Fail Conditions: % - Mission giver dies before encounter is activated end .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the Stop Mission task %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_StopNPCMission&IsEnemyNPC 2> SubPlot2 <*CS_WinGoal 1> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting *.%id%_GoGreet <*IHearYouAreFightingNPC %2% .%id%_GoOfferMission> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoOfferMission result%id%01 result%id%02 *result%id%03 <*RejectMission .%id%_GoR3Exit> .%id%_GoR3Exit result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 <%name1% was defeated by %name2%. You agreed to help \OPR %1% get revenge.> Msg%id%04 Msg%id%05 <\HINT %id2% I trust this will be enough?> Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 end Content name desc requires <*CS_Conversation ~C:POLIC ~1:POLIC ~P:POLIC E:++ F:-- (1:PCFAC|1:ALLY) (*CORE_DEF_|*CORE_OFF_|*CORE_R_MON|*CORE_INTRO)> changes % E1 is the NPC that the PC will speak with % E2 is the Enemy NPC % E3 is the enemy faction that the Enemy is working for element2 element3 % Fail Conditions: % - Mission giver dies before encounter is activated end .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_StopNPCMission&IsEnemyNPC 2> SubPlot2 <*CS_WinGoal 1> SubPlot3 <*CS_LoseGoal 1> sub Persona 1 greeting .%id%_GoOfferMission *.%id%_GoRemind <*GenericMissionReminder> *.%id%_GoKnownGreeting <*IHaveAJobForYou .%id%_GoBriefing> *.%id%_GoUnknownGreeting <*AreYouHereAboutJob&Mecha .%id%_GoBriefing> *.%id%_GoAccept <*GoodLuckPlusHint .%id%_GoR1Exit %id1%> .%id%_GoR1Exit .%id%_GoBriefing result%id%01 result%id%02 Msg%id%01 Msg%id%02 <%name1% hired you to stop %name2% from completing a mission for %name3%.> Msg%id%03 Prompt%id%01 Prompt%id%02 end Content name desc requires <*CS_Conversation 1:MEDIC -1:ENEMY (F:CRIME|F:--|E:EVIL_|+Pun) *CORE_DEF_ ~E:EVIL_> % E1 is the person being spoken to. % E2 is the outdoors location where the encounter will be placed % E3 is the encounter to be found % E4 is a government building, in case the PC really wants a reward element2 element3 place3 <2> element4 % Fail Conditions: % - Mission giver dies before encounter is activated % - Revalation subplot fails end .%id%_GoCheckReveal .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the encounter revealation %% SubPlot3 is the winning condition %% SubPlot4 is the loss condition %% SubPlot5 is the unwinnable condition SubPlot1 <*:CS_MechaEncounter&Raiders 2 3> SubPlot2 <*:CS_RevealEncounter_Raiders 2 3> SubPlot3 <*CS_WinGoal&NoCash 1> SubPlot4 <*CS_LoseGoal 1> SubPlot5 <*CS_MootGoal 1> sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoOfferMission> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoOfferMission result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 Prompt%id%03 end inv stc CORE-ACTIVATABLE name end Plot name requires <*CS_Conversation E:++ (1:ADVEN|1:POLIC) (1:A.---|1:A.sr_|1:A.equ) R:-- -1:ENEMY (*CORE_OFF_|*CORE_R_LAN)> desc Changes Size 1 % E1 is the NPC that the PC will speak with % E2 is the outdoors location where the encounter will be placed % E3 is the encounter to be found element2 element3 place3 <2> % P%id%01 = Have gained E1 as a lancemate % P%id%02 = Initialization Counter % Fail Conditions: % - Mission giver dies before encounter is activated % - Revalation subplot fails update end .%id%_GoCheckReveal .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the encounter revealation %% SubPlot3 is the unwinnable condition SubPlot1 <*:CS_MechaEncounter&Raiders 2 3> SubPlot2 <*:CS_RevealEncounter_Raiders 2 3> SubPlot3 <*CS_MootGoal 1> sub Persona 1 greeting .%id%_GoChat .%id%_GoCheckProgress *.%id%_GoByPhone <*NotByPhone> *.%id%_GoGreet <*IHearYouAreFightingNPC &EnemyNPC .%id%_GoReveal> .%id%_GoReveal .%id%_GoTrivial *result%id%01 <*ComeBackWhenYouKnowSomething> result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02 Msg%id%03 Msg%id%04 <%name1% revealed the presence of raiders in %name2%.> Msg%id%05 Msg%id%06_1 <%name1% joined your lance to go fight \PERSONA &EnemyNPC in %name2%.> CMsg%id%06_1 Msg%id%06_2 <%name1% joined your lance to go fight the raiders in %name2%.> CMsg%id%06_2 Msg%id%07 Msg%id%08 <%name1% told you that \PERSONA &EnemyNPC is in town on a mission; \SPR %1% offered to join forces with you if you can find where \SPR &EnemyNPC is.> Msg%id%09 <%name1% offered to join forces with you against \PERSONA &EnemyNPC .> Msg%id%10 Msg%id%11 Msg%id%12 Msg%id%13 <%name1% told you that \PERSONA &EnemyNPC is in town on a mission.> Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 end inv stc CORE-ACTIVATABLE name <%name1%'s Mission> end Content name desc requires <*CS_Conversation 1:MEDIA ~1:FRIEND E:++ ~E:MEDIA ~E:POLIT ~E:GOOD_> % E1 is the person being spoken to % E2 is the outdoors location where the encounter will be placed % E3 is the encounter to be found element2 element3 place3 <2> % Fail Conditions: % - Mission giver dies before encounter is activated % - Revalation subplot fails end .%id%_GoCheckReveal .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the encounter revealation %% SubPlot3 is the winning condition %% SubPlot4 is the loss condition %% SubPlot5 is the unwinnable condition SubPlot1 <*:CS_MechaEncounter 2 3> SubPlot2 <*:CS_RevealEncounter_Raiders 2 3> SubPlot3 <*CS_WinGoal 1> SubPlot4 <*CS_LoseGoal 1> SubPlot5 <*CS_MootGoal 1> sub Persona 1 greeting .%id%_GoRemind *.%id%_GoOfferMission <*IHearYouAreFightingNPC &EnemyNPC .%id%_GoBriefing> .%id%_GoBriefing *.%id%_GoDeny <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 end inv stc CORE-ACTIVATABLE name <%name1%'s Mission> end %% %% *OLD_Conversation Content %% %% Old stuff. The new stuff is above. %% %% ******************************** %% *** +P-- Peaceful Life *** %% ******************************** Content name desc requires <*OLD_Conversation +P-- P:-- (1:Friend|1:LABOR|1:TRADE) (C:LABOR|C:CRAFT|C:TRADE|C:CORPO) ~+Gmo> % Element1 is the NPC to meet % Element2 is a local corporate character % Element3 is a public scene to meet in % Element4 is E2's faction Element2 Element3 Element4 start sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoAsk> .%id%_GoAsk .%id%_GoNoEnemy result%id%01 .%id%_GetMission <+Tgt +Fmi +Pun> result%id%02 result%id%03 .%id%_RefuseMission <+T-- +F-- +Pun> result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 <%name1% told you that %name4% was being attacked.> Msg%id%05 <%name2% in %name3% is looking for a pilot to protect %name4%.> Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Prompt%id%06 end %% ***************************** %% *** +Pme Meet Enemy *** %% ***************************** Plot name desc requires <*OLD_Conversation +Pme 1:FRIEND -E:--> Size 1 % E1 is the information source % E2 is a scene where N1 was apparently seen % E3 is the enemy NPC Element2 Element3 Place3 start sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoAsk> .%id%_GoAsk .%id%_GoEnd .%id%_win <+Tgs +Fin> result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 <%name1% said that \SPR %1% had seen %name3% at %name2%.> Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 <%name3% can die, for all I care.> CPrompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%04_1 <%name3% is working for \FACTION &EnemyFac .> CPrompt%id%04_1 Prompt%id%05 end %% ***************************************** %% *** +Pun Unknown Enemy Attacks *** %% ***************************************** Plot name desc requires <*OLD_Conversation +Pun +Fin F:++ I:-- ~+Gmo> Size 1 % E1 is the information source % E2 is the artifact being sought % E3 is a local scene for the information-gathering next component Element2 Place2 Element3 start sub Persona 1 greeting *.%id%_GoGreet <*QuestionsAboutFaction&IsEnemy .%id%_GoExplain &EnemyFac> .%id%_GoExplain result%id%01 result%id%02 result%id%03 result%id%04 .%id%_next <+Pla +Tgs +Fin> Msg%id%01 Msg%id%02 <%name1% told you that \FACTION &EnemyFac was looking for the %name2%.> Msg%id%03 <\ITEM_DESC %2%> Msg%id%04 <\ITEM_HISTORY %2%> Msg%id%05 <\ITEM_USAGE %2%> Msg%id%06 Msg%id%07 <%name1% told you that \FACTION &EnemyFac is looking for the %name2%. You might be able to find more information in %name3%.> Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 end Plot name desc requires <*OLD_Conversation +Pun (C:MEDIA|+Gkn) ~E:-- ~F:-- (1:POLIT|1:MEDIA|1:POLIC|1:ADVEN|1:MILIT|1:CORPO)> Size 1 % E1 is the friend % E2 is the NPC to be sought Element2 start sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoExplain> .%id%_GoExplain Result%id%01 .%id%_next <+Tgt +Fin> Result%id%02 Result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 <%name2% might know something about the mecha attacks. Unfortunately \SPR %2% is in hiding.> Msg%id%04 <%name1% told you that %name2% might know something about the mecha attacks.> Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 end %% ***************************** %% *** +Psh Conscripted *** %% ***************************** %% ************************************* %% *** +Pla Learn of Artifact *** %% ************************************* Plot name desc requires <*OLD_Conversation +Pla +Fin (1:FAITH|1:ADVEN) I:++> Size 1 % E1 is the person with the information % E2 is the monk who knows about it Element2 Place2 start sub Persona 1 greeting *.%id%_GoIntro <*LookingForItem &TargetItem .%id%_GoExplain> .%id%_GoExplain .%id%_win <+Tgt +Fin> result%id%01 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 end inv NPC Monk end %% **************************************** %% *** +Pew Enemy Weapon Program *** %% **************************************** %% ********************************** %% *** +C-- Nothing Special *** %% ********************************** %% ******************* %% *** DEFAULT *** %% ******************* Content name desc requires <*OLD_Conversation R:-- 1:FRIEND> Size 1 % E1 is the friend in question % v%id%01 = InitiaFlization Counter update start sub Persona 1 greeting *.%id%_GoNotHere <*NotByPhone> *.%id%_GoGreet <*HowAreYou .%id%_GoInform> .%id%_GoInform .%id%_GoJoin <&SetPartnerNPC %1% &AddPartnerToLance SetXXRAttitude %1% XXR_A_IsJunior WinComp 0 .%id%_next StoryNote %id%02 Goto .%id%_GoJoinSpeech> .%id%_next <+T-- +F--> *.%id%_GoJoinSpeech <*WelcomeToLance> result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 .%id%_refuse <+T-- +F--> result%id%07 Msg%id%01 Msg%id%02 msg%id%03 msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 end Content name desc requires <*OLD_Conversation 1:POLIC ~C:POLIC ~C:MEDIA ~C:THIEF ~+Gkn -1:ENEMY> % E1 is the NPC that the PC will speak with % Fail Conditions: % - Mission giver dies before mission is activated end .%id%_loss <+T--> .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is Gather Information %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_GatherInformation 1> SubPlot2 <*CS_WinTask 1> SubPlot3 <*CS_LoseTask 1> sub Persona 1 greeting *.%id%_GoOfferMission <*HowAreYou .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing Result%id%01 *Result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit .%id%_changes <+T-- +F--> Msg%id%01 Msg%id%02 <%name1% asked you to help \OPR %1% investigate a case.> Prompt%id%01 Prompt%id%02 CPrompt%id%02 end Content name desc requires <*OLD_Conversation 1:MEDIA ~C:MEDIA ~+Gkn> % E1 is the NPC that the PC will speak with % Fail Conditions: % - Mission giver dies before mission is activated end .%id%_loss <+T--> .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is Gather Information %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_GatherInformation 1> SubPlot2 <*CS_WinTask 1> SubPlot3 <*CS_LoseTask 1> sub Persona 1 greeting .%id%_GoOfferMission *.%id%_GoRemind <*GenericMissionReminder> *.%id%_GoKnownGreeting <*HowAreYou .%id%_GoBriefing> *.%id%_GoUnknownGreeting <*AreYouHereAboutJob&Investigation .%id%_GoBriefing> .%id%_GoBriefing Result%id%01 *Result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit .%id%_changes <+T-- +F--> Result%id%03 Msg%id%01 Msg%id%02 <%name1% asked you to help \OPR %1% investigate a news story.> Msg%id%03 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 <[Continue]> end Content name desc requires <*OLD_Conversation 1:FRIEND P:--> Size 1 % E1 is the person being spoken to. % E2 is a nearby person who might be offering a mission. % E3 is a nearby public scene. Element2 Element3 % Fail Conditions: % - E1 dies start .%id%_loss <+T-- +F--> sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoOfferJob> .%id%_GoOfferJob .%id%_GoGiveMission .%id%_next <+Tgt +Fmi> .%id%_GoE2Died .%id%_fail <+T-- +F--> result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 <%name1% suggested that you ask %name2% at %name3% for a mission.> Msg%id%03 <%name2% over at %name3% is looking for someone to do a mission. You should go talk to \OPR %2% . If I put in a good word, you're almost certain to be the one who gets the job.> Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 end Content name desc requires <*OLD_Conversation +F-- (1:FRIEND|1:PCFAC) (1:MILIT|1:ADVEN) -1:ENEMY> % E1 is the person being spoken to. % E2 is the outdoors location where the encounter will be placed % E3 is the encounter to be found element2 element3 place3 <2> % Fail Conditions: % - Mission giver dies before encounter is activated % - Revalation subplot fails end .%id%_GoCheckReveal .%id%_loss <+T--> .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02 %% SubPlot1 is the combat encounter itself %% SubPlot2 is the encounter revealation %% SubPlot3 is the winning condition %% SubPlot4 is the loss condition %% SubPlot5 is the unwinnable condition SubPlot1 <*:CS_MechaEncounter&Raiders 2 3> SubPlot2 <*:CS_RevealEncounter_Raiders 2 3> SubPlot3 <*CS_WinTask 1> SubPlot4 <*CS_LoseTask 1> SubPlot5 <*CS_MootMission 1> sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoOfferMission> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoOfferMission result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 end inv stc CORE-ACTIVATABLE name <%name1%'s Mission> end Content name desc requires <*OLD_Conversation (1:LABOR|1:TRADE|1:CRAFT) (1:FRIEND|1:PCFAC|+Fin)> Size 1 % Element1 is the NPC to meet % Element2 is a location for the trade show element2 start .%id%_loss <+T-- +F--> sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_GoInform> .%id%_GoInform .%id%_win <+Tgs +Fcc> result%id%01 result%id%02 Msg%id%01 Msg%id%02 <%name1% told you about a trade show at %name2%.> Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 end Content name desc requires <*OLD_Conversation -+Fin (1:THIEF|1:REDMA|1:CRIHN) (1:FRIEND|1:ALLY|1:LOVER|1:FAMILY)> % Element1 is the NPC to meet % Element2 is the item to retrieve Element2 % Fail Conditions: % - Mission giver dies before encounter is activated % - Fetch subplot fails end .%id%_GoCheckReveal .%id%_loss <+T--> % Upon getting the item, set the win subplot. Get%2% Msg%id%01 Msg%id%02 % SubPlot1 is the FetchItem task % SubPlot2 is the reward % SubPlot3 is the loss condition SubPlot1 <*:CS_FetchItem 2> SubPlot2 <*CS_WinFetchItem 1 2> SubPlot3 <*CS_LoseTask 1> sub Persona 1 greeting *.%id%_GoGreet <*HowAreYou .%id%_OfferMission> .%id%_OfferMission *.%id%_GoRemind <*GenericMissionReminder> result%id%01 result%id%02 result%id%03 result%id%04 .%id%_refuse <+T-- +F--> Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 <%name1% asked you to help \OPR %1% locate a Bing Vase.> Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 end inv Treasure name desc fudge 10000 StolenGoods end Content name desc requires <*OLD_Conversation (1:FRIEND|1:FAMILY|1:LOVER) -!Ne -!Lo> % E1 is the NPC that the PC will speak with % E2 is the item to fetch Element2 % Fail Conditions: % - Mission giver dies before encounter is activated % - Fetch subplot fails end .%id%_GoCheckReveal .%id%_loss <+T--> % Upon getting the item, set the win subplot. Get%2% Msg%id%01 Msg%id%02 %% SubPlot1 is Fetch Item %% SubPlot2 is the winning condition %% SubPlot3 is the loss condition SubPlot1 <*:CS_FetchItem&Stolen 2> SubPlot2 <*CS_WinFetchItem 1 2> SubPlot3 <*CS_LoseTask 1> sub Persona 1 greeting *.%id%_GoOfferMission <*HowAreYou .%id%_GoBriefing> *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoBriefing .%id%_GoStartMission result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 end inv Treasure name desc <%name1%'s family heirloom. You decide against checking to see what's inside.> end Plot name desc requires <*OLD_Conversation +Fin -1:FRIEND -1:ALLY -1:LOVER -1:FAMILY ~1:ENEMY ~1:CORPO> Size 3 % E1 is the person with the information start .%id%_loss <+T-- +F--> % SubPlot1 is the secret to be revealed SubPlot1 <*CS_Revelation&Friendly 1> sub Persona 1 % V%id%01 = Asking Price greeting *.%id%_GoIntro <*IHaveInformation .%id%_GoSetMoney na> .%id%_GoSetMoney .%id%_GoReveal result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 .%id%_lose <+T-- +F--> Msg%id%01 Msg%id%02 <%name1% offered to sell you information for $ \VAL v%id%01 .> Msg%id%03 Msg%id%04 Msg%id%05 msg%id%06 msg%id%07 msg%id%08 msg%id%09 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 <[Continue]> Prompt%id%07 end GH2/series/QUEST_MechaArena_Manager.txt0000644000175000017500000007660611365256063016554 0ustar kaolkaol%% %% Mecha Arena Quest Components %% %% %% *:Q_MECHA_ARENA_BASE %% Contains the arena manager. %% %% Set QuestStatus when manager admits PC to arena; end the quest after the PC has %% completed the championship. %% %% Each manager persona must have a GoBigWin script defined, to be called when the %% 10th match is won. This script must reset the arena. %% - If this particular arena doesn't conclude on the tenth match, then you %% don't need a GoBigWin script and you shouldn't use a standard challenger %% as either the last fight or the 10th fight. %% Content name requires <*:Q_MECHA_ARENA_BASE YATSP> % E1 is the manager % E2 is the arena % E3 is the manager's home scene % E4 is the environs scene for the arena Element1 Place1 <3 (Citizens) pass ally> Element2 Place2 <4> Element3 Element4 SubPlot1 <*:Q_MECHA_ARENA_CHALLENGE #30 1 2> SubPlot2 <*:Q_MECHA_ARENA_CHALLENGE #42 1 2> SubPlot3 <*:Q_MECHA_ARENA_CHALLENGE #54 1 2> SubPlot4 <*:Q_MECHA_ARENA_CHALLENGE #66 1 2> sub Persona 1 rumor0 <%name1% manages the underground dueling meets at Aurora Night.> greeting GoCheckLoss GoCheckReady GoArenaWin *GoMinorWin <*BasicArenaWin %2% GoResetArena> *GoArenaLoss <*BasicArenaLoss %2% GoResetArena> GoResetArena *GoArenaFull <*ArenaIsFull %2%> *GoArenaBusy <*ArenaIsBusy %2%> GoChooseBattle GoInvalidMecha GoCheckQ3 > GoCheckQ2 > GoCheckQ1 *GoNormalBattle <*StartArenaBattle %2% GoStartFight> GoStartFight *GoRefuseBattle <*RefuseArenaFight %2%> GoStartQuest GoOfferFight GoBigWin .mektype *GoPreQuest <*NiceToMeetYou GoArenaIntro> GoArenaIntro result1 result2 result3 result4 result5 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 CPrompt5 MetaScene 2 2 name entrance <*QUEST-INACTIVE> RockyTiles Vacuum SpaceBackdrop Ceiling terrain type special map start GoCheckBattle GoBoringStart nu1 nu2 GoEndBattle Msg1 Msg2 Msg3 Msg5 Msg6 sub Team 1 name home SetEnemy 2 ParaX 7 ParaY 7 Team 2 name home SetFaction 5 SetEnemy 1 Deploy ParaX 34 ParaY 34 end end inv NPC Arena Pilot SetFaction 5 chardesc Shy Passionate NonCombatant sdl_colors <203 34 51 255 212 195 56 26 81> end Content name requires <*:Q_MECHA_ARENA_BASE GAOSP> % E1 is Hilda % E2 is the arena % E3 is the arena site Element1 Place1 <3 (Citizens) pass ally> Element2 Place2 <3> Element3 SubPlot1 <*:Q_MECHA_ARENA_CHALLENGE #33 1 2> SubPlot2 <*:Q_MECHA_ARENA_CHALLENGE #45 1 2> SubPlot3 <*:Q_MECHA_ARENA_CHALLENGE #57 1 2> SubPlot4 <*:Q_MECHA_ARENA_CHALLENGE #69 1 2> SubPlot5 <*:Q_MECHA_ARENA_CHALLENGE #81 1 2> sub Persona 1 greeting GoCheckLoss GoCheckReady GoArenaWin *GoMinorWin <*BasicArenaWin %2% GoResetArena> *GoArenaLoss <*BasicArenaLoss %2% GoResetArena> GoResetArena *GoArenaFull <*ArenaIsFull %2%> *GoArenaBusy <*ArenaIsBusy %2%> GoChooseBattle GoInvalidMecha GoCheckQ4 GoCheckQ3 GoCheckQ2 GoCheckQ1 *GoNormalBattle <*StartArenaBattle %2% GoStartFight> GoStartFight *GoRefuseBattle <*RefuseArenaFight %2%> GoStartQuest GoOfferFight % Remember, there's a lot of fiddly bits for the big win. GoBigWin .mektype *GoPreQuest <*NiceToMeetYou GoArenaIntro> GoArenaIntro result1 result2 result3 GoR3NoRenown GoR3NoMember result4 result5 result6 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 MetaScene 1 sub room name special desig minimap <.............1......&---&> room name special minimap <######&2&##...##...##...#> inv Elevator name desig use GoNoEnter Msg101 MiniMapComponent 2 end end STC QS_MechaArena name SetID 2 entrance <> end inv NPC Celebrity name job chardesc Female Easygoing Sociable Renowned Renowned Age 6 SetFaction 5 SDL_PORTRAIT SDL_COLORS <1 75 67 255 212 195 245 200 100> end Content name requires <*:Q_MECHA_ARENA_BASE CAYLE> % E1 is Peters % E2 is the arena % E3 is the steelworks lounge % E4 is the environs scene for the arena % E5 is the steelworks building % E6 is Kodie % E7 is the lounge entrance Element1 Place1 <3 (Citizens) pass ally> Element2 Place2 <4> Element3 Place3 <5> Element4 Element5 Element6 Place6 <3 (Guards) sd ally> Element7 Place7 <5> %% P1 = Have gained access to the arena SubPlot1 <*:Q_MECHA_ARENA_CHALLENGE #35 1 2> SubPlot2 <*:Q_MECHA_ARENA_CHALLENGE #60 1 2> sub Persona 1 rumor0 greeting GoCheckLoss GoCheckReady GoArenaWin *GoMinorWin <*BasicArenaWin %2% GoResetArena> *GoArenaLoss <*BasicArenaLoss %2% GoResetArena> GoResetArena *GoArenaFull <*ArenaIsFull %2%> *GoArenaBusy <*ArenaIsBusy %2%> GoChooseBattle GoInvalidMecha GoCheckQ1 *GoNormalBattle <*StartArenaBattle %2% GoStartFight> GoStartFight *GoRefuseBattle <*RefuseArenaFight %2%> GoStartQuest GoOfferFight % Remember, there's a lot of fiddly bits for the big win. GoBigWin .mektype *GoPreQuest <*NiceToMeetYou GoArenaIntro> GoArenaIntro result1 result2 result3 result4 result5 result6 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 MetaScene 1 sub room name special desig minimap <#...#........1......#---#> end STC QS_MechaArena name SetID 2 AsteroidMap RockyTiles Vacuum SpaceBackdrop MetaScene 3 name type mapwidth 24 mapheight 24 ClubMap IndustrialTiles special % V1 = Initialization % V2 = Have been allowed into the lounge. % V3 = CID of the bouncer start Msg1 NeededCells 4 content1 content2 content3 Content4 content9 sub team 1 team 2 name Passive team 3 name SetAlly 2 room name desig Content room name minimap <############1##..........> inv Elevator MiniMapComponent 1 Destination -1 end end Persona 6 special % V1 = Has offered training greeting GoCheckPermission GoSayAnything GoChallenge GoCheckHome GoCheckPriva GoNoEntry % It's like getting your hand stamped to get back in.. GoStamp result1 result2 GoR2Fail result3 result4 GoR4Fail result7 result8 result9 .skills <4 5 6> result10 Msg1 Msg2 Msg3 Msg4 Msg5 Msg7 Msg8 Msg9 Msg10 Msg12 Msg13 Msg14 Msg15 Msg15_1 Msg15_2 Msg16 Msg17 Msg18 Prompt1 Prompt2 CPrompt2 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt7 Prompt8 Prompt9 Prompt10 MetaScene 7 sub room special desig minimap <......###..#1#...........> end end inv NPC Citizen name job chardesc Male Passionate Sociable Cheerful Pragmatic Age 2 SetFaction 5 NPC Soldier job home Renown 20 Knowledge 5 Charm 5 name desig chardesc Male Passionate Sociable SetTeam 3 Age -1 Elevator name desig use GoTalkKodie GoJustGo Msg101 end Content name requires <*:Q_MECHA_ARENA_BASE MAQSP> % E1 is Mullins % E2 is the arena % E3 is the Privateer's Guild % E4 is an environs scene for the arena Element1 Place1 <3 (Citizens) Pass Ally> Element2 Place2 <4> Element3 Element4 SubPlot1 <*:Q_MECHA_ARENA_CHALLENGE #30 1 2> SubPlot2 <*:Q_MECHA_ARENA_CHALLENGE #42 1 2> SubPlot3 <*:Q_MECHA_ARENA_CHALLENGE #54 1 2> SubPlot4 <*:Q_MECHA_ARENA_CHALLENGE #66 1 2> sub Persona 1 rumor rumor0 greeting GoCheckLoss GoCheckReady GoArenaWin *GoMinorWin <*BasicArenaWin %2% GoResetArena> *GoArenaLoss <*BasicArenaLoss %2% GoResetArena> GoResetArena *GoArenaFull <*ArenaIsFull %2%> GoArenaBusy *GoReallyBusy <*ArenaIsBusy %2%> GoChooseBattle GoInvalidMecha GoCheckQ3 > GoCheckQ2 > GoCheckQ1 *GoNormalBattle <*StartArenaBattle %2% GoStartFight> GoStartFight *GoRefuseBattle <*RefuseArenaFight %2%> GoStartQuest GoOfferFight GoBigWin GoSkillTraining *GoPreQuest <*NiceToMeetYou GoArenaIntro> GoArenaIntro result1 result2 result3 .skills <1 2 3 4 5 6 11 15 16> result4 result5 result6 result7 result8 result9 result10 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Prompt1 CPrompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 Prompt10 STC QS_MechaArena name SetID 2 SpaceMap Microgravity Vacuum SpaceBackdrop terrain end inv NPC Mercenary name % Mullins is Miaga's mother. She was a hero in the privateer fleet % until being horribly injured in the line of duty. The left half % of Mullins's body is mostly cybernetic. NonCombatant statline 13 10 14 16 8 17 10 13 CharDesc Female Heroic Criminal Sociable Melancholy Renowned % Age 45; since basic char age is 20, add +25. Age 25 %% Mullins is a member of the Pro Duelist Association SetFaction 5 SDL_PORTRAIT SDL_SPRITE SDL_COLORS <200 200 0 142 62 39 1 75 67> end Content name requires <*:Q_DEBUGARENA> %% This is a copy of the Cayley Arena used for debugging purposes. It should only load if %% an arena challenger is marked as *:Q_DEBUG_MECHA_ARENA_CHALLENGE % E1 is the manager % E2 is the arena % E3 is the cavalier's club Element1 Place1 <3 (Citizens) Pass Ally> Element2 Place2 <3> Element3 SubPlot1 <*:Q_DEBUG_MECHA_ARENA_CHALLENGE #5 1 2> sub Persona 1 greeting GoCheckLoss GoCheckReady GoArenaWin *GoMinorWin <*BasicArenaWin %2% GoResetArena> *GoArenaLoss <*BasicArenaLoss %2% GoResetArena> GoResetArena *GoArenaFull <*ArenaIsFull %2%> *GoArenaBusy <*ArenaIsBusy %2%> GoChooseBattle *GoNormalBattle <*StartArenaBattle %2% GoStartFight> GoStartFight *GoRefuseBattle <*RefuseArenaFight %2%> GoStartQuest GoOfferFight % Remember, there's a lot of fiddly bits for the big win. GoBigWin .mektype GoPreQuest result1 result2 result3 result4 result5 result6 Msg1 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 MetaScene 1 sub room name special desig minimap <#...#..2.....1......#---#> inv Trapdoor MiniMapComponent 2 desig end end STC QS_MechaArena name SetID 2 end inv NPC Citizen job SDL_COLORS <170 50 171 255 212 195 244 216 28> SetFaction 5 end %% %% *:Q_CHARA_ARENA_BASE %% Contains a personal-scale arena. %% %% These places will typically only show up in dystopian, dangerous, %% and typically bad cities. %% Content name requires <*:Q_CHARA_ARENA_BASE THESP> % E1 is the manager % E2 is the arena % E3 is the prize % E4 is the arena site Element1 Place1 <4 (Citizens) Pass Ally> Element2 Place2 <4> Element3 Place3 Element4 sub Persona 1 greeting GoCheckProgress GoCheckLoss GoCheckReady GoArenaWin *GoMinorWin <*CharaArenaWin %2% GoResetArena> *GoArenaLoss <*CharaArenaLoss %2% GoResetArena> GoResetArena *GoArenaFull <*ArenaIsFull %2%> *GoArenaBusy <*ArenaIsBusy %2%> *GoChooseBattle <*StartCharaBattle %2% GoStartFight> GoStartFight *GoRefuseBattle <*RefuseArenaFight %2%> GoStartQuest GoOfferFight % Remember, there's a lot of fiddly bits for the big win. GoBigWin *GoPreQuest <*NiceToMeetYou GoArenaIntro> GoArenaIntro result1 result2 result3 result4 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Prompt1 CPrompt1 Prompt2 Prompt3 Prompt4 MetaScene 1 sub room name special desig minimap <.......2.....1......&---&> inv TrapDoor desig MiniMapComponent 2 end end STC QS_CharaArena name SetID 2 end inv NPC Bandit job HeadArmor 7 mass -12 name Sealed Hardened Ego 10 end GH2/series/PLOT_MOOD_FindEncounter.txt0000644000175000017500000000367511341163445016363 0ustar kaolkaol%% %% *FindEncounter Plots %% %% An encounter needs to be found and activated with SetEncounterActive. %% Upon activation, the plot involved should KillPlotMood to prevent another %% redundant plot from loading. %% %% Mood Spec: %% E1 = The outdoors scene %% E2 = The encounter being sought %% Plot name desc requires <*FindEncounter> % E1 is the outdoor scene where the encounter is % E2 is the MetaScene being sought % E3 is a character who can tell where it is Element1 Element2 Element3 %% FAIL CONDITIONS: %% - E3 dies before revealing the secret start sub Persona 3 rumor <%name3% knows where to find %name2%.> *greeting <*IHaveInformation&AboutScene GoSpiel %2%> GoSpiel Msg1 Msg1_1 CMsg1_1 Msg1_2 <%name2% is in \EXACT_SCENE %1% , at these coordinates.> CMsg1_2 Msg1_3 <%name2% can be found in \EXACT_SCENE %1% at these coordinates. Getting there should be a piece of cake.> CMsg1_3 Msg1_4 CMsg1_4 Msg1_5 CMsg1_5 Msg1_6 CMsg1_6 end GH2/series/PLOT_CORE_Offense.txt0000644000175000017500000000005611333736445015174 0ustar kaolkaol%% %% Lead-ins for *CORE_DEF_ missions. %% GH2/series/PFRAG_Services.txt0000644000175000017500000012576311326004537014650 0ustar kaolkaol% TYPE: *SHOP_[whatever] % Someone is gonna sell stuff to the PC. % PARAM1: Exit script label % TYPE: *SHOP_ESSENTIALS Persona requires <*SHOP_ESSENTIALS> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_CASINO Persona requires <*SHOP_CASINO> START result%id%01 result%id%02 .%id%_wares Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_SPECIALIST Persona requires <*SHOP_SPECIALIST ~restaurant ~cuisine> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Easygoing> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Passionate> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Pragmatic> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Cheerful> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Melancholy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_SPECIALIST Shy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_HEALTHCLUB Persona requires <*SHOP_HEALTHCLUB> START result%id%01 .%id%_Skills <7 8 9> result%id%02 Msg%id%01 Msg%id%02 Msg%id%02_1 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_HEALTHCLUB Passionate> % V%id%01 = Have visited before START result%id%01 .%id%_Skills <7 8 9> result%id%02 result%id%03 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%03_1 CPrompt%id%03_1 % TYPE: *SHOP_ELECTRONICS Persona requires <*SHOP_ELECTRONICS> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ELECTRONICS Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ELECTRONICS Shy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ELECTRONICS Cheerful> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_MECHA Persona requires <*SHOP_MECHA> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Shy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Easygoing> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Passionate> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Cheerful> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Melancholy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MECHA Pragmatic> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % *SHOP_ARMOR Persona requires <*SHOP_ARMOR> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Shy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%01_1 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Cheerful> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Charm ~Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Melancholy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Easygoing> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Passionate> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR Wangtta ~Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_ARMOR C:MUGLE> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % *SHOP_WEAPON Persona requires <*SHOP_WEAPON> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Melancholy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Easygoing> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Passionate> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Shy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%01_1 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Pragmatic> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_WEAPON Spiritual ~FAITH> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_BLACKMARKET % Every black market shop should sell CONTRABAND, but other than that they can sell anything. Persona requires <*SHOP_BLACKMARKET Easygoing> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_BLACKMARKET Sociable> START result%id%01 .%id%_GoR1Win result%id%02 .%id%_wares .%id%_badwares KEYWORDS Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_BLACKMARKET> START result%id%01 .%id%_GoR1Fail result%id%02 .%id%_wares .%id%_badwares KEYWORDS Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_GENERAL Persona requires <*SHOP_GENERAL ~Village> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_GENERAL> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_GENERAL Sociable> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_GENERAL Shy> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_GENERAL asteroid> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_GENERAL Spinner> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SHOP_MONK % What the hell is a monk shop? Well, one may encounter monks with shops % throughout the game. These shops may do just about anything, from train % the PC's skills to selling weapons or herbal medicine. Persona requires <*SHOP_MONK> START result%id%01 result%id%02 .%id%_wares KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MONK Passionate> START result%id%01 result%id%02 .%id%_skills <5 7 24> KEYWORDS Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SHOP_MONK Renowned> START result%id%01 result%id%02 .%id%_wares KEYWORDS result%id%03 .%id%_GoNoSpirit .%id%_madskilz <4 5 6 11 24> .%id%_badskilz <5 24> Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 Prompt%id%03 % TYPE: *SKILL_TRAINING % This NPC will train the PC's skills. % PARAM1: Exit script label Persona requires <*SKILL_TRAINING> START result%id%01 result%id%02 .%id%_skills <1 2 3 4 5 6> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SKILL_TRAINING POLIT Common> START result%id%01 result%id%02 .%id%_skills <1 2 3 4 5 6 10 17 27> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SKILL_TRAINING CORPO Common> START result%id%01 result%id%02 .%id%_skills <1 2 3 4 5 6 13 18 15> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SKILL_TRAINING POLIC Common> START result%id%01 result%id%02 .%id%_skills <1 2 3 4 5 6 16 20 27> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SKILL_TRAINING (MILIT|ADVEN) Common> START result%id%01 result%id%02 .%id%_skills <1 2 3 4 5 6 11 16 26> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SERVICE_MECHANIC % PARAM1: Exit script label Persona requires <*SERVICE_MECHANIC> START result%id%01 result%id%02 .%id%_wares <> Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SERVICE_MECHANIC SOCIABLE> START result%id%01 result%id%02 .%id%_wares Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SERVICE_MECHANIC PASSIONATE> START result%id%01 result%id%02 .%id%_wares Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SERVICE_MECHANIC SPIRITUAL> START result%id%01 result%id%02 .%id%_wares Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SERVICE_BARTENDER % PARAM1: Exit script label Persona requires <*SERVICE_BARTENDER> START result%id%01 result%id%02 Msg%id%01 Msg%id%02 Prompt%id%01 CPrompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 % TYPE: *SERVICE_DOCTOR % PARAM1: Exit script label Persona requires <*SERVICE_DOCTOR> START result%id%01 result%id%02 .%id%_wares Keywords Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Persona requires <*SERVICE_DOCTOR Cyberdoc> START result%id%01 result%id%02 .%id%_wares Keywords Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 % TYPE: *SERVICE_RESTAURANT % PARAM1: Exit script label Persona requires <*SERVICE_RESTAURANT> START result%id%01 .%id%_GoNoMoney .%id%_GoNotHere Keywords result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Persona requires <*SERVICE_RESTAURANT Spinner Renowned> START result%id%01 .%id%_GoNoMoney .%id%_GoNotHere Keywords result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*SERVICE_RESTAURANT L5PAT ~Renowned> START Keywords result%id%01 .%id%_GoNoMoney .%id%_GoNotHere result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Persona requires <*SERVICE_RESTAURANT L5PAT ~Wangtta Young> START Keywords .%id%_GoNotHere result%id%01 .%id%_GoNoMoney result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 CPrompt%id%04 % TYPE: *SERVICE_SHUTTLE % PARAM1: Exit script label Persona requires <*SERVICE_SHUTTLE Spinner> START Keywords result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 Persona requires <*SERVICE_SHUTTLE> START Keywords result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 GH2/series/MEGA_CORE_MIX_Encounter.txt0000644000175000017500000001360411376734116016224 0ustar kaolkaol%% %% *:CS_MIX_Encounter Content %% %% Y'know how to get more randomness in your random story generator? Mix things %% up a bit. %% %% This is a combat encounter against the core story enemy. The events of the %% encounter will probably be based on the nature of the enemy faction. The %% enemy NPC's character development will be handled by a separate Confrontation %% subplot. %% %% Upon conclusion, this subplot will set one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% It will also hide the encounter with SetEncounterInactive. %% %% Please see the *:CS_MIX_Confrontation specification for details about how %% this encounter must be structured. %% %% Due to the confrontation, this is a MAIN COURSE subplot. %% %% PARAM1: The outdoors scene where the encounter is %% PARAM2: The encounter to be used; parent plot responsible for reveal %% PARAM3: The enemy NPC %% Content name requires <*:CS_MIX_Encounter F:CRIME> desc % E1 is the outdoors scene for the encounter % E2 is the encounter gear itself % E3 is the enemy NPC % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update % The narrative hooks for the Confrontation .%id2%_%plotid2%_GoWin .%id2%_%plotid2%_GoLoss % The triggers for GainAdvantage .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders % SubPlot2 = Confrontation SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> SubPlot2 <*:CS_MIX_Confrontation 3 1> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu5 GoLoseMission nu3 end Msg1 Msg2 Msg3 Msg4 Msg5 sub team 1 SetEnemy 2 5 SetAlly 3 4 home team 2 name type SetEnemy 1 3 4 SetAlly 5 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 name setenemy 2 5 setally 1 4 team 4 name setenemy 2 5 setally 1 3 home Deploy team 5 name Deploy SetEnemy 1 3 4 SetAlly 2 ParaX 45 ParaY 45 rect name special width 12 height 12 MFX 12 MFY 12 sub SuperProp requires <*Cruiser> SetTeam 3 end end end Content name requires <*:CS_MIX_Encounter> desc % E1 is the outdoors scene for the encounter % E2 is the encounter gear itself % E3 is the enemy NPC % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update % The narrative hooks for the Confrontation .%id2%_%plotid2%_GoWin .%id2%_%plotid2%_GoLoss % The triggers for GainAdvantage .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders % SubPlot2 = Confrontation SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> SubPlot2 <*:CS_MIX_Confrontation 3 1> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu3 end Msg1 Msg2 Msg3 Msg4 Msg5 sub team 1 setally 4 SetEnemy 2 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 4 SetAlly 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 name SetEnemy 1 4 SetAlly 2 ParaX 45 ParaY 45 team 4 setally 1 setenemy 2 3 Deploy ParaX 10 ParaY 5 end end GH2/series/MEGA_ThiefMissions.txt0000644000175000017500000001030111343146535015504 0ustar kaolkaol%% %% *TM_PlunderShip Content %% %% The PC will learn the location of a cruiser, and have the chance to go %% plunder it. Performing this mission will result in Criminal reputation, %% obviously. %% %% Upon activation of this subplot, activate the encounter %2%. %% %% This encounter will end the plot, either by itself or via the FilthyLucre %% subplot. The master plot should have a payrate %% set; this pay rate should be 500% for a regular raid or higher if this raid %% was handed out as a promotion reward or somesuch. %% %% Param1: The outdoors scene where the cruiser is. %% Param2: The encounter to be used %% Content name requires <*TM_PlunderShip> desc % P%id%01 = Time Limit % P%id%02 = Combat started update start %% SubPlot1 is the Filthy Lucre rewards. Activate this plot if the ship %% is captured; it will give a reward and end the plot. SubPlot1 <*TM_FilthyLucre> sub MetaScene 2 2 %% The ship's exterior %% To capture the ship, defeat all the guards, weapons, and %% thrusters. Damaging the cargo segments of the ship may result %% in the ship being destroyed. % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = "Just defeat the guards" message counter % L4 = Initial headcount of Team 3 MapWidth 50 MapHeight 50 Start nu1 nu2 GoSmallVictory GoBigVictory nu4 nu5 end Msg1 Msg2 Msg3 Msg4 sub team 1 SetEnemy 2 4 5 ParaX 5 ParaY 25 team 2 name SetEnemy 1 SetAlly 4 5 Deploy ParaX 45 ParaY 25 team 3 name % This team is neutral- the PC wants to capture these % parts intact. If any of these parts get destroyed, % there's a chance the desired loot will be lost. team 4 name setenemy 1 setally 2 3 5 % Destroy the weapons and propulsion to incapacitate the % cruiser. team 5 name setenemy 1 setally 2 3 4 rect name special width 12 height 12 MFX 37 MFY 19 sub SuperProp requires <*Cruiser> Team1 3 Team2 4 Team3 5 end end end %% %% *TM_FilthyLucre Content %% %% The PC has just captured a ship, plundered a convoy, or unearthed a buried %% treasure. Give the PC some stuff and end the plot. %% Content name requires <*TM_FilthyLucre Common> update Content name requires <*TM_FilthyLucre ~!Ne ~!Lo ~!Md> update .%id%_facs Msg%id%01 Content name requires <*TM_FilthyLucre ~!Hi ~!Ex> update .%id%_facs Msg%id%01 Msg%id%01_1 GH2/series/MEGA_CORE_MAIN_MechaEncounter.txt0000644000175000017500000034031211376734116017250 0ustar kaolkaol%% *:CS_MechaEncounter Content %% %% &Raiders The enemies are raiders aligned with the enemy NPC/Faction %% &Secondary This is a secondary encounter %% %% This is the basic building block of any adventure- a mecha fight scene. %% This component will likely alter the context of the core story. %% %% The parent plot sets the plot status to this layer's ID when ready. %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% It will also hide the encounter with SetEncounterInactive. %% %% This is a MAIN COURSE subplot. %% %% Param1: The outdoors scene where the encounter will be placed %% Param2: The encounter to be used as the entrance %% %% ******************************** %% *** +P-- Peaceful Life *** %% ******************************** Content name desc requires <*:CS_MechaEncounter ~&Raiders E:++ P:++ ~P:PCFAC F:-- ~E:A.nme ~E:A.jr_ ~E:A.sr_ ~E:A.equ ~E:A.env> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the new enemy faction % E4 is the enemy fighter % E5 is the Enemy NPC, for safe keeping Element3 Element4 Place4 <2 (Enemies) sd enemy> Element5 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation %% Debriefing Message - Win or Lose Msg%id%11 <%name5% is working for %name3%... at least we now know what we're up against.> % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 4 special greeting GoGreet *GoThemeExpo <*THEME_EXPO&Enemy na> result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 end inv NPC Mecha Pilot end Content name size 8 desc requires <*:CS_MechaEncounter +P-- (P:CORPO|F:CRIME) -C:THIEF> changes <+P> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is a local public scene % E4 is a trucker to be used in the exposition Element3 Element4 NeverFail4 Place4 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation Msg%id%11 Msg%id%11_1 CMsg%id%11_1 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Decimation&Reinforcements> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start .changes <+Pun> nu1 nu2 GoLoseMission end Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 sub team 1 SetEnemy 2 SetAlly 3 4 ParaX 15 ParaY 25 team 2 SetEnemy 1 3 4 Deploy GoSmallDeploy ParaX 45 ParaY 25 team 3 name setenemy 2 setally 1 4 team 4 name setenemy 2 setally 1 3 home Deploy rect name special width 12 height 12 MFX 1 MFY 19 sub SuperProp requires <*Cruiser> SetTeam 3 end end end Content name size 7 desc requires <*:CS_MechaEncounter &Raiders (+P--|+Pun) -!Ne -!Lo> changes <+P> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % P%id%01 = Initialization Counter % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoDecimation %% Debriefing Msg - Found weapons shipment Msg%id%11 Msg%id%11_1 CMsg%id%11_1 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 GoCheckWin .mek .changes <+Pew> GoLoseEncounter end Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 sub team 1 SetEnemy 2 3 ParaX 45 ParaY 25 team 2 SetEnemy 1 SetAlly 3 Deploy GoSmallDeploy home team 3 setenemy 1 setally 2 team 4 name rect name special width 12 height 12 MFX 1 MFY 19 sub SuperProp requires <*Cruiser> Team1 4 Team2 3 Team3 3 end end end %% **************************** %% *** +Pme Meet Enemy *** %% **************************** %% ******************************* %% *** +Pun Under Attack *** %% ******************************* Content name size 6 desc requires <*:CS_MechaEncounter &Raiders F:-- +Pun (!Ne|!Lo|&Secondary)> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the new enemy faction Element3 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation %% Debriefing Msg Msg%id%11 Msg%id%11_1 CMsg%id%11_1 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 Msg5 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end Content name size 10 desc requires <*:CS_MechaEncounter +Pun -!Ne> changes <+P> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % P%id%01 = Initialization Counter % P%id%02 = Have gained decimation update .%id1%_%plotid1%_GoDecimation %% Debriefing Msg Msg%id%11 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start .changes <+Pew> nu1 nu2 nu3 GoWinMission GoLoseMission end Msg1 Msg2_1 Msg2_2 <> Msg2_3 <> Msg3 Msg4 Msg5 Msg6 Msg7 sub team 1 SetEnemy 2 3 ParaX 5 ParaY 10 team 2 name SetEnemy 1 SetAlly 3 Deploy GoSmallDeploy ParaX 40 ParaY 40 Team 3 name SetAlly 2 SetEnemy 1 ParaX 45 ParaY 45 rect name special width 5 height 5 MFX 40 MFY 40 sub SuperProp requires <*Superweapon> SetTeam 3 end end end %% ************************************* %% *** +Pla Learn of Artifact *** %% ************************************* %% **************************************** %% *** +Pew Enemy Weapon Program *** %% **************************************** Content name size 10 desc requires <*:CS_MechaEncounter +Pew &Secondary> changes <+P> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % P%id%01 = Initialization Counter % P%id%02 = Have gained decimation % P%id%03 = Have gained reinforcements update .%id1%_%plotid1%_GoDecimation .%id1%_%plotid1%_GoReinforcements % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Decimation&Reinforcements> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu3 GoWinMission GoLoseMission end Msg1 Msg2_1 Msg2_2 <> Msg2_3 <> Msg3 Msg4 Msg5 Msg6 Msg7 sub team 1 SetEnemy 2 3 SetAlly 4 ParaX 5 ParaY 10 team 2 name SetEnemy 1 4 SetAlly 3 Deploy GoSmallDeploy ParaX 40 ParaY 40 Team 3 name SetAlly 2 SetEnemy 1 4 ParaX 45 ParaY 45 Team 4 name SetAlly 1 SetEnemy 2 3 Deploy ParaX 10 ParaY 5 rect name special width 5 height 5 MFX 40 MFY 40 sub SuperProp requires <*Superweapon> SetTeam 3 end end end %% ************************************* %% *** +Clo Woo Love Interest *** %% ************************************* %% ******************************* %% *** CHOOSE YER POISON *** %% ******************************* %% %% A special sub-class of mecha encounters in which the PC is introduced to the %% hometown's primary antagonists. Generally, the player will then be able to %% decide which faction is going to become the core story enemy. Content name desc requires <*:CS_MechaEncounter L:FCOMS F:-- (L:Ally|P:--) -L:Enemy ~*CORE_INTRO ~*CORE_OFF_> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is one pirate team % E4 is the other one % E5 is the Pirate with whom the PC will speak Element3 Element4 Element5 Place5 <2 (Enemies1) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have E3 update % You can get reinforcements, but no decimation. .%id1%_%plotid1%_GoReinforcements %% Debriefing Msg - Fight E3 Msg%id%11 <> %% Debriefing Msg - Fight E4 Msg%id%12 <> % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu4 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 4 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy ParaX 40 ParaY 45 team 3 setally 1 setenemy 2 4 Deploy ParaX 10 ParaY 5 team 4 name SetEnemy 1 3 Deploy ParaX 45 ParaY 40 end Persona 5 special greeting result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04_1 CMsg%id%04_1 Msg%id%04_2 CMsg%id%04_2 Msg%id%05_1 CMsg%id%05_1 Msg%id%05_2 CMsg%id%05_2 Msg%id%06 Msg%id%07_1 CMsg%id%07_1 Msg%id%07_2 CMsg%id%07_2 Msg%id%08 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 end inv NPC Pirate end Content name desc requires <*:CS_MechaEncounter L:RISHI F:-- (L:Ally|P:--) -L:Enemy ~*CORE_INTRO ~*CORE_OFF_> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the L5 Alliance % E4 is the Blades of Crihna % E5 is the Alliance pilot with whom the PC will speak Element3 Element4 Element5 Place5 <2 (Enemies1) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have challenged Aegis update % You can get reinforcements, but no decimation. .%id1%_%plotid1%_GoReinforcements %% Debriefing Message: PC fought Blades (win or lose) Msg%id%01 %% Debriefing Message: PC fought Knights (win or lose) Msg%id%02 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu4 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 4 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy ParaX 40 ParaY 45 team 3 setally 1 setenemy 2 4 Deploy ParaX 10 ParaY 5 team 4 name SetEnemy 1 3 Deploy ParaX 45 ParaY 40 end Persona 5 special greeting result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 CPrompt%id%07 Prompt%id%08 end inv NPC Knight end Content name desc requires <*:CS_MechaEncounter L:MAQUI F:-- (L:Ally|P:--) -L:Enemy ~*CORE_INTRO ~*CORE_OFF_> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the Aegis Space Force % E4 is the Red Mask Raiders % E5 is the Aegis pilot with whom the PC will speak Element3 Element4 Element5 Place5 <2 (Enemies1) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have challenged Aegis update % You can get reinforcements, but no decimation. .%id1%_%plotid1%_GoReinforcements %% Debriefing Msg - Fight Red Mask Raiders Msg%id%11 %% Debriefing Msg - Fight Aegis Space Force Msg%id%12 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu4 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 4 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy ParaX 40 ParaY 45 team 3 setally 1 setenemy 2 4 Deploy ParaX 10 ParaY 5 team 4 name SetEnemy 1 3 Deploy ParaX 45 ParaY 40 end Persona 5 special greeting result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 end inv NPC Mecha Pilot end %% ******************* %% *** GENERAL *** %% ******************* Content name desc requires <*:CS_MechaEncounter &Raiders E:-- F:++ -!Ne ~!Hi> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the new enemy Element3 Place3 <2 (Enemies) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end GoFreeze Msg1 Msg2 Msg3 Msg4 Msg6 <%name1% defeated you.> Msg7 Msg9 <%name3%'s cockpit is mysteriously empty. Could \SPR %3% have survived?> sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoSmall ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 3 special greeting *.%id%_GoUnknownGreeting <*BattleChallenge result%id%01 na> *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 end Content name desc requires <*:CS_MechaEncounter &Raiders E:-- F:++ ~!Lo -!Md -!Hi -!Ex> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the new enemy Element3 Place3 <2 (Enemies) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation %% Debriefing Message Msg%id%11 <%name3%, you say? I don't know anything about \OPR %3% ... I expect you'll find out shortly.> Msg%id%11_1 CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 <%name3% has fought with \FACTION &AllyFac before. I advise you to take care; you'll probably be crossing paths with \OPR %3% again.> CMsg%id%11_3 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end GoFreeze Msg1 Msg2 Msg3 Msg4 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg6 <%name1% defeated you.> Msg7 Msg8 Msg8_1 CMsg8_1 Msg8_2 CMsg8_2 Msg9 <%name3%'s cockpit is mysteriously empty. Could \SPR %3% have survived?> sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoSmall ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 3 special greeting *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 end Content name desc requires <*:CS_MechaEncounter +H-- A:++ (A:LABOR|A:CRAFT|A:FAITH|A:CORPO|A:TRADE|A:ADVEN) (A:A.tha|A:A.jr_|A:A.---) -A:ENEMY ~+Glo> changes % E1 is the outdoors scene where this encounter will take place % E2 is the encounter % E3 is the love interest element3 Place3 <2 (Lover) sd ally> % P%id%01 = Initialization Counter % P%id%03 = Have gained decimation update % Set decimation here .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 .next <+Hra> GoSetEnvy end Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 <%name3% returns to \PPR %3% convoy.> sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 SetEnemy 1 3 Deploy GoSmallDeploy ParaX 30 ParaY 30 team 3 name SetEnemy 2 SetAlly 1 ParaX 45 ParaY 45 end Persona 3 special % V%id%01 = Can only get this conversation once greeting *result%id%01 <*LetsDefeatThem> result%id%02 Msg%id%01 Msg%id%01_1 <\PC ! You certainly know the right time to show up... A convoy that I was escorting got attacked by these mecha. I could use some help to finish them off.> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 end Content name size 5 desc requires <*:CS_MechaEncounter F:BOHEM E:++ -&Secondary -!Ne ~!Md ~!Hi> % The Bohemian Collective, being largely an anarchic group of live-and-let-live leave-me-aloneist % libertarians/libertines, doesn't make a very good enemy. Honestly, I can see pissing one or two % of them off, or maybe even three or four, but the entire faction? You made them agree on something? % Impossible. If a splinter group wants to kill you, they're probably just posers. Or pirates. changes % E1 is the outdoors scene where this encounter will take place % E2 is the encounter % E3 is an envoy from the Dusty Ring % E4 is a pirate faction to be the new enemy % E5 is a pirate/mook of the Enemy NPC Element3 Place3 <2 (Envoy) sd ally> Element4 Element5 % P%id%01 = Initialization Counter update % This subplot must propgate the effects of its own subplot backwards to % the parent plot. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss %% Debriefing Msg Msg%id%11_1 <\PERSONA &EnemyNPC actually works for %name4%... I'm not surprised. The Bohemians generally just want to be left alone.> CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 <\PERSONA &EnemyNPC actually works for %name4%... Can't say I'm surprised. The Bohemians colonies are too disorganized to pull off the kind of attacks we've been seeing.> CMsg%id%11_3 % SubPlot1 is the Stop E5 mission SubPlot1 <*:CS_StopNPCMission&Secondary 5> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 <%name3% revealed that \PERSONA &EnemyNPC was a pirate, not a bohemian.> Msg3_1 <\PERSONA &EnemyNPC 's number one fighter %name5% is here on a mission. Stop \OPR %5% , and many lives will be spared. We will meet again.> CMsg3_1 Msg3_2 <\PERSONA &EnemyNPC ... \PPR &EnemyNPC henchman... you must stop %name5%... we... we will meet again...> CMsg3_2 Msg4 Msg5 <%name3% told you that \PERSONA &EnemyNPC works for %name4%, and \PPR &EnemyNPC henchman %name5% is in town on a mission.> sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 25 ParaY 45 team 3 name setally 1 setenemy 2 Deploy ParaX 45 ParaY 5 end Persona 3 special greeting result1 result2 result3 Msg1 Msg2 <\PERSONA &EnemyNPC does not represent us; in fact, \SPR &EnemyNPC has long been a member of %name4%, and it is they who have directed \PPR &EnemyNPC actions against you.> Msg3 Msg4 Prompt1 Prompt2 Prompt3 end inv NPC Explorer chardesc Old SetFaction 12 NPC Pirate chardesc Young job_desig end Content name size 10 desc requires <*:CS_MechaEncounter F:MUGLE (P:--|P:CORPO) (E:M.---|E:M.mer) -E:A.nme (!Ne|!Lo)> %% MUGL Enterprises isn't a particularly good villain... or, should I say, not a particularly intimidating %% villain. I mean, how would you feel if Sanrio were trying to kill you? Actually, that does sound pretty %% terrifying... In any case this component will help to cement their reputation as the newest and to-date %% least respected of the L5 mecha corporations. changes % E1 is the outdoors scene where this encounter will be placed % E2 is the encounter % E3 is the enemy NPC % E4 is the faction which gave the NPC the better offer % E5 is a local public scene % E6 is another MUGL pilot who E3 is assisting, supposedly Element3 Place3 <2 (Enemies) sd enemy> % The new enemy faction should be an enemy to both the PC and to MUGL. Element4 Element5 Element6 NeverFail6 Place6 <2 (Enemies) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained decimation update % Gaining the decimation advantage means that E6 won't show up to the battle. .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = Enemy Died/Plot Immunity Counter MapWidth 50 MapHeight 50 Start GoDecimated nu1 nu2 GoE3AliveEnd GoE3DiedEnd Faint%3% end GoSetAttitude Msg1 Msg2_1 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9_1 Msg10 <%name3% quit MUGL to go work for %name4%.> Msg11 Msg12 Msg13_1 Msg14 Msg15 <%name6% arrived too late to help %name3%.> sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end end Content name size 9 desc requires <*:CS_MechaEncounter E:++ F:++ (E:M.---|E:M.rev) (E:A.hat|E:A.mut|E:A.obs|E:A.env|E:A.---|E:A.jr_|E:A.sr_) !Hi -&Secondary> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the "kicker" encounter- the one which starts the main plot % E3 is the enemy % E4 is the urban scene for the encounters to follow % E5 is the final battle scene % E6 is the Enemy's lieutenant % E7 is the third in command % E8 is the fourth % E9 is the *FactionRampage mood which accompanies this episode % E10 is an enemy of the enemy faction needed for narrative purposes Element3 Place3 Element4 Element5 Place5 <4> Element6 NeverFail6 Place6 <5 (Enemies) SD enemy> Element7 Place7 Element8 Place8 Element9 Element10 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation % P%id%04 = Completed first combat % P%id%05 = E8 backed out of fight % P%id%06 = E7 backed out of (final) fight update % Set either reinforcements or decimation here .%id3%_%plotid3%_GoReinforcements .%id3%_%plotid3%_GoDecimation .%id1%_%plotid1%_GoWin .%id2%_%plotid2%_GoWin Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 % SubPlot1 is the first substage boss battle % SubPlot2 is the second substage boss battle % SubPlot3 is the ability to gain an advantage in the final battle SubPlot1 <*:CS_SubBossRoadblock 4 8> SubPlot2 <*:CS_SubBossRoadblock 4 7> SubPlot3 <*:CS_GainAdvantageVsNPC&Decimation&Reinforcements 6> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 30 MapHeight 30 Start nu1 nu2 end GoBeginRoadblock Msg1 Msg2 Msg3 Msg4 <%name3% ordered \PPR %3% lieutenant %name6% to raze \SCENE &EpisodeScene .> Msg5 <%name3% left \PPR %3% lieutenant to raze \SCENE &EpisodeScene . %name6% ordered %name7% and %name8% to hunt you down.> Msg6 <%name6% assigned %name7% and %name8% to hunt you down.> Msg7 Msg8 Msg9 Msg10 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 25 ParaY 25 end MetaScene 5 2 % This is the final battle against E6 and the surviving henchmen. It's likely to be % very tough, so hopefully the PC has gained the advantage or removed some of the % henchmen. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 Deploy ParaX 10 ParaY 10 end Persona 6 special greeting *result%id%01 <*THEME_EXPO&Enemy na> Msg%id%01 Prompt%id%01 Persona 7 special % V%id%01 = Have fought before % If the PC completed the first combat and heard E7 speak, it might be % possible to convince them to not fight now. greeting *.%id%_GoSecondTime <*Back_For_More> *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 result%id%03 Msg%id%01 Msg%id%02 <%name6% isn't the boss of me! I don't have to listen to \OPR %6%... it's just that right now I want to kick your ass of my own free will!> Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 <[Continue]> Persona 8 special % V%id%01 = Have fought before % If the PC completed the first combat and heard E8 speak, it might be % possible to convince them to not fight now. greeting *.%id%_GoSecondTime <*Back_For_More> *.%id%_GoArgumentFail <*IDisagreeYouDie> .%id%_GoArgumentWin .%id%_GoArgumentMeh .%id%_GoJoinLance *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 <%name3% must be stopped, for everyone's sake.> Prompt%id%07 end inv stc CORE-ACTIVATABLE name <%name6%'s Lance> Mood 3 name <"I Have Become Death" Mood> plot_type <*FactionRampage> Element1 Update Msg1_1 <%city% under siege as %name3%'s personal forces take control.> Msg1_2 Msg1_3 <%name3% has begun a purge of \PPR %3% enemies from %city%; authorities powerless to stop the destruction.> % Meme Messages Msg_1 CMsg_1 Msg_2 CMsg_2 Msg_3 CMsg_3 end Content name size 5 desc requires <*:CS_MechaEncounter F:++ +Hed H:++ ~H:FRIEND ~H:LOVER ~H:FAMILY -H:ALLY> changes <+H> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the former enemy % E4 is a local public scene Element3 Place3 <2 (Former Enemy) sd ally> Element4 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation % At initialization, set the NPC's level and clear its faction. update % Set either reinforcements or decimation here .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start .context <+H--> nu1 nu2 GoDeleteE3 end Msg1 Msg2 Msg3 Msg4 <%name3% pledged to help you defeat \PPR %3% former allies.> sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 SetEnemy 1 3 Deploy GoSmallDeploy ParaX 30 ParaY 30 team 3 name Deploy SetEnemy 2 SetAlly 1 ParaX 45 ParaY 45 end Persona 3 special % V%id%01 = Can only get this conversation once greeting *result%id%01 <*LetsDefeatThem> *result%id%02 <*THEME_EXPO&Ally .%id%_GoEndR2> .%id%_GoEndR2 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 <> end Content name size 12 desc requires <*:CS_MechaEncounter E:++ E:A.hat (E:M.---|E:M.com|E:M.see) -&Secondary> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy % E4 is an environs scene % E5 is an urban scene % E6 is Seeker #1 % E7 is Seeker #2 Element3 Place3 Element4 Element5 Element6 Place6 <4> Element7 Place7 <5> % P%id%01 = Have activated henchmen % This subplot must propgate the effects of its own subplot backwards to % the parent plot. Yahoo. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 = The enemy mission you have to stop SubPlot1 <*:CS_StopNPCMission&IsEnemyNPC&Secondary 3> sub Persona 3 special greeting GoChat result%id%01 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%01_1 MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 <%name3% mobilized all of \PPR %3% forces to destroy you.> sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end MetaScene 6 2 special % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu3 end Msg2 sub team 1 SetEnemy 2 3 ParaX 25 ParaY 25 team 2 name SetEnemy 1 SetAlly 3 Deploy ParaX 20 ParaY 5 team 3 name SetEnemy 1 SetAlly 2 Deploy ParaX 30 ParaY 45 end MetaScene 7 2 special % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu3 end Msg2 sub team 1 SetEnemy 2 3 ParaX 25 ParaY 25 team 2 name SetEnemy 1 SetAlly 3 Deploy ParaX 5 ParaY 20 team 3 name SetEnemy 1 SetAlly 2 Deploy ParaX 45 ParaY 30 end end inv Encounter name % V1 = Recharge counter update EncounterMove 20 use ATTACK Msg-2 Encounter name % V1 = Recharge counter update EncounterMove 20 use ATTACK Msg-2 end Content name desc requires <*:CS_MechaEncounter +Hre H:++> changes <+H> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy fighter Element3 Place3 <2 (Enemies) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation %% Debriefing Message Msg%id%11 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start .clear <+H--> nu1 nu2 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 3 special greeting GoGreet *result%id%01 <*THEME_EXPO&Enemy na> Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 end Content name desc requires <*:CS_MechaEncounter E:A.jr_ F:++ ~!Md ~!Hi> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy fighter % E4 is the replacement enemy Element3 Place3 <2 (Enemies) sd enemy> Element4 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation %% Debriefing Msg - PC throws fight Msg%id%11 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end GoE3Defeated GoE3Died Msg1 <%name3% was ordered to defeat you or be fired.> Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 <%name3%, you have failed \FACTION &EnemyFac for the last time. From this point on I will deal with \PC myself.> Msg9 Msg10 Msg11 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 3 special % V%id%01 = reaction bonus greeting *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 result%id%03 .%id%_GoR3Win result%id%04 result%id%05 .%id%_GoR5Win result%id%06 result%id%07 result%id%08 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Prompt%id%06 CPrompt%id%06 Prompt%id%07 Prompt%id%08 end inv NPC Mecha Pilot Initiative ElectronicWarfare SpotWeakness Talent 5 Talent 8 Talent 16 MOTIVATION:Professional end Content name desc requires <*:CS_MechaEncounter ~&Raiders F:++ &Secondary -!Ne -!Lo> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy fighter Element3 Place3 <2 (Enemies) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 3 special greeting *GoGreet <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> end Content name desc requires <*:CS_MechaEncounter E:++ F:-- -!Ne (L:FCOMS|L:MAQUI|L:RISHI) ~E:THIEF ~E:M.mer ~C:POLIC ~P:POLIC ~P:SILKN ~P:ROCKE> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the pirate faction % E4 is the enemy fighter % E5 is the enemy NPC Element3 Element4 Place4 <2 (Enemies) sd enemy> Element5 Place5 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation %% Debriefing Message Msg%id%11_1 <%name5% has taken up the black flag. I guess that means we're going to be taking on all of %name3%, now.> CMsg%id%11_1 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter rumor%id% <%name5% has a meeting planned with some unsavory types.> MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 Msg5 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 4 special greeting GoGreet *result1 <*THEME_EXPO&Enemy na> result2 Msg1 prompt1 prompt2 end inv NPC Pirate end Content name desc requires <*:CS_MechaEncounter (E:M.pro|E:M.see) (E:A.---|E:A.sr_) -!Ne -&Secondary> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the encounter for the second fight. Brutal! % E4 is the enemy % E5 is a secret- that there's an ambush waiting! Element3 Place3 <1> Element4 Place4 <3 (Enemies)> Element5 % P%id%01 = Initialization Counter % P%id%02 = Decimation Counter % P%id%03 = Reinforcements Counter % P%id%04 = Learn Secret Counter update .%id1%_%plotid1%_GoDecimation .%id1%_%plotid1%_GoReinforcements .%id2%_%plotid2%_GoLearnSecret % SubPlot1 is the Gain Advantage task % SubPlot2 is the Learn Secret task SubPlot1 <*:CS_GainAdvantageVsNPC&Decimation&Reinforcements 4> SubPlot2 <*:CS_LearnSecretAboutNPC 5 4> sub MetaScene 2 2 % This first scene is a pretty easy fight. If the PC doesn't know that there % is going to be an ambush, all advantages will be spent here. Otherwise the % advantages will be spent in the second, tougher encounter. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 Msg5 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy GoBigDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end MetaScene 3 2 % This second scene is much harder. The Enemy is here, along with a full % complement of guards... unless the PC managed to get both the advantage and % the secret, that is. % L1 = Encounter Over Counter % L2 = Initialization Counter special MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy GoBigDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end inv STC CORE-MECHAAMBUSH-ACTIVATABLE name Secret Msg <%name4% has been watching you; \SPR %4% 's not going to let you get away this time.> end Content name desc requires <*:CS_MechaEncounter E:++ (E:A.equ|E:A.env|E:A.adm|E:A.---) E:M.--- ~E:MILIT ~E:POLIT ~E:GOOD_ -&Secondary> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy % E4 is the secondary enemy Element3 Place3 Element4 Place4 % P%id%01 = Initialization Counter % At initialization, prepare E4 for combat update % This subplot must propgate the effects of its own subplot backwards to % the parent plot. Yahoo. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 = The enemy mission you have to stop SubPlot1 <*:CS_StopNPCMission&IsEnemyNPC&Secondary 3> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 30 MapHeight 30 % At startup, E3 and E4 argue over the situation. % You must win this fight, or E4's forces will complete E3's mission. Start nu1 nu2 end Msg1 Msg1_1 Msg1_2 Msg2 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 Msg3 Msg3_1 Msg3_2 Msg4 Msg5 Msg6 Msg7 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 25 ParaY 25 end end inv NPC Mercenary job_desig chardesc passionate melancholy end Content name desc requires <*:CS_MechaEncounter E:++ E:A.nme -&Secondary> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy Element3 Place3 % This subplot must propgate the effects of its own subplot backwards to % the parent plot. Yahoo. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 = The enemy mission you have to stop SubPlot1 <*:CS_StopNPCMission&IsEnemyNPC&Secondary 3> sub Persona 3 special greeting GoChat result%id%01 Msg%id%01 <\PC , so we finally meet. I am %name3%. I understand that you gave my men some trouble back there.> Msg%id%02 Msg%id%03 <%name3% leaves the area.> Prompt%id%01 MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 30 MapHeight 30 % Whether you win or lose this fight isn't important. Of course, there are some benefits % to winning... The timer starts from the conclusion of the fight, because I can be just % as cruel to my NPCs as I am to the PC. Start nu1 nu2 end Msg1 Msg2 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 25 ParaY 25 end end Content name desc requires <*:CS_MechaEncounter &Raiders E:--> changes % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the new enemy Element3 Place3 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements update .%id1%_%plotid1%_GoReinforcements %% Debriefing Message Msg%id%11 <%name3%, you say? I don't know anything about \OPR %3% ... I expect you'll find out shortly.> Msg%id%11_1 CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 <%name3% has fought with \FACTION &AllyFac before. I advise you to take care; you'll probably be crossing paths with \OPR %3% again.> CMsg%id%11_3 % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2_1 Msg2_2 Msg2_3 CMsg2_3 Msg2_4 CMsg2_4 Msg2_5 CMsg2_5 Msg3 Msg4 Msg5 Msg6 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end Content name desc requires <*:CS_MechaEncounter ~&Raiders E:++ -E:A.--- &Secondary -!Md -!Hi -!Ex> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1_1 Msg1_2 <> Msg1_3 <> Msg1_4 <> Msg1_5 <> Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end Content name desc requires <*:CS_MechaEncounter ~&Raiders E:++ ~F:++ ~P:++ -&Beancounter> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the Enemy NPC Element3 % P%id%01 = Initialization Counter update .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 = MIX Encounter SubPlot1 <*:CS_MIX_Encounter 1 2 3> % *************************** % *** OMEGA MISSIONS *** % *************************** % % In certain cases, the core story development will outpace the PC's % advancement. This can happen if the PC loses some episodes after triggering % their context changes. In this case, the following episodes will be % omega missions until the PC reaches the next difficulty band. Content name desc requires <*:CS_MechaEncounter (E:A.sr_|E:A.ant|E:A.sec|E:A.equ|E:A.env|E:A.pch|E:A.hat|E:A.mut|E:A.obs) (E:M.pro|E:M.com|E:M.ggd|E:M.see|E:M.rev|E:M.cha|E:M.nih) ~!Lo -!Md -!Hi -!Ex -&Beancounter> % Certain advanced motivations/attitudes will also trigger this omega % state, even though they may not be part of the beanpole or even strictly % possible at this time. % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy NPC Element3 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2_1 Msg2_2 CMsg2_2 Msg2_3 CMsg2_3 Msg2_4 CMsg2_4 Msg2_5 CMsg2_5 Msg3 Msg4 Msg5 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end Content name desc % If the enemy NPC is both nihilistic and obsessed with the PC, no % further character development is possible. requires <*:CS_MechaEncounter E:A.obs E:M.nih F:++ -&Beancounter> % E1 is the outdoors scene where this encounter will be placed % E2 is the metascene to be used for this encounter % E3 is the enemy fighter Element3 NeverFail3 Place3 <2 (Enemies) sd enemy> % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements % P%id%03 = Have gained decimation update .%id1%_%plotid1%_GoReinforcements .%id1%_%plotid1%_GoDecimation % SubPlot1 = Gain Advantage vs Raiders SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements&Decimation> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 Msg4 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 3 special greeting *GoGreet <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> end GH2/series/CHOICE_Utilities.txt0000644000175000017500000000127611326004537015123 0ustar kaolkaolChoice 0 name desc desig requires <> Choice -1 name desc desig % E1 is the city for the next episode. Element1 Prompt Reply Alert Choice Msg%id%01 GH2/series/QUEST_EGG_Primary.txt0000644000175000017500000000402711326004535015217 0ustar kaolkaol% % *EGG_Primary Quests % % These are quests attached to the PC's primary mentor. They should place the % mentor in the adventure, usually in the PCHOME scene. % Content name requires <*EGG_Primary> Element1 Place1 <2 (Guards) SD ally> Element2 sub Persona 1 greeting GoCheckElite GoCheckVeteran GoCheckRegular GoCheckGreen GoNotMuch .%id%_GoFirstTime Msg%id%01 <\PC my \OFFSPRING 0 , you are at the very top of your field. You've done good.> Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 end Content name requires <*EGG_Secondary> Element1 Place1 <2 (Guards) SD ally> Element2 sub Persona 1 greeting result%id%01 Msg%id%01 Msg%id%02 Prompt%id%01 CPrompt%id%01 end GH2/series/ARENAMISSION_Corporate.txt0000644000175000017500000000455211326004535016042 0ustar kaolkaol%% ************************************** %% *** [!Ne+] BEGINNER MISSIONS *** %% ************************************** %% **************************************** %% *** [!Lo+] LOW LEVEL MISSIONS *** %% **************************************** ArenaMission name requires <*MISSION -!Ne CORPORATE> desc MapWidth 50 MapHeight 50 PayRate 1000 WildMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Base Counter Start nu1 nu2 nu3 GoN3SomeLeft GoWinMission GoLoseMission 5Min Msg1 Msg2 Msg3 Msg4_1 CMsg4_1 Msg4_2 CMsg4_2 Msg5 sub Team 1 SetEnemy 2 3 4 ParaX 5 ParaY 5 Team 2 SetEnemy 1 SetAlly 3 4 Deploy home Team 3 SetEnemy 1 SetAlly 2 4 home Team 4 SetEnemy 1 SetAlly 2 3 home Deploy rect width 5 height 5 name rect name MFX 35 MFY 35 width 10 height 10 sub SuperProp requires <*Fortress> SetTeam 3 end end %% ******************************************* %% *** [!Md+] MEDIUM LEVEL MISSIONS *** %% ******************************************* %% ***************************************** %% *** [!Hi+] HIGH LEVEL MISSIONS *** %% ***************************************** %% *************************************** %% *** [!Ex] ACE LEVEL MISSIONS *** %% *************************************** GH2/series/ARENAMISSION_REWARD_MechaSource.txt0000644000175000017500000001276411326004535017352 0ustar kaolkaol%% %% *REWARD missions %% %% Don't let the name fool you- these missions aren't rewards in themselves; %% rather, they present an opportunity for the PC to earn a significant reward. %% The reward missions are typically more difficult than regular missions. %% %% The MechaSource rewards require a MECHA_SOURCE_MISSION coupon. Also check to %% make sure that the appropriate mecha source tag is present. Note that gaining %% a mecha source may unlock additional missions and rewards. %% %% The mecha source missions are arranged by faction, in order of Faction ID. %% %% *********************************** %% *** [f2] COMET METALWORKS *** %% *********************************** ArenaMission name requires <*REWARD MECHA_SOURCE_MISSION [f2] (SILKN|L5LAW|PRIVA|ROCKE)> desc terrain MapWidth 50 MapHeight 50 PayRate 500 AsteroidMap terrain RockyTiles Vacuum SpaceBackdrop Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Base Counter Start nu1 nu2 nu3 nu4 Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 4 SetAlly 3 home Team 2 SetAlly 4 SetEnemy 1 3 Deploy ParaX 20 ParaY 40 Team 3 SetEnemy 2 4 SetAlly 1 home Team 4 SetAlly 2 SetEnemy 1 3 Deploy ParaX 40 ParaY 20 rect name MFX 5 MFY 5 Height 8 Width 8 FloorType 6 end inv STC BUNKER-1 name SetTeam 3 Scale 2 STC BUNKER-1 name SetTeam 3 Scale 2 STC BUNKER-1 name SetTeam 3 Scale 2 end %% ************************************** %% *** [f5] PRO DUELIST LEAGUE *** %% ************************************** ArenaMission name requires <*REWARD MECHA_SOURCE_MISSION [f5] -PDASS> desc special terrain MapWidth 50 MapHeight 50 PayRate 500 ArenaMap Element1 Element2 Element3 Element4 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg1_1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 end inv NPC Arena Pilot SetTeam 2 NPC Arena Pilot SetTeam 2 NPC Arena Pilot SetTeam 2 end %% ************************************* %% *** [f7] HOELLER INDUSTRIES *** %% ************************************* %% *********************************** %% *** [f13] MUGL ENTERPRISES *** %% *********************************** ArenaMission name requires <*REWARD MECHA_SOURCE_MISSION [f13] (SILKN|L5LAW|PRIVA|ROCKE|CRIHN)> desc MapWidth 50 MapHeight 50 PayRate 500 SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop Element1 Element2 Element3 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 GoLose Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 Team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 SetAlly 1 ParaX 15 ParaY 15 end inv NPC Mecha Pilot job Charm 19 job_desig SetFaction -1 SetTeam 3 end GH2/series/PLOT_MOOD_FightToDestination.txt0000644000175000017500000001337411374513723017367 0ustar kaolkaol%% %% *FightToDestination Plots %% %% The PC wants to get somewhere. In order to do that, they're gonna have to %% kick a lot of arse. %% %% %name2% will be used to identify the destination, so name the %% encounter something sensible!!! %% %% Mood Spec: %% E1 = The outdoors scene %% E2 = The encounter being sought %% E3 = The faction behind it %% Plot name desc requires <*FightToDestination> % E1 is the outdoors scene % E2 is an encounter to place there % E3 is the encounter being sought % E4 is the NPC to be negotiated with % E5 is the faction being fought Element1 Element2 Place2 <1> Element3 Element4 Place4 <2 (Enemies)> Element5 % P1 = Time Limit/Initialization Counter % P2 = E4 refused to fight PC start update &RevealEncounter sub MetaScene 2 2 rumor%id% <%name4% is protecting %name3%.> % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start nu1 nu2 GoJustEnd Msg1 Msg2 Msg3 Msg3_1 Msg3_2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 end Persona 4 special *greeting <*F2Dest_Greeting %threat% %3% GoReveal GoFight> GoReveal GoFight GoNoFight *GoThemeExpo <*THEME_EXPO&Enemy na> Msg1 Msg1_1 <%name3% is in %name1%... Here are the coordinates.> Msg1_2 Msg2 Msg2_1 Msg2_2 end inv Encounter name <%name3%?> EncounterMove 20 use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 end Plot name desc requires <*FightToDestination> % E1 is the outdoors scene % E2 is an encounter to place there % E3 is the encounter being sought % E4 is the faction Element1 Element2 Place2 <1> Element3 Element4 % P1 = Time Limit/Initialization Counter start update &RevealEncounter sub MetaScene 2 2 rumor%id% <%name3% is somewhere in %name1%, but you'll have to get past %name4%.> % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start nu1 nu2 GoCheckSurvival GoTrackFailed Msg1 Msg2 Msg3 Msg4 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 end end inv Encounter name <%name3%?> EncounterMove 20 use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 end GH2/series/PLOT_LancemateStuff.txt0000644000175000017500000000075411341163445015676 0ustar kaolkaol%% %% *GENERAL plots relating to the upkeep of lancemates, including the stub for %% the noncombat character development. %% Plot name desc requires <*GENERAL> % E1 is the lancemate in question Element1 % Upon activation, call the subplot. update % SubPlot1 is the character development. SubPlot1 <*LM_NonComCharDev 1> GH2/series/UNICON_Combat.txt0000644000175000017500000000555211326004537014417 0ustar kaolkaol%% %% Combat Random Scene Content- Random scene content that's all out of bubblegum! %% %% This file contains random scene combat that's meant to be used in combat %% missions. In order to use any of these things, your scene may need to meet %% certain criteria: %% - The faction of the scene is set to the faction being fought. %% - There's a hostile team named "Enemies" %% - There's a friendly team named "Allies" %% %% *CRC_Nemesis Content %% %% One of your many enemies will show up to oppose you. This content %% will not appear at !Ne renown, but will start showing up thereafter. %% Content name requires <*CRC_Nemesis (!Hi|!Ex)> Element1 <.> Element2 Team2 TeamData2 start end sub Persona 2 special &LowAmount &MedAmount &HighAmount greeting GoChat result1 result2 result3 GoR3Fail GoDefect result4 result5 result6 *GoThemeExpo <*THEME_EXPO&Enemy na> Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Prompt1 Prompt2 CPrompt2 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt5 Prompt6 CPrompt6 end inv NPC Assassin Renown 70 end %% %% *CRC_Backup Content %% %% One of your friends or allies will show up to provide backup. %% GH2/series/RESCUE_Default.txt0000644000175000017500000000525311326004537014567 0ustar kaolkaol%% DEFAULT RESCUE FILES %% %% RESCUE CONTEXTS %% The context type beginning with a * is the type of rescue required. %% *DEATH - The PC has died. %% *RECOVERY - The PC has died, but is recovered by his lancemates. %% *PICKUP - The PC is trapped somewhere and needs help. %% Location context, desig, type, and terrain %% Root scene desig, faction desig %% PC extended description (job, traits) %% %% RESCUE ELEMENTS %% Element 1 = Scene from which to be rescued. %% The rest of the elements can be used as required. %% %% RESCUE ACTIONS %% Be sure to use FORCEEXIT rather than EXIT to move the PC to the recovery %% area, since the exit has probably already been set by whatever killed him. Plot name requires <*PICKUP SPACE> Element1 Element2 update start Msg1 Plot name requires <*PICKUP> Element1 Element2 update start Msg1 Plot name requires <*DEATH> Element1 Element2 update start Msg1 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 Plot name requires <*RECOVERY> Element1 Element2 update start Msg1 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 Plot name requires <*RECOVERY> Element1 Element2 update start Msg1 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 GH2/series/MEGA_CORE_MAIN_GatherInformation.txt0000644000175000017500000016464611347637671020014 0ustar kaolkaol%% *:CS_GatherInformation Content %% &AboutEnemy The information is about the enemy faction or NPC %% %% In this component, the PC will be able to gain some information via a difficult source. %% This component will almost certainly change the story context. %% %% The Param1 NPC must have a persona which calls the following script: %% .%id%_GoInform %% This script tells the PC about the situation, setting up the rest of this %% subplot. The SubPlotID must be set separately. %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% %% This is a MAIN COURSE subplot. Favored by: POLIT, MEDIA, ACADE %% %% %% Param1: The NPC who will tell the PC about the situation. %% After introducing the component, this NPC will serve no further purpose %% in this narrative thread. %% %% ******************************** %% *** +P-- Peaceful Life *** %% ******************************** Content name desc requires <*:CS_GatherInformation +P-- (P:--|P:PDASS) (L:MAQUI|L:RISHI|L:FCOMS) -1:MAQUI -1:RISHI -1:FCOMS ~1:MEDIA ~1:NOFAC> changes <+P> % E1 is the person who tells the PC about the attacks % E2 is the city faction, which must exclude E1 % E3 is a local space scene % E4 is the encounter Element2 Element3 Element4 Place4 <3> %% FAIL CONDITIONS: %% - None % Debriefing Message Msg%id%10 sub Persona 1 .%id%_GoInform Msg%id%01 Msg%id%02 <%name1% revealed that the fleet has been mobilized to find something in %name3%, but the technocracy is trying to keep it a secret.> Msg%id%03 MetaScene 4 2 rumor%id% MapWidth 50 MapHeight 50 SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop % L1 = Initialization Counter % L2 = Have released Team4 % L3 = Initial T3 number % L4 = End Message Counter % L5 = End of mission counter start .next <+Pun> end nu1 nu2 nu3 nu4 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 sub Team 1 SetEnemy 2 4 ParaX 30 ParaY 25 Team 2 SetEnemy 1 Deploy home Team 3 %% The battleship starts out as neutral. It will stay neutral %% as long as the PC doesn't try anything stupid, such as %% attacking it. Team 4 SetEnemy 1 SetAlly 2 home void name width 12 height 12 MFX 10 MFY 19 sub SuperProp requires <*Battleship> SetTeam 3 end end end inv STC CORE-INVISIBLEENCOUNTER name end %% **************************** %% *** +Pme Meet Enemy *** %% **************************** %% ***************************************** %% *** +Pun Unknown Enemy Attacks *** %% ***************************************** Content name desc requires <*:CS_GatherInformation (+Pun|+Pla|+Pew) E:++ R:LANCE (R:M.rev|R:M.ggd|R:M.nih) (R:A.---|R:A.jr_|R:A.sr_)> changes Size 6 % E1 is the person who tells the PC about the mobile base % E2 is an outdoors scene for the mobile base % E3 is the mobile base itself % E4 is the enemy NPC, needed to find its base % E5 is the partner Element2 Element3 Place3 <2> Element4 Place4 Element5 %% FAIL CONDITIONS: %% - The RevealEncounter task fails % P%id%01 = Initialization Counter % P%id%02 = Conversation Counter % P%id%03 = Have told E5 to calm down start .%id%_scenetype update end % SubPlot1 is the Reveal Enemy Base task SubPlot1 <*:CS_RevealEncounter_NPCBase&EnemyNPC 2 3 4> sub Persona 1 .%id%_GoInform Msg%id%01 <%name4% is searching for something in \SCENE &EpisodeScene . In fact, \SPR %4% 's got a recon team operating in town right now.> Msg%id%02 <%name1% revealed that %name4% has a recon team operating in \SCENE &EpisodeScene .> Msg%id%03 <%name1% revealed that %name4% sent a recon team to \SCENE &EpisodeScene .> Persona 5 % V1 = Have had discussion greeting GoChat *GoReveal <*LancemateWantsToTalk GoNow GoLater> GoLater GoNow result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 MetaScene 3 special boxmap MapWidth 17 MapHeight 17 special % L1 = Encounter Over Counter % L2 = Initialization Counter start GoSoloEntry GoSubtleEntry GoSoloLoud GoLoudEntry .%id%_%plotid%_LocalEvent end nu3 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 sub Team 1 SetEnemy 2 Team 2 name SetEnemy 1 Deploy type Team 3 name end inv STC COMPUTER-1 SetTeam 3 use GoNotNow Msg1 Msg2 end end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name end Content name desc requires <*:CS_GatherInformation +Pun (E:++|F:++) I:--> changes <+P I:++ E:++> Size 5 % E1 is the person who tells the PC about it % E2 is the item being sought % E3 is an urban scene where the enemies will search % E4 is the basement where you'll find your enemies % E5 is a replacement enemy, in case your current enemy is dead at the time of revelation Element2 Place2 Element3 Element4 Place4 <3> Element5 Place5 %% FAIL CONDITIONS: %% - The RevealEncounter task fails % P%id%01 = Initialization Counter update end %% Debriefing Message Msg%id%11 Msg%id%11_1 CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 CMsg%id%11_3 % SubPlot1 is the locate enemy target task SubPlot1 <*:CS_RevealEncounter_Raiders&ReconMission 3 4> sub Persona 1 .%id%_GoInform Msg%id%01_1 <\PERSONA &EnemyNPC is in town... \SPR &EnemyNPC 's searching for something. I don't know what it is, but it must be pretty important.> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%02 Metascene 4 type mapwidth 50 mapheight 50 MonkeyMap LockedDoorChance 0 SecretDoorChance 10 NeededCells 3 % L1 = Initialization Counter start GoReplaceEnemy <&SetEnemyNPC %5% SetXXRAttitude %5% XXR_A_NeverMet Goto GoMonologue> GoMonologue .next <+Pla> GoBeenBefore end Msg1 Msg2 Msg3 Msg4 Msg5 content1 content2 content3 sub Team 1 SetEnemy 2 Team 2 name type Deploy SetEnemy 1 room special MarbleType 22 room minimap <............2............> sub StairsUp Destination -1 MiniMapComponent 2 use end end end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name end %% ************************************* %% *** +Pla Learn of Artifact *** %% ************************************* Content name desc requires <*:CS_GatherInformation +Pla I:++ E:++ F:--> changes Size 5 % E1 is the person who tells the PC about it % E2 is the item % E3 is the enemy NPC % E4 is a local environs scene % E5 is E3's base % E6 is the new enemy faction % E7 is E3's lieutenant % E8 is a secret Element2 Element3 Place3 Element4 Element5 Place5 <4> Element6 Element7 Place7 <5 (guards) sd enemy> Element8 % There is a base to infiltrate. Either crash the party and fight your way to % the computer, or find a peaceful way in and pose as a new recruit. Once you've % discovered the identity of the enemy faction you can leave and it counts as % a win. %% FAIL CONDITIONS: %% - SubPlot1 fails %% - SubPlot2 fails % P%id%01 = Initialization Counter % P%id%02 = Peaceful entry counter update .%id1%_%plotid1%_GoLearnSecret .%id3%_%plotid3%_GoPeacefulEntry end .%id%_GoCheckSP2 Msg%id%01 <%name3%'s lieutenant %name7% is in town recruiting pilots to help find the %name2%.> % Debriefing Message Msg%id%11 % SubPlot1 is the secret, that E7 is recruiting % SubPlot2 is the reveal enemy base task % SubPlot3 is the gain peaceful entry task SubPlot1 <*:CS_TellSecretAboutNPC&LearnPlans&IsEnemy 8 3> SubPlot2 <*:CS_RevealEncounter_NPCBase&EnemyNPC 4 5 7> SubPlot3 <*:CS_PeacefulEntry_NPCBase 5 7> sub Persona 1 .%id%_GoInform Msg%id%01 Msg%id%02 <%name1% revealed that %name3%'s men are searching for the %name2%.> Msg%id%03 <%name1% asked who %name3% is working for.> MetaScene 5 % This is E7's base. type mapwidth 35 mapheight 35 MonkeyMap LockedDoorChance 10 SecretDoorChance 10 NeededCells 3 start end GoCheckNPC PCAttack Surrender%7% GoAlreadyWon Msg1 Msg2 Msg3 Content1 content3 sub Team 1 SetEnemy 2 Team 2 name type Deploy SetEnemy 1 SetAlly 3 Team 3 name SetEnemy 1 SetAlly 2 room special minimap <..1.........2............> desig sub TrapDoor Destination -1 MiniMapComponent 2 use end room special minimap <............1............> inv STC COMPUTER-INDESTRUCTIBLE MiniMapComponent 1 % V1 = Virys counter use GoBeenBefore CLUE_CODEBREAKING GoFail GoNotYet Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 end end Persona 7 special greeting *GoGreet <*AreYouHereAboutJob&Recruit&Investigation GoBriefing> GoBriefing GoReveal GoAttack result1 result2 .facs result3 Msg1 <%name3% was asked to find the %name2% by %name6%... That's all I know, I swear.> Msg2 <%name7% revealed that %name3% works for %name6%.> Msg3 Msg4 Msg5 Msg6 Msg7 Prompt1 Prompt2 Prompt3 end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name NPC Mecha Pilot Secret Msg <%name3%'s lieutenant %name7% is in town recruiting pilots to search for the %name2%.> end Content name desc requires <*:CS_GatherInformation +Pla I:++ E:++ F:++ -!Ne -I:TL_Md -I:TL_Hi -I:TL_Ex> changes <+P> Size 4 % E1 is the person who tells the PC about it % E2 is the item % E3 is the enemy NPC Element2 Element3 Place3 % P%id%01 = Have gotten item/Have lost item % P%id%02 = Initialization Counter %% FAIL CONDITIONS: %% - ItemCozy fails %% If the PC has retrieved the item, entering an outdoors scene will trigger the ending %% conversation with the enemy. start .%id%_scene update end Get%2% Msg%id%01 %% Debriefing Msg - Win or Lose, the PC always loses Msg%id%11 Msg%id%11_1 CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 % SubPlot1 is the Item Cozy subplot1 <*:CS_ItemCozy&Abandoned 2> sub Persona 1 .%id%_GoInform Msg%id%01 Msg%id%02 <%name1% revealed that %name3% is in town searching for the %name2%.> Msg%id%03 <%name1% revealed that the %name2% was nearby.> Persona 3 special greeting result1 result2 .next <+Pew> .nu1 .nu2 Msg1 <\PC , I should have expected to find you here. Well, it hardly matters- you're too late to stop me.> Msg2 Msg3 <%name3% revealed that \FACTION &EnemyFac were working on a new weapons program.> Msg4 Msg5 Prompt1 CPrompt1 Prompt2 CPrompt2 end %% **************************************** %% *** +Pew Enemy Weapon Program *** %% **************************************** Content name desc requires <*:CS_GatherInformation (!Md|!Hi) +Pew P:++ F:++ ~&AboutEnemy> changes <+P> Size 15 % E1 is the person who tells the PC. % E2 is the scene where the prototype is % E3 is the environs scene where the lab is located % E4 is the lab itself % E5 is the research head % E6 is the "high alert" mood Element2 Element3 Element4 Place4 <3> Element5 Place5 <4 (Scientist) sd enemy> Element6 % P%id%01 = Initialization Counter % P%id%02 = Safe Entry Counter update .%id3%_%plotid3%_GoPeacefulEntry % SubPlot1 is the peaceful entry task SubPlot1 <*:CS_PeacefulEntry_NPCBase 4 5> sub Persona 1 .%id%_GoInform Msg%id%01 Msg%id%02 <%name1% informed you that \FACTION &EnemyFac has a weapons lab in %name3%, and their prototype is nearly ready for production.> Msg%id%03 <%name1% told you where to find the weapons lab in %name2%.> MetaScene 4 rumor%id% MapWidth 35 MapHeight 35 MonkeyMap Ceiling IndustrialTiles SecretDoorChance 10 LockedDoorChance 10 type special NeededCells 1 %% L1 = Have opened mecha bay doors %% L2 = Have prepared mecha core %% L3 = Have released construction harness %% L4 = Bonus build points %% L5 = Bonus customization points start GoCancelSP1 PCAttack end GoCheckLoss Surrender%5% Msg1 Msg2 Msg3 Msg4 Msg5 <%name5% gives you the passcodes for the prototype mecha.> Content1 sub Team 1 SetEnemy 3 4 Team 2 name type Deploy SetEnemy 1 SetAlly 3 Team 3 name SetEnemy 1 SetAlly 2 Room name special sub Trapdoor Destination -1 use end Room %% Hangar Door Computer sub STC COMPUTER-INDESTRUCTIBLE name use GoBeenBefore CLUE_CODEBREAKING Msg1 Msg2 Msg3 Msg4 Msg5 Msg9 end Room %% Power Core Computer sub STC COMPUTER-INDESTRUCTIBLE name use GoNonSequence GoBeenBefore CLUE_SCIENCE GoSciFail CLUE_CODEBREAKING Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 end Room %% Construction Frame Computer sub STC COMPUTER-INDESTRUCTIBLE name % V1 = Have corrupted database use GoNonSequence GoBeenBefore CLUE_SCIENCE GoSciFail CLUE_CODEBREAKING Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 end Room name minimap <......###..#2#...........> inv Elevator name MiniMapComponent 2 use GoEndPlot .facs .next <+Ppw> GoNotOpen GoNotActive GoNotFree CLUE_CODEBREAKING GoDoorOpen GoFailCB Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 end end end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name NPC Scientist Combatant MechaEngineering Mood 3 name <"High Alert" Mood> plot_type <*FactionRampage> Element1 Update Msg1_1 <%city% heightens security after report of immanent terror attack.> Msg1_2 % Meme Messages Msg_1 Msg_2 Msg_3 end %% ******************* %% *** GENERAL *** %% ******************* Content name desc requires <*:CS_GatherInformation E:++ -E:A.nme F:-- (L:Ally|P:PDASS)> changes % E1 is the person who tells the PC % E2 is the enemy NPC % E3 is an outdoor scene for the encounter % E4 is the encounter itself % E5 is the new enemy faction % E6 is an allied local faction, to be used here merely as a victim Element2 Place2 Element3 Element4 Place4 <3> Element5 Element6 % P%id%01 = Initialization Counter % P%id%02 = Decimation Counter % P%id%03 = Reinforcements Counter %% FAIL CONDITIONS: %% - DiscoverNPCMission Fails update end .%id2%_%plotid2%_GoDecimation .%id2%_%plotid2%_GoReinforcements %% DEBRIEFING MESSAGE Msg%id%11 %% SubPlot1 = Discover the NPC's mission %% SubPlot2 = Gain advantage vs NPC SubPlot1 <*:CS_DiscoverNPCMission&EnemyNPC 3 4 2> SubPlot2 <*:CS_GainAdvantageVsNPC&Decimation&Reinforcements 2> sub Persona 1 .%id%_GoInform Msg%id%01 Msg%id%02 <%name1% revealed that %name2% is in town on a mission. This could be a good opportunity to discover who \SPR %2% works for.> Msg%id%03 Persona 2 special greeting result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%04 Msg%id%04_1 CMsg%id%04_1 Msg%id%05 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_2 Prompt%id%03 CPrompt%id%03 MetaScene 4 2 special % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start Local1 nu1 nu2 GoCheckGone GoJustDefeat end Msg1 Msg2 Msg3 Msg4 Msg5_1 CMsg5_1 Msg5_2 <%name2%'s lancemates move in to attack.> CMsg5_2 Msg6 Msg7 Msg8 Msg9 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end inv stc CORE-ACTIVATABLE name <%name2%'s Lance> end Content name desc requires <*:CS_GatherInformation E:A.env (E:M.---|E:M.mer|E:M.com) ~&AboutEnemy ~*CORE_R_EXP> changes Size 5 % E1 is the person who tells the PC % E2 is an environs scene for the enemy base % E3 is the base itself % E4 is a local public scene for the mentor's home % E5 is the mentor % E6 is the enemy % E7 is a secret Element2 Element3 Place3 <2> Element4 Element5 Place5 <3 (Citizens) pass> Element6 Place6 <3 (Guards) sd> Element7 %% FAIL CONDITIONS: %% - DiscoverNPCisMissing Fails %% - Reveal NPC Base Fails % P%id%01 = Initialization Counter % P%id%02 = Have learned secret update start .%id%_GoCheckSP2 .%id%_GoLoseMission .%id1%_%plotid1%_GoDiscover .%id3%_%plotid3%_GoLearnSecret %% Debriefing Message - Normal Ending Msg%id%11 Msg%id%11_1 CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 CMsg%id%11_3 %% Debriefing Message - Good Ending Msg%id%12 Msg%id%12_1 CMsg%id%12_1 %% Debriefing Message - Trainer ends up dead, somehow. Msg%id%13 % SubPlot1 is the revelation that the NPC is missing % SubPlot2 is the Reveal NPC Base task % SubPlot3 is the secret revelation SubPlot1 <*:CS_DiscoverNPCKidnappedByCSE&NotReally 4 5> SubPlot2 <*:CS_RevealEncounter_NPCBase&EnemyNPC 2 3 6> SubPlot3 <*:CS_LearnSecretAboutNPC 7 5> sub Persona 1 .%id%_GoInform Msg%id%01 <%name6% has recently been in contact with the famous mecha trainer %name5%. I don't know what kind of history those two have, but you can ask \OPR %5% about it at %name4%.> Msg%id%02 Msg%id%03 STC MS_EmptyBuilding SetID 3 start end faint%5% Persona 5 special greeting GoBeenBefore result1 result2 result3 result4 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 <%name5% taught you a few things about mecha combat.> Msg8 Prompt1 Prompt2 Prompt3 Prompt4 Persona 6 special greeting GoNormalEnding GoGoodEnding GoEnd result1 result2 result3 result4 GoR4Fail result5 result6 Msg1 <\PC ... You are a persistent one, I'll give you that. Unfortunately I am in no mood to fight just now.> Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg1_3 CMsg1_3 Msg1_4 CMsg1_4 Msg1_5 CMsg1_5 Msg1_6 CMsg1_6 Msg2 Msg2_1 Msg2_2 Msg3 Msg3_1 Msg3_2 Msg4 <%name6% leaves the area.> Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 <%name6% expressed regret that the two of you were enemies.> Prompt1 Prompt1_1 Prompt2 Prompt2_1 Prompt3 CPrompt3 Prompt4 Prompt5 CPrompt5 Prompt6 CPrompt6 end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name % Since this is the main combat of this episode, make it a harder than usual. GoStartCombat Secret Msg <%name5% used to be %name6%'s teacher. I think \SPR %5% was always disappointed in the path %name6% chose.> end Content name desc requires <*:CS_GatherInformation E:A.sr_ (E:M.---|E:M.mer) ~&AboutEnemy> changes Size 7 %% The centerpiece of this component is an encounter in which the PC can help %% defend the ex-lover against the ex-partner. It should be possible, if the PC %% plays the cards right, for the encounter to be won without a shot being %% fired. % E1 is the person who tells the PC. % E2 is the ex-lover % E3 is the ex-partner % E4 is an outdoors scene for the duel % E5 is the encounter for the duel % E6 is E3's secret which may be revealed % E7 is the enemy, which must be grabbed because it's going to get modified Element2 Place2 Element3 Place3 Element4 Element5 Place5 <4> Element6 Element7 Place7 % FAIL CONDITIONS % - LocateDuel fails % P%id%01 = Initialization Counter % P%id%02 = Decimation Counter % P%id%03 = Learn Secret Counter % P%id%04 = Have won the encounter % At initialization, move and level both of the characters, then activate % the three subplots. update start .%id2%_%plotid2%_GoDecimation .%id3%_%plotid3%_GoLearnSecret Msg%id%01 %% Debriefing Msg - Win or Lose Msg%id%11 <%name7% has quite the interesting personal life, I'll say that much.> Msg%id%11_1 <%name7% rejected %name2%... Maybe \SPR %2% can help you fight \OPR %7% later on. They say there's no pilot like a jilted lover.> CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 <%name7% was smart to get rid of %name2%. Relationships can only tie a cavalier down.> CMsg%id%11_3 Msg%id%11_4 <%name2% gave up too easily... True love is stopped by nothing, not even thermonuclear weapons!!!> CMsg%id%11_4 Msg%id%11_5 CMsg%id%11_5 Msg%id%11_6 <%name2% got off easy. You should hear about some of the things my exes have done...> CMsg%id%11_6 Msg%id%11_7 <%name7% and %name2%... but the one I'm most concerned about is %name3%. Someone like that is bound to come back and haunt you.> CMsg%id%11_7 %% SubPlot1 is the LocateDuel task %% SubPlot2 is the GainAdvantageVsNPC one %% SubPlot3 is the ability to learn E3's secret SubPlot1 <*:CS_LocateDuel 4 5 2 3> SubPlot2 <*:CS_GainAdvantageVsNPC&Decimation 3> SubPlot3 <*:CS_LearnSecretAboutNPC 6 3> sub Persona 1 .%id%_GoInform Msg%id%01 <%name7%'s ex-lover %name2% is in town; \SPR %2% 's on the run from %name7%'s partner %name3%. I guess their breakup must have been pretty rough.> Msg%id%02 Persona 2 % E2 will speak with the PC after the combat is won; before then, no special message. special greeting GoNotNow GoWin result1 result2 Msg1 Msg2 Msg3 Msg%id%01 Msg%id%02 Prompt1 Prompt2 Persona 3 % E3 will speak with the PC at the beginning of combat. If the PC has learned the secret, it's % possible through a series of skill rolls to cause E3 to flee without firing a shot. special % V1 = One time only counter greeting *GoThemeInfo <*THEME_EXPO&Enemy NA> result1 result2 result3 result4 result5 result6 result7 GoR7Fail result8 Msg1 <\PC , I should have expected to see you here. I suppose you plan on defending %name2%?> Msg2 Msg3 Msg4 Msg5 <%name3% leaves the area.> Msg6 Msg7 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 CPrompt4 Prompt5 <%name7% doesn't even know about this, does \SPR %7% ?> CPrompt5 Prompt6 Prompt7 Prompt8 MetaScene 5 2 % This is where the PC will be able to try defending E2 from E3. Succeed, and % E3 will tell the PC some things about the enemy. Fail, and, well... you fail. MapWidth 50 MapHeight 50 % L1 = Encounter Over Counter % L2 = Initialization Counter Start nu1 nu2 GoLose end Msg1 Msg2 Msg3 Msg4 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 SetEnemy 1 3 % Team 2 is the enemy side. If the PC earned the Decimation advantage, it will consist % only of E3; otherwise it's E3 and a bunch of mecha. Ouch. Deploy ParaX 25 ParaY 25 team 3 SetEnemy 2 SetAlly 1 ParaX 45 ParaY 45 end end inv stc CORE-ACTIVATABLE name <%name2% and %name3%> Secret Msg <%name3% hates %name2% because \SPR %3% was secretly in love with %name7%.> end GH2/series/ARENADATA_Personalities.txt0000644000175000017500000001260211326004535016310 0ustar kaolkaol%% %% Every faction that has "ArenaOK" in its type needs a list of NPCs. These NPCs are used to introduce missions, %% debrief the player, so on and so forth. The role of the NPC is determined by setting its persona number manually %% using the SetPersona command. %% %% It's important that all the NPCs listed here have the correct pictures, traits, and biographical data. %% Skills, equipment, and even faction aren't needed. %% %% Also note where the character can be found in the RPG campaign. %% %% ROLES: %% 1 = Faction Head, usually the PC won't interact with this character much %% 2 = Commander, the person the missions normally come from %% 3 = Mechanic, who will give damage reports %% 4 = Medic, who will give casulty reports %% 5 = Supply, who will tell the PC when new equipment becomes available %% 6 = Information, who will give intel reports and tell about earned rewards Set 2 name inv NPC Corporate Executive name Age 28 job SDL_PORTRAIT SDL_COLORS <234 180 88 150 112 89 199 188 162> Chardesc Female Melancholy Sociable Pragmatic SetPersona 1 NPC Corporate Executive name % ATLAS_L5Region, placed at Comet Tower Age 6 SDL_PORTRAIT SDL_COLORS <234 180 88 255 212 195 1 75 67> Chardesc Male Sociable Easygoing Pragmatic SetPersona 2 NPC Mechanic name % UNICON_Services.txt Age 4 SDL_PORTRAIT SDL_COLORS <234 180 88 255 212 195 166 47 32> Chardesc Female Sociable Passionate Pragmatic SetPersona 3 NPC Corporate Executive name % UNICON_LocalContent, placed at Comet Tower Age 12 job SDL_PORTRAIT SDL_COLORS <234 180 88 255 212 195 50 50 50> Chardesc Male Villainous Sociable Melancholy SetPersona 4 NPC Mecha Pilot name % QUEST_ARENA_Challenger Age 9 job job_desig CharDesc Male Sociable Easygoing Lawful StatLine 13 17 14 13 14 12 9 11 SDL_Portrait SDL_Colors <234 180 88 255 212 195 10 50 100> SetPersona 5 NPC Scientist name Age 4 job SDL_PORTRAIT SDL_COLORS <234 180 88 255 212 195 168 153 230> Chardesc Female Sociable Cheerful Pragmatic SetPersona 6 end Set 3 name inv NPC Knight name % ATLAS_L5Region, placed in Cesena Spinner/Fleet HQ Age 52 job SDL_PORTRAIT SDL_COLORS <150 205 229 235 212 195 130 143 114> chardesc Lawful Heroic Easygoing Shy Cheerful Male Renowned statline 19 22 18 19 14 16 17 15 SetPersona 1 NPC Knight name Age 7 SDL_PORTRAIT SDL_COLORS <150 205 229 255 212 195 240 160 35> Chardesc Female Lawful Heroic Sociable Spiritual SetPersona 2 NPC Mechanic name % UNICON_LocalContent, Placed in Silver Fortress Age 5 SDL_PORTRAIT SDL_COLORS <150 205 229 255 215 195 152 61 97> CharDesc Female Lawful Shy Easygoing Pragmatic SetPersona 3 NPC Cyberdoc name % UNICON_LocalContent, Placed in Silver Sanctum Age 16 SDL_PORTRAIT SDL_COLORS <150 205 229 123 63 0 55 25 81> chardesc Pragmatic Melancholy Heroic Male SetPersona 4 NPC Knight name % UNICON_LocalContent, Placed in Silver Sanctum Age 19 SDL_PORTRAIT SDL_COLORS <150 205 229 150 112 89 103 3 45> chardesc Passionate Shy Cheerful Lawful Heroic Female SetPersona 5 NPC Knight name % UNICON_LocalContent, Placed in Silver Sanctum Age 22 chardesc Spiritual Easygoing Lawful Heroic Male SDL_PORTRAIT SDL_COLORS <150 205 229 255 212 195 65 121 119> job SetPersona 6 end Set 6 name inv NPC Pirate name Age 36 job SetPersona 1 SDL_PORTRAIT SDL_COLORS <20 90 130 205 162 141 166 47 32> chardesc Male Criminal Shy Cheerful Passionate NPC Privateer name % QUEST_MechaArena_Challenger SetPersona 2 Age 5 SDL_PORTRAIT SDL_COLORS <20 90 130 142 62 39 234 180 88> chardesc Male Cheerful Heroic Easygoing StatLine 15 13 15 15 13 13 10 14 NPC Mechanic name Age 11 SDL_PORTRAIT SDL_COLORS <20 90 130 255 212 195 123 63 0> CharDesc Female Heroic Sociable Cheerful SetPersona 3 NPC Doctor name Age 7 SDL_PORTRAIT SDL_COLORS <20 90 130 142 62 39 255 105 180> CharDesc Female Heroic Passionate Spiritual SetPersona 4 NPC Privateer name % UNICON_LocalContent, placed in Maquise Spinner/Privateer Club Age 23 job SetPersona 5 CharDesc Lawful Pragmatic Passionate Male SDL_PORTRAIT SDL_COLORS <20 90 130 150 112 89 168 153 230> NPC Thief name Age 4 job SetPersona 6 CharDesc Male Criminal Heroic Shy Melancholy SDL_PORTRAIT SDL_COLORS <20 90 130 255 212 195 66 121 119> end GH2/series/PLOT_BountyHunt.txt0000644000175000017500000000567211376734116015127 0ustar kaolkaolPlot name requires <*GENERAL> PayRate 75 %% This is a fairly easy bounty hunting job. The target should be captured %% alive- no disintegrations. % E1 is the character offering the mission. % E2 is the town scene % E3 is the target Element1 Element2 Element3 %% P1 = Timer/Initialization Indicator update Start GoDelete % For timeout, delete the target here. The CatchMinorNPC and LoseMinorNPC % subplots will deal with the target on their ends. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss .%id1%_%plotid1%_GoTimeOver Msg%id%01 Msg%id%02 % SubPlot1 is Capture the Target. % SubPlot2 is the Win Task % SubPlot3 is the Lose Task % SubPlot4 is the Time Over condition SubPlot1 <*:BH_CaptureTarget 3> SubPlot2 <*BH_CatchMinorNPC 1 3> SubPlot3 <*BH_LoseMinorNPC 1 3> SubPlot4 <*Util_TimeOver 1> sub Persona 1 rumor0 <%name1% is offering a bounty for a wanted criminal.> &FullRate <* 3 Reward %threat% PayRate> greeting *GoRemind <*GenericMissionReminder> *GoFirstTime <*AutoMissionTest&Chara GoMissionBriefing GoRejectMission GoCheckSkill na na> *GoCheckSkill <*GenericMissionTest&Chara GoMissionBriefing GoEnd GoRejectMission na na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <%name1% hired you to capture %name3%, a wanted criminal.> Prompt1 Prompt1_1 Prompt1_2 Prompt2 Prompt2_1 Prompt2_2 end GH2/series/PLOT_PoliceMissions.txt0000644000175000017500000003133311374513723015736 0ustar kaolkaolPlot name % The PC is off to capture a smuggler's vessel. % As a police job, this misssion doesn't offer salvage but it will % give a point of Lawful reputation. % This job will result in the PC making an enemy. requires <*GENERAL -!Ne -!Lo (sleazy|tradehub)> PayRate 250 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is the enemy faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*POLICEMISSION_Capture 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \SCENE NPCSCene %2% :// Hey \RANK , I've got a mission for you. Give me a call.> Msg1_1 <%name2%@ \SCENE NPCSCene %2% :// \RANK \PC , report to \EXACT_SCENE NPCSCene %2% for a mission at once.> Msg1_2 <%name2%@ \SCENE NPCSCene %2% :// Your services are required by \FACTION NPCFac %2% . Come see me for mission briefing.> Msg1_3 <%name2%@ \SCENE NPCSCene %2% :// I've got a mission for you. Come to \EXACT_SCENE NPCSCene %2% for the briefing.> sub Persona 2 rumor0 <%name2% needs a pilot to aprehend some smugglers.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac %4%> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac %4% %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission GoR1Final ChatNPCFac %4%> GoR1Final result2 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 Msg2 <%name2% in \SCENE NPCSCene %2% hired you to stop some smugglers in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % The basic patrol job is a fight against some generic enemy mecha. % As a police job, this misssion doesn't offer salvage but it will % give a point of Lawful reputation. % This job will not result in the PC making any enemies. requires <*GENERAL -Lawless> PayRate 90 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place Element1 Element2 Element3 % SubPlot1 is the combat encounter SubPlot1 <*POLICEMISSION_Basic 2 3> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \SCENE NPCSCene %2% :// Hey \RANK , I've got a mission for you in %name1%.> Msg1_1 <%name2%@ \SCENE NPCSCene %2% :// Report to \EXACT_SCENE NPCSCene %2% for a mission at once.> Msg1_2 <%name2%@ \SCENE NPCSCene %2% :// Your services are required by \FACTION NPCFac %2% . Come see me for mission briefing.> Msg1_3 <%name2%@ \SCENE NPCSCene %2% :// I've got a mission for you. Come to \EXACT_SCENE NPCSCene %2% for the briefing.> sub Persona 2 rumor0 <%name2% needs a pilot to fight some criminals.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <%name2% in \SCENE NPCSCene %2% hired you to fight some mecha criminals in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % The basic patrol job is a fight against some generic enemy mecha. % As a police job, this misssion doesn't offer salvage but it will % give a point of Lawful reputation. % This job will result in the PC making an enemy. requires <*GENERAL -!Ne -Lawless -Lawful> PayRate 135 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is the enemy faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*POLICEMISSION_Versus 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \SCENE NPCSCene %2% :// Hey \RANK , I've got a mission for you in %name1%.> Msg1_1 <%name2%@ \SCENE NPCSCene %2% :// Report to \SCENE NPCSCene %2% for a mission at once.> Msg1_2 <%name2%@ \SCENE NPCSCene %2% :// Your services are required by \FACTION NPCFac %2% . Come see me for mission briefing.> Msg1_3 <%name2%@ \SCENE NPCSCene %2% :// I've got a mission for you. Come to \SCENE NPCSCene %2% for the briefing.> sub Persona 2 rumor0 <%name2% needs a pilot to fight %name4%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac %4%> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac %4% %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission GoR1Final ChatNPCFac %4%> GoR1Final result2 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 Msg2 <%name2% in \SCENE NPCSCene %2% hired you to fight %name4% in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end GH2/series/MEGA_CORE_Conclusion.txt0000644000175000017500000010536211401151245015645 0ustar kaolkaol%% %% *CS_END_Intro %% %% This is the beginning of the end. The intro serves as the introduction to the %% conclusion, and also the first big fight scene. Following the battle it invokes %% the following subplots: %% *CS_END_Buildup %% *:CS_END_Partner_Arc %% *:CS_END_LoveInterest_Arc %% %% Note that this subplot passes the Enemy NPC on to the final battle, so if redemption %% is going to happen it's going to happen right here. If the PC entered the conclusion %% without an enemy NPC, a new enemy must be created for the final battle and introduced %% right here. %% %% PARAM1: The conclusion mood %% Content name desc requires <*CS_END_Intro E:++> % E1 is the mood % E2 is the enemy NPC % E3 is an outdoors scene for the base % E4 is the base itself Element2 Element3 Element4 Place4 <3> % P%id%01 = Initialization Counter % P%id%02 = Peaceful Entry Counter % At startup, activate the RevealEncounter task. update .%id5%_%plotid5%_GoPeacefulEntry % SubPlot1 is the final battle buildup % This serves the purpose of a RevealEncounter task for the last fight % SubPlot2 is the partner's subplot % SubPlot3 is the love interest's subplot % SubPlot4 is the RevealEncounter for the Enemy's base % SubPlot5 is the peaceful entry for the Enemy's base SubPlot1 <*CS_END_Buildup 1 2> SubPlot2 <*:CS_END_Partner_Arc> SubPlot3 <*:CS_END_LoveInterest_Arc> SubPlot4 <*:CS_RevealEncounter_NPCBase&EnemyNPC 3 4 2> SubPlot5 <*:CS_PeacefulEntry_NPCBase 4 2> sub MetaScene 4 rumor%id% <%name2% is preparing to launch a big operation.> special mapwidth 35 mapheight 35 MonkeyMap LockedDoorChance 10 SecretDoorChance 5 NeededCells 2 % L1 = Have completed task % L2 = Have spotted computer % L3 = Computer UID % L4 = History Message start end tmove1 PCAttack Msg1 Msg2 Msg3 Msg4 Content1 sub Team 1 SetEnemy 2 3 4 Team 2 name type Deploy SetEnemy 1 SetAlly 3 4 Team 3 name home type Deploy SetEnemy 1 SetAlly 2 4 Team 4 name home type Deploy SetEnemy 1 SetAlly 2 3 room name special sub TrapDoor Destination -1 use end room name sub STC COMPUTER-INDESTRUCTIBLE use update Msg1 end end Persona 2 special greeting GoChat *GoGreet <*CS_END_Intro_Greeting GoExplain> *GoExplain <*CS_END_Intro_Explanation> end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name end %% %% *CS_END_Buildup %% %% The fights leading up to (and revealing) the final battle. %% %% PARAM1: The mood %% PARAM2: The enemy NPC %% Content name desc requires <*CS_END_Buildup> % E1 is the mood % E2 is the enemy NPC % E3 is an outdoors scene for the final battle % E4 is the final battle encounter- % to be revealed once all the mini-bosses are done. Element3 Element4 Place4 <3> % P%id%01 = Initialization Counter update .%id1%_%plotid1%_GoWin .%id2%_%plotid2%_GoWin .%id3%_%plotid3%_GoWin Msg%id%01 % SubPlot1 is MiniBoss #1 % SubPlot2 is MiniBoss #2 % SubPlot3 is MiniBoss #3 % SubPlot4 is the final battle SubPlot1 <*:CS_END_MiniBoss 4> SubPlot2 <*:CS_END_MiniBoss 4> SubPlot3 <*:CS_END_MiniBoss 4> SubPlot4 <*CS_END_FinalBattle 1 2 3 4> inv STC CORE-ACTIVATABLE name end %% %% *CS_END_FinalBattle %% %% The final battle. Exactly as advertised. %% %% The enemy mecha forces must be Team 2, since any minibosses previously defeated %% will be added to this team. %% %% NOTE: This component must kill the mood or at least give it a time limit. %% %% PARAM1: The mood %% PARAM2: The enemy NPC %% PARAM3: The outdoor scene where the battle will take place %% PARAM4: The encounter for the final battle %% Content name desc requires <*CS_END_FinalBattle *CORE_FINAL> % E1 is the mood % E2 is the enemy NPC % E3 is an outdoors scene for the final battle % E4 is the final battle encounter % P%id%01 = Initialization Counter update % SubPlot1 is the ending stub; activate this subplot to start the ending. subplot1 <*:CS_END_TheEnd 2> sub MetaScene 4 2 rumor%id% <%name2%'s ship is in %name3%. This could mean the end of everything.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time % L3 = Second deployment happened % L4 = Enemy NPC died MapWidth 50 MapHeight 50 Start GoFightEnemy nu1 nu2 nu3 nu5 Faint%2% end GoPartial GoVictory Msg1 Msg2 Msg3 Msg4 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 sub team 1 SetAlly 4 SetEnemy 2 3 5 ParaX 45 ParaY 25 team 2 name SetEnemy 1 4 SetAlly 3 5 Deploy GoCheckOFF4 GoBigBattle home team 3 name setenemy 1 4 setally 2 5 team 4 name setenemy 2 3 5 setally 1 Deploy GoCheckDef4 GoSmallAllies home team 5 name home Deploy GoCheckOFF4 GoBigBattle setenemy 1 4 setally 2 3 rect name special width 5 Height 48 MFX 1 MFY 2 Content1 rect name special width 5 Height 48 MFX 45 MFY 2 Content1 rect name special width 12 height 12 MFX 6 MFY 19 sub SuperProp requires <*BattleShip> SetTeam 3 end end Persona 2 special greeting *.%id%_GoSecondTime <*Back_For_More> *.%id%_GoGreet <*CS_END_FinalBattle_Greeting .%id%_GoThemeExpo> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end Content name desc requires <*CS_END_FinalBattle *CORE_F_PAL> %% Before the final battle starts, you get a conversation in which to %% argue for peace. Succeed and you activate the special ending. Fail and %% you need to fight everybody as normal, plus you can only get the %% partial victory ending. % E1 is the mood % E2 is the enemy NPC % E3 is an outdoors scene for the final battle % E4 is the final battle encounter % P%id%01 = Initialization Counter update % SubPlot1 is the ending stub; activate this subplot to start the ending. % SubPlot2 is the special ending subplot1 <*:CS_END_TheEnd 2> subplot2 <*:CS_END_Special_PAL 2> sub MetaScene 4 2 rumor%id% <%name2%'s ship is in %name3%. This could mean the end of everything.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time % L3 = Second deployment happened % L4 = Enemy NPC died MapWidth 50 MapHeight 50 Start GoFightEnemy nu1 nu2 nu3 nu5 Faint%2% end GoPartial GoPyrrhic Msg1 Msg2 Msg3 Msg4 Msg5 sub team 1 SetAlly 4 SetEnemy 2 3 5 ParaX 45 ParaY 25 team 2 name SetEnemy 1 4 SetAlly 3 5 Deploy GoCheckOFF4 GoBigBattle home team 3 name setenemy 1 4 setally 2 5 team 4 name setenemy 2 3 5 setally 1 Deploy home team 5 name home Deploy GoCheckOFF4 GoBigBattle setenemy 1 4 setally 2 3 rect name special width 5 Height 48 MFX 1 MFY 2 Content1 rect name special width 5 Height 48 MFX 45 MFY 2 Content1 rect name special width 12 height 12 MFX 6 MFY 19 sub SuperProp requires <*BattleShip> SetTeam 3 end end Persona 2 special % V%id%01 = First time counter % V%id%02 = Final skill roll difficulty counter greeting *.%id%_GoSecondTime <*Back_For_More> %% You get one chance to secure the peace. Let's see how it goes. %% The three bonuses: %% - Have a high Heroism score %% - Have a friend/ally/lover/family on the other side %% - Have the authority to speak now *.%id%_GoGreet <*CS_END_FPAL_Greeting .%id%_GoQuest1 .%id%_GoThemeExpo> *.%id%_GoQuest1 <*CS_END_FPAL_Quest1 .%id%_GoQuest2 .%id%_GoQ1Okay .%id%_GoQ1Fail> .%id%_GoQ1Okay .%id%_GoQ1Fail *.%id%_GoQuest2 <*CS_END_FPAL_Quest2 .%id%_GoQuest3 .%id%_GoQ2Okay .%id%_GoQ2Fail> .%id%_GoQ2Okay .%id%_GoQ2Fail *.%id%_GoQuest3 <*CS_END_FPAL_Quest3 .%id%_GoQ3Good .%id%_GoQ3Okay .%id%_GoQ3Fail> .%id%_GoQ3Good .%id%_GoQ3Okay .%id%_GoQ3Fail %% Question #4 is optional- if the player scored perfect on all tests so far, the %% peace deal is guaranteed. Otherwise the PC needs to make a skill roll to seal the deal. *.%id%_GoQuest4 <*CS_END_FPAL_Quest4 v%id%02 .%id%_GoMakePeace .%id%_GoRejectPeace> *.%id%_GoMakePeace <*CS_END_FPAL_Accept .%id%_GoWinGame> .%id%_GoWinGame *.%id%_GoRejectPeace <*CS_END_FPAL_Reject .%id%_GoThemeExpo> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end %% %% *:CS_END_MiniBoss %% %% The PC has to fight some minibosses before getting to the main boss. Hooray! %% If a miniboss is defeated but not killed, it will be moved to the final battle %% scene and assigned to team 2 there. %% %% If the PC loses the battle, no problem: the fight will repeat every 3-5 hours %% until the PC wins. If the boss NPC dies, this counts as a win even if the PC %% doesn't finish off the lancemates. %% %% The minibosses will be leveled to renown 82, as opposed to the main boss who %% will be leveled to 90. %% %% When this subplot concludes, it sets the following trigger: %% .%id%_%plotid%_GoWin %% %% PARAM1: The final battle scene %% Content name desc requires <*:CS_END_MiniBoss F:++> Size 6 % E1 is the final battle scene % E2 is an outdoors scene for the encounter % E3 is the encounter itself % E4 is the NPC to fight Element2 Element3 Place3 <2> Element4 Place4 <3 (Enemies) SD enemy> NeverFail4 % P%id%01 = Initialization Counter update sub MetaScene 3 2 rumor%id% <%name4% has been looking for you in %name2%.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time MapWidth 50 MapHeight 50 Start nu1 nu2 Faint%4% GoVictory end Msg1 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end Persona 4 special greeting *.%id%_GoSecondTime <*Back_For_More> *.%id%_GoGreet <*BattleChallenge .%id%_GoThemeExpo na> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end inv Encounter name <%name4%'s Lance> % V1 = Recharge Counter % This encounter will appear once every 3-5 hours, as long as this is the active subplot. update GoSetTimer GoSetOrders use GoAutoAttack GoAvoidAttack end Content name desc requires <*:CS_END_MiniBoss> Size 6 % E1 is the final battle scene % E2 is an outdoors scene for the encounter % E3 is the encounter itself % E4 is the NPC to fight Element2 Element3 Place3 <2> Element4 Place4 <3 (Enemies) SD enemy> % P%id%01 = Initialization Counter update sub MetaScene 3 2 rumor%id% <%name4% is hunting for you in %name2%.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time MapWidth 50 MapHeight 50 Start nu1 nu2 Faint%4% GoVictory end Msg1 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end Persona 4 special greeting *.%id%_GoSecondTime <*Back_For_More> *.%id%_GoGreet <*BattleChallenge .%id%_GoThemeExpo na> *.%id%_GoThemeExpo <*THEME_EXPO&Enemy na> end inv Encounter name <%name4%'s Lance> % V1 = Recharge Counter % This encounter will appear once every 3-5 hours, as long as this is the active subplot. update GoSetTimer GoSetOrders use GoAutoAttack GoAvoidAttack end %% %% *:CS_END_RallyDefenses %% %% The PC will be able to get some help for the final battle here. This task is %% based on the +T context, so if the PC is going to go talk with somebody then %% that's what should happen. %% %% Upon rallying the defenses, this plot will set the following trigger: %% .%id%_%plotid%_GoRallyDefenses %% Content name requires <*:CS_END_RallyDefenses (+T--|+Tgs|T:--)> desc %% E1 is a public scene for the meeting %% E2 is the person to be met %% E3 is an urban scene for the armory Element1 Element2 Place2 <1 (Guards) pass> Element3 % if E2 died, that cancels this subplot, but if the armory liberation has already % been started that can still succeed. start % Winning the armor liberation will give the RallyDefenses advantage, % regardless of whether E1 is alive or dead. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%04 % SubPlot1 is the armory liberation SubPlot1 <*:CS_CaptureBuilding 3> sub Persona 2 rumor%id% <%name2% has been leading the resistance movement.> greeting GoCheckLoss GoCheckMission GoCheckFirst GoChat *result1 <*CS_END_StatusReport result2> result2 Msg1 Msg1_1 CMsg1_1 Msg1_3 CMsg1_3 Msg2 Msg2_1 CMsg2_1 Msg2_3 CMsg2_3 Msg3 Msg3_1 CMsg3_1 Msg3_3 CMsg3_3 Msg4 Msg4_1 <\PC , it's a good thing you're here. \PERSONA &EnemyNPC has taken control and we need your help to defeat \OPR &EnemyNPC .> CMsg4_1 Msg4_2 CMsg4_2 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg6 <%name2% revealed that the armory had been captured.> Msg7 Prompt1 Prompt1_1 Prompt2 Prompt2_1 end Content name requires <*:CS_END_RallyDefenses +Tgt (+Fmi|T:PCFAC) T:++> desc %% E1 is a public scene for the meeting %% E2 is the person to be met %% E3 is an urban scene for the armory Element1 Element2 Place2 <1 (Citizens) pass> Element3 % if E2 died, that cancels this subplot, but if the armory liberation has already % been started that can still succeed. start % Winning the armor liberation will give the RallyDefenses advantage, % regardless of whether E1 is alive or dead. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%04 % SubPlot1 is the armory liberation SubPlot1 <*:CS_CaptureBuilding 3> sub Persona 2 greeting GoCheckLoss GoCheckMission GoCheckFirst GoChat *result1 <*CS_END_StatusReport result2> result2 Msg1 Msg1_1 CMsg1_1 Msg1_3 CMsg1_3 Msg2 Msg2_1 CMsg2_1 Msg2_3 CMsg2_3 Msg3 Msg3_1 CMsg3_1 Msg3_3 CMsg3_3 Msg4 <\PC , thank goodness you're here. Things are falling apart in \SCENE &EpisodeScene .> Msg4_1 <\PC , am I ever glad to see you. \PERSONA &EnemyNPC 's gone on a rampage; the entire city has been deadlocked.> CMsg4_1 Msg4_2 <\PC , you've come at just the right time. This city is under siege by \FACTION &EnemyFac ; we are completely at their mercy.> CMsg4_2 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg6 <%name2% revealed that the armory had been captured.> Msg7 Prompt1 Prompt1_1 Prompt2 Prompt2_1 end Content name requires <*:CS_END_RallyDefenses +Tgt -+Fmi -+Ffi -+Far T:++> desc %% E1 is a public scene for the meeting %% E2 is the person to be met %% E3 is an urban scene for the armory Element1 Element2 Place2 <1 (Citizens) pass> Element3 % if E2 died, that cancels this subplot, but if the armory liberation has already % been started that can still succeed. start % Winning the armor liberation will give the RallyDefenses advantage, % regardless of whether E1 is alive or dead. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%04 % SubPlot1 is the armory liberation SubPlot1 <*:CS_CaptureBuilding 3> sub Persona 2 greeting GoStartMission GoChat *result1 <*CS_END_StatusReport GoStartMission> Msg1 <\PC , I'm glad to see you but I'm afraid it's not a good time to be visiting \SCENE &EpisodeScene .> Msg1_1 <\PC ! I didn't expect you to come here, not after what \PERSONA &EnemyNPC did... it's not a good time to be in \SCENE &EpisodeScene .> CMsg1_1 Msg1_2 <\PC , I didn't expect you to make it. This isn't a good time to be in \SCENE &EpisodeScene if you're an enemy of \FACTION &EnemyFac .> CMsg1_2 Msg2 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 Msg3 <%name2% revealed that the armory had been captured.> Msg4 Prompt1 Prompt1_1 end %% %% *:CS_END_Partner_Arc %% %% The partner and the PC can do something together before the end of the game, %% hopefully completing the partner's story arc. %% Content requires <*:CS_END_Partner_Arc> Content name requires <*:CS_END_Partner_Arc R:--> desc %% %% *:CS_END_LoveInterest_Arc %% %% The love interest can see the PC one last time before the end of the game. %% Maybe they can finally get that date? Or this might not be a good time... %% Content requires <*:CS_END_LoveInterest_Arc> Content name requires <*:CS_END_LoveInterest_Arc A:--> desc GH2/series/QUEST_Concert.txt0000644000175000017500000006671211374513723014527 0ustar kaolkaol%% %% Concert Quest Series %% %% %% The PC wants to play a concert at one of the big venues, but first has to %% work his way up and maybe perform some sub-quests. %% %% *:Q_Concert_Venue Content %% %% Contains a manager for a concert venue and the "goal" concert. %% %% The venue manager's persona activates the subquests. %% %% Needed Macros: %% &WinStep Called when the current step is completed %% &LoseStep [threat] Called when a step is rendered incompletable Content name requires <*:Q_Concert_Venue "ATHSP"> % Element1 is the venue manager % Element2 is the venue Element1 Place1 <2 (Citizens) Pass> Element2 % If Phivos dies, cancel everything. end SubPlot1 <*:Q_Concert_Step #30 1> SubPlot2 <*:Q_Concert_Step #42 1> SubPlot3 <*:Q_Concert_Step #54 1> SubPlot4 <*:Q_Concert_Step #66 1> SubPlot5 <*Q_Concert_Final #78 1> sub Persona 1 % V1 = Completed stage % V2 = Delay time % V3 = Delay renown % V4 = Stage Renown &WinStep &LoseStep greeting GoNotReady GoNeedFame GoCheckOffer GoChat GoStep1 GoStep2 GoStep3 GoStep4 GoConclude Result1 Result2 Result3 Result4 Result5 Msg1 Msg2_1 Msg2_2 CMsg2_2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 <%name1% said you should take a break from performing before starting your next gig.> Msg9 Msg10 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 end inv NPC Celebrity name job home SDL_PORTRAIT SDL_COLORS <201 205 229 240 199 120 244 216 28> Age 4 Chardesc Male Renowned end %% %% *Q_Concert_Final Content %% %% Each concert venue above should have a matching Concert_Final subquest to represent %% the eventual concert at the big venue and its associated reward. %% %% Param1: Venue Manager %% %% Each *Concert_Final subquest needs a .%id%_GoInit script for the venue manager. %% Content name requires <*Q_Concert_Final "ATHSP"> % E1 is Phivos the producer. % E2 is the trophy for completing the concert. % E3 is the meme. Element2 Place2 Element3 sub Persona 1 % V%id%01 = Delay Time % V%id%02 = Delay Renown % V%id%03 = Concert Result % V%id%04 = Audience randomizer greeting .%id%_GoInit .%id%_GoDelay .%id%_GoRedeem .%id%_GoWinMission .%id%_GoLoseMission Result%id%01 Result%id%02 Result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Msg%id%11_1 CMsg%id%11_1 Msg%id%11_2 CMsg%id%11_2 Msg%id%11_3 CMsg%id%11_3 Msg%id%12 Prompt%id%01 Prompt%id%02 Prompt%id%03 <[Begin Concert]> end inv Instrument 4 name desc Category sub BeamGun 5 name mass -4 Speed 4 PowerSource 3 mass -5 end Meme MaxMemeViews 3 MemeTimeLimit Msg Msg_1 CMsg_1 Msg_2 CMsg_2 Msg_3 <> CMsg_3 Msg_4 <\PC ! Can I get your autograph? That concert you had was fantastic.> CMsg_4 Msg_5 <> CMsg_5 Msg_6 CMsg_6 Msg_7 CMsg_7 Msg_8 CMsg_8 end %% *:Q_Concert_Step Content %% %% Contains a subquest that must be completed before the next stage can %% be activated. Often this will be a concert that must be performed somewhere, %% but may be another type of adventure instead. %% %% If a task fails, it can be repeated after 24 hours. If a task is made %% incompletable (due to the death of a required NPC or whatever else) then %% the venue manager should do a Renown-based delay before the next task. %% %% Set PlotStatus to -1 for successful completion, -2 for irrevocable failure, %% and -3 for resolved with the venue manager. %% %% PARAM1: Venue Manager %% %% Each *:Q_Concert_Step subquest needs a .%id%_GoInit script for the venue manager. Content name desc requires <*:Q_Concert_Step (!Ne|!Lo)> % E1 is the venue manager % E2 is the shopkeeper % E3 is the shop Element2 Place2 <3 (Citizens) Pass> Element3 end sub Persona 1 Greeting .%id%_GoInit .%id%_GoAbort .%id%_GoConclude .%id%_GoRemind Result%id%01 Result%id%02 Msg%id%01 Msg%id%02 <%name1% sent you to do some advertising for %name2% at %name3%.> Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%02 Persona 2 % V1 = Mission recharge timer % V2 = Concert Result % V3 = First Time Counter greeting *GoGreet <*NiceToMeetYou GoShop> *GoShop <*SHOP_GENERAL GoBye> *GoBye <*GOODBYE> *GoBusy <*IAmBusy&OnPhone> GoDelay *GoFirstTime <*SomeoneSentPCToTalk&ForJob %1% GoExplain> GoExplain GoWinMission GoLoseMission Result1 Result2 Result3 Result4 Result5 Result6 Result7 Result8 Result9 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 <[Begin Concert]> Metascene 2 sub room minimap <.#...+#.......1.....&---&> special desig end end inv NPC Shopkeeper Chardesc Easygoing Sociable end Content name desc requires <*:Q_Concert_Step !Md> % E1 is the venue manager % E2 is the nurse % E3 is the hospital Element2 Place2 <3 (Citizens) Pass> Element3 end sub Persona 1 Greeting .%id%_GoInit .%id%_GoAbort .%id%_GoConclude .%id%_GoRemind Result%id%01 Result%id%02 Msg%id%01 Msg%id%02 <%name1% sent you to do a charity show at %name3%. %name2% is organizing the event.> Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 Persona 2 % V1 = Mission recharge timer % V2 = Concert Result % V3 = First Time Counter % V4 = Difficulcy Rating greeting *GoGreet <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> *GoBusy <*IAmBusy&OnPhone> GoDelay *GoFirstTime <*SomeoneSentPCToTalk&ForJob %1% GoExplain> GoExplain GoWinMission GoLoseMission Result1 Result2 Result3 Result4 Result5 Result6 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 <%name1% told me you'd probably say that. Shall we begin the performance right now or wait for later?> Msg11 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 CPrompt5 Prompt6 <[Begin Concert]> end inv NPC Nurse end Content name desc requires <*:Q_Concert_Step !Md> % E1 is the venue manager % E2 is the DJ at the club % E3 is the nightclub Element2 Place2 <3 (Citizens) Pass> Element3 end sub Persona 1 Greeting .%id%_GoInit .%id%_GoAbort .%id%_GoConclude .%id%_GoRemind Result%id%01 Result%id%02 Msg%id%01 Msg%id%02 <%name1% sent you to perform at %name3%.> Msg%id%03 Msg%id%04 <%name2% was quite impressed with your performance. You should know that \SPR %2% isn't impressed very often. Come back tomorrow and I'll see if I have another gig for you.> Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 Persona 2 % V1 = Mission recharge timer % V2 = Concert Result % V3 = First Time Counter greeting *GoGreet <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> *GoBusy <*IAmBusy&OnPhone> GoDelay GoFirstTime GoExplain GoWinMission GoLoseMission Result1 Result2 Result3 Result4 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt2 Prompt3 Prompt4 <[Begin Concert]> Metascene 2 sub room minimap <######...##.1.##...##---#> special desig end end inv NPC Celebrity job Chardesc Sociable Cheerful Renowned end Content name desc requires <*:Q_Concert_Step (!Hi|!Ex)> % E1 is the venue manager % E2 is the event organizer % E3 is the park Element2 Place2 <3 (Citizens) Pass> Element3 end sub Persona 1 Greeting .%id%_GoInit .%id%_GoAbort .%id%_GoConclude .%id%_GoRemind Result%id%01 Result%id%02 Msg%id%01 Msg%id%02 <%name1% sent you to perform at %name3%. %name2% is organizing the concert.> Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 Persona 2 % V1 = Mission recharge timer % V2 = Concert Result % V3 = First Time Counter % V4 = Concert size randomizer greeting *GoGreet <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> *GoBusy <*IAmBusy&OnPhone> GoDelay *GoFirstTime <*SomeoneSentPCToTalk&ForJob %1% GoExplain> GoExplain GoWinMission GoLoseMission Result1 Result2 Result3 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Prompt1 Prompt2 Prompt3 <[Begin Concert]> end inv NPC Citizen Chardesc Melancholy Sociable end GH2/series/MEGA_CORE_MAIN_StopNPC.txt0000644000175000017500000013054211365256063015636 0ustar kaolkaol%% *:CS_StopNPCMission Content %% %% &IsEnemyNPC The character to be stopped is the enemy NPC %% &Secondary Is a secondary combat; disallow this tag if calling %% a second main course subplot %% %% Somebody is going on a mission. It's the PC's job to locate that %% person and stop them from completing the mission. Most of these %% components are timed- if the PC doesn't discover and halt the mission %% in time, it counts as a loss. %% %% This subplot may alter the story context. %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% It will also hide the encounter with SetEncounterInactive. %% %% This is a MAIN COURSE subplot. Favored by: POLIC %% %% %% Param1: The NPC to be stopped %% %% ******************************** %% *** +P-- Peaceful Life *** %% ******************************** %% **************************** %% *** +Pme Meet Enemy *** %% **************************** %% ***************************************** %% *** +Pun Unknown Enemy Attacks *** %% ***************************************** %% ************************************* %% *** +Pla Learn of Artifact *** %% ************************************* Content name desc requires <*:CS_StopNPCMission &IsEnemyNPC E:M.--- -E:A.nme +Pla F:++ -F:POLIC I:++ ~+Gmo> changes Size 8 % E1 is the NPC to be stopped % E2 is an outdoor scene for the encounter % E3 is the encounter itself % E4 is the core story item Element2 Element3 Place3 <2> Element4 HINT_%id% <:%id1%> % P%id%01 = Initialization Counter % P%id%02 = Decimation Counter % P%id%03 = Reinforcements Counter %% FAIL CONDITIONS %% - SubPlot1 (DiscoverNPCMission) Lost update end .%id2%_%plotid2%_GoDecimation .%id2%_%plotid2%_GoReinforcements %% SubPlot1 = Discover the NPC's mission %% SubPlot2 = Gain advantage vs NPC SubPlot1 <*:CS_DiscoverNPCMission&EnemyNPC&Searching 2 3 1> SubPlot2 <*:CS_GainAdvantageVsNPC&Decimation&Reinforcements 1> sub MetaScene 3 2 %% This battle starts with an offer from the enemy: switch sides and %% share the wealth. % L1 = Encounter Over Counter % L2 = Initialization Counter special MapWidth 50 MapHeight 50 Start nu1 nu2 nu4 GoOddWin GoWinMission end Msg1 Msg2 Msg3 Msg4 Msg5 <%name1% flees the battlefield.> Msg6 Msg7 <%name1% is down! Let's get the blazes out of here!> Msg8 <%name1%'s lancemates flee the battlefield.> sub team 1 setally 3 SetEnemy 2 4 ParaX 5 ParaY 10 team 2 name SetEnemy 1 3 SetAlly 4 Deploy GoBigDeploy ParaX 45 ParaY 45 team 3 name setally 1 setenemy 2 4 Deploy ParaX 10 ParaY 5 team 4 name SetEnemy 1 3 SetAlly 4 ParaX 45 ParaY 45 end Persona 1 special % V1 = First time counter greeting GoChat result1 result2 GoR2_II result3 result4 result5 result6 GoR6Conclude .fac .next <+T-- +F--> *result7 <*THEME_EXPO&Enemy na> 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 Msg2 Msg3 Msg4 Msg5 Msg5_1 Msg6 Msg7 Msg7_1 Msg8 Msg9 Msg10 Prompt1 <[Continue]> Prompt2 Prompt3 Prompt4 Prompt5 Prompt6 Prompt7 <[Continue]> end inv stc CORE-ACTIVATABLE name <%name1%'s Lance> end %% **************************************** %% *** +Pew Enemy Weapon Program *** %% **************************************** %% ******************* %% *** GENERAL *** %% ******************* Content name desc requires <*:CS_StopNPCMission &IsEnemyNPC (E:A.hat|E:A.mut) (E:M.---|E:M.rev|E:M.nih) F:++ -!Ne -!Lo -&Secondary> changes Size 10 % E1 is the NPC to be stopped % E2 is an outdoors scene for the first McGuffin encounter % E3 is the McGuffin encounter % E4 is the enemy faction Place1 Element2 Element3 Place3 <2> Element4 HINT_%id% <:%id2%> % P%id%01 = Initialization Counter %% FAIL CONDITIONS: %% - Reveal Encounter (SP2) fails %% - Either of the two combats are lost % At the beginning, activate the McGuiffin encounter and its locator. update end .%id%_GoLoseMission % Failure in either of the sub-missions results in the failure of the main mission. .%id1%_%plotid1%_GoLoss .%id2%_%plotid2%_GoLoss .%id1%_%plotid1%_GoWin .%id3%_%plotid3%_GoWin Msg%id%01 <%name1% challenged you to stop \OPR %1% from attacking a location in town; \SPR %1% did not appear to be acting rationally.> Msg%id%02 <%name1% challenged you to stop \OPR %1% .> Msg%id%03 Msg%id%04 <%name1% claimed that you are soul-foes, destined to kill one another.> % SubPlot1 is a McGuffin combat encounter used to set up the rest of the plot % SubPlot2 is the task to reveal the above encounter % SubPlot3 is the attack the PC must prevent SubPlot1 <*:CS_MechaEncounter&Secondary 2 3> SubPlot2 <*:CS_DiscoverNPCMission&EnemyNPC 2 3 1> SubPlot3 <*:CS_StopAttack 4> sub Persona 1 special greeting result%id%01 result%id%02 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 end inv stc CORE-ACTIVATABLE name <%name1%'s Lance> end Content name desc requires <*:CS_StopNPCMission &IsEnemyNPC E:A.hat (E:M.---|E:M.mer|E:M.com|E:M.rev|E:M.nih) +H-- -!Ne> changes Size 8 % E1 is the NPC to be stopped % E2 is an urban scene for the base % E3 is the base % E4 is the friend % E5 is the friend's stand-in, a VICTIM prop % E6 is a local hospital scene % E7 is the secret Element2 Element3 Place3 <2> Element4 Place4 Element5 Place5 <3> Element6 Element7 HINT_%id% <:%id1%> %% FAIL CONDITIONS %% - TellSecret fails %% - RevealEncounter fails % P%id%01 = Initialization Counter % P%id%02 = Time Limit % P%id%03 = Gain peaceful entry counter start .%id%_GoCheckSP2 % At first, start the Tell Secret subplot. update % When the secret is revealed set the timer and start the Reveal Base subplot. % Oh, and change the attitude to mutual hatred. Can't forget that. .%id1%_%plotid1%_GoLearnSecret % If the PC gains peaceful entry, that's a good thing. .%id3%_%plotid3%_GoPeacefulEntry Msg%id%01 Msg%id%02 <%name1% has kidnapped %name4%; \SPR %1% is likely planning to torture \OPR %4% for information about you.> Msg%id%03 % SubPlot1 is the FindSecret: this one starts the timer and the rest of the plot. % SubPlot2 is the locate base task % SubPlot3 is the peaceful entry task SubPlot1 <*:CS_TellSecretAboutNPC&LearnPlans&IsEnemy 7 1> SubPlot2 <*:CS_RevealEncounter_NPCBase&EnemyNPC&Kidnapping 2 3 1> SubPlot3 <*:CS_PeacefulEntry_NPCBase 3 1> sub MetaScene 3 mapwidth 35 mapheight 35 MonkeyMap LockedDoorChance 10 SecretDoorChance 5 IndustrialTiles NeededCells 3 % L1 = Friend has died start end GoCheckWin % If the PC attacks at all after gaining peaceful entry, the guards will become % hostile again. PCAttack Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 3 Team 2 name type SetEnemy 1 Deploy Team 3 name home type SetEnemy 1 Deploy room name special sub TrapDoor Destination -1 use end room desig minimap <......###..#1=..###......> end Persona 4 special greeting Msg1 end inv STC CORE-ACTIVATABLE-STATIONARY-DEFENDED name STC VICTIM name <%name4%> use GoFriendDied CLUE_MEDICINE Pass -100 GoSaveFriend .next <+Hrt> Msg1 Msg2 Msg3 Secret Msg <%name1% has captured %name4%. As far as I know, it's because \SPR %1% wants to learn about your weaknesses.> end Content name desc requires <*:CS_StopNPCMission &Secondary &IsEnemyNPC> % E1 is the NPC to be stopped % E2 is an outdoors scene % E3 is an encounter for the mecha encounter Element2 Element3 Place3 <2> HINT_%id% <:%id2%> %% FAIL CONDITIONS %% - DiscoverNPCMission fails % P%id%01 = Initialization Counter update end % Pass the results of the mecha encounter backwards to whatever subplot called this one. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 is the mecha encounter % SubPlot2 is the DiscoverNPCMission task SubPlot1 <*:CS_MechaEncounter&Raiders&Secondary 2 3> SubPlot2 <*:CS_DiscoverNPCMission&EnemyNPC&GatheringForces 2 3 1> inv stc CORE-ACTIVATABLE name <%name1%'s Forces> end Content name %% Based on the above component, but different. desc requires <*:CS_StopNPCMission &IsEnemyNPC E:++ ~F:++ ~P:++ -&Beancounter> % E1 is the NPC to be stopped % E2 is an outdoors scene % E3 is an encounter for the mix encounter Element2 Element3 Place3 <2> HINT_%id% <:%id2%> %% FAIL CONDITIONS %% - DiscoverNPCMission fails % P%id%01 = Initialization Counter update end % Pass the results of the mecha encounter backwards to whatever subplot called this one. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 is the mix encounter % SubPlot2 is the DiscoverNPCMission task SubPlot1 <*:CS_MIX_Encounter 2 3 1> SubPlot2 <*:CS_DiscoverNPCMission&EnemyNPC&GatheringForces 2 3 1> inv stc CORE-ACTIVATABLE name <%name1%'s Forces> end Content name desc requires <*:CS_StopNPCMission -&IsEnemyNPC &Secondary> Size 8 % E1 is the NPC launching the attack % E2 is an urban scene for placing the factory % E3 is the metascene for the mission Element2 Element3 Place3 <2> HINT_%id% <:%id1%> %% FAIL CONDITIONS %% - The DiscoverNPCMission subplot fails %% - The timer runs out before combat is entered % p%id%01 = Initialization Counter % p%id%02 = Timer (24 to 30 hours) % p%id%03 = Reinforcements Counter % At initialization, set the timer and activate the two subplots. update HalfHour .%id%_GoDistant end .%id2%_%plotid2%_GoReinforcements Msg%id%01 Msg%id%02 <\SCENE &EpisodeScene is rocked as %name1%'s lance destroys its target.> Msg%id%03 % SubPlot1 is the DiscoverNPCMission task % SubPlot2 is the GainAdvantage_VsNPC task SubPlot1 <*:CS_DiscoverNPCMission&EnemyNPC&DestroyFactory 2 3 1> SubPlot2 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 3 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 CityBlockMap terrain % At startup, clear the timer so the mission doesn't timeout while the PC is defending % the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission end Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 SetAlly 3 4 ParaX 7 ParaY 7 Team 2 SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 SetAlly 1 4 team 4 setally 1 3 setenemy 2 Deploy ParaX 25 ParaY 7 rect name desig sub SuperProp requires <*Fortress> SetTeam 3 end end Persona 1 special greeting *GoGreet <*BattleChallenge GoThemeExpo na> *GoThemeExpo <*THEME_EXPO&Enemy na> end inv stc CORE-ACTIVATABLE name <%name1%'s Goal> end Content name desc requires <*:CS_StopNPCMission &IsEnemyNPC E:A.equ (E:M.com|E:M.ggd)> changes % E1 is the NPC launching this attack % E2 is an outdoors scene for the encounters % E3 is an encounter for the supposed mission- the PC completes this one first % E4 is the encounter for the ambush- after the PC wins E3 % E5 is a friend/lover/ally to come to the rescue % E6 is a second one Place1 Element2 Element3 Place3 <2> Element4 Place4 <2> Element5 Place5 <4 (Allies) sd ally> Element6 Place6 <4 (Allies) sd ally> HINT_%id% <:%id2%> % P%id%01 = Initialization Counter % At initialization, activate all the subplots and level the NPCs. update % Upon the Mecha Encounter subplot being won, this subplot's ambush will be activated. % If the sub-encounter is lost, this plot is lost without needing an ambush. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss % SubPlot1 is the combat encounter that serves as the supposed mission % SubPlot2 is the locate encounter thingamabob SubPlot1 <*:CS_MechaEncounter&Secondary&Raiders 2 3> SubPlot2 <*:CS_DiscoverNPCMission&EnemyNPC&GatheringForces 2 3 1> sub Persona 1 special greeting GoEndConversation result1 result2 Msg1 Msg2 Prompt1 Prompt2 Metascene 4 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start end nu1 nu2 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 <%name1%'s lance has been defeated.> Msg9 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetAlly 1 SetEnemy 2 ParaX 10 ParaY 5 end end inv stc CORE-ACTIVATABLE name <%name1%'s Forces> STC CORE-MECHAAMBUSH-ACTIVATABLE name end Content name desc requires <*:CS_StopNPCMission ~&IsEnemyNPC (!Ne|!Lo) &Secondary> Size 8 % E1 is the NPC launching the attack % E2 is an urban scene for placing the factory % E3 is the metascene for the mission Element2 Element3 Place3 <2> HINT_%id% <:%id1%> %% FAIL CONDITIONS %% - The DiscoverNPCMission subplot fails %% - The timer runs out before combat is entered % p%id%01 = Initialization Counter % p%id%02 = Timer (24 to 30 hours) % p%id%03 = Reinforcements Counter % At initialization, set the timer and activate the two subplots. update HalfHour .%id%_GoDistant end .%id2%_%plotid2%_GoReinforcements Msg%id%01 Msg%id%02 <\SCENE &EpisodeScene is rocked as %name1%'s lance destroys its target.> Msg%id%03 % SubPlot1 is the DiscoverNPCMission task % SubPlot2 is the GainAdvantage_VsNPC task SubPlot1 <*:CS_DiscoverNPCMission&EnemyNPC&DestroyFactory 2 3 1> SubPlot2 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 3 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 CityBlockMap terrain % At startup, clear the timer so the mission doesn't timeout while the PC is defending % the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission end Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 SetAlly 3 4 ParaX 7 ParaY 7 Team 2 SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 SetAlly 1 4 team 4 setally 1 3 setenemy 2 Deploy ParaX 25 ParaY 7 rect name desig sub SuperProp requires <*Fortress> SetTeam 3 end end end inv stc CORE-ACTIVATABLE name <%name1%'s Goal> end Content name desc requires <*:CS_StopNPCMission &IsEnemyNPC E:A.nme> changes % E1 is the new enemy % E2 is the metascene to be used for this encounter % E3 is the scene in which to place it Place1 <2 (Enemies) SD Enemy> Element2 Place2 <3> Element3 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements update %% FAIL CONDITIONS: %% - RevealEncounter subplot fails. end .%id1%_%plotid1%_GoReinforcements Msg%id%01 <%name1% has gotten away.> %% Hint Redirection HINT_%id% <:%id2%> % SubPlot1 = Gain Advantage vs Raiders % SubPlot2 = Discover NPC mission SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements> SubPlot2 <*:CS_RevealEncounter_Raiders 3 2> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end GoFreeze Msg1 Msg2 Msg3 Msg4 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg6 <%name1% defeated you.> Msg7 Msg8 Msg8_1 CMsg8_1 Msg8_2 CMsg8_2 Msg9 <%name1%'s cockpit is mysteriously empty. Could \SPR %1% have survived?> sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end Persona 1 special greeting *result%id%01 <*THEME_EXPO&Enemy na> result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end inv stc CORE-ACTIVATABLE name <%name1%'s Lance> end Content name desc requires <*:CS_StopNPCMission -&IsEnemyNPC E:--> changes % E1 is the new enemy % E2 is the metascene to be used for this encounter % E3 is the scene in which to place it Place1 Element2 Place2 <3> Element3 % P%id%01 = Initialization Counter % P%id%02 = Have gained reinforcements update %% FAIL CONDITIONS: %% - RevealEncounter subplot fails. end .%id1%_%plotid1%_GoReinforcements Msg%id%01 <%name1% has gotten away.> %% Hint Redirection HINT_%id% <:%id2%> % SubPlot1 = Gain Advantage vs Raiders % SubPlot2 = Discover NPC mission SubPlot1 <*:CS_GainAdvantageVsMecha&Reinforcements> SubPlot2 <*:CS_DiscoverNPCMission&EnemyNPC&DestroyFactory 3 2 1> sub MetaScene 2 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2_1 Msg2_2 Msg2_3 <%name1% doesn't like senseless violence, so \SPR %1% left as soon as \PPR %1% mission was done. The rest of us, on the other hand, can't get enough of the stuff...> CMsg2_3 Msg2_4 CMsg2_4 Msg2_5 CMsg2_5 Msg3 Msg4 Msg5 Msg6 sub team 1 setally 3 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 setally 1 setenemy 2 Deploy ParaX 10 ParaY 5 end end inv stc CORE-ACTIVATABLE name <%name1%'s Goal> end Content name desc requires <*:CS_StopNPCMission -&IsEnemyNPC E:-- (!Ne|!Lo)> changes % E1 is the NPC launching the attack % E2 is an urban scene for placing the factory % E3 is the metascene for the mission Place1 <3 (Enemies) SD enemy> Element2 Element3 Place3 <2> HINT_%id% <:%id1%> %% FAIL CONDITIONS %% - The DiscoverNPCMission subplot fails %% - The timer runs out before combat is entered % p%id%01 = Initialization Counter % p%id%02 = Timer (24 to 30 hours) % p%id%03 = Reinforcements Counter % P%id%04 = Combat Started % At initialization, set the timer and activate the two subplots. % If the timer runs out, set E1 as the core story enemy + set attitude update HalfHour .%id%_GoDistant end .%id2%_%plotid2%_GoReinforcements Msg%id%01 Msg%id%02 <\SCENE &EpisodeScene is rocked as %name1%'s lance destroys its target.> Msg%id%03 % SubPlot1 is the DiscoverNPCMission task % SubPlot2 is the GainAdvantage_VsNPC task SubPlot1 <*:CS_DiscoverNPCMission&EnemyNPC&DestroyFactory 2 3 1> SubPlot2 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 3 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 CityBlockMap terrain % At startup, clear the timer so the mission doesn't timeout while the PC is defending % the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission end Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 SetAlly 4 ParaX 7 ParaY 7 Team 2 name SetEnemy 1 4 Deploy ParaX 45 ParaY 45 Team 3 SetAlly 1 4 team 4 setally 1 setenemy 2 Deploy ParaX 25 ParaY 7 rect name desig sub SuperProp requires <*Fortress> SetTeam 3 end end Persona 1 greeting .%id%_GoCombatGreet *result%id%01 <*THEME_EXPO&Enemy na> Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_11 CMsg%id%02_11 Msg%id%02_12 CMsg%id%02_12 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 end inv stc CORE-ACTIVATABLE name <%name1%'s Goal> end %% %% *:CS_StopAttack Content %% %% &IsEnemyFac The faction to be stopped is the core story enemy %% %% A faction is planning to attack something. The PC will have 24 hours to find %% out what and prevent the attack. This is kind of like the above subplot type %% but involves a faction rather than an NPC and is not a main course. Generally, %% this subplot will be invoked when a threat is discovered, and may well form %% the primary combat for an episode. %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% %% PARAM1: The faction behind the attack %% Content name desc requires <*:CS_StopAttack> Size 8 % E1 is the faction launching the attack % E2 is an urban scene for placing the factory % E3 is the metascene for the mission Element2 Element3 Place3 <2> %% FAIL CONDITIONS %% - The DiscoverNPCMission subplot fails %% - The timer runs out before combat is entered % p%id%01 = Initialization Counter % p%id%02 = Timer (24 to 30 hours) % p%id%03 = Reinforcements Counter % At initialization, set the timer and activate the two subplots. update HalfHour .%id%_GoDistant end .%id2%_%plotid2%_GoReinforcements Msg%id%01 Msg%id%02 <\SCENE &EpisodeScene is rocked as mecha from %name1% destroy their target.> Msg%id%03 % SubPlot1 is the DiscoverNPCMission task % SubPlot2 is the GainAdvantage_VsNPC task SubPlot1 <*:CS_RevealEncounter_FactionTarget&IsEnemy&OfAttack 2 3 1> SubPlot2 <*:CS_GainAdvantageVsMecha&Reinforcements> sub MetaScene 3 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 CityBlockMap terrain % At startup, clear the timer so the mission doesn't timeout while the PC is defending % the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission end Msg1 Msg2 Msg3 Msg4 Msg5 sub Team 1 SetEnemy 2 SetAlly 3 4 ParaX 7 ParaY 7 Team 2 SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 SetAlly 1 4 team 4 setally 1 3 setenemy 2 Deploy ParaX 25 ParaY 7 rect name desig sub SuperProp requires <*Fortress> SetTeam 3 end end end inv stc CORE-ACTIVATABLE name end GH2/series/PLOT_CityMissions.txt0000644000175000017500000006347011374513723015442 0ustar kaolkaol%% %% City missions are given by civil servants. They can be politicians, police %% officers, military, or anything else. %% Plot name % There's a recon airplane and a Wurzels reference in town, and they % both have to be destroyed. requires <*GENERAL -!Ne -!Lo Capitol> PayRate 250 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is the enemy faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Capture 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \SCENE NPCSCene %2% :// \RANK \PC , report to \EXACT_SCENE EScene 2 for a mission.> Msg1_1 <%name2%@ \SCENE NPCSCene %2% :// There are enemies nearby. Your services are required.> Msg1_2 <%name2%@ \SCENE NPCSCene %2% :// %name1% needs your help. Give me a call for the mission details.> Msg1_3 <%name2%@ \SCENE NPCSCene %2% :// This is a matter of grave importance to %name1%. Call me as soon as possible.> sub Persona 2 rumor0 <%name2% needs a cavalier for a secret mission.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac %4%> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac %4% %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <%name2% in \SCENE NPCScene %2% hired you to intercept a espionage team sent by %name4%.> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % A basic fight against some generic enemy mecha in a cave. % This job gives salvage. This job will not result in the PC making % any enemies. requires <*GENERAL Mine> PayRate 105 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place Element1 Element2 Element3 % SubPlot1 is the combat encounter SubPlot1 <*MECHACAVE_Basic 2 3> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Hey \RANK , I've got a mission for you in %name1%.> Msg1_1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Report to \EXACT_SCENE EScene 2 for a mission at once.> Msg1_2 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Your services are required by \FACTION NPCFac E2 . Come see me for mission briefing.> Msg1_3 <%name2%@ \EXACT_SCENE NPCSCene %2% :// I've got a mission for you. Come to \EXACT_SCENE ESCene 2 for the briefing.> sub Persona 2 rumor0 <%name2% needs a mecha pilot to ferret out some bandits.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to drive some bandits from an abandoned mine in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % It's a cuisine city. Go protect the incoming veggies. requires <*GENERAL cuisine -!Ne> PayRate 175 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is E2's faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Escort 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// \RANK \PC , you have work to do in %name1%. Call me for the briefing.> Msg1_1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Report to \EXACT_SCENE EScene 2 for a mission.> Msg1_2 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Hey \PC , I have a mission for you. Our veggies are on the line!> Msg1_3 <%name2%@ \EXACT_SCENE NPCSCene %2% :// I've got a mission for you. This is of vital importance to %name1%.> Msg1_4 <%name2%@ \EXACT_SCENE NPCSCene %2% :// %name1% needs you, \RANK \PC . Call me for the briefing.> sub Persona 2 rumor0 <%name2% needs a pilot to defend our produce.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to escort a convoy in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % It's a trade hub city. Go defend a convoy from someone or another. requires <*GENERAL tradehub -!Ne> PayRate 175 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is E2's faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Escort 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// \RANK \PC , I've got a mission for you in %name1%.> Msg1_1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Report to \EXACT_SCENE EScene 2 for a mission.> Msg1_2 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Hey \PC , I know how much you love driving around! Have I got a job for you...> Msg1_3 <%name2%@ \EXACT_SCENE NPCSCene %2% :// You've got a mission for %name4%. Come to \EXACT_SCENE ESCene 2 and I'll fill you in.> Msg1_4 <%name2%@ \EXACT_SCENE NPCSCene %2% :// %name1% needs you, \RANK \PC . Come see me for the details.> sub Persona 2 rumor0 <%name2% needs a pilot to defend the shipping lanes.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to escort a convoy in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % It's an industrial city. Go defend a factory from someone or another. requires <*GENERAL industrial -!Ne> PayRate 175 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is E2's faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Defense 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// \RANK \PC , I've got a mission for you in %name1%.> Msg1_1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Report to \EXACT_SCENE EScene 2 for a mission.> Msg1_2 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Hey \PC , I know how much you love guard duty! Give me a call.> Msg1_3 <%name2%@ \EXACT_SCENE NPCSCene %2% :// You've got a mission for %name4%. Come to \EXACT_SCENE ESCene 2 and I'll tell you about it.> Msg1_4 <%name2%@ \EXACT_SCENE NPCSCene %2% :// %name1% needs you, \RANK \PC . Come see me for the details.> sub Persona 2 rumor0 <%name2% needs a pilot for some guard duty.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 Msg1 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg1_3 CMsg1_3 Msg1_4 <%name1% is a capital of industry, making it a prime target for our enemies! Your mission is to defend a factory. For this I can offer you $ \VAL Reward %threat% PayRate plus salvage.> CMsg1_4 Msg1_5 <%name1% has grown strong from our solar-class industry, and industry needs brave cavaliers to defend it. I want you to guard a factory in \EXACT_SCENE %3% . You'll get $ \VAL Reward %threat% PayRate and salvage.> CMsg1_5 Msg1_6 CMsg1_6 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to defend a factory in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % The basic patrol job is a fight against some generic enemy mecha. % This job gives salvage. This job will not result in the PC making % any enemies. requires <*GENERAL> PayRate 100 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place Element1 Element2 Element3 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Basic 2 3> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Hey \RANK , I've got a mission for you in %name1%.> Msg1_1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Report to \EXACT_SCENE EScene 2 for a mission at once.> Msg1_2 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Your services are required by \FACTION NPCFac E2 . Come see me for mission briefing.> Msg1_3 <%name2%@ \EXACT_SCENE NPCSCene %2% :// I've got a mission for you. Come to \EXACT_SCENE ESCene 2 for the briefing.> sub Persona 2 rumor0 <%name2% needs a mecha pilot to intercept an enemy patrol.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to fight some unauthorized mecha in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % The basic patrol job is a fight against some generic enemy mecha. % This job gives salvage. requires <*GENERAL -!Ne -SAFE> PayRate 150 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is the enemy faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Versus 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// \RANK \PC , report to \SCENE EScene 2 for a mission.> Msg1_1 <%name2%@ \EXACT_SCENE NPCSCene %2% :// I have a job for you at \SCENE EScene 2 .> Msg1_2 <%name2%@ \EXACT_SCENE NPCSCene %2% :// %name1% needs your help. Come see me at \SCENE EScene 2 .> Msg1_3 <%name2%@ \EXACT_SCENE NPCSCene %2% :// Call when you can; I've got a mission for you.> sub Persona 2 rumor0 <%name2% is looking for someone to drive %name4% out of town.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac %4%> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac %4% %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final ChatNPCFac na> GoR1Final result2 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 Msg2 <\ELEMENT 2 in \SCENE NPCScene %2% hired you to fight %name4% in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end GH2/series/ENTRANCE_Default.txt0000644000175000017500000000447011326004535014776 0ustar kaolkaol%% DEFAULT ENTRANCES MetaTerrain 0 requires <*COLONY-1> roguechar <*> SDL_SPRITE Frame 1 use GoFight GoNoSee GoStealthFail GoStartCombat .nu1 .nu2 GoEnter Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 STC QUEST-MAPMARKER-STATIONARY requires <*QUEST-INACTIVE> STC QUEST-MAPMARKER-ACTIVE-STATIONARY requires <*QUEST-ACTIVE> StairsUp requires <*GoUp> StairsDown requires <*GoDown> Building name requires <*SPACEPORT> Altitude 3 BM_SpacePort roguechar

    Building name requires <*HOSPITAL> Altitude 2 BM_Hospital roguechar Building name requires <*GARAGE> Altitude 2 BM_Garage roguechar Building name requires <*DEPARTMENTSTORE> Altitude 3 BM_DepartmentStore roguechar Building name requires <*CAVCLUB> Altitude 1 BM_CAVCLUB roguechar Building name requires <*SILVERFORTRESS> Altitude 4 BM_SilverFortress roguechar Building name requires <*ARENA> Altitude 2 BM_Arena roguechar Building name requires <*PARK> Altitude 1 BM_Park roguechar <%> Building name requires <*CITYHALL> Altitude 3 BM_Government roguechar Building name requires <*BUILDING Short> Altitude 1 Building name

    requires <*BUILDING> Altitude 2 Building name requires <*BUILDING> Altitude 3 Building name requires <*BUILDING Tall> Altitude 4 GH2/series/QUEST_LocalQuest_L5PAT.txt0000644000175000017500000007355511401151245016102 0ustar kaolkaol%% %% *:Q_LocalQuest Content %% %% This file contains the quest frameworks for the cities of the L5 Region. %% %% The requires field should include *:Q_LocalQuest and the designation of the %% city to which this quest applies. %% Content name requires <*:Q_LocalQuest "CLUND"> % E1 is the exterior scene % E2 is the Memorial Land entry dome % E3 is the Memorial Land main dome % E4 is the property manager who can open the door to the ruins. % E5 is the ruin surface (the Boneyard) % E6 is the main ruin dungeon % E7 is the lower ruin dungeon Element1 Element2 Place2 <1> Element3 Place3 <2> Element4 Place4 <3 (Citizens) pass> Element5 Place5 <1> Element6 Place6 <5> % Element7 % Place7 <6> % SubPlot1 = Upper Ruins Quest SubPlot1 <*:Q_DungeonQuest #40 6> sub MetaScene 2 name type special entrance <*Building SHORT> ClubMap MapWidth 22 MapHeight 22 PalaceParkTiles % L1 = First time entry counter start GoBeenBefore Msg1 Msg2 NeededCells 4 Content1 Content2 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room minimap <############2###...##...#> special inv Elevator MiniMapComponent 2 desig % V1 = Admission Timer use GoBuyTicket GoNoMoney CLUE_INSIGHT Msg1 Msg2 Msg3 Msg4 Msg5 end room name desig Content Content2 end MetaScene 3 name type special BoxMap MapWidth 22 MapHeight 22 FloorType 1 PalaceParkTiles % L1 = Have unlocked the ruins. % L2 = Number of games won. start Msg1 NeededCells 4 Content Content2 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 rect forest forest end Persona 4 *greeting <*NiceToMeetYou GoCheckGate> GoCheckGate GoChat GoOpenRuins result1 result2 result3 result4 result5 Msg1 Msg2 Msg3 Msg4 Msg5 <%name4% opened the Ruins of Terror for you on Clund Rock.> Msg6 <%name4% opens the Ruins of Terror.> Msg7 Msg8 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 CPrompt5 MetaScene 4 % The home for the property manager sub rect desig minimap < ### #2# ..1 > inv Elevator name MiniMapComponent 2 desig use GoLocked CLUE_CodeBreaking GoUnlocked GoNoGood Msg1 Msg2 Msg3 msg4 end end MetaScene 5 name type terrain habitat entrance <*Building SHORT> % Suffocation effect... Vacuum Ceiling mapwidth 50 mapheight 50 AsteroidMap RockyTiles SpaceBackdrop DifficultyLevel 20 % L1 = First time entry counter % L2 = Have discovered trapdoor start GoBeenBefore msg1 msg2 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 1 rect width 5 height 5 mfx 1 mfy 1 special room width 5 height 5 minimap < 1.. .2. ... > inv STC RUBBLE-INDESTRUCTIBLE CLUE_SCIENCE CLUE_SURVIVAL MiniMapComponent 1 CLUE_INSIGHT GoFail GoDone Msg1 Msg2 CMsg_5 end Content name requires <*:Q_LocalQuest "CAYLE"> % E1 is the exterior scene % E2 is the mine entrance % E3 is Wynter's Mine % E4 is Lower Wynter's Mine % E5 is the blocked level % E6 is Cayley Caves % E7 is Cayley Core % E8 is the root scene % E9 is the disaster mood, following the earthquake Element1 Element2 Place2 <1> Element3 Place3 <2> Element4 Place4 <3> Element5 Place5 <4> Element6 Place6 <5> Element7 Place7 <6> Element8 <.> Element9 % P1 = Elevator Repair Counter (From main building to mine bottom) % 0: Broken, 1: Repaired % P2 = Initialization Counter % Activate the Wynter's Mine and Lower Wynter's Mine quests right away. update % Activate the lower quests some time after the PC reaches Renown 50. PUMPNEWS GoEarthQuake Msg1 Msg2 % SubPlot1 = Wynter's Mine Quest % SubPlot2 = Lower Wynter's Mine Quest % SubPlot3 = Cayley Caves Quest % SubPlot4 = Cayley Core Quest SubPlot1 <*:Q_DungeonQuest #10 3> SubPlot2 <*:Q_DungeonQuest #30 4> SubPlot3 <*:Q_Unique_CAYLE_Caves #55 6> SubPlot4 <*:Q_Unique_CAYLE_Core #85 7> sub MetaScene 2 name type terrain habitat entrance <*Building> MonkeyMap MapWidth 21 MapHeight 21 special IndustrialTiles NeededCells 1 content % V1 = First time entry indicator. Start GoBeenBefore Msg1 Msg2 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 room sub trapdoor desig use GoFirstTime Msg101 Msg102 Msg103 end room minimap <......###..#1#...........> inv elevator desig MiniMapComponent 1 use GoBroken CLUE_REPAIR CLUE_INSIGHT GoRepairFail GoWorking Msg101 Msg102 Msg103 Msg104 end end STC QS_Dungeon_AsteroidMine SetID 3 name DifficultyLevel 10 STC QS_Dungeon_AsteroidMine SetID 4 name entrance <*GoDown> DifficultyLevel 30 STC QS_Dungeon_AsteroidMine SetID 5 name type entrance <*GoDown> DifficultyLevel 40 sub Room minimap <......###..#1#...... ... > width 5 height 5 inv Elevator desig MiniMapComponent 1 use GoAlreadyWorking CLUE_REPAIR GoNoDamage CLUE_INSIGHT Msg101 Msg102 Msg103 Msg104 Msg105 Msg106 Msg107 end Room width 5 height 5 sub StairsDown desig use GoBlocked CLUE_REPAIR CLUE_SURVIVAL CLUE_SCIENCE CLUE_INSIGHT CLUE_MYSTICISM Msg2 Msg3 Msg4 end end STC QS_Dungeon_AsteroidCave SetID 6 name DifficultyLevel 65 sub Team 3 name SetAlly 2 SetEnemy 1 room %% The expansion generator width 5 height 5 ForEntryLevel minimap < %%% %%^&%^%1&%^%%^% %^% > inv STC MACHINE-1 % V1 = Science XP counter % V2 = Insight XP counter MiniMapComponent 1 name use CLUE_SCIENCE GoSciBefore GoSciFail CLUE_REPAIR CLUE_INSIGHT GoInsightBefore GoInsightFail CLUE_MYSTICISM Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 end end STC QS_Dungeon_AsteroidCave SetID 7 name entrance <*GoDown> DifficultyLevel 85 end inv Mood 2 name plot_type <*Disaster> MoodTimeLimit % V1 = Initialization Counter Update Msg1_1 Msg1_2 % Meme Messages Msg_1 Msg_2 Msg_3 Msg_11 CMsg_11 Msg_12 CMsg_12 Msg_13 CMsg_13 Msg_14 CMsg_14 Msg_15 CMsg_15 Msg_16 CMsg_16 end GH2/series/MEGA_Duel.txt0000644000175000017500000002222011374513723013615 0ustar kaolkaol%% %% *NC_Duel Content %% %% The PC is about to duel someone. This component will move the opponent from %% their current location to the duel scene, then move the opponent back to %% the original scene at the end of the battle. %% %% Unlike the Core Story duel, this one will only level the NPC if they are %% currently lower than the PC. %% %% Param1: The Challenger %% Param2: The Challenger's home scene, for the purpose of this component %% Param3: The outdoor scene where the duel will take place %% %% NEEDED SCRIPTS: To deal with the ending, the parent plot must define two %% scripts: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLose %% These scripts should set story memos. %% %id% is the ID of the duel layer; so, if it was generated as subplot 1, %% the calling plot would use %id1%. Content name desc requires <*NC_Duel> Size 5 % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is the duel encounter Element4 Place4 <3> % P%id%01 = Initialization Counter update sub Persona 1 greeting *.%id%_GoChallenge <*ArenaChallenge .%id%_GoThemeInfo> *.%id%_GoThemeInfo <*THEME_EXPO&Enemy NA> *.%id%_GoNotInPlay <*WaitingForDuel %3%> MetaScene 4 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L4 = Identity of enemy NPC MapWidth 30 MapHeight 30 special Start nu1 nu2 Msg2 <%name1% returns to %name2%.> sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 ParaX 25 ParaY 25 end end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update end %% %% *:NC_TournamentMatch Content %% %% Non-Core Tournament Match %% There's apparently a tournament going on. The PC will be able to challenge a NPC %% to a duel, which will be handled by the above subplot type. %% %% NEEDED SCRIPTS: To deal with the ending, the parent plot must define two %% scripts: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLose %% %% PARAM1: The NPC to be challenged %% PARAM2: The home scene for this NPC Content name desc requires <*:NC_TournamentMatch> % E1 is the NPC to be challenged % E2 is the home scene for the NPC % E3 is a location for the duel. Element3 % As long as we're still at the first phase, the NPC's death will end the plot. start % Resolution Scripts .%id1%_%plotid%_GoWin .%id1%_%plotid%_GoLose % SubPlot1 is the duel itself SubPlot1 <*NC_Duel 1 2 3> sub Persona 1 % v%id%01 = Renown Counter greeting *.%id%_GoFirstTime <*PCChallengeNPC .%id%_GoAttemptChallenge .%id%_GoWussOut> .%id%_GoAttemptChallenge *.%id%_GoQuestionChallenge <*SmallFryChallenge .%id%_GoMakeChallenge .%id%_GoGiveUp .%id%_GoGiveUp> .%id%_GoGiveUp .%id%_GoMakeChallenge *.%id%_GoAcceptChallenge <*IAcceptYourChallenge %3%> *.%id%_GoWussOut <*GodImBored> Msg%id%01 end %% %% *InformalChallenge Content %% %% The PC will challenge an NPC to a duel. %% %% PARAM1: The NPC to be challenged %% PARAM2: The home scene for this NPC %% Content name desc requires <*InformalChallenge> % E1 is the NPC to be challenged % E2 is the home scene for the NPC % E3 is a location for the duel. Element3 % P%id%01 = The Pot. Earn double this on a win. % P%id%02 = Time Limit % As long as we're still at the first phase, the NPC's death will end the plot. end update start % Resolution Scripts .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLose Msg%id%01 Msg%id%02 % SubPlot1 is the duel itself SubPlot1 <*NC_Duel 1 2 3> sub Persona 1 % v%id%01 = Renown Counter % v%id%02 = Made it through renown check &%id%_BigAmount &%id%_MediumAmount &%id%_SmallAmount greeting *.%id%_GoFirstTime <*PCChallengeNPC .%id%_GoAttemptChallenge .%id%_GoWussOut> .%id%_GoAttemptChallenge *.%id%_GoQuestionChallenge <*SmallFryChallenge .%id%_GoMakeChallenge .%id%_GoGiveUp .%id%_GoGiveUp> .%id%_GoGiveUp *.%id%_GoWussOut <*GodImBored> *.%id%_GoGoodbye <*BrushOff> .%id%_GoMakeChallenge .%id%_GoStartDuel *.%id%_GoAcceptChallenge <*IAcceptYourChallenge %3%> result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 end %% %% *NC_WinDuelReturn %% %% The PC has just won a duel against this character. There will likely be some %% kind of reward, then the NPC will return to their home scene. %% %% PARAM1: The NPC in question %% PARAM2: The home scene %% Content name requires <*NC_WinDuelReturn> % E1 is the NPC % E2 is their home scene end sub Persona 1 greeting Msg%id%01 end %% %% *NC_LoseDuelReturn %% %% The PC has just lost a duel against this character. There will likely be some %% taunting, then the NPC will return to their home scene. %% %% PARAM1: The NPC in question %% PARAM2: The home scene %% Content name requires <*NC_LoseDuelReturn> % E1 is the NPC % E2 is their home scene % P%id%01 = Timer update start end sub Persona 1 greeting Msg%id%01 end GH2/series/MEGA_MechaMission.txt0000644000175000017500000007124711376734116015323 0ustar kaolkaol%% %% *MECHAMISSION_Capture Content %% %% The PC is going to capture an enemy vehicle. %% %% This mission gives salvage plus four FacXP points. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The enemy faction %% Content name requires <*MECHAMISSION_Capture> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The enemy faction % E4: The combat scene Element4 Place4 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 4 2 %% The ship's exterior %% To capture the ship, defeat all the guards, weapons, and %% thrusters. Damaging the cargo segments of the ship may result %% in the ship being destroyed. SetFaction 3 % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = "Just defeat the guards" message counter % L4 = Initial headcount of Team 3 MapWidth 50 MapHeight 50 Start nu1 nu2 GoLoseMission GoWinMission nu4 nu5 end Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 4 5 SetAlly 6 ParaX 5 ParaY 25 team 2 name type SetEnemy 1 6 SetAlly 4 5 Deploy ParaX 45 ParaY 25 team 3 name % This team is neutral- the PC wants to capture these % parts intact. team 4 name setenemy 1 6 setally 2 3 5 % Destroy the weapons and propulsion to incapacitate the % cruiser. team 5 name setenemy 1 6 setally 2 3 4 Team 6 name setally 1 setenemy 2 4 5 rect name special width 12 height 12 MFX 37 MFY 19 sub SuperProp requires <*Cruiser> Team1 3 Team2 4 Team3 5 end end end inv STC PLOT-MECHAMISSION-WANDER end %% %% *MECHAMISSION_Escort Content %% %% The PC is going to defend a convoy against enemy mecha. %% %% This mission gives salvage plus four FacXP points. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The faction being defended %% Content name requires <*MECHAMISSION_Escort !Ex> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The faction being defended % E4: The faction attacking the convoy % E5: The combat scene Element4 Element5 Place5 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 5 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E4 SetFaction 4 % At startup, set the counter so the mission doesn't timeout % while the PC is defending the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub Team 1 SetEnemy 2 SetAlly 3 4 home Team 2 name type SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 name SetEnemy 2 SetAlly 1 4 team 4 name SetEnemy 2 SetAlly 1 3 ParaX 10 ParaY 10 rect name special width 12 height 12 MFX 2 MFY 2 sub SuperProp requires <*Cruiser> SetTeam 3 end end end inv STC CORE-STATIONARY name <%name1%'s Mission> Attack < > end Content name requires <*MECHAMISSION_Escort !Hi> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The faction being defended % E4: The faction attacking the convoy % E5: The combat scene Element4 Element5 Place5 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 5 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E4 SetFaction 4 % At startup, set the counter so the mission doesn't timeout % while the PC is defending the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub Team 1 SetEnemy 2 SetAlly 3 4 home Team 2 name type SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 name SetEnemy 2 SetAlly 1 4 team 4 name SetEnemy 2 SetAlly 1 3 ParaX 10 ParaY 10 rect name special width 12 height 12 MFX 11 MFY 11 sub SuperProp requires <*Cruiser> SetTeam 3 end end end inv STC CORE-STATIONARY name <%name1%'s Mission> Attack < > end Content name requires <*MECHAMISSION_Escort -!Hi -!Ex> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The faction being defended % E4: The faction attacking the convoy % E5: The combat scene Element4 Element5 Place5 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 5 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E4 SetFaction 4 % At startup, set the counter so the mission doesn't timeout % while the PC is defending the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub Team 1 SetEnemy 2 SetAlly 3 4 home Team 2 name type SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 name SetEnemy 2 SetAlly 1 4 team 4 name SetEnemy 2 SetAlly 1 3 ParaX 10 ParaY 10 rect name special width 12 height 12 MFX 19 MFY 19 sub SuperProp requires <*Cruiser> SetTeam 3 end end end inv STC CORE-STATIONARY name <%name1%'s Mission> Attack < > end %% %% *MECHAMISSION_Defense Content %% %% The PC is going to defend a base against enemy mecha. %% %% This mission gives salvage plus 4 FacXP points. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The faction being defended %% Content name requires <*MECHAMISSION_Defense -2:SPACE> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The faction being defended % E4: The faction attacking the base % E5: The combat scene Element4 Element5 Place5 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 5 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 CityBlockMap terrain % Set this metascene's faction to E4 SetFaction 4 % At startup, set the counter so the mission doesn't timeout % while the PC is defending the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub Team 1 SetEnemy 2 SetAlly 3 4 ParaX 7 ParaY 7 Team 2 name type SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 SetAlly 1 4 team 4 name SetEnemy 2 SetAlly 1 3 ParaX 10 ParaY 10 rect name desig sub SuperProp requires <*Fortress> SetTeam 3 end end end inv STC CORE-STATIONARY name <%name1%'s Mission> end Content name requires <*MECHAMISSION_Defense 2:SPACE> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The faction being defended % E4: The faction attacking the base % E5: The combat scene Element4 Element5 Place5 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 5 2 % L1 = Initialization Counter % L2 = Victory Counter MapWidth 50 MapHeight 50 AsteroidMap RockyTiles % Suffocation effect... Vacuum SpaceBackdrop terrain % Set this metascene's faction to E4 SetFaction 4 % At startup, set the counter so the mission doesn't timeout % while the PC is defending the factory. That would just be silly. Start nu1 nu2 GoLoseMission GoWinMission Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub Team 1 SetEnemy 2 SetAlly 3 4 ParaX 7 ParaY 7 Team 2 name type SetEnemy 1 3 4 Deploy ParaX 45 ParaY 45 Team 3 SetEnemy 2 SetAlly 1 4 team 4 name SetEnemy 2 SetAlly 1 3 ParaX 10 ParaY 10 rect name MFX 5 MFY 5 Width 5 Height 5 sub SuperProp requires <*Fortress> SetTeam 3 end end end inv STC CORE-STATIONARY name <%name1%'s Mission> end %% %% *MECHAMISSION_Versus Content %% %% The PC has been sent to fight some mecha belonging to an enemy faction. %% This is going to result in an enemy faction being gained. %% %% This mission gives salvage. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The faction being fought Content name requires <*MECHAMISSION_Versus> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The faction being fought % E4: The combat scene Element4 Place4 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email % SubPlot4 = Lancemate character development SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 4 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E3 SetFaction 3 Start .side_story <*CombatSideStory %e2% %e3%> nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end %% %% *MECHAMISSION_Basic Content %% %% The PC has been sent to fight some mecha, but hasn't been told who he'll be fighting %% or anything else about it. Therefore, this mission is a blank slate of violent goodness. %% %% This mission gives salvage. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed Content name requires <*MECHAMISSION_Basic> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene Element3 Place3 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 Start nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end Content name requires <*MECHAMISSION_Basic 2:SPACE> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene Element3 Place3 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 AsteroidMap RockyTiles % Suffocation effect... Vacuum SpaceBackdrop Start nu1 nu2 Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end Content name requires <*MECHAMISSION_Basic ~!Md ~!Hi ~!Ex> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene % E4: The enemy faction Element3 Place3 <2> Element4 % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E4 SetFaction 4 Start .side_story <*CombatSideStory %e2% %e4%> nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end %% %% *MECHACAVE_Basic Content %% %% The PC has been sent to fight some mecha, but hasn't been told who he'll be fighting %% or anything else about it. Therefore, this mission is a blank slate of violent goodness. %% %% This mission gives salvage. It takes place in a cave. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed Content name requires <*MECHACAVE_Basic> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene Element3 Place3 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 40 MapHeight 40 CaveMap RockyTiles special Ceiling terrain Start nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 team 2 name home type SetEnemy 1 3 Deploy team 3 name SetEnemy 2 SetAlly 1 room name Width 7 Height 7 end end inv STC CORE-STATIONARY name <%name1%'s Mission> Attack < > end GH2/series/ARENACORE_Default.txt0000644000175000017500000000030611326004537015072 0ustar kaolkaol%% %% ************************************ %% *** CORE CAMPAIGN MISSIONS *** %% ************************************ %% %% This file contains the core campaign missions. %% GH2/series/MEGA_CORE_Duel.txt0000644000175000017500000005633511374513723014443 0ustar kaolkaol%% %% *CS_Duel Content %% %% %% The PC is about to duel somebody as part of the core story. %% %% The parent plot sets the plot status to this layer's ID when ready. %% This component should move the opponent from its current location to the %% contest location at update, and then move the opponent back to its %% original location at the end of the battle. %% %% Param1: The Challenger %% Param2: The Challenger's home scene, for the purpose of this component %% Param3: The outdoor scene where the duel will take place %% %% NEEDED SCRIPTS: To deal with the ending, the parent plot must define two %% scripts: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLose %% These scripts should set story memos. %% %id% is the ID of the duel layer; so, if it was generated as subplot 1, %% the calling plot would use %id1%. Content name desc requires <*CS_Duel 3:SPACE> Size 5 % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is the duel encounter Element4 Place4 <%e3%> % P%id%01 = Initialization Counter update sub Persona 1 greeting *.%id%_GoChallenge <*ArenaChallenge .%id%_GoThemeInfo> *.%id%_GoThemeInfo <*THEME_EXPO&Enemy NA> *.%id%_GoNotInPlay <*WaitingForDuel %3%> MetaScene 4 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L4 = Identity of enemy NPC MapWidth 30 MapHeight 30 AsteroidMap RockyTiles % Suffocation effect... Vacuum SpaceBackdrop special Start nu1 nu2 Msg1 <%name1% found a large asteroid to fight on> Msg2 <%name1% returns to %name2%.> Msg4 Msg5 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 ParaX 25 ParaY 25 end end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update end Content name desc requires <*CS_Duel -1:PDASS ~1:THIEF ~1:EVIL_ -1:GOOD_ ~1:CRIHN ~+Gfa -!Ne ~!Hi ~!Ex> Size 8 % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is the duel encounter Element4 Place4 <%e3%> % P%id%01 = Initialization Counter update sub Persona 1 rumor%id% <%name1% plays by Martian rules.> greeting *.%id%_GoNotInPlay <*WaitingForDuel %3%> .%id%_GoChallenge result%id%01 result%id%02 *result%id%03 <*THEME_EXPO&Enemy NA> Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 <[Continue]> MetaScene 4 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L4 = Identity of enemy NPC MapWidth 30 MapHeight 30 special Start nu1 nu2 Msg2 <%name1% returns to %name2%.> Msg4 Msg5 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 25 ParaY 25 end end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update end Content name desc requires <*CS_Duel> Size 5 % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is the duel encounter Element4 Place4 <%e3%> % P%id%01 = Initialization Counter update sub Persona 1 greeting *.%id%_GoChallenge <*ArenaChallenge .%id%_GoThemeInfo> *.%id%_GoThemeInfo <*THEME_EXPO&Enemy NA> *.%id%_GoNotInPlay <*WaitingForDuel %3%> MetaScene 4 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L4 = Identity of enemy NPC MapWidth 30 MapHeight 30 special Start nu1 nu2 Msg2 <%name1% returns to %name2%.> Msg4 Msg5 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 ParaX 25 ParaY 25 end end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update end %% %% *:CS_TournamentMatch Content %% %% There's apparently a tournament going on. The PC will be able to challenge a NPC %% to a duel, which will be handled by the above subplot type. %% %% NEEDED SCRIPTS: To deal with the ending, the parent plot must define two %% scripts: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLose %% %% PARAM1: The NPC to be challenged %% PARAM2: The home scene for this NPC Content name desc requires <*:CS_TournamentMatch> % E1 is the NPC to be challenged % E2 is the home scene for the NPC % E3 is a location for the duel. Element3 % As long as we're still at the first phase, the NPC's death will end the plot. start % Resolution Scripts .%id1%_%plotid%_GoWin .%id1%_%plotid%_GoLose % SubPlot1 is the duel itself SubPlot1 <*CS_Duel&NoDerailment 1 2 3> sub Persona 1 greeting *GoFirstTime <*PCChallengeNPC GoMakeChallenge GoWussOut> GoMakeChallenge *GoAcceptChallenge <*IAcceptYourChallenge %3%> *GoWussOut <*GodImBored> Msg%id%01 end %% %% *CS_MajorDuel %% %% The following duels derail the plot; they've been quarantined here until %% I decide how to incorporate them into the new paradigm. %% Content name desc requires <*CS_MajorDuel (+P--|+Pme) E:-- ~+Gre> % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is the dueling scene % E5 is the opposing character % E6 is a hospital, to use if the PC wins Element4 Place4 <3> Element5 Element6 % P%id%01 = Initialization Counter % P%id%02 = Initialization Counter update % SubPlot1 = If you managed to rescue E1... SubPlot1 <*CS_ThanksForRescue&FromCSEnemy 1> sub MetaScene 4 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = E1 powerup var MapWidth 30 MapHeight 30 special Start .alter <+Pun> nu1 .loss <+T-- +F--> nu2 % If E1 is left to die, there's a chance he'll survive and become the PC's enemy. GoLeaveE1ToDie GoDeleteE1 .win <+T-- +F--> % E5 has script immunity for this encounter... end Msg1 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 ParaX 25 ParaY 25 end Persona 5 special greeting result1 result2 GoR2Fail result3 result4 *result5 <*THEME_EXPO&Enemy NA> Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Prompt1 Prompt1_1 Prompt2 Prompt3 Prompt4 Prompt4_1 Prompt5 <[Continue]> end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update NPC Mecha Pilot chardesc Villainous Renowned end Content name Size 5 desc requires <*CS_MajorDuel (+P--|+Pme) F:-- (L:MAQUI|L:FCOMS|L:RISHI) -L:Enemy (C:MILIT|C:ADVEN|C:POLIC|P:PDASS) -P:CRIME> % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is L5Law % E5 is a crime faction % E6 is the duel scene itself % E7 is the police officer Element4 Element5 Element6 Place6 <3> element7 NeverFail7 % P%id%01 = Initialization Counter % P%id%02 = E7's original home update % SubPlot1 = The Induction subplot1 <*ForcedInduction 7 4> sub MetaScene 6 2 % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 30 MapHeight 30 special Start nu1 nu2 .next <+T-- +F--> end Msg1 Msg2 Msg2_1 Msg2_2 sub team 1 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 SetFaction 9 Deploy ParaX 25 ParaY 25 team 3 SetEnemy 2 ParaX 10 ParaY 5 end Persona 1 greeting Msg%id%01 Persona 7 % V1 = Have chatted counter special greeting GoBeenBefore result1 result2 result3 result4 .attack <+Pun> result5 result6 result7 result8 result9 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update end Content name Size 8 desc requires <*CS_MajorDuel (+P--|+Pme) ~F:-- ~T:Ally ~T:Friend -L:Enemy ~+Gpe> % E1 is the challenger % E2 is the challenger's home scene % E3 is the outdoor scene where the duel will take place % E4 is the faction whose mecha are present % E5 is the faction of the current city % E6 is the duel scene itself element4 % The faction of the root scene must not be an enemy of the character % you're dueling... element5 Element6 Place6 <%e3%> % P%id%01 = Initialization Counter % P%id%02 = Duel target skill level calculator update % SubPlot1 is the victory bit SubPlot1 <*CS_ThanksForAssist 1> sub MetaScene 6 2 % L1 = Encounter Over Counter % L2 = Initialization Counter % L4 = Identity of enemy NPC MapWidth 30 MapHeight 30 special Start nu1 nu2 GoFoughtNPC .next <+T-- +F--> .change <+Pun> end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 10 team 2 SetEnemy 1 3 Deploy ParaX 15 ParaY 25 team 3 SetEnemy 2 ParaX 25 ParaY 10 end Persona 1 % V%id%01 = Have spoken already greeting .%id%_GoFirstTime *.%id%_GoWaiting <*WaitingForDuel %3%> result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Msg%id%03 Msg%id%03_1 Msg%id%04 Msg%id%04_1 Msg%id%05 Msg%id%05_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 end inv STC ENCOUNTER-DUEL name <%name1%'s Mecha> update end GH2/series/STC_props.txt0000644000175000017500000005443111341163445014014 0ustar kaolkaol% ***************** % *** SCENERY *** % ***************** Rubble name desig sdl_sprite Rubble 0 name desig sdl_sprite Prop 10 name desig roguechar SDL_Sprite SDL_Colors <244 216 28 201 205 229 181 185 207> Prop 10 name desig roguechar SDL_Sprite SDL_Colors <244 216 28 201 205 229 181 185 207> Prop 10 name desig roguechar SDL_Sprite SDL_Colors <244 216 28 201 205 229 181 185 207> Prop 15 name desig roguechar <_> Mesh 13 SDL_SKIN SDL_SPRITE Prop 5 name desig Mesh 11 SDL_SKIN Frame 0 SDL_SPRITE Prop 10 name desig Mesh 10 SDL_SPRITE SDL_SKIN Frame 1 Prop 10 name desig Mesh 10 SDL_SPRITE SDL_SKIN Frame 2 Prop 10 name desig Mesh 10 SDL_SPRITE SDL_SKIN Frame 3 Prop 10 name desig Mesh 10 SDL_SKIN SDL_SPRITE Frame 6 Prop 10 name desig Mesh 10 SDL_SKIN SDL_SPRITE Frame 7 Prop 5 name desig Mesh 12 SDL_SKIN Frame 0 SDL_SPRITE Prop 4 name desig roguechar <=> SDL_Sprite Prop 5 name desig roguechar <=> SDL_SPRITE MetaTerrain 0 0 desig name roguechar SDL_SPRITE Prop 20 desig roguechar <*> SDL_SPRITE Prop 15 name desig roguechar <&> SDL_SPRITE Prop 5 name Desig roguechar SDL_SPRITE MetaTerrain 0 0 name Desig roguechar SDL_SPRITE Altitude 1 Pass -100 Prop 15 name desig roguechar <~> SDL_SPRITE Prop 25 name desig roguechar <~> SDL_SPRITE Prop 5 name desig sdl_sprite sdl_colors <185 159 92 185 159 92 170 132 80> Prop 15 name desig sdl_sprite sdl_portrait sdl_colors <170 153 230 170 153 230 49 91 159> % ********************* % *** ENTRANCES *** % ********************* Elevator % V1 = First time menu check name desig use GoFirstTime GoExit GoNoEnter Msg1 Msg2 Msg3 Msg4 Msg5 Elevator % V1 = First time menu check name desig use GoFirstTime GoExit GoNoMeks Msg1 Msg2 Msg3 Msg4 Msg5 Elevator desig use Msg1 Metaterrain 0 0 desig Stat 5 -1 Building name desig stat 1 2 Scale 3 Building name desig Altitude 3 Scale 3 BM_SpacePort roguechar
    minimap <2....1..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 1 % Set her age to 37 Age 17 name job SDL_Colors <75 200 212 255 212 195 66 121 119> chardesc Female Sociable Passionate Pragmatic Renowned statline 13 10 14 16 13 17 16 17 MiniMapComponent 1 NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 2 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 24 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content3 Content4 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end % End of Maquise Spinner... end %% %% *************************** %% *** ATHERA SPINNER *** %% *************************** %% Scene 0 3 mapwidth 30 mapheight 10 name exact_name XPos 38 YPos 21 % Athera Spinner is very peaceful, so low tolerance. Tolerance -5 world desc special type personatype habitat MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 1 factions desig start GoSecondTime Msg1 Msg2 Quest1 <*:Q_Concert_Venue> sub Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 30 MapHeight 17 start Msg1 NeededCells 6 Content1 Content2 Content3 Content4 Content6 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 start Msg1 NeededCells 2 Content Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Garage> special MallMap IndustrialTiles MapWidth 19 MapHeight 19 start Msg1 NeededCells 3 Content1 Content2 Content3 Content4 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building Short> special ClubMap MapWidth 20 MapHeight 19 start Msg1 NeededCells 4 Content1 Content2 Content3 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*PARK> special SetFaction 1 WildMap PalaceParkTiles GAPFILL <-4 -9 -4 -4> MapWidth 25 MapHeight 20 start Msg1 Content Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 rect FloorType 13 MFX 21 MFY 1 Width 5 Height 20 sub rect MFX 21 MFY 7 width 5 height 5 FloorType 6 WallType 13 minimap <1##############2####3####> inv Elevator name MiniMapComponent 1 StairsUp name MiniMapComponent 2 destination StairsUp name MiniMapComponent 3 destination end end rect MFX 1 MFY 8 Width 5 Height 5 sub STC HIDDEN_ENTRANCE Destination -1 end rect MFX 1 MFY 10 width 20 height 2 FloorType 6 %% End Athera Garden main scene subs Scene name type special SetFaction 1 ClubMap PalaceParkTiles MapWidth 24 MapHeight 24 start Msg1 NeededCells 2 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 sub room minimap <......###..#1#...........> inv Elevator MiniMapComponent 1 Destination -1 end end room name minimap <..1......................##+##> inv NPC Politician name SDL_Portrait SDL_Colors <75 200 212 150 112 89 152 61 97> statline 10 13 12 12 14 19 15 16 SetTeam 2 SetFaction 1 MiniMapComponent 1 CharDesc Sociable Passionate Male end room name minimap <.....1..............#...#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 end room name minimap <2....1..............#...#> inv NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 1 NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 2 end end inv NPC Police Officer SetTeam 3 SetFaction 9 NPC Knight SetTeam 3 SetFaction 3 end Scene name desig type special PARAM PalaceParkTiles MapWidth 23 MapHeight 24 start GoBeenBefore Msg1 Msg2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 rect name XPos 2 YPos 2 Width 20 Height 20 PARAM Content Content2 rect MFX 1 MFY 11 Width 5 Height 3 FloorType 6 sub STC HIDDEN_ENTRANCE Destination -1 end end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 24 MapHeight 21 start Msg1 NeededCells 3 Content1 Content2 Content3 Content4 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end Scene name SetFaction 13 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 3 Content2 5Min sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy SetAlly 1 2 room name desig Content1 Content3 Content4 end inv NPC Corporate Executive home SetFaction 13 end % End of Athera Spinner... end %% %% ************************ %% *** CAYLEY ROCK *** %% ************************ %% Scene mapwidth 30 mapheight 30 name exact_name XPos 44 YPos 9 entrance <*COLONY-1> % Cayley Rock is somewhat lawless. Tolerance 5 world desc special type personatype habitat Quest1 <*:Q_LocalQuest> Quest2 <*:Q_MECHA_ARENA_BASE> ClubMap % V1 = Message Counter SetFaction 1 factions desig NeededCells 6 Content1 Content2 Content4 Content5 Content6 Content7 start GoSecondTime Msg1 Msg2 inv NPC Politician name home job SetTeam 2 SetFaction 1 Age 23 chardesc Male Heroic statline 13 16 12 11 16 13 14 13 NPC Pirate job home SetTeam 3 SetFaction 6 Age 14 end sub % BEGIN Cayley Rock Main Scene Stuff team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 Persona name % V1 = "Got the spiel" indicator. After one time through, will just get SayANything. greeting GoFirstTime GoFromAway result1 result2 result3 result4 result5 Msg1 Msg1_1 CMsg1_1 Msg2 Msg3 Msg4 Msg5 <\PC , I heard that you'll be leaving the station. They say you're planning to become a cavalier.> Msg6 Msg7 Prompt1 Prompt2_1 CPrompt2_1 Prompt2_2 CPrompt2_2 Prompt3 Prompt4 Prompt5 room name desig special Content Content2 room name minimap <.....1..............##+##> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 end room % This is the area that will contain the airlock to outside and the lift to the % orbital catapult. minimap <###########1#2##...##...#> inv Elevator name MiniMapComponent 1 use GoFirstTime msg1 Msg101 Msg102 Msg103 STC AIRLOCK-2 MiniMapComponent 2 destination end room % This is the area that will contain the elevator to habitation and a snack machine minimap <############1###2..##...#> inv Elevator name MiniMapComponent 1 STC Vending-1 PDir 2 MiniMapComponent 2 end room name minimap <....................##+##> % END Cayley Rock Main Scene Stuff Scene mapwidth 30 mapheight 30 name special type ClubMap NeededCells 5 Content1 Content2 Content3 Content4 Content7 start Msg1 sub % BEGIN Cayley Habitation Level Stuff team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig special Content Content2 room % This is the area that will contain the elevator to the main level minimap <############1###...##...#> inv Elevator Destination -1 MiniMapComponent 1 end Persona name % V1 = Second time coming back. % V2 = Monster description counter. % V3 = Have been told about Exorgs. rumor greeting GoFirstTime GoNotFromHere result1 result2 result3 result4 result5 result6 result7 result8 result9 result10 GoR10NotYet result11 result12 result13 result14 result15 Msg1 <\PC , that's where you've been. I hear that you've decided that you're a cavalier now. Well, if that's the case, I just might be able to give you some pointers.> Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Msg15 Prompt1 Prompt2 Prompt3 Prompt3_1 CPrompt3_1 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 Prompt9 CPrompt9 Prompt10 Prompt10_1 Prompt10_2 Prompt10_3 Prompt11 Prompt12 Prompt13 CPrompt13 Prompt14 Prompt15 end inv NPC Miner name chardesc Old Male Sociable Easygoing Melancholy Age 48 statline 11 15 13 14 18 16 13 9 home SetTeam 2 end Scene 0 3 name mapwidth 30 mapheight 30 XPos 1 YPos 1 special type AsteroidMap RockyTiles % Suffocation effect... Vacuum SpaceBackdrop inv stc Building name destination -1 end sub % Cayley Rock Exterior subcoms... CityZone Width 8 Height 8 CityZOne Width 5 Height 5 Scene name type %% The Steel Arena will be placed here. entrance <*Building> mapwidth 17 mapheight 12 special BoxMap IndustrialTiles start Msg1 Content inv NPC Corporate Executive home SetFaction 7 NPC Corporate Executive home SetFaction 2 end sub % BEGIN Cayley Steelworks Stuff team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name Content2 end % End Cayley Rock Exterior... end % End of Cayley Rock... end %% %% ************************** %% *** CESENA SPINNER *** %% ************************** %% Scene 0 3 mapwidth 30 mapheight 10 name XPos 46 YPos 16 Tolerance 0 world desc special type personatype habitat MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 1 factions desig start GoSecondTime Msg1 Msg2 sub Scene name type entrance <*Building> special MallMap MapWidth 30 MapHeight 18 start Msg1 NeededCells 5 Content3 Content4 Content6 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 NeededCells 2 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*SilverFortress> special SetFaction 3 ClubMap MapWidth 25 MapHeight 25 NeededCells 4 Content1 Content2 Content3 5Min start Msg1 sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy type SetAlly 1 2 room name minimap <2...31..............#---#> inv NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 1 NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 2 end room special minimap <##1##...............##=##> sub Door update end inv Elevator name MiniMapComponent 1 use GoGiveWarning Msg1 Msg101 Msg102 Msg103 end room name desig Content2 Content4 Scene name type special SetFaction 3 ClubMap MapWidth 25 MapHeight 25 NeededCells 6 Content Content2 5Min start Msg1 sub team 1 team 2 name SetAlly 1 3 Passive Team 3 name type SetAlly 1 2 room name desig Content sub room minimap <......###..#1#...........> inv Elevator MiniMapComponent 1 Destination -1 end end end end Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 20 MapHeight 15 start Msg1 NeededCells 4 Content1 Content2 Content3 Content4 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Garage> special MallMap MapWidth 20 MapHeight 20 IndustrialTiles start Msg1 NeededCells 4 Content1 Content2 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building> special SetFaction 1 ClubMap PalaceParkTiles MapWidth 24 MapHeight 21 start Msg1 NeededCells 2 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2...31..............#---#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 2 end room name
    minimap <....31..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 1 MiniMapComponent 1 NPC Knight name job SDL_PORTRAIT SDL_COLORS <150 205 229 235 212 195 130 143 114> SetTeam 3 SetFaction 3 MiniMapComponent 3 chardesc Lawful Heroic Easygoing Shy Cheerful Male Renowned Renown 90 Age 52 statline 19 22 18 19 14 16 17 15 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 20 MapHeight 21 start Msg1 NeededCells 6 Content1 Content2 Content3 Content4 Content5 Content6 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 inv NPC Knight SetTeam 3 SetFaction 3 end room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end Scene name SetFaction 7 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 2 Content2 5Min sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy SetAlly 1 2 room name desig Content1 Content3 Content4 end inv NPC Corporate Executive home SetFaction 7 end Scene name SetFaction 2 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 2 Content2 5Min sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy SetAlly 1 2 room name desig Content1 Content3 Content4 end inv NPC Corporate Executive home SetFaction 2 end % End of Cesena Spinner... end %% %% *************************** %% *** XIANZAI SPINNER *** %% *************************** %% Scene 0 3 mapwidth 30 mapheight 10 name exact_name XPos 28 YPos 26 %% Slightly lawless, compared to the other L5 colonies anyhow. Tolerance 5 world desc special type habitat personatype MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 10 factions desig start GoSecondTime Msg1 Msg2 Quest1 <*:Q_LocalQuest> sub Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 30 MapHeight 25 start Msg1 NeededCells 6 Content1 Content2 Content4 Content5 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room name minimap <#######1##...............> inv Elevator name MiniMapComponent 1 end Scene name type ClubMap MapWidth 30 MapHeight 24 start Msg1 NeededCells 5 Content3 Content4 Content6 Content7 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......###..#1#...........> inv Elevator Destination -1 MiniMapComponent 1 end end end Scene name type entrance <*Building Short> special MallMap MapWidth 10 MapHeight 10 start Msg1 NeededCells 4 Content1 Content2 Content5 Content7 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 NeededCells 2 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Building Short> special MallMap MapWidth 20 MapHeight 17 SetFaction 9 start Msg1 NeededCells 1 Content Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room name minimap <2....1..............#---#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC bureaucrat job SetTeam 2 SetFaction 10 MiniMapComponent 2 end end inv NPC Corporate Executive home SetFaction 2 NPC Corporate Executive home SetFaction 7 NPC Corporate Executive home SetFaction 13 end Scene name type entrance <*Garage> special MallMap IndustrialTiles MapWidth 15 MapHeight 15 start Msg1 NeededCells 2 Content1 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building> special SetFaction 10 ClubMap PalaceParkTiles MapWidth 30 MapHeight 21 start Msg1 NeededCells 2 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2....1..............#...#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 end room name minimap <2....1..............#...#> inv NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 1 NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 2 end room name
    minimap <2....1..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 10 MiniMapComponent 1 NPC Soldier job SetTeam 3 SetFaction 10 MiniMapComponent 2 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 24 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content4 Content5 Content6 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end % End of Xianzai Spinner... end %% ************************* %% *** GAOS SPINNER *** %% ************************* Scene 0 3 mapwidth 25 mapheight 10 name exact_name XPos 17 YPos 20 %% Fairly tightly regulated. All kinds of things you can get elsewhere aren't allowed here. Tolerance -5 world desc special % Gaos Spinner is not a bad place to live, but wages are low and there aren't many % jobs outside of the tourism industry. Because of this, many people who were born % here leave to find work in other spinners. type habitat personatype CityMap IndustrialTiles entrance <*COLONY-1> % V1 = Message Counter SetFaction 10 factions desig Quest1 <*:Q_MECHA_ARENA_BASE> Quest2 <*:Q_LocalQuest> start GoSecondTime Msg1 Msg2 sub Scene name %% This building is the gateway between the lower level and the upper level. type entrance <*SpacePort> special ClubMap MapWidth 24 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content4 Content5 Content7 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <......###..###..#5#......> special inv Elevator MiniMapComponent 5 name destination end room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end Scene 0 3 mapwidth 25 mapheight 10 name XPos 35 YPos 3 special type ForestMap SetFaction 10 start Msg1 inv STC Building-SPACEPORT name Destination -1 end sub % There's a band of city running through the middle of the park, % and beaches on either side. CityZone Width 25 Height 4 MFX 1 MFY 4 Rect FloorType 4 Width 25 Height 1 MFX 1 MFY 1 Rect FloorType 4 Width 25 Height 1 MFX 1 MFY 10 % Begin Gaos Upper Level scenes Scene name type entrance <*ARENA> special SetFaction 5 ClubMap PalaceParkTiles MapWidth 24 MapHeight 22 start Msg1 NeededCells 2 Content1 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*BUILDING tall> special MallMap PalaceParkTiles MapWidth 42 MapHeight 17 start Msg1 NeededCells 3 Content1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type special entrance <*PARK> WildMap GAPFILL <-4 -9 -4 -4> PalaceParkTiles MapWidth 35 MapHeight 35 start Msg1 Content1 Content2 Content3 sub team 1 name team 2 name SetAlly 1 Passive team 3 name SetAlly 2 rect MFX 1 MFY 15 Width 5 Height 5 sub STC HIDDEN_ENTRANCE Destination -1 end rect Width 7 Height 7 name minimap <.###.#####1#########.###.> FloorType 1 inv Elevator name MiniMapComponent 1 Destination end Scene name type ClubMap PalaceParkTiles MapWidth 25 MapHeight 25 start Msg1 NeededCells 3 Content1 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......###..###..#5#......> inv Elevator MiniMapComponent 5 Destination -1 end end end % End Gaos Upper Level scenes end % End Gaos Spaceport end Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 20 MapHeight 15 start Msg1 NeededCells 4 Content1 Content2 Content4 Content5 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 16 NeededCells 1 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Building> special SetFaction 10 ClubMap PalaceParkTiles MapWidth 23 MapHeight 20 start Msg1 NeededCells 1 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2....1..............#...#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 2 end room name minimap <2....1..............#...#> inv NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 1 NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 2 end room name
    minimap <.....1..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 10 MiniMapComponent 1 end end % End of Gaos Spinner... end %% %% ************************** %% *** FUCHAL SPINNER *** %% ************************** %% Scene 0 3 mapwidth 23 mapheight 10 name exact_name XPos 30 YPos 20 % Fuchal is fairly tightly regulated. Tolerance -5 world desc special type personatype habitat MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 1 factions desig start GoSecondTime Msg1 Msg2 Quest1 <*:Q_LocalQuest> sub Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 36 MapHeight 20 start Msg1 NeededCells 4 Content3 Content4 Content7 Content8 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 15 MapHeight 20 NeededCells 2 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 25 MapHeight 25 start Msg1 NeededCells 4 Content1 Content2 Content3 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building> special SetFaction 1 ClubMap PalaceParkTiles MapWidth 24 MapHeight 21 start Msg1 NeededCells 2 Content1 Content2 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2...31..............#---#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 2 end room name
    minimap <....31..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 1 MiniMapComponent 1 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 20 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content3 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end Scene name SetFaction 2 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 2 Content2 5Min sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy SetAlly 1 2 room name desig Content1 Content3 Content4 end inv NPC Corporate Executive home SetFaction 2 end % End of Fuchal Spinner... end %% %% ************************** %% *** KOSAKA SPINNER *** %% ************************** %% Scene 0 3 mapwidth 30 mapheight 10 name exact_name XPos 20 YPos 24 % Low tolerance here- it's the beuracracy. Tolerance -5 world desc special type habitat personatype MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 10 factions desig start GoSecondTime Msg1 Msg2 sub Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 30 MapHeight 30 start Msg1 NeededCells 6 Content1 Content2 Content3 Content4 Content5 Content6 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 NeededCells 2 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Building Short> special ClubMap MapWidth 20 MapHeight 19 start Msg1 NeededCells 4 Content1 Content2 Content3 Content4 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end inv NPC Privateer home SetFaction 6 end Scene name type entrance <*Garage> special MallMap IndustrialTiles MapWidth 19 MapHeight 12 start Msg1 NeededCells 3 Content1 Content2 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building Tall> special SetFaction 10 ClubMap PalaceParkTiles MapWidth 30 MapHeight 21 start Msg1 NeededCells 2 Content1 Content2 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2...31..............#...#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 2 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 3 end room name minimap <2...31..............#...#> inv NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 1 NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 2 end room name
    minimap <2....1..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 10 job MiniMapComponent 1 NPC Soldier SetTeam 3 SetFaction 10 MiniMapComponent 2 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 24 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content3 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end Scene name SetFaction 13 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 2 Content2 5Min sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy SetAlly 1 2 room name desig Content1 Content3 Content4 end inv NPC Corporate Executive home SetFaction 13 end Scene name type entrance <*Building> special SetFaction 1 ClubMap PalaceParkTiles MapWidth 15 MapHeight 15 start Msg1 NeededCells 1 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name
    minimap <2....1..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 1 job MiniMapComponent 1 NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 2 end end Scene name type entrance <*Building> special SetFaction 11 ClubMap PalaceParkTiles MapWidth 15 MapHeight 15 start Msg1 NeededCells 1 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name
    minimap <2....1..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 11 job MiniMapComponent 1 NPC Rocket Star SetTeam 3 MiniMapComponent 2 end end % End of Kosaka Spinner... end %% %% ************************* %% *** TOHRU SPINNER *** %% ************************* %% Scene 0 3 mapwidth 20 mapheight 10 name exact_name XPos 27 YPos 13 Tolerance 0 world desc special type personatype habitat MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 10 factions desig Quest1 <*:Q_LocalQuest #50> start GoSecondTime Msg1 Msg2 sub Scene name type entrance <*Building> special MallMap MapWidth 25 MapHeight 17 start Msg1 NeededCells 4 Content3 Content4 Content6 Content7 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 24 MapHeight 15 NeededCells 2 Content Content3 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Garage> special MallMap MapWidth 20 MapHeight 20 IndustrialTiles SetFaction 10 % V1 = First time counter start GoBeenBefore Msg1 Msg2 NeededCells 3 Content1 Content2 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <####################1#2#3> inv Elevator name destination MiniMapComponent 1 Elevator name destination MiniMapComponent 2 Elevator name destination MiniMapComponent 3 end Scene name SetFaction 2 special type ClubMap MapWidth 25 MapHeight 25 start Msg1 NeededCells 2 Content2 sub team 1 team 2 name SetAlly 1 Passive Team 3 name SetAlly 1 2 room name desig Content3 Content4 room minimap <############1###...##...#> inv Elevator Destination -1 MiniMapComponent 1 end end inv NPC Corporate Executive home SetFaction 2 end Scene name SetFaction 7 type special ClubMap MapWidth 25 MapHeight 25 start Msg1 NeededCells 2 Content2 sub team 1 team 2 name SetAlly 1 Passive Team 3 name SetAlly 1 2 room name desig Content3 Content4 room minimap <############1###...##...#> inv Elevator Destination -1 MiniMapComponent 1 end end inv NPC Corporate Executive home SetFaction 7 end Scene name SetFaction 13 type special ClubMap MapWidth 25 MapHeight 25 start Msg1 NeededCells 2 Content2 sub team 1 team 2 name SetAlly 1 Passive Team 3 name SetAlly 1 2 room name desig Content3 Content4 room minimap <############1###...##...#> inv Elevator Destination -1 MiniMapComponent 1 end end inv NPC Corporate Executive home SetFaction 13 end end Scene name type entrance <*Building> special SetFaction 10 ClubMap MapWidth 24 MapHeight 19 start GoFirstTime Msg1 Msg2 NeededCells 1 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2...31..............#---#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC MEcha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 3 end room name
    minimap <..2.31..............#&+&#> inv NPC Politician SetTeam 2 SetFaction 10 MiniMapComponent 1 NPC Privateer SetTeam 3 SetFaction 6 MiniMapComponent 3 STC DISPLAYCASE-1 name MiniMapComponent 2 Use Clue_Survival Clue_Science Clue_Repair Clue_Mysticism Clue_Insight Msg1 Msg2 Msg3 Msg4 Msg5 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 20 MapHeight 21 start Msg1 NeededCells 3 Content1 Content2 Content5 Content6 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end % End of Tohru Spinner... end % *************************** % *** EMERALD SPINNER *** % *************************** Scene 0 3 mapwidth 20 mapheight 10 name exact_name XPos 10 YPos 22 Tolerance 0 world desc special type habitat personatype MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 10 factions desig start GoSecondTime Msg1 Msg2 sub Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 22 MapHeight 17 start Msg1 NeededCells 2 Content2 Content5 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......#1#..2#3..#4#......> inv Elevator MiniMapComponent 1 name destination Elevator MiniMapComponent 2 name destination Elevator MiniMapComponent 3 name destination Elevator MiniMapComponent 4 name destination end Scene name MapWidth 17 MapHeight 17 type BoxMap Special FloorType 1 Content Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 rect minimap <......###..#1#...........> special inv Elevator MiniMapComponent 1 Destination -1 end rect special minimap <...........123..###......> inv STC VENDING-1 MiniMapComponent 1 PDir 2 STC VENDING-1 MiniMapComponent 2 PDir 2 STC VENDING-1 MiniMapComponent 3 PDir 2 end forest forest lake end Scene name type special MallMap MapWidth 17 MapHeight 17 start Msg1 NeededCells 2 Content3 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......##...#1...##.......> inv Elevator Destination -1 MiniMapComponent 1 end end Scene name type special MallMap MapWidth 17 MapHeight 17 start Msg1 NeededCells 2 Content3 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......##...#1...##.......> inv Elevator Destination -1 MiniMapComponent 1 end end Scene name type special MallMap MapWidth 17 MapHeight 17 start Msg1 NeededCells 2 Content1 Content1 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......##...#1...##.......> inv Elevator Destination -1 MiniMapComponent 1 end end end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 NeededCells 2 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Building> special SetFaction 10 MallMap PalaceParkTiles MapWidth 36 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content3 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2...31..............#...#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 2 end room name minimap <2...31..............#...#> inv NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 1 NPC Mecha Pilot SetTeam 3 SetFaction 10 MiniMapComponent 2 end room name
    minimap <2....1..............#&+&#> inv NPC Politician name SetTeam 2 SetFaction 10 job MiniMapComponent 1 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 2 end end Scene name type entrance <*SpacePort> special ClubMap MapWidth 20 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content3 Content4 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end Scene name SetFaction 13 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 3 Content2 sub team 1 team 2 name SetAlly 1 Passive Team 3 name SetAlly 1 2 room name desig Content3 Content4 end inv NPC Corporate Executive home SetFaction 13 end Scene name SetFaction 7 type entrance <*Building> special MallMap MapWidth 15 MapHeight 15 start Msg1 NeededCells 3 Content2 sub team 1 team 2 name SetAlly 1 Passive Team 3 name SetAlly 1 2 room name desig Content3 Content4 end inv NPC Corporate Executive home SetFaction 7 end % End of Emerald Spinner... end %% %% ************************** %% *** THELES SPINNER *** %% ************************** %% Scene 0 3 mapwidth 20 mapheight 10 name exact_name XPos 31 YPos 33 % What the hey!? Despite the lawlessness inside, Theles Spinner's trade % is tightly controlled. Most weapons are unavailable... unless you can % find the black market. Tolerance -10 world desc special type habitat personatype MegaCityMap entrance <*COLONY-1> % V1 = Message Counter % V2 = If nonzero, the ladder quest has been completed SetFaction 10 factions desig start GoSecondTime Msg1 Msg2 Quest1 <*:Q_CHARA_ARENA_BASE> Quest2 <*:Q_LocalQuest #25> sub Scene name type entrance <*SpacePort> special ClubMap MapWidth 20 MapHeight 21 start Msg1 NeededCells 4 Content1 Content2 Content3 Content4 Content5 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 room minimap <############1###...##...#> sub STC AIRLOCK-1 MiniMapComponent 1 destination end Scene 0 3 name type special terrain MapWidth 30 MapHeight 30 SpaceMap nu1 Microgravity Vacuum SpaceBackdrop inv STC COLONY-1 name Destination -1 XPos 15 YPos 15 end end Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 21 MapHeight 20 start Msg1 NeededCells 4 Content1 Content4 Content5 Content7 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building> mapwidth 28 mapheight 21 special MonkeyMap IndustrialTiles % L1 = First time counter start GoBeenBefore Msg1 Msg2 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room minimap <......###..#1#..#=#......> special sub StairsUp MiniMapComponent 1 name Door use GoJustUse GoLeaveDoor Msg101 Msg102 Msg103 Msg104 end room Content room Content2 Scene name special type terrain dentrance <*GoUp> NeededCells 1 content1 mapwidth 35 mapheight 35 MonkeyMap IndustrialTiles start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 10 room width 3 height 3 sub StairsDown Destination -1 end end % End Kapec Robots... end Scene name type entrance <*Building> mapwidth 28 mapheight 21 special MallMap start Msg2 Content1 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content1 Content2 Content3 room minimap <#######1###...##...##...#> special inv Elevator MiniMapComponent 1 name end Scene name special type terrain dentrance <*GoUp> NeededCells 3 content1 content2 content3 mapwidth 35 mapheight 35 MonkeyMap start msg1 sub Team 1 SetEnemy 2 Team 2 name type SetEnemy 1 Stat 2 15 room width 3 height 3 sub StairsDown Destination -1 end Scene name type Tolerance 20 special mapwidth 15 mapheight 15 MallMap entrance <*GoUp> NeededCells 3 Content1 Content2 Content3 Content4 Start GoBeenBefore Msg1 Msg2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 minimap <......##...#1...##.......> inv Elevator Destination -1 MiniMapComponent 1 end end % Hovel Market... end % Dark Hovel... end % Hovel Manor Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 NeededCells 2 Content1 Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name type entrance <*Building Short> special ClubMap MapWidth 20 MapHeight 19 start Msg1 NeededCells 3 Content2 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end % End of Theles Spinner... end %% %% ************************** %% *** YATATE SPINNER *** %% ************************** %% Scene 0 3 mapwidth 27 mapheight 10 name exact_name XPos 20 YPos 29 Tolerance 0 world desc special type personatype habitat MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 11 factions desig Quest1 <*:Q_MECHA_ARENA_BASE> start GoSecondTime Msg1 Msg2 sub Scene name

    MetaTerrain 0 0 desig roguechar <*> SDL_SPRITE use Altitude 1 Scale 3 Pass -100 Msg1 MetaTerrain 0 0 desig roguechar <*> SDL_SPRITE use Altitude 1 Pass -100 Msg1 % ********************** % *** CONTAINERS *** % ********************** Prop 5 name desig roguechar <=> SDL_SPRITE use GoLocked CLUE_CODEBREAKING GoNoUnlock GoNotLocked CLUE_INSIGHT Msg-1 Msg-2 Msg-3 Msg-4 Msg-5 Msg-6_1 CMsg-6_1 Msg-6_2 CMsg-6_2 Prop 10 name Desig roguechar <=> use GoLocked CLUE_CODEBREAKING GoNoUnlock GoNotLocked CLUE_INSIGHT Mesh 11 SDL_SKIN SDL_SPRITE Frame 2 Msg-1 Msg-2 Msg-3 Msg-4 Msg-5 Msg-6_1 CMsg-6_1 Msg-6_2 CMsg-6_2 Prop 50 name Desig roguechar <=> use GoLocked CLUE_CODEBREAKING GoNoUnlock GoNotLocked CLUE_INSIGHT Mesh 4 Frame 4 sdl_SKIN sdl_SPRITE Msg-1 Msg-2 Msg-3 Msg-4 Msg-5 Msg-6_1 CMsg-6_1 Msg-6_2 CMsg-6_2 % ******************* % *** USABLES *** % ******************* MetaTerrain 0 0 desig % The PC may be able to revive this victim. name roguechar SDL_SPRITE Use GoNoSkill Clue_Medicine GoDie Msg1 Msg2 Msg3 Msg4 Prop 10 name desig SDL_SPRITE % V1 = Recharge Counter roguechar <+> use CLUE_MYSTICISM GoNoDice GoBeenBefore Msg1 Msg2 Msg3 Msg4 Prop 50 name desig roguechar <+> SDL_Sprite SDL_Colors <197 80 69 201 205 229 181 185 207> % V1 = Recharge Counter use CLUE_MYSTICISM GoNoDice GoBeenBefore CLUE_INSIGHT CLUE_SCIENCE Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Prop 5 name Msg5 Msg6 Msg7 Msg8 Msg9 Msg10_1 <%name5% got away... but at least we stopped \OPR %5% for now.> CMsg10_1 Msg10_2 <%name5%'s legacy of evil stops here...> CMsg10_2 Msg11 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC PLOT-LMPERSONALJOB NPC Mecha Pilot chardesc Old Villainous bio <%name5% was %name1%'s employer a long time ago. There's a lot of bad blood between the two.> end %% %% *LM_GoSolo Content %% %% The Lancemate is doing something for a day or two, after which they'll %% probably reappear in the city someplace. %% %% PARAM1: The lancemate in question %% PARAM2: The city in which this job will happen %% Content name requires <*LM_GoSolo> % E1 is the LM % E2 is the town % E3 is a local meeting scene, for afterwards Element3 % P%id%01 = timer update start GH2/series/CG_BIO_Default.txt0000644000175000017500000000153411326004535014557 0ustar kaolkaol%% Biography Event List %% %% The biography will introduce the personal conflict of this PC. It may also %% contribute some NPCs or other details to the egg. %% %% CONFLICT TYPES: %% .General The default %% .Destitute The PC's family is too poor Biography desc requires Cheerfulness 15 Biography desc requires Cheerfulness -15 Biography desc requires Easygoingness 15 Biography desc requires Easygoingness -15 Biography desc requires Sociability 15 Biography desc requires Sociability -15 GH2/series/MEGA_PoliceMissions.txt0000644000175000017500000005075411374513723015701 0ustar kaolkaol%% %% *POLICEMISSION_Capture Content %% %% The PC is going to capture an enemy vehicle. %% %% This mission gives a point of law and four FacXP points. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The enemy faction %% Content name requires <*POLICEMISSION_Capture> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The enemy faction % E4: The combat scene Element4 Place4 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 4 2 %% The ship's exterior %% To capture the ship, defeat all the guards, weapons, and %% thrusters. Damaging the cargo segments of the ship may result %% in the ship being destroyed. SetFaction 3 % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = "Just defeat the guards" message counter % L4 = Initial headcount of Team 3 MapWidth 50 MapHeight 50 Start nu1 nu2 GoLoseMission GoWinMission nu4 nu5 end Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 4 5 SetAlly 6 ParaX 5 ParaY 25 team 2 name type SetEnemy 1 6 SetAlly 4 5 Deploy ParaX 45 ParaY 25 team 3 name % This team is neutral- the PC wants to capture these % parts intact. team 4 name setenemy 1 6 setally 2 3 5 % Destroy the weapons and propulsion to incapacitate the % cruiser. team 5 name setenemy 1 6 setally 2 3 4 Team 6 name setally 1 setenemy 2 4 5 rect name special width 12 height 12 MFX 37 MFY 19 sub SuperProp requires <*Cruiser> Team1 3 Team2 4 Team3 5 end end end inv STC PLOT-MECHAMISSION-WANDER end %% %% *POLICEMISSION_Bonus Content %% %% In General: %% The PC has just defeated some mecha... and uncovered a clue leading to %% another encounter. The player can either take the reward so far and run, %% or perform the second mission for a huge bonus. %% %% In practice, not all bonus missions will follow this pattern. %% To set the bonus reward use %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The enemy faction Content name requires <*POLICEMISSION_Bonus> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The enemy faction % E4: The combat scene Element4 Place4 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start Msg%id%01 Msg%id%02 % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub Persona 1 % V%id%01 = Debriefing msg greeting .%id%_GoOfferReward result%id%01 result%id%02 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 MetaScene 4 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E3 SetFaction 3 Start nu1 nu2 Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER name end %% %% *POLICEMISSION_Versus Content %% %% The PC has been sent to fight some mecha belonging to an enemy faction. %% This is going to earn the PC the enemity of that faction. %% %% Winning this mission gives a point of Lawful reputation. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed %% Param3: The enemy faction Content name requires <*POLICEMISSION_Versus> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The enemy faction % E4: The combat scene Element4 Place4 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle w/o insight, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email % SubPlot4 = Activate the bonus mission SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> SubPlot4 <*POLICEMISSION_Bonus 1 2 3> sub MetaScene 4 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E3 SetFaction 3 Start nu1 nu2 GoBonusEnding GoBasicEnding Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end Content name requires <*POLICEMISSION_Versus> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The enemy faction % E4: The combat scene Element4 Place4 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 4 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E3 SetFaction 3 Start .side_story <*CombatSideStory %e2% %e3%> nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end %% %% *POLICEMISSION_Basic Content %% %% The PC has been sent to fight some mecha, but hasn't been told who he'll be fighting %% or anything else about it. Therefore, this mission is a blank slate of violent goodness. %% %% Winning this mission gives a point of Lawful reputation. %% %% This subplot is responsible for loading its own conclusions: typically, there will be %% one conclusion for winning the battle, one conclusion for losing the battle, and a third %% conclusion for running out of time. %% %% The master plot must have a PayRate set. The master plot is also responsible for E1's %% mission reminder message. %% %% Param1: The NPC offering the mission %% Param2: The outdoors scene where the encounter will be placed Content name requires <*POLICEMISSION_Basic> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene Element3 Place3 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 Start nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end Content name requires <*POLICEMISSION_Basic 2:SPACE> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene Element3 Place3 <2> % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 AsteroidMap RockyTiles % Suffocation effect... Vacuum SpaceBackdrop Start nu1 nu2 Msg1 Msg2 Msg3 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end Content name requires <*POLICEMISSION_Basic> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene % E4: The enemy faction Element3 Place3 <2> Element4 % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email % SubPlot4 = Bonus Mission SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> SubPlot4 <*POLICEMISSION_Bonus 1 2 4> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E4 SetFaction 4 Start nu1 nu2 GoBonusEnding GoBasicEnding Msg1 Msg2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end Content name requires <*POLICEMISSION_Basic> desc % E1: The Mission Provider % E2: The scene where the encounter will be placed % E3: The combat scene % E4: The enemy faction Element3 Place3 <2> Element4 % P%id%01 = Time Limit % P%id%02 = Have entered combat update start % SubPlot1 = Win the battle, get a reward % SubPlot2 = Lose the battle, get scorned % SubPlot3 = Run out of time, get email SubPlot1 <*Util_WinMission&Mecha 1> SubPlot2 <*Util_LoseMission&Mecha 1> SubPlot3 <*Util_TimeOver 1> sub MetaScene 3 2 % L1 = Encounter Over Counter MapWidth 50 MapHeight 50 % Set this metascene's faction to E4 SetFaction 4 Start .side_story <*CombatSideStory %e2% %e4%> nu1 nu2 Msg1 Msg2 % Random scene content- there's a chance that an enemy or an ally will show up. Content1 Content2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 team 3 name SetEnemy 2 SetAlly 1 ParaX 10 ParaY 5 end end inv STC PLOT-MECHAMISSION-WANDER end GH2/series/ARENAMISSION_CrimeCorporate.txt0000644000175000017500000000417611326004535017024 0ustar kaolkaol%% Crime Corporate missions! Missions which may be taken by either crime %% or corporate factions! It's like those crazy multicolored magic cards! ArenaMission name requires <*MISSION (Crime|Corporate) -!Ne> desc %% Created by palefire %% Edited by Joe MapWidth 50 MapHeight 50 PayRate 400 AsteroidMap terrain RockyTiles Vacuum SpaceBackdrop Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Investigation Counter Start nu1 nu2 nu3 GoWinMission GoDebrisLost Msg1 Msg2 Msg3 Msg4 sub Team 1 SetEnemy 2 3 ParaX 5 ParaY 5 Team 2 SetEnemy 1 3 Deploy ParaX 20 ParaY 30 Team 3 setEnemy 1 2 Deploy ParaX 30 ParaY 20 Team 4 setEnemy 1 ParaX 25 ParaY 25 end inv CombatProp 30 SetTeam 4 name sdl_sprite roguechar <&> use msg5 Scale 2 sub Turret name size 8 armor 8 sub MLauncher 15 name sub stc dcm-15 Magazine 10 end end end end GH2/series/PLOT_Personal.txt0000644000175000017500000000657211326004535014561 0ustar kaolkaol%% %% *************************** %% *** PERSONAL PLOTS *** %% *************************** %% %% These plots don't involve mecha, or missions, or things blowing up. %% Mostly they're just here to move the NPCs around a bit and maybe %% give the PC a few non-combat tasks. %% Plot name desc requires <*GENERAL> % E1 is the NPC seeking repairs % E2 is the garage where repairs are to be had element1 element2 % P1 = Initialization Counter/Time Limit % P2 = Original Home of E1 update start end sub Persona 1 rumor <%name1%'s mecha is in the shop.> % V1 = Have already interacted greeting GoChat GoNoEnemy GoEndPlot *result1 <*BetterBeGoing GoEndPlot> result2 GoR2Accept GoR2Fail result3 GoR3Fail 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 Msg2_1 Msg2_2 Msg2_3 Msg3_1 Msg3_2 Msg3_3 Msg4 Msg5 Msg6 Prompt1 Prompt1_2 Prompt1_3 Prompt1_4 Prompt1_5 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 Prompt3 <[Attempt Repair]> end GH2/series/MEGA_LM_CharDev.txt0000644000175000017500000016016011376734116014641 0ustar kaolkaol%% %% Lancemate Character Development %% %% %% *LM_NonComCharDev Content %% %% Something will happen outside of combat. %% %% PARAM1: The lancemate in question %% Content name desc requires <*LM_NonComCharDev -&beancounter> % E1 is the lancemate in question % E2 is the root scene Element2 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> % SubPlot1 is the personal job. % SubPlot2 is the solo mission- if the PC refuses to come along SubPlot1 <*LM_PersonalJob 1 2> SubPlot2 <*LM_GoSolo 1 2> sub Persona 1 greeting *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> result%id%01 result%id%02 result%id%03 .%id%_GoGoing .%id%_GoQuit Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Msg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 Msg%id%05_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 <%name1%, we don't have time for this.> Prompt%id%03_1 Prompt%id%03_2 end Content name desc requires <*LM_NonComCharDev 1:PDASS> % E1 is the lancemate in question % E2 is a local meeting/garage/etc scene % E3 is a local mecha pilot to challenge % E4 is the root scene Element2 Element3 Place3 <2 (Guards) sd ally> Element4 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLose Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 % SubPlot1 is the informal duel. SubPlot1 <*:NC_TournamentMatch 3 2> sub Persona 1 greeting .%id%_GoChat *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoJustEnd .%id%_GoEnd result%id%01 *result%id%02 <*LM_RefuseRequest .%id%_GoEnd> result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Msg%id%03_10 <%name3% is an ace... \SPR %3% 's practically unbeatable.> CMsg%id%03_10 Msg%id%03_11 CMsg%id%03_11 Msg%id%03_12 <%name3% is an ace pilot, the best of the best. Honestly speaking I don't think you have a chance to beat \OPR %3% .> CMsg%id%03_12 Msg%id%03_13 CMsg%id%03_13 Msg%id%03_20 <%name3% is one of the elites... If you're interested in this fight, you better be ready to face one of the best pilots in the league.> CMsg%id%03_20 Msg%id%03_30 <%name3% is an arena veteran. It's up to you to judge whether or not you can take \OPR %3% .> CMsg%id%03_30 Msg%id%03_40 <%name3% is an average pilot. Of course, since this tourney started \SPR %3% 's been getting a lot of practice.> CMsg%id%03_40 Msg%id%03_50 <%name3% is just a rookie... or, at least, \SPR %3% was the last time I checked. I think you should be able to beat \OPR %1% .> CMsg%id%03_50 Msg%id%03_60 CMsg%id%03_60 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 <%name1% suggested that you challenge %name3% in \EXACT_SCENE %2% as part of a Pro Duelist League tournament.> Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 <%name3%, you say? I can beat \OPR %3% .> CPrompt%id%01_3 Prompt%id%01_4 <%name3%, huh? That could be trouble.> CPrompt%id%01_4 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 CPrompt%id%05 Prompt%id%05_1 end Content name desc requires <*LM_NonComCharDev 1:M.com> % E1 is the lancemate in question % E2 is a local meeting/garage/etc scene % E3 is a local mecha pilot to challenge % E4 is the root scene Element2 Element3 Place3 <2 (Guards) sd ally> Element4 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> % SubPlot1 is the informal duel. SubPlot1 <*InformalChallenge 3 2> sub Persona 1 greeting *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoJustEnd result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%03 Msg%id%03_1 Msg%id%04 Msg%id%04_1 CMsg%id%04_1 Msg%id%04_2 CMsg%id%04_2 Msg%id%04_3 CMsg%id%04_3 Msg%id%04_4 CMsg%id%04_4 Msg%id%05 Msg%id%05_1 CMsg%id%05_1 Msg%id%06 Msg%id%06_1 Msg%id%07 Msg%id%07_1 Msg%id%08 <%name1% suggested that you challenge %name3% to a duel. \SPR %3% can be found at \EXACT_SCENE %2% .> Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Prompt%id%05_1 end Content name desc requires <*LM_NonComCharDev 1:A.jr_ (1:M.pro|1:M.see|1:M.cha|1:MOOK_) -!Ne 1:TRAIN> % E1 is the lancemate in question % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> sub Persona 1 greeting *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoEndPlot result%id%01 result%id%02 .%id%_R2Skills result%id%03 .%id%_R3Skills result%id%04 .%id%_R4Skills result%id%05 .%id%_R5Skills result%id%06 .%id%_R6Skills result%id%07 .%id%_R7Skills Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Msg%id%03 Msg%id%03_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 end Content name desc requires <*LM_NonComCharDev 1:M.pro -!Ne> % E1 is the lancemate in question % E2 is the root scene % E3 is a local character, with a faction, not an enemy of E2 or E1 % E4 is a mecha pilot enemy of E3, not an ally of E1 or the PC Element2 Element3 Element4 % P%id%01 = Chat counter % P%id%02 = SkillXP amount start .%id%_scenetype .%id%_GoEndPlot % Store the renown of the NPC in question. This will be used to give a SkillXP bonus % at the end of the mission. update % Upon winning, give an XP award to the three mecha combat skills. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> Msg%id%03 Msg%id%03_1 Msg%id%03_2 <%name4% didn't stand a chance.> Msg%id%04 <%name4% got away...> Msg%id%04_1 Msg%id%04_2 %% SubPlot1 is the mission itself SubPlot1 <*:StopNPC 4> sub Persona 1 % V%id%01 = Renown counter, for telling how good E4 is. greeting .%id%_GoEnd *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoAcceptMission .%id%_GoDenyMission *result%id%01 <*LM_LetsDoIt&SeekRumor .%id%_GoAcceptMission> *result%id%02 <*LM_RefuseRequest .%id%_GoDenyMission> result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <%name4% is here on a mission. I advise that we be the ones who stop \OPR %4% .> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <%name4% is in %name2% for some nefarious purpose. We must stop \OPR %4% !> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 <%name4% is in %name2% on a mission. %name1% suggested that you attempt to stop \OPR %4% .> Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%03_6 CMsg%id%03_6 Msg%id%04_1 <%name4% is just a rookie pilot... \SPR %4% may have a bit more experience than you, but I still think you can take \OPR %4% .> CMsg%id%04_1 Msg%id%04_2 <%name4% is an experienced pilot. It may be a difficult fight, but it's not like \SPR %4% 's a combat veteran or anything.> CMsg%id%04_2 Msg%id%04_3 <%name4% is a combat veteran. If you don't think we can take \OPR %4% , maybe we shouldn't do this.> CMsg%id%04_3 Msg%id%04_4 <%name4% is an elite pilot. Maybe you're right, this match-up isn't a good idea...> CMsg%id%04_4 Msg%id%04_5 <%name4% is an ace pilot. Yeah, this fight is probably impossible... but imagine what people will say if you win!> CMsg%id%04_5 Msg%id%04_11 <%name4% isn't much more experienced than you. I really believe that we can take \OPR %4% .> CMsg%id%04_11 Msg%id%04_12 CMsg%id%04_12 Msg%id%04_13 CMsg%id%04_13 Msg%id%04_14 <%name4% is a mediocre pilot who's marginally more renowned than you. I can't imagine an easier match which would still count as a fair fight.> CMsg%id%04_14 Msg%id%04_15 CMsg%id%04_15 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 <%name1%, I don't want to do that.> Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 CPrompt%id%04 Prompt%id%04_1 <%name4%'s a really good pilot, isn't \SPR %4% ?> Prompt%id%05 Prompt%id%05_1 Prompt%id%06 Prompt%id%06_1 Prompt%id%07 Prompt%id%07_1 end Content name desc requires <*LM_NonComCharDev 1:M.see> PayRate 150 % E1 is the lancemate in question % E2 is the character who will offer the mission % E3 is the root scene % E4 is the NPC's faction Element2 Element3 Element4 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> %% SubPlot1 is the mission itself SubPlot1 <*UTIL_ReferredMission 1 2> sub Persona 1 &%id%_E2Scene greeting .%id%_GoEnd *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoAcceptMission .%id%_GoDenyMission *result%id%01 <*LetsGoToMission .%id%_GoAcceptMission %2% &%id%_E2Scene> *result%id%02 <*LM_WhyNotMission .%id%_GoAcceptMission .%id%_GoDenyMission %2%> Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <%name1% suggested that you go see %name2% about a mission for %name4%.> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name desc requires <*LM_NonComCharDev -1:NOFAC -1:PDASS> PayRate 150 % E1 is the lancemate in question % E2 is the character who will offer the mission % E3 is the root scene % E4 is the lancemate's faction Element2 Element3 Element4 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> %% SubPlot1 is the mission itself SubPlot1 <*UTIL_ReferredMission 1 2> sub Persona 1 &%id%_E2Scene greeting .%id%_GoEnd *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoAcceptMission .%id%_GoDenyMission *result%id%01 <*LetsGoToMission .%id%_GoAcceptMission %2% &%id%_E2Scene> *result%id%02 <*LM_WhyNotMission .%id%_GoAcceptMission .%id%_GoDenyMission %2%> Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name desc requires <*LM_NonComCharDev 1:M.pro 1:NOFAC 1:TRAIN> % E1 is the lancemate in question % E2 is the PC's faction % E3 is a local member of said faction % E4 is the root scene Element2 Element3 Element4 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> %% SubPlot1 is the join task SubPlot1 <*LM_WannaJoinFaction 1 2 3> sub Persona 1 &%id%_E2Scene greeting .%id%_GoEnd *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <%name1% told you that \SPR %1% wants to join %name2%. You should go speak to %name3% about it.> Msg%id%03 Msg%id%03_1 <> Msg%id%03_2 <> Msg%id%04 Msg%id%04_1 <> Msg%id%04_2 <> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 <%name3%'s the one you need to see about that.> Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end Content name desc %% Results vary based on PC's actions: %% - Pay off LM's debts, good talk afterwards: M->Seeker %% - Pay off LM's debts, poor talk afterwards: M->Competition %% - Distance self from E1: M->Competition, leaves lance %% - Scare away debt-owner: A->Admire %% - Don't do anything, lose to debt-owner: A->Disrespect requires <*LM_NonComCharDev 1:M.mer (1:A.---|1:A.jr_|1:A.equ) 1:evil_ -!Ne 1:TRAIN> % E1 is the lancemate in question % E2 is the character owed money Element2 % P%id%01 = Have started confrontation. Once the confrontation has begun, % leaving the building triggers the ambush. % P%id%02 = Phase counter, if 0 then E2 will speak, if 1 then E1 will speak % P%id%03 = Reset counter. If nonzero, PC has resolved situation and just % needs to speak with E1. % P%id%04 = Have listened to E2's story % P%id%05 = E2 has left the building and is waiting for ambush % Upon entering a public building, nearly any public building, this % plot will unfold. start .%id%_scenetype %% If E1 dies, the debt is considered settled. Set E1's motivation %% to Seeker, since even if the PC revives them this is bound to be %% a life-altering moment. Faint%1% %% Killing off E2 will make E1 like you. Faint%2% %% If the plot has started, E1 and E2 will hurl insults at one another. 5min .%id%_GoE1Turn End .%id%_GoForceChat Go_%id1%_%plotid1%_GoWin Go_%id1%_%plotid1%_GoLoss .%id%_GoEndPlot .%id%_%plotid%_GoChangeSeeker .%id%_seeker_skills .%id%_%plotid%_GoChangeCompetitor .%id%_%plotid%_GoChangeAdmire .%id%_competitor_skills .%id%_%plotid%_GoQuitLance Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%06_1 Msg%id%06_2 Msg%id%06_3 Msg%id%06_4 Msg%id%07 Msg%id%07_1 Msg%id%07_2 Msg%id%07_3 Msg%id%07_4 Msg%id%08 Msg%id%09 % SubPlot1 is the dynamic ambush SubPlot1 <*:DynamicAmbushByNPC 2> sub Persona 1 greeting .%id%_GoRevert % The PC has solved this issue through conversation, or by paying % off E1's debts. End the plot and set a new life path for E1. .%id%_GoTalkingEnd % The PC can try speaking with E1 about the issue, but it's likely % to be useless... .%id%_GoDiscuss result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 .%id%_GoR6Fail result%id%07 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%05_1 Prompt%id%06 Prompt%id%06_1 Prompt%id%07 CPrompt%id%07 Persona 2 greeting .%id%_GoChat .%id%_GoExplain result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 result%id%09 result%id%10 result%id%11 result%id%12 result%id%13 result%id%14 Msg%id%01 Msg%id%02 Msg%id%03 <%name1% storms out of the building.> Msg%id%04 Msg%id%05 Msg%id%06 <%name1% and I did some freelance work together a while back. After completing a string of missions \SPR %1% ran off with all the pay, my mecha, and stuck me with the repair bill to boot!> Msg%id%07 Msg%id%08 Msg%id%09 Msg%id%10 <%name2% has left the building.> Msg%id%11 Msg%id%12 Msg%id%13 Msg%id%14 Msg%id%15 Msg%id%16 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 CPrompt%id%07 Prompt%id%08 Prompt%id%09 CPrompt%id%09 Prompt%id%10 CPrompt%id%10 Prompt%id%11 Prompt%id%12 Prompt%id%13 CPrompt%id%13 Prompt%id%14 CPrompt%id%14 end inv NPC Mecha Pilot end Content name desc requires <*LM_NonComCharDev 1:M.mer> PayRate 200 % E1 is the lancemate in question % E2 is the character who will offer the mission % E3 is the root scene Element2 Element3 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> %% SubPlot1 is the mission itself SubPlot1 <*UTIL_ReferredMission 1 2> sub Persona 1 &%id%_E2Scene greeting .%id%_GoEnd *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> .%id%_GoAcceptMission .%id%_GoDenyMission *result%id%01 <*LetsGoToMission .%id%_GoAcceptMission %2% &%id%_E2Scene> *result%id%02 <*LM_WhyNotMission .%id%_GoAcceptMission .%id%_GoDenyMission %2%> result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <%name1% told you about a lucrative mission being offered by %name2%.> Msg%id%03 Msg%id%03_1 <> CMsg%id%03_1 Msg%id%03_2 <> CMsg%id%03_2 Msg%id%03_3 <> CMsg%id%03_3 Msg%id%03_4 <> CMsg%id%03_4 Msg%id%03_5 <> CMsg%id%03_5 Msg%id%03_6 <> CMsg%id%03_6 Msg%id%04_1 Msg%id%04_2 <%name2% owes me a favor. I'm owed lots of favors. Let's call \OPR %2% and get to work.> Msg%id%04_3 Msg%id%04_4 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 <%name1%, we're too busy to bother with that.> Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%06 end Content name desc requires <*LM_NonComCharDev 1:A.tha 1:TRAIN> % E1 is the lancemate in question % E2 is a local outdoors scene % E3 is the town- end the plot if we leave town before activating secret place Element2 Element3 % P%id%01 = Chat counter start .%id%_scenetype .%id%_GoEndPlot Msg%id%01 <%name1% wants to talk with you about something.> Msg%id%02 <%name1% approaches you.> %% SubPlot1 is the LM's secret place SubPlot1 <*LM_SecretPlace 1 2> sub Persona 1 greeting *.%id%_GoWantsToTalk <*LancemateWantsToTalk .%id%_GoExplainPlan .%id%_GoTalkLater> .%id%_GoExplainPlan *.%id%_GoTalkLater <*TalkAgainWhenYouHaveTime> result%id%01 result%id%02 .%id%_GoR2Fail Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%03 <%name1% wants to bring you to a special place in \EXACT_SCENE %2% .> Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 Msg%id%05_1 Msg%id%05_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 end %% %% *LM_CombatCharDev Content %% %% Something will happen during combat. This character development is responsible for %% ending its own plot. %% %% PARAM1: Outdoors scene where encounter will take place %% PARAM2: Enemy faction %% PARAM3: The lancemate in question %% Content name desc requires <*LM_CombatCharDev 3:A.---> %% If the NPC is in play, set Attitude. update .%id%_GoCheckMinor .%id%_GoEqual Msg%id%01 Msg%id%02 Msg%id%03 GH2/series/ATLAS_L5Region.txt0000644000175000017500000041520311376734116014515 0ustar kaolkaol% Default Atlas File World 200 name special desig desc <> SpaceMap MapWidth 50 MapHeight 50 XYWrap %% %% *************************** %% *** MAQUISE SPINNER *** %% *************************** %% Scene 0 3 mapwidth 30 mapheight 10 name exact_name XPos 42 YPos 22 Tolerance 0 world desc special type habitat personatype MegaCityMap entrance <*COLONY-1> % V1 = Message Counter SetFaction 1 factions desig Quest1 <*:Q_MECHA_ARENA_BASE> start GoSecondTime Msg1 Msg2 sub Scene name type entrance <*DEPARTMENTSTORE> special MallMap MapWidth 30 MapHeight 20 start Msg1 NeededCells 6 Content1 Content2 Content3 Content4 Content5 Content6 Content9 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Hospital> special MallMap MapWidth 20 MapHeight 10 NeededCells 2 Content Content2 start Msg1 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room desig Content Content2 end Scene name % L1 = If alarm sounded on factory level, will be nonzero. SetFaction 2 type entrance <*Building Tall> special MallMap MapWidth 20 MapHeight 15 NeededCells 2 Content1 Content2 5Min start Msg1 inv NPC Corporate Executive name SDL_PORTRAIT SDL_COLORS <234 180 88 255 212 195 1 75 67> Chardesc Male Sociable Easygoing Pragmatic Age 6 home SetFaction 2 end sub team 1 team 2 name SetAlly 1 Passive Team 3 name Deploy SetAlly 1 2 room minimap <..1.................##=##> Special sub Door % The following update will unlock locked doors and reveal secret ones % if the PC has been granted armory access. update end inv StairsDown name MiniMapComponent 1 end room name desig Content1 Content3 Content4 Scene name SetFaction 2 type special % V1 = Security System Disabled % V2 = Message initialization Content1 Content2 Content3 Content4 MonkeyMap MapWidth 35 MapHeight 35 LockedDoorChance 85 SecretDoorChance 15 Start GoSecondTime Alarm 5Min TM1 PCATTACK Msg1 Msg2 Nsg3 Msg4 sub Door % The following update will unlock locked doors and reveal secret ones % if the PC has been granted armory access. update Team 1 Team 2 name SetAlly 3 Team 3 name type SetAlly 2 room Content sub StairsUp Destination -1 end end end Scene name type entrance <*Building Short> special MallMap MapWidth 20 MapHeight 15 start Msg1 NeededCells 3 Content1 Content2 Content3 Content4 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Garage> special MallMap IndustrialTiles MapWidth 19 MapHeight 12 start Msg1 NeededCells 2 Content1 Content3 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content Content2 end Scene name type entrance <*Building> special SetFaction 1 ClubMap PalaceParkTiles MapWidth 30 MapHeight 21 start Msg1 NeededCells 2 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 1 room name desig Content Content2 room name minimap <2...31..............#...#> inv NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 1 NPC Police Officer SetTeam 3 SetFaction 9 MiniMapComponent 2 end room name minimap <2...31..............#...#> inv NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 1 NPC Knight SetTeam 3 SetFaction 3 MiniMapComponent 2 end room name

    type habitat <> statline 3 3 3 3 1 1 1 1 MonsterTV 15 CloseCombat 1 Toughness 10 Dodge 2 NoCorpse Sealed Biotech sub torso sub Melee 1 name type Speed 1 end tail name end GH2/series/PFRAG_NiceToMeetYou.txt0000644000175000017500000010405411326004535015542 0ustar kaolkaol% TYPE: *NiceToMeetYou % &NoTrivial - A conversation must be involved, don't just jump to %1% % The PC is meeting someone presumably for the first time. % PARAM1: Exit script label Persona requires <*NiceToMeetYou> %% The absolute default NiceToMeetYou. One message, one reply, based on traits. Start result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Persona requires <*NiceToMeetYou LABOR> Start result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Persona requires <*NiceToMeetYou HasMecha> Start result%id%01 result%id%02 .%id%_GoR2TryCon .%id%_GoR2Fail result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 <\PC ? No, that doesn't ring any bells... It's nice to meet you, anyhow.> Msg%id%03_1 Msg%id%03_2 Msg%id%04 <\PC ? I think I've heard that name before... they say you're really good. It's nice to meet you.> Msg%id%04_1 Msg%id%04_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Persona requires <*NiceToMeetYou Shy> Start result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 Msg%id%03_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Persona requires <*NiceToMeetYou Village> Start .%id%_GoOutsider result%id%01 result%id%02 result%id%03 result%id%04 .%id%_GoLieFailed result%id%05 .%id%_GoFail result%id%06 result%id%07 result%id%08 Msg%id%01 Msg%id%01_1 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%04 Msg%id%05 Msg%id%06 Msg%id%07 Msg%id%08 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Prompt%id%05 Prompt%id%06 Prompt%id%07 Prompt%id%07_1 Prompt%id%08 Prompt%id%08_1 Persona requires <*NiceToMeetYou ~Shy -&NoTrivial> START Persona requires <*NiceToMeetYou Shopkeeper Heroic Ego -&NoTrivial> Start result%id%01 .%id%_GoFail result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Persona requires <*NiceToMeetYou Doctor> Start result%id%01 result%id%02 Msg%ID%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*NiceToMeetYou Executive -&NoTrivial> Start result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Persona requires <*NiceToMeetYou Charm ~Renowned> Start result%id%01 result%id%02 .%id%_GoFail result%id%03 result%id%04 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%03_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Persona requires <*NiceToMeetYou "Comet" ~Shopkeeper ~Passionate> Start result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Persona requires <*NiceToMeetYou "PDASS"> Start result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 Persona requires <*NiceToMeetYou "PDASS" ~Ego> Start result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%03_6 CMsg%id%03_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%06_1 Persona requires <*NiceToMeetYou Cheerful> Start result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*NiceToMeetYou Wangtta Easygoing ~Student> Start result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%03_1 Persona requires <*NiceToMeetYou Passionate NoMecha ~Citizen ~Young ~Sociable> START .%id%_NoRenown result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%04_1 Persona requires <*NiceToMeetYou Villainous -&NoTrivial> START .%id%_GoSecond result%id%01 Msg%id%01 Msg%id%02 Prompt%id%01 <[more]> Persona requires <*NiceToMeetYou ShopKeeper Young -&NoTrivial> START result%id%01 Msg%id%01 Prompt%id%01 <[more]> Persona requires <*NiceToMeetYou ShopKeeper Shy ~Old ~Ego> START result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Persona requires <*NiceToMeetYou ShopKeeper> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*NiceToMeetYou ShopKeeper Renowned ~Charm> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*NiceToMeetYou Sociable Passionate> START result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Persona requires <*NiceToMeetYou Sociable> START result%id%01 result%id%02 Msg%ID%01 Msg%ID%01_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%02 Persona requires <*NiceToMeetYou Spiritual ~Wangtta> START result%id%01 result%id%02 result%id%03 result%id%04 Msg%ID%01 Msg%ID%01_1 CMsg%ID%01_1 Msg%ID%01_2 CMsg%ID%01_2 Msg%ID%02 Msg%ID%02_1 CMsg%ID%02_1 Msg%ID%02_2 CMsg%ID%02_2 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Persona requires <*NiceToMeetYou> START result%id%01 result%id%02 result%id%03 result%id%04 Msg%ID%01 Msg%ID%01_1 CMsg%ID%01_1 Msg%ID%01_2 CMsg%ID%01_2 Msg%ID%01_3 CMsg%ID%01_3 Msg%ID%01_4 CMsg%ID%01_4 Msg%ID%01_5 CMsg%ID%01_5 Msg%ID%01_6 CMsg%ID%01_6 Msg%ID%02 Msg%ID%02_1 CMsg%ID%02_1 Msg%ID%02_2 CMsg%ID%02_2 Msg%ID%02_3 CMsg%ID%02_3 Msg%ID%02_4 CMsg%ID%02_4 Msg%ID%02_5 CMsg%ID%02_5 Msg%ID%02_6 CMsg%ID%02_6 Msg%ID%02_7 CMsg%ID%02_7 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Persona requires <*NiceToMeetYou +Tgs +Fcc> % requires that this happen with a sociable NPC at a corporate schmoozing event. START result%id%01 result%id%02 Msg%ID%01 Prompt%id%01 Prompt%id%02 GH2/series/RANCON_Corporate.txt0000644000175000017500000002106611326004537015133 0ustar kaolkaol% Corporate Scene Content % *CORP_EMPLOYEE content % Just some employees of the corporation. Content requires <*CORP_EMPLOYEE Public> element1 team1 teamdata1 inv NPC Corporate Executive update end Content requires <*CORP_EMPLOYEE Public> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_GENERAL GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper update end Content requires <*CORP_EMPLOYEE Public Comet> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> end inv NPC Corporate Executive SetFaction 2 end Content requires <*CORP_EMPLOYEE Public Comet> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_MECHA GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper SetFaction 2 end Content requires <*CORP_EMPLOYEE> element1 team1 teamdata1 inv NPC Scientist update end Content requires <*CORP_EMPLOYEE> element1 team1 teamdata1 inv NPC Mecha Pilot job update end Content requires <*CORP_EMPLOYEE ~Private> content Content requires <*CORP_EMPLOYEE Public HOELL> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> end inv NPC Corporate Executive SetFaction 7 end Content requires <*CORP_EMPLOYEE Public HOELL> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_WEAPON GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper SetFaction 7 end Content requires <*CORP_EMPLOYEE Public MUGLE> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> end inv NPC Corporate Executive SetFaction 13 end Content requires <*CORP_EMPLOYEE Public MUGLE> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_ARMOR GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper SetFaction 13 end % *CORP_RND content % Something the corporation has been working on. A PC who belongs to the corp and % advances far enough may be given permission to enter the R&D section and take % everything. Otherwise, this content exists to give enterprising Criminal PCs % something to steal. % PARAM: Number of local variable set when alarm deactivated Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv Treasure name Fudge 500000 desc end Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv Treasure name Fudge 350000 desc CLUE_SCIENCE GoFail Msg1 Msg2 end Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv HeadArmor 6 name desc mass -11 end Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv BodyArmor 7 name desc mass -11 end Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv ArmArmor 7 name desc mass -12 end Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv LegArmor 7 name desc mass -12 end Content requires <*CORP_RND> element1 Get%1% Msg%id%01 inv StatCyberware name desc CyberSlot Reflexes 3 Perception 4 Craft 2 Charm -1 end % *CORP_PUBLIC content % The stuff that everyone can visit. Low level offices, shops selling % the corporation's stuff, and so on. Content requires <*CORP_PUBLIC> name minimap <............1.......&---&> element1 team1 teamdata1 content sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_GENERAL GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper update Shopping 12 end Content requires <*CORP_PUBLIC Comet> name minimap <............1.......&---&> element1 team1 teamdata1 content sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_GENERAL GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper SetFaction 2 Shopping 12 end Content requires <*CORP_PUBLIC HOELL> name minimap <............1.......&---&> element1 team1 teamdata1 content sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_MECHA GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper SetFaction 7 Shopping 12 end Content requires <*CORP_PUBLIC MUGLE> name minimap <............1.......&---&> element1 team1 teamdata1 content sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_GENERAL GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper SetFaction 13 Shopping 12 end Content requires <*CORP_PUBLIC> name minimap <............1.......##+##> element1 team1 teamdata1 content1 sub Persona 1 *greeting <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> end inv NPC Corporate Executive update end % *CORP_Recruiter Content % Someone to recruit the PC into this corporation, if appropriate. Content requires <*CORP_RECRUITER> minimap <............1.......##+##> element1 team1 teamdata1 content1 sub Persona 1 greeting *GoTrain <*SKILL_TRAINING GoGoodbye> *GoCheckEligibility <*ENEMY_CHECK GoCheckFaction ChatNPCFac GoNil> GoCheckFaction *GoCheckSkills <*FACTION_ENTRANCE_EXAM GoJobOffer ChatNPCFac> *GoChat <*MISC_CHATTER> *GoGoodbye <*GOODBYE> *GoJobOffer <*FACTION_JOIN_OFFER ChatNPCFac GoJoin> *GoJoin <*FACTION_JOIN_ACCEPT ChatNPCFac> end inv NPC Corporate Executive update job end GH2/series/PLOT_CorpMissions.txt0000644000175000017500000004474011374513723015434 0ustar kaolkaolPlot name requires <*GENERAL -!Ne -Poor> PayRate 175 % E1 is a character who will offer the mission % E2 is the corporate faction that E1 belongs to % E3 is a scene where the encounter will take place % E4 is the town- needed for email check Element1 element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Escort 1 3 2> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// You have a mission from %name2%. Come see me about it.> Msg1_1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// You're needed for guard duty. Call when you can.> Msg1_2 <\ELEMENT 1 @ \SCENE NPCScene %1% :// It's about time you started earning that paycheck, \RANK \PC . Give me a ring.> Msg1_3 <\ELEMENT 1 @ \SCENE NPCScene %1% :// You're wanted for company business. I'll be waiting here with the details.> Msg1_4 <\ELEMENT 1 @ \SCENE NPCScene %1% :// \RANK \PC , %name2% requires your expertise.> sub Persona 1 rumor0 <%name1% needs a cavalier to escort a convoy for %name2%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail %2% GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill %2% na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission %2% na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final %2% na> GoR1Final result2 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 Msg2 <%name1% in \EXACT_SCENE NPCScene %1% hired you to escort a convoy in \EXACT_SCENE %3% for %name2%.> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % Go defend a factory. Get 2 FacXP. requires <*GENERAL -!Ne -!Lo -Poor> PayRate 185 % E1 is a character who will offer the mission % E2 is the corporate faction that E1 belongs to % E3 is a scene where the encounter will take place % E4 is the town- needed for email check Element1 element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Defense 1 3 2> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// You have a mission from %name2%. Give me a ring.> Msg1_1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// We're having some trouble down at the mill. Call when you can.> Msg1_2 <\ELEMENT 1 @ \SCENE NPCScene %1% :// Your services are required by %name2%. Come see me for the briefing.> Msg1_3 <\ELEMENT 1 @ \SCENE NPCScene %1% :// Hey \RANK , it's time to get to work. I'll be waiting here with the details.> Msg1_4 <\ELEMENT 1 @ \SCENE NPCScene %1% :// \RANK \PC , your talents are needed by %name2%.> sub Persona 1 rumor0 <%name1% needs a cavalier to guard a factory for %name2%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail %2% GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill %2% na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission %2% na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final %2% na> GoR1Final result2 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 Msg2 <%name1% in \EXACT_SCENE NPCScene %1% hired you to defend a factory in \EXACT_SCENE %3% for %name2%.> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % The basic patrol job is a fight against some generic enemy mecha. % This job gives salvage. This job will not result in the PC making % any enemies. requires <*GENERAL> PayRate 105 % E1 is a character who will offer the mission % E2 is the corporate faction that E1 belongs to % E3 is a scene where the encounter will take place % E4 is the town- needed for email check Element1 element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Basic 1 3> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// I have a mission for you from \ELEMENT 2 .> Msg1_1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// Report to \EXACT_SCENE EScene 1 for a mission at once.> Msg1_2 <\ELEMENT 1 @ \SCENE NPCScene %1% :// Your services are required by \ELEMENT 2 . Come see me for mission briefing.> Msg1_3 <\ELEMENT 1 @ \SCENE NPCScene %1% :// I've got a mission for you. Come here for the briefing.> Msg1_4 <\ELEMENT 1 @ \SCENE NPCScene %1% :// There's a matter that requires your attention. This is official \ELEMENT 2 business, so come as soon as possible.> sub Persona 1 rumor0 <%name1% needs a mecha pilot to do a mission for %name2%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail %2% GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill %2% na> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission %2% na %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission&NoEnemyFac GoR1Final %2% na> GoR1Final result2 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 Msg2 <\ELEMENT 1 in \SCENE NPCScene %1% hired you to fight some bandits in \EXACT_SCENE %3% for \ELEMENT 2 .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end Plot name % This is a fight against an enemy criminal faction. % This job gives salvage. This job will result in the PC making % an enemy. requires <*GENERAL -!Ne> PayRate 160 % E1 is a character who will offer the mission % E2 is the corporate faction that E1 belongs to % E3 is a scene where the encounter will take place % E4 is the enemy faction % E5 is the town, needed for email check Element1 element2 Element3 Element4 Element5 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Versus 1 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// I have a mission for you against %name4%.> Msg1_1 <\ELEMENT 1 @ \SCENE NPCScene %1% :// Report to \EXACT_SCENE EScene 1 for a mission against %name4%.> Msg1_2 <\ELEMENT 1 @ \SCENE NPCScene %1% :// Your services are required by \ELEMENT 2 . Come see me for mission briefing.> Msg1_3 <\ELEMENT 1 @ \SCENE NPCScene %1% :// We're under attack by %name4%. Come here for the briefing.> Msg1_4 <\ELEMENT 1 @ \SCENE NPCScene %1% :// There's a matter that requires your attention. This is official \ELEMENT 2 business, so come as soon as possible.> sub Persona 1 rumor0 <%name1% needs a pilot to fight %name4% for %name2%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail %2% GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill %2% %4%> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission %2% %4% %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission GoR1Final %2% %4%> GoR1Final result2 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 Msg2 <%name1% in \SCENE NPCScene %1% hired you to fight some mecha from %name4% in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end GH2/series/PLOT_MOOD_Mischief.txt0000644000175000017500000001367511343146535015353 0ustar kaolkaol%% %% *Mischief Plots %% %% Someone is being rowdy and unmutual. The PC can fight some henchmen or %% associates to learn the location of the enemy's hideout. %% %% %name2% will be used to identify the mischief maker, so name the %% encounter something sensible!!! %% %% NOTE: Don't try grabbing the NPC, because it's most likely in use by %% whatever plot/quest spawned this mood. Then why is it part of the spec? %% Because its context might be useful to know, that's why. %% %% Mood Spec: %% E1 = The outdoors scene %% E2 = The encounter being sought %% E3 = **The NPC behind it all %% Plot name desc requires <*Mischief> % E1 is the outdoors scene % E2 is an encounter to place there % E3 is the encounter being sought % E4 is the NPC to be negotiated with Element1 Element2 Place2 <1> Element3 Element4 Place4 <2 (Enemies)> % P1 = Time Limit/Initialization Counter % P2 = E4 refused to fight PC start update &RevealEncounter sub MetaScene 2 2 rumor%id% <%name4% is on the way to %name3%.> % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start nu1 nu2 GoJustEnd Msg1 Msg2 Msg3 Msg3_1 Msg3_2 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 end Persona 4 special *greeting <*Mischief_EnemyGreeting %threat% %3% GoReveal GoFight> GoReveal GoFight GoNoFight *GoThemeExpo <*THEME_EXPO&Enemy na> Msg1 Msg1_1 <%name3% is in %name1%... Here are the coordinates.> Msg1_2 Msg2 Msg2_1 Msg2_2 end inv Encounter name EncounterMove 20 use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 end Plot name desc requires <*Mischief> % E1 is the outdoors scene % E2 is an encounter to place there % E3 is the encounter being sought Element1 Element2 Place2 <1> Element3 % P1 = Time Limit/Initialization Counter start update &RevealEncounter sub MetaScene 2 2 rumor%id% % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 start nu1 nu2 GoCheckSurvival GoTrackFailed Msg1 Msg2 Msg3 Msg4 sub team 1 SetEnemy 2 SetAlly 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 3 Deploy ParaX 45 ParaY 45 end end inv Encounter name EncounterMove 20 use attack GoAutoAttack GoTrack Msg1 Msg2 Msg3 Msg4 end GH2/series/MEGA_CORE_Default.txt0000644000175000017500000004430611374513723015131 0ustar kaolkaol%% %% *:CS_BASE_PlunderShip %% %% The PC will plunder a ship, hopefully belonging to the enemy. This component %% does not contain any story development of its own. Instead, it will set a %% number of triggers which the calling subplot can use to activate events. %% I am doing things this way because the plundering scenario is extremely %% complex and duplicating it for different story contexts would be tough. %% %% Signals sent by this plot: %% .%id%_%plotid%_GoAttackShip The PC has entered the ship exterior %% scene. Check the hostility of Team 2 to %% see whether or not peaceful entry has %% been achieved. %% .%id%_%plotid%_GoBoardShip The PC has snuck on board the ship. %% Again, check the hostility of Team 2 to %% find out if the entry was peaceful. %% .%id%_%plotid%_GoPlunderOK The PC has successfully plundered the %% ship. Dole out loot and consequences %% appropriate. %% .%id%_%plotid%_GoPlunderBad The PC has captured the ship, but %% screwed up on the plundering... probably %% by blowing everything to heck. %% .%id%_%plotid%_GoPlunderFail The PC didn't plunder jack. %% %% PARAM1: The outdoors scene for the encounter %% PARAM2: The encounter to be used for the ship; contains the name of the %% ship. %% PARAM3: The faction which controls the ship. %% Content name desc requires <*:CS_BASE_PlunderShip> % E1 is the outdoors scene where the encounter is % E2 is the ship's exterior metascene % E3 is the faction controlling the ship % E4 is the ship's inte‭rior metascene % E5 is the ship's captain Element4 Place4 <1> Element5 NeverFail5 Place5 <4 (Guards)> % P%id%01 = Initializat‭ion Counter % P%id%02 = Have gained peaceful entry update .%id1%_%plotid1%_GoPeacefulEntry % This hint will tell the PC where to find the ship. HINT_%id% % SubPlot1 is the PeacefulEntry task SubPlot1 <*:CS_PeacefulEntry_Location 2> sub MetaScene 2 2 %% The ship's exterior % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = "Just defeat the guards" message counter % L4 = Initial headcount of Team 3 MapWidth 50 MapHeight 50 % % As soon as this scene is entered, prompt to see whether the PC wants to % attack the cruiser head-on or enter the ship on foot. Entering the ship % is automatic if the PC has gained peaceful entry or is on foot; otherwise % a stealth roll is needed. % % Once the destination has been decided, set the story trigger and let % the parent subplot deal with things. % Start GoEasyStealth GoNoStealth GoHardStealth GoAllowChoice GoAttemptSneak GoSneakIn GoSneakFail % If you gained peaceful entry, the guards and guns start out % as neutral. Of course they turn hostile as soon as you attack % the engines. GoFightMecha % Launching any sort of attack will alert the escort that something % isn't entirely kosher. PCAttack nu1 nu2 GoBringReinforcements GoSmallVictory GoBigVictory nu4 nu5 end Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 sub team 1 SetEnemy 2 4 5 ParaX 5 ParaY 25 team 2 name SetEnemy 1 SetAlly 4 5 Deploy ParaX 45 ParaY 25 team 3 name % This team is neutral- the PC wants to capture these % parts intact. If any of these parts get destroyed, % there's a chance the desired loot will be lost. team 4 name setenemy 1 setally 2 3 5 % Destroy the weapons and propulsion to incapacitate the % cruiser. team 5 name setenemy 1 setally 2 3 4 rect name special width 12 height 12 MFX 37 MFY 19 sub SuperProp requires <*Cruiser> Team1 3 Team2 4 Team3 5 end end MetaScene 4 %% The ship's interior. To capture the ship, you can: %% - Speak with the captain and intimidate/etc %% - Fight the captain and make them surrender %% - Disable the three control stations %% Leaving the ship for any reason results in a loss. MonkeyMap MapWidth 50 MapHeight 50 NeededCells 4 % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = Captain surrender counter % L4 = Number of systems at start % This info might be useful when negotiating with captain % L5 = Discovery Counter % L6 = Warning message counter start surrender%5% end % The PC risks discovery based on time and stealth. TM1 5min GoAdvanceClock GoCheckWarning GoUpGuards Alarm % Launching any sort of attack will alert the guards that something % isn't entirely kosher. PCAttack nu1 nu3 Msg1 Msg2 Msg3 Msg4_1 CMsg4_1 Msg4_2 CMsg4_2 Msg5 Msg6 % Content1: The systems you need to disable. % Content2: A very good chance of a disaster room. Content1 Content2 sub team 1 SetEnemy 2 3 4 team 2 name SetEnemy 1 SetAlly 3 4 team 3 name % Disabling the major systems of the ship will force it % to surrender. This can be done by locating and blowing % each of them up, or by using skills to deactivate them. SetEnemy 1 team 4 name type SetEnemy 1 SetAlly 2 3 Deploy room special sub TrapDoor use end room desig minimap <............1............> end Persona 5 %% The captain of this ill-fated vessel greeting *.%id%_GoSurrender <*CS_BASEPS_CaptainSurrenders .%id%_GoWin> .%id%_GoWin *.%id%_GoNegotiate <*CS_BASEPS_CaptainNegotiates .%id%_GoWin .%id%_GoAttack na> .%id%_GoAttack Msg%id%01 end inv Encounter name % The interior of the ship. Now, we don't want this encounter to % ever be visible. What it'll do instead is to match the position % of the ship exterior encounter. That way, whichever scene you're % leaving, you should emerge in the same tile on the outdoors map. update Use < > Attack < > end %% %% *:CS_SubBossRoadblock %% %% This is an odd little utility component. In order for something to happen, %% the PC will need to locate and defeat a certain NPC. The battle will happen %% in an invisible wandering encounter; at the start of the fight, the NPC will %% contact the PC. %% %% If the PC loses the battle, no problem: the fight will repeat every 3-5 hours %% until the PC wins. If the boss NPC dies, this counts as a win even if the PC %% doesn't finish off the lancemates. %% %% NOTE: THIS SUBPLOT DOES NOT INCLUDE A PERSONA FOR THE NPC! It's up to the %% parent plot to provide that. %% %% NOTE: THIS SUBPLOT DOES NOT SET A HISTORY NOTE! Again, that's up to the %% parent. %% %% When this subplot concludes, it sets the following trigger: %% .%id%_%plotid%_GoWin %% %% PARAM1: The outdoors scene for the encounter %% PARAM2: The NPC to be met %% Content name desc requires <*:CS_SubBossRoadblock> Size 6 % E1 is the outdoors scene for the encounter % E2 is the NPC to be met % E3 is the encounter itself Element3 Place3 <1> % P%id%01 = Initialization Counter update sub MetaScene 3 2 rumor%id% <%name2% has been seen in %name1%.> special % L1 = Encounter Over Counter % L2 = Initialization Counter; resets each time MapWidth 50 MapHeight 50 Start nu1 nu2 Faint%2% GoVictory end sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv Encounter name <%name2%'s Lance> % V1 = Recharge Counter % This encounter will appear once every 3-5 hours, as long as this is the active subplot. update GoSetTimer GoSetOrders use GoAutoAttack GoAvoidAttack end %% %% *:CS_DifficultMeeting %% &IsEnemy The NPC to be met is an enemy %% &Protected The NPC shouldn't get killed, by the PC or anything else %% %% The PC needs to meet with someone, but arranging a meeting is difficult. %% Hence the name of the component. Duh. This subplot will set a PLACE for %% the relevant NPC and may or may not start a conversation with FORCECHAT, %% but will not contain the NPC's persona or make any other assumptions %% about what's going on. %% %% One note about the persona: The conversation should start with an acknowledgment %% that the PC was looking for E1. %% %% The parent subplot should disable this subplot when the conversation is over. %% %% PARAM1: The NPC the PC is seeking %% Content name desc requires <*:CS_DifficultMeeting &IsEnemy> Size 5 % E1 is the NPC that the PC is seeking % E2 is an outdoors scene for the combat % E3 is the combat encounter itself Place1 Element2 Element3 Place3 <2> sub MetaScene 3 2 rumor%id% <%name1% is doing something in %name2%.> % V1 = Encounter Over Counter % V2 = Initialization Counter MapWidth 50 MapHeight 50 % Defeat the henchmen, and E1 shows up. Get defeated by them, and lose your chance % forever. start nu1 nu2 Msg1 Msg2 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-INVISIBLEENCOUNTER name <%name1%'s Henchmen> EncounterMove 20 end %% %% *:CS_DiscoverNPCKidnappedByCSE %% &NotReally It wasn't really a kidnapping, or some other misunderstanding %% %% A very specific subplot. The NPC you're searching for has been kidnapped %% by the core story enemy... or apparently so, anyhow. %% %% When this subplot concludes, the following trigger must be set: %% .%id%_%plotid%_GoDiscover %% %% PARAM1: The scene where the NPC supposedly is %% PARAM2: The NPC being sought %% Plot name desc requires <*:CS_DiscoverNPCKidnappedByCSE> Size 3 % E1 is the scene where the NPC supposedly is % E2 is the NPC being sought % E3 is a character in E1 who can tell the PC that E2 has left Element3 Place3 <1 (Citizens) pass ally> % This plot cannot fail. E3 isn't actually important. You don't even need to win % the fight against the core story enemy. % Entering the location primes the attack; exiting to the city map executes the attack. % The attack will be handled with a dynamic encounter. % p%id%01 = Attack Primed START .%id%_desc .%id%_GoCheckPrimer .%id%_GoStartAttack .%id%_nu1 .%id%_nu2 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 sub Persona 3 greeting GoChat *GoGreet <*LookingForNPC&NoFailure %2% GoExplain GoExplain> GoExplain Msg1 <%name2% was here earlier, but I saw \OPR %2% talking to some big guys and they left in a hurry. Is \SPR %2% in any trouble?> Msg2 end GH2/series/ARENAMISSION_Default.txt0000644000175000017500000002512011326004537015464 0ustar kaolkaol%% ************************************** %% *** [!Ne+] BEGINNER MISSIONS *** %% ************************************** ArenaMission name requires <*MISSION ArenaOK> desc MapWidth 50 MapHeight 50 PayRate 400 CityBlockMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 5 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 45 rect name desig rect name desig end ArenaMission name requires <*MISSION L5PAT> desc AsteroidMap terrain RockyTiles Vacuum SpaceBackdrop MapWidth 50 MapHeight 50 PayRate 400 Element1 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 end ArenaMission name requires <*MISSION ArenaOK> desc MapWidth 50 MapHeight 50 PayRate 400 WildMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 end ArenaMission name requires <*MISSION ArenaOK> desc MapWidth 50 MapHeight 50 PayRate 400 ForestMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 end ArenaMission name requires <*MISSION (SILKN|COMET|HOELL|PRIVA)> desc MapWidth 50 MapHeight 50 PayRate 400 SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop Element1 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 end %% **************************************** %% *** [!Lo+] LOW LEVEL MISSIONS *** %% **************************************** ArenaMission name requires <*MISSION -!Ne> desc WildMap terrain MapWidth 50 MapHeight 50 PayRate 600 Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 Persona 2 Greeting *GoGreet <*BattleChallenge GoThemeInfo GoThemeInfo> *GoThemeInfo <*THEME_EXPO&Enemy GoStartCombat> GoStartCombat Msg1 end inv NPC Mecha Pilot SetFaction -1 SetTeam 2 end ArenaMission name requires <*MISSION -!Ne> desc SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop MapWidth 50 MapHeight 50 PayRate 600 Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 Persona 2 Greeting *GoGreet <*BattleChallenge GoThemeInfo GoThemeInfo> *GoThemeInfo <*THEME_EXPO&Enemy GoStartCombat> GoStartCombat Msg1 end inv NPC Mecha Pilot SetFaction -1 SetTeam 2 end ArenaMission name requires <*MISSION -!Ne> desc AsteroidMap terrain RockyTiles Vacuum SpaceBackdrop MapWidth 50 MapHeight 50 PayRate 600 Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter Start nu1 nu2 Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 ParaX 5 ParaY 15 Team 2 SetEnemy 1 Deploy ParaX 45 ParaY 35 Persona 2 Greeting *GoGreet <*BattleChallenge GoThemeInfo GoThemeInfo> *GoThemeInfo <*THEME_EXPO&Enemy GoStartCombat> GoStartCombat Msg1 end inv NPC Mecha Pilot SetFaction -1 SetTeam 2 end ArenaMission name requires <*MISSION -!Ne> desc MapWidth 50 MapHeight 50 PayRate 800 WildMap terrain Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Base Counter Start nu1 nu2 nu3 nu4 GoWinMission GoLoseMission Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 4 SetAlly 3 home Team 2 SetAlly 4 SetEnemy 1 3 Deploy ParaX 20 ParaY 40 Team 3 SetEnemy 2 4 SetAlly 1 home Team 4 SetAlly 2 SetEnemy 1 3 Deploy ParaX 40 ParaY 20 rect name MFX 5 MFY 5 Height 8 Width 8 FloorType 6 sub SuperProp requires <*Fortress> SetTeam 3 end end %% ******************************************* %% *** [!Md+] MEDIUM LEVEL MISSIONS *** %% ******************************************* %% ***************************************** %% *** [!Hi+] HIGH LEVEL MISSIONS *** %% ***************************************** ArenaMission name requires <*MISSION ArenaOK (!Hi|!Ex)> desc MapWidth 50 MapHeight 50 PayRate 3200 SpaceMap terrain SpaceScroll Microgravity Vacuum SpaceBackdrop Element1 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Destroy Base Counter Start nu1 nu2 nu3 GoWinMission GoLoseMission 5Min Msg1 Msg2 Msg3 Msg5 sub Team 1 SetEnemy 2 3 ParaX 45 ParaY 25 Team 2 SetEnemy 1 SetAlly 3 Deploy home Team 3 SetEnemy 1 SetAlly 2 void name width 12 height 12 MFX 1 MFY 19 sub SuperProp requires <*Battleship> SetTeam 3 end end %% *************************************** %% *** [!Ex] ACE LEVEL MISSIONS *** %% *************************************** GH2/series/PFRAG_Combat.txt0000644000175000017500000013071711365256063014273 0ustar kaolkaol%%% COMBAT PERSONA FRAGMENTS % TYPE: *HereToHelpFight % This NPC is here to aid the PC in an upcoming battle. The PC can choose to send % them away. Of course, they might not listen... % PARAM1: Accept Aid (no message printed here) % PARAM2: Reject Aid (no message printed here) % PARAM3: Reject Rejection (message printed here) Persona requires <*HereToHelpFight> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <\PC ! I'm here to assist you in this battle... Let's blow some ash up!> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *RejectAid % The PC has rejected the NPC's aid. % PARAM1: Exit label Persona requires <*RejectAid> START result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *WelcomeToMyLair % The PC has found the NPC's hideout, and combat is immanent. % PARAM1: Exit label Persona requires <*WelcomeToMyLair> % v%id%01 = Single Use Counter START result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 <> CPrompt%id%01_3 Prompt%id%01_4 <> CPrompt%id%01_4 Prompt%id%01_5 <> CPrompt%id%01_5 Prompt%id%01_6 <> CPrompt%id%01_6 % TYPE: *WaitingForDuel % The NPC is waiting for the PC to come fight him. % PARAM1: Duel location Persona requires <*WaitingForDuel> Start Msg%id%01 % TYPE: *HelpMeVsMecha % &Reward The PC will get a reward for helping % PARAM1: The PC will help exit label [no message] % PARAM2: The PC won't help exit label [no message] Persona requires <*HelpMeVsMecha Easygoing> Start result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Persona requires <*HelpMeVsMecha> Start result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%02 % TYPE: *WeWon % &Reward The PC is getting a reward for this. % The PC and the NPC fought together, and won. Hooray! % The PC is also getting a reward. % PARAM1: Allied Faction [optional] % PARAM2: Enemy Faction [optional] Persona requires <*WeWon> start Msg%id%01 Persona requires <*WeWon &Reward> start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 CMsg%id%01_12 Msg%id%01_13 CMsg%id%01_13 Msg%id%01_14 CMsg%id%01_14 Msg%id%01_15 CMsg%id%01_15 Msg%id%01_16 CMsg%id%01_16 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_21 CMsg%id%01_21 Msg%id%01_22 CMsg%id%01_22 Msg%id%01_23 CMsg%id%01_23 Msg%id%01_24 CMsg%id%01_24 Msg%id%01_25 CMsg%id%01_25 Msg%id%01_26 CMsg%id%01_26 % TYPE: *LetsDefeatThem % The PC has joined a fight on the side of the NPC. % PARAM1: Enemy Faction Persona requires <*LetsDefeatThem> Start Msg%id%01 Persona %% Pyromaniac Version requires <*LetsDefeatThem [MT1]> Start Msg%id%01 Persona %% Soldier Version requires <*LetsDefeatThem ([MT2]|MILIT)> Start Msg%id%01 Persona %% Sophisticated Version requires <*LetsDefeatThem [MT3]> Start Msg%id%01 Persona %% Industrial Version requires <*LetsDefeatThem [MT4]> Start Msg%id%01 Persona %% Pirate Version requires <*LetsDefeatThem [MT5]> Start Msg%id%01 Persona %% Mad Science Version requires <*LetsDefeatThem ([MT6]|Scientist)> Start Msg%id%01 Persona %% Beauty Version requires <*LetsDefeatThem [MT7]> Start Msg%id%01 Persona %% Dark Version requires <*LetsDefeatThem [MT8]> Start Msg%id%01 Persona %% Dragon Version requires <*LetsDefeatThem [MT9]> Start Msg%id%01 Persona %% Ninja Version requires <*LetsDefeatThem [MT10]> Start Msg%id%01 % TYPE: *LetsStartPatrol % The PC has joined a patrol, and combat is about to begin. % PARAM1: Exit label Persona requires <*LetsStartPatrol> start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *JoinOurFacPatrol % There's a patrol belonging to the PC's faction or otherwise some ally % of the PC. He'll be expected to join. % PARAM1: Join exit label % PARAM2: Refuse exit label (reply will be spoken here) % PARAM3: Identity of the faction % PARAM4: Identity of the enemy Persona requires <*JoinOurFacPatrol =MIL_DEFAULT_OFFENSE> start result%id%01 result%id%02 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Persona requires <*JoinOurFacPatrol> start result%id%01 result%id%02 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 % TYPE: *Back_For_More % % The PC fought this enemy previously and presumably lost; now they're % back for more. % Persona requires <*Back_For_More> Start Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *THEME_EXPO % &Ally NPC will be fighting alongside PC % &Enemy NPC will be fighting against the PC % Combat is starting, so it's time for this NPC to explain his schtick. % This pfrag should terminate the conversation and exit immediately to %1%. % % PARAM1: Exit label % Persona %% Themeless Expo- just give the name of the NPC's mecha. requires <*THEME_EXPO> Start Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona %% Themeless Expo- just give the name of the NPC's mecha. requires <*THEME_EXPO &Enemy> Start Msg%id%01 Persona %% Pyromaniac Theme requires <*THEME_EXPO [MT1]> Start Msg%id%01 Persona %% Pyromaniac Theme requires <*THEME_EXPO [MT1] &ENEMY> Start Msg%id%01 Persona %% Soldier Theme requires <*THEME_EXPO [MT2]> Start Msg%id%01 Persona %% Soldier Theme requires <*THEME_EXPO [MT2] &ENEMY> Start Msg%id%01 Persona %% Sophisticated Theme requires <*THEME_EXPO [MT3]> Start Msg%id%01 Persona %% Sophisticated Theme requires <*THEME_EXPO [MT3] &ENEMY> Start Msg%id%01 Persona %% Industrial Theme requires <*THEME_EXPO [MT4]> Start Msg%id%01 Persona %% Industrial Theme requires <*THEME_EXPO [MT4] &ENEMY> Start Msg%id%01 Persona %% Pirate Theme requires <*THEME_EXPO [MT5]> Start Msg%id%01 Persona %% Pirate Theme requires <*THEME_EXPO [MT5] &ENEMY> Start Msg%id%01 Persona %% Mad Science Theme requires <*THEME_EXPO [MT6]> Start Msg%id%01 Persona %% Mad Science Theme requires <*THEME_EXPO [MT6] &ENEMY> Start Msg%id%01 Persona %% Beauty Theme requires <*THEME_EXPO [MT7]> Start Msg%id%01 Persona %% Beauty Theme requires <*THEME_EXPO [MT7] &ENEMY> Start Msg%id%01 Persona %% Dark Theme requires <*THEME_EXPO [MT8]> Start Msg%id%01 Persona %% Dark Theme requires <*THEME_EXPO [MT8] &ENEMY> Start Msg%id%01 Persona %% Dragon Theme requires <*THEME_EXPO [MT9]> Start Msg%id%01 Persona %% Dragon Theme requires <*THEME_EXPO [MT9] &ENEMY> Start Msg%id%01 Persona %% Ninja Theme requires <*THEME_EXPO [MT10]> Start Msg%id%01 Persona %% Ninja Theme requires <*THEME_EXPO [MT10] &ENEMY> Start Msg%id%01 % TYPE: *BattleIntroduction % &SentToFight The PC was sent here to fight the NPC % The PC is meeting a new person for the first time... in combat. % PARAM1: Start combat exit label (no message printed here) Persona requires <*BattleIntroduction &SentToFight> start result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*BattleIntroduction> start result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *ArenaChallenge % The PC and NPC get a chance to trash-talk one another before battle... % in a pro-wrestler sort of way. % PARAM1: Start combat exit label (no message printed here) Persona requires <*ArenaChallenge> start result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*ArenaChallenge "PDASS"> start result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *BattleChallenge % &CharDev Character development (@:A,@:M) is allowed % In general character development should be reserved % for times when the PC is intentionally and specifically % confronting this NPC, or when the NPC already has a % pre-existing relationship with the PC. % &Avoid Combat can be avoided. % The PC and NPC get a chance to trash-talk one another before battle. % There's a chance the PC can avoid this combat. Note that the PC might % avoid combat either through intimidation/etc or by running away, in which % case he can expect to lose some renown. % PARAM1: Start combat exit label (no message printed here) % PARAM2: Avoid combat exit label (message is printed here) Persona %% The Attitude Sorter requires <*BattleChallenge &CharDev "@:A.---"> %% This pfrag will set a preliminary attitude for the NPC, depending %% on a number of factors. The choices are: %% - Antagonistic if Melancholy %% - Keeping Secret if Shy %% - Senior if older and more renowned %% - Junior if younger and less renowned %% - Equal otherwise %% EndChat & AddChat done at beginning; just Say+SetXXRAttitude per op start .%id%_GoCheckSecret .%id%_GoCheckSenior .%id%_GoCheckMinor .%id%_GoEqual result%id%01 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 <\PC ! I challenge you to battle!> Msg%id%05_1 CMsg%id%05_1 Msg%id%05_2 CMsg%id%05_2 Msg%id%05_3 CMsg%id%05_3 Msg%id%05_4 CMsg%id%05_4 Msg%id%05_5 CMsg%id%05_5 Msg%id%05_6 CMsg%id%05_6 Msg%id%05_7 CMsg%id%05_7 Msg%id%05_8 CMsg%id%05_8 Prompt%id%01 <[Continue]> Persona requires <*BattleChallenge "Friend"> start result%id%01 Msg%id%01 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Persona requires <*BattleChallenge "PRIVA"> start result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Prompt%id%01 Persona requires <*BattleChallenge "SILKN"> start result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 Prompt%id%01 Persona requires <*BattleChallenge "AEGIS"> start result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_30 CMsg%id%01_30 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Prompt%id%01 Persona requires <*BattleChallenge &Avoid Cheerful> start .%id%_GoNoEnemy result%id%01 result%id%02 result%id%03 result%id%04 .%id%_Go4Fail result%id%05 result%id%06 result%id%07 result%id%08 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_100 CMsg%id%01_100 Msg%id%01_101 CMsg%id%01_101 Msg%id%01_104 CMsg%id%01_104 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 Msg%id%05_1 Msg%id%05_2 Msg%id%06_100 CMsg%id%06_100 Msg%id%06_200 CMsg%id%06_200 Msg%id%06_201 CMsg%id%06_201 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 CPrompt%id%03 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 CPrompt%id%04_2 Prompt%id%04_3 Prompt%id%04_4 CPrompt%id%04_4 Prompt%id%05 Prompt%id%05_1 Prompt%id%05_2 Prompt%id%06 Prompt%id%06_1 Prompt%id%07 Prompt%id%08 Persona requires <*BattleChallenge &Avoid> start Result%id%01 Result%id%02 .%id%_Go2Fail Result%id%03 .%id%_Go3Work result%id%04 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Msg%id%05 Msg%id%05_1 Msg%id%05_2 Msg%id%06 Msg%id%06_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 <[More]> Persona requires <*BattleChallenge ~Shy> Start Result%id%01 Msg%id%01 Msg%id%01_100 CMsg%id%01_100 Msg%id%01_101 CMsg%id%01_101 Msg%id%01_102 CMsg%id%01_102 Msg%id%01_103 CMsg%id%01_103 Msg%id%01_104 CMsg%id%01_104 Msg%id%01_105 CMsg%id%01_105 Msg%id%01_106 CMsg%id%01_106 Prompt%id%01 Persona requires <*BattleChallenge> Start result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <\PC , I've heard tell of your piloting skills. Now I'll get the chance to see if you live up to your reputation.> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *YouLose % &DontComeBack PC will be told to never return here % The PC has lost a battle to the NPC. Persona requires <*YouLose> Start Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*YouLose &DontComeBack> Start Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 % TYPE: *YouWonFair % &MeetAgain The NPC expresses a desire to meet again % The NPC has lost a battle to the PC. Unlike *GetYouNextTime, the NPC is gracious % in his defeat. Persona requires <*YouWonFair> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *GetYouNextTime % The NPC has lost a battle to the PC. Persona requires <*GetYouNextTime "PRIVA"> Start Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Persona requires <*GetYouNextTime Wangtta> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*GetYouNextTime "AEGIS"> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Persona requires <*GetYouNextTime "SILKN"> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 Persona requires <*GetYouNextTime> Start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 GH2/series/PLOT_ENC_CityDefense.txt0000644000175000017500000001040711401151245015650 0ustar kaolkaolPlot name requires <*GENERAL> % E1 is the Town scene % E2 is the outdoors scene for the patrol. % E3 is the encounter for the patrol % E4 is the pilot to be used Element1 Element2 Element3 Place3 <2> Element4 Place4 <3 (Guards) sd enemy> % P1 = Timer % P2 = Have entered combat start update GoCheckTime GoDelete sub MetaScene 3 2 MapWidth 30 MapHeight 30 % L2 = Encounter Over Indicator start end 5min nu1 nu2 % The PC has won this encounter. GoVictory % The PC has lost this encounter... GoLoss sub Team 1 SetEnemy 2 ParaX 5 ParaY 5 Team 2 name SetEnemy 1 Deploy ParaX 25 ParaY 25 end Persona 4 special rumor <%name4% is out on patrol.> % If the PC isn't of sufficient level to handle this NPC, the battle % will be left up to the lancemates. greeting *GoDoWin <*GetYouNextTime> GoCheckLoss *GoDoLoss <*YouLose> *GoFightPC <*BattleChallenge&Avoid GoCheckRenown GoExit> GoCheckRenown *GoThemeExpo <*THEME_EXPO&Enemy GoStartCombat> GoExit Msg1 Msg1_1 Msg1_2 end inv STC ENCOUNTER-WANDER name <%name4%'s Patrol> &ifShouldAttack use GoTooSmall GoNoAttack Attack GoMakeChoice Msg1 Msg2 Msg3 Msg4 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg5_3 CMsg5_3 Msg5_4 CMsg5_4 Msg5_5 CMsg5_5 Msg5_6 CMsg5_6 Msg5_7 CMsg5_7 Msg5_8 CMsg5_8 Msg5_9 CMsg5_9 Msg5_10 CMsg5_10 Msg5_11 Msg6 Msg7 ENCOUNTER_Defense end GH2/series/MEGA_CORE_Conclusion_End.txt0000644000175000017500000003313611401151245016432 0ustar kaolkaol%% %% *:CS_END_TheEnd %% %% Upon activation this subplot calls all the *CS_END_ENDING macros. %% This subplot should be supply two macros: &EndCompleteVictory and %% &EndPartialVictory. %% %% PARAM1: The Enemy NPC %% Content name requires <*:CS_END_TheEnd> % This ending mechanism is a bit kludgier looking than I'd like, but it works. % P%id%01 = Complete or partial victory achieved % P%id%02 = Can only win the game once counter &EndCompleteVictory &EndPartialVictory update .%id%_GoPartialVictory <&ENDING_Propp_Partial &ENDING_Enemy_Partial XPV 500 Goto .%id%_GoEndStory> .%id%_GoCompleteVictory <&ENDING_Propp_Complete &ENDING_Enemy_Complete XPV 1000 Goto .%id%_GoEndStory> .%id%_GoEndStory % SubPlot1 is the Propp state ending % SubPlot2 is the Enemy NPC ending subplot1 <*CS_END_ENDING_Propp 1> subplot2 <*CS_END_ENDING_Enemy 1> %% %% *CS_END_ENDING Content %% %% These are not proper subplots. They are merely containers for %% macros which get called when the player wins the final battle. %% %% Each macro should show an alert, and it should probably add something %% to the history list as well. %% %% %% *CS_END_ENDING_Propp %% %% The Propp ending tells what happens regarding %% the +P story state and the enemy/ally factions. %% %% It needs the following macros: %% &ENDING_Propp_Complete ( for kicking the enemy's arse soundly ) %% &ENDING_Propp_Partial ( defeated NPC, but failed other objectives ) %% %% PARAM1: The Enemy NPC %% Content name requires <*CS_END_ENDING_Propp -+Psh F:CRIHN (P:--|P:POLIT|P:MILIT|P:POLIC)> &ENDING_Propp_Complete &ENDING_Propp_Partial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%02 Content name requires <*CS_END_ENDING_Propp -+Psh F:REDMA> &ENDING_Propp_Complete &ENDING_Propp_Partial Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Content name requires <*CS_END_ENDING_Propp -+Psh P:MUGLE ~F:CORPO> &ENDING_Propp_Complete &ENDING_Propp_Partial Msg%id%01 Msg%id%02 Content name requires <*CS_END_ENDING_Propp -+Psh F:++> &ENDING_Propp_Complete &ENDING_Propp_Partial Msg%id%01 Msg%id%02 Content name requires <*CS_END_ENDING_Propp +Psh P:++> &ENDING_Propp_Complete &ENDING_Propp_Partial Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 %% %% *CS_END_ENDING_Enemy %% %% This ending tells what happens to the enemy NPC after the battle. If the NPC %% is dead, there may still be something to tell. %% %% It needs the following macros: %% &ENDING_Enemy_Complete ( for kicking the enemy's arse soundly ) %% &ENDING_Enemy_Partial ( for being semi-victorious ) %% %% PARAM1: The Enemy NPC %% Content name requires <*CS_END_ENDING_Enemy F:REDMA> &ENDING_Enemy_Complete &ENDING_Enemy_Partial Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 <%name1% becomes even more popular in death than \SPR %1% was while alive. Wayward teens across the solar system idolize \OPR %1% as the ultimate example of the "live fast, die young, blow lots of things up" pirate lifestyle.> CMsg%id%02_2 Content name requires <*CS_END_ENDING_Enemy (C:POLIC|P:POLIC) (E:THIEF|F:CRIME)> &ENDING_Enemy_Complete &ENDING_Enemy_Partial Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 <%name1% died in battle, thereby saving taxpayers the cost of a lengthy investigation and trial.> CMsg%id%01_2 Msg%id%02_1 <%name1% slips away after the battle and avoids arrest; \SPR %1% returns to plague you several times over the years.> CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Content requires <*CS_END_ENDING_Enemy> &ENDING_Enemy_Complete &ENDING_Enemy_Partial Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%02_1 <%name1% never forgives you for this defeat, and returns to plague you numerous times in the years to come.> CMsg%id%02_1 Msg%id%02_2 <%name1% is revered as a martyr by \PPR %1% followers. Fortunately, they never again gain the level of power that they once had.> CMsg%id%02_2 %% %% *CS_END_PEACE_Enemy %% %% This ending tells how the battle is resolved when things happen peacefully. %% %% It needs the following macros: %% &ENDING_Enemy_Peace %% %% PARAM1: The Enemy NPC %% Content name requires <*CS_END_PEACE_Enemy 1:good_ -1:family -1:lover> &ENDING_Enemy_Peace Msg%id%01_1 <%name1%'s forces withdraw from the battlefield. During the subsequent peace negotiations \SPR %1% becomes the chief diplomat for \FACTION &EnemyFac .> CMsg%id%01_1 Msg%id%01_2 <%name1%'s forces withdraw from the battlefield. During the subsequent peace negotiations \SPR %1% works hard to resolve old wounds and to create a better future for all sides.> CMsg%id%01_2 Content name requires <*CS_END_PEACE_Enemy -1:evil_> &ENDING_Enemy_Peace Msg%id%01 <%name1%'s forces lower their weapons and withdraw to neutral territory. Shortly thereafter, the peace negotiations begin in earnest.> Content name requires <*CS_END_PEACE_Enemy 1:evil_> &ENDING_Enemy_Peace Msg%id%01_1 <%name1% leaves the field of battle and shortly thereafter quits \FACTION &EnemyFac ; \SPR %1% is never heard from again.> CMsg%id%01_1 Msg%id%01_2 <%name1% exits the field of battle, leaving peace negotiations to \PPR %1% subordinates ; \SPR %1% never returns to trouble you again.> CMsg%id%01_2 %% %% *:CS_END_Special_PAL %% %% The PC has achieved the special "Peace and Love" ending. Resolve things. %% %% PARAM1: The Enemy NPC %% Content name <"Peace and Love": Blades vs Aegis> requires <*:CS_END_Special_PAL (F:CRIHN|F:BOHEM) (P:AEGSF|P:AEGIS)> % Solid state ending % P%id%02 = Can only win the game once counter update .%id%_GoEndStory Msg%id%01 Msg%id%02 % SubPlot1 is the Enemy state ending subplot1 <*CS_END_PEACE_Enemy 1> Content name <"Peace and Love": Blades vs Rishiri Axis> requires <*:CS_END_Special_PAL (F:CRIHN|F:BOHEM) (P:L5LAW|P:RISHI|P:FCOMS|P:AEGSF|P:ROCKE)> % Solid state ending % P%id%02 = Can only win the game once counter update .%id%_GoPartialVictory .%id%_GoEndStory Msg%id%01 Msg%id%02 Msg%id%03 % SubPlot1 is the Enemy state ending subplot1 <*CS_END_PEACE_Enemy 1> Content name <"Peace and Love": Blades vs Anyone Else> requires <*:CS_END_Special_PAL (F:CRIHN|F:BOHEM) -P:L5LAW -P:RISHI -P:FCOMS -P:AEGSF -P:ROCKE -P:AEGIS> % Solid state ending % P%id%02 = Can only win the game once counter update .%id%_GoEndStory Msg%id%01 % SubPlot1 is the Enemy state ending subplot1 <*CS_END_PEACE_Enemy 1> Content name <"Peace and Love": Red Mask Raiders> requires <*:CS_END_Special_PAL F:REDMA> % Solid state ending % P%id%02 = Can only win the game once counter update .%id%_GoEndStory Msg%id%01 Msg%id%02 % SubPlot1 is the Enemy state ending subplot1 <*CS_END_PEACE_Enemy 1> GH2/series/stub_cavalierclub.txt0000644000175000017500000000252311326004535015621 0ustar kaolkaol%% This file holds the "Cavalier's Club", which will be the starting %% location for the PC. All scenes in this file must have the designation %% "00000" and the type "cavclub". They don't have to be named "Cavalier's %% Club", though. Scene name desig <00000> type entrance <*CAVCLUB> special MallMap MapWidth 15 MapHeight 15 % L1 = Initialization Counter % Don't print a message the first time this building is entered; % the core story will do that for us. start GoFirstTime Msg1 neededcells 2 Content1 Content2 Content3 % Load the hometown quests. Quest1 <*:Q_LeadershipSeries> Quest2 <*:Q_FREEMECHA 5> % Load the debug quests. For testing purposes, set difficulcy to 1. Quest7 <*:Q_DEBUG 1> Quest8 <*:Q_DEBUGARENA 1> Quest9 <*:Q_DEBUGGIVEMISSION 1> sub Team 1 Team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content2 Content3 special end GH2/series/corestorystub.txt0000644000175000017500000000350311334007164015056 0ustar kaolkaol% Core Story Stub XRanStory name CONTEXT <+P-- +C-- +H--> XXRAN_PATTERN <*CORE_> % Starting difficulty level is 3... % Going up by 7 points per level, there will be 13 episodes, just like 08th MS Team. NAtt 7 26 3 % Starting episode number is 1... NAtt -7 6 1 % V1 = Initialization Counter start update reset_core_story % For now, a fairly bland opening. Msg1 Msg2 &SetEnemyNPC &EnemyNPC &SetEnemyFac &EnemyFac &SetAllyFac &AllyFac &SetPartnerNPC &AddPartnerToLance &RemovePartnerFromLance &RemovePartnerNPC &PartnerNPC &SetTargetNPC &TargetNPC &SetCompScene %% Note that "SetCompScene" is not normally called, since it's done %% already by NextComp. &CompScene &SetEpisodeScene &EpisodeScene &SetTargetItem &TargetItem &SetAmoreNPC &AmoreNPC &AddAmoreToLance &RemoveAmoreFromLance &SetHangingNPC &HangingNPC element1 palette_entry_code1 element2 palette_entry_code2 element3 palette_entry_code3

    type habitat <> SDL_SPRITE SDL_COLORS <56 26 81 170 112 90 123 63 0> CHAT_ATTACK1 CHAT_ATTACK2 CHAT_ATTACK3 RangedCombat 1 CloseCombat 1 Dodge 1 sub head torso arm sub hand end arm sub hand end leg leg end Arch Bandit statline 10 10 10 10 9 9 9 9 MonsterTV 68 roguechar type habitat <> SDL_SPRITE SDL_COLORS <112 28 28 150 112 89 136 151 50> CHAT_ATTACK1 CHAT_ATTACK2 CHAT_ATTACK3 RangedCombat 5 CloseCombat 6 Dodge 8 sub head armor 1 torso armor 1 arm armor 1 sub Hand inv item Shotgun end end arm armor 1 sub Hand end leg armor 1 leg armor 1 end Arch Bandit statline 10 10 10 10 9 9 9 9 MonsterTV 42 roguechar type habitat <> SDL_SPRITE SDL_COLORS <112 28 28 150 112 89 56 26 81> CHAT_ATTACK1 CHAT_ATTACK2 CHAT_ATTACK3 RangedCombat 5 CloseCombat 6 Dodge 8 sub head armor 1 torso armor 1 arm armor 1 sub % Hand Hand end arm armor 1 sub Hand inv item Hand Axe desig type <> end end leg armor 1 leg armor 1 end GH2/series/PLOT_ConcertJob1.txt0000644000175000017500000000471611326004537015107 0ustar kaolkaolPlot name requires <*GENERAL> % This is a basic concert job for performers. % E1 is the character to offer the job. Element1 %% P1 = Timer/Initialization Counter %% P2 = Difficulcy Rating; PC's renown at time of initialization start update GoDelete GoCheckTime CleanUp sub Persona 1 rumor <%name1% needs a singer.> % V1 = Have offered job % V2 = Concert Score greeting GoCheckPerformance GoNotHere *GoAutoOffer <*DoYouWantAJob&NonCombat GoIntroJob> GoIntroJob *GoNoAuto <*PerformanceTest GoOfferJob GoRejectJob GoFailJob> GoOfferJob GoRejectJob GoFailJob GoNoJob result1 result2 result3 result4 result5 GoWinMission GoLoseMission Msg1 Msg2 Msg3 Msg4 Msg5 <\PERSONA E1 in \SCENE Escene 1 will hire you to sing at \PPR E1 bar.> Msg6 Msg7 Msg8 Msg9 Msg10 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 <[Begin Concert]> end GH2/series/ARENAMISSION_REWARD_General.txt0000644000175000017500000000646411326004535016531 0ustar kaolkaol%% %% *REWARD missions %% %% Don't let the name fool you- these missions aren't rewards in themselves; %% rather, they present an opportunity for the PC to earn a significant reward. %% The reward missions are typically more difficult than regular missions. %% %% The General rewards don't require a coupon, and consequently don't give %% rationed rewards. ArenaMission name requires <*REWARD -!Ne (Military|Police)> desc MapWidth 50 MapHeight 50 PayRate 700 CityBlockMap terrain Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Mecha Renown Start nu1 nu2 nu4 .faction <\FACTION_DESIG %1% GENERAL> Msg1 Msg2 Msg3 Msg4 sub Team 1 SetEnemy 2 SetAlly 3 home Team 2 SetAlly 4 SetEnemy 1 3 Deploy ParaX 45 ParaY 5 Team 3 SetEnemy 2 4 SetAlly 1 Team 4 SetAlly 2 SetEnemy 1 3 Deploy ParaX 40 ParaY 45 rect name desig rect name desig sub SuperProp requires <*Fortress> SetTeam 3 end end ArenaMission name requires <*REWARD -!Ne -!Lo> desc AsteroidMap terrain RockyTiles Vacuum SpaceBackdrop MapWidth 50 MapHeight 50 PayRate 500 Element1 Element2 % L1 = Initialization Counter % L2 = Victory Counter % L3 = Number of mecha to award Start nu1 nu2 GoMechaFactory .faction <\FACTION_DESIG %1% GENERAL> GoWinMission Msg1 Msg2 Msg3 sub Team 1 SetEnemy 2 3 ParaX 5 ParaY 15 Team 2 SetEnemy 1 SetAlly 3 Deploy ParaX 45 ParaY 45 Team 3 home SetEnemy 1 SetAlly 2 rect name MFX 35 MFY 35 Height 8 Width 8 FloorType 6 end inv NPC Mecha Pilot SetFaction -1 SetTeam 2 STC BUNKER-1 name SetTeam 3 STC BUNKER-1 name SetTeam 3 STC BUNKER-1 name SetTeam 3 end GH2/series/MEGA_CORE_MAIN_Plunder.txt0000644000175000017500000006124311376734116015764 0ustar kaolkaol%% *:CS_Plunder Content %% %% %% The PC has been sent to plunder a ship, warehouse, or whatever else sort of %% thing a pirate might be inclined to attack. Note that this is quite likely to %% make the PC some enemies... Or maybe even upgrade their enemy. %% %% Depending on the core story enemy faction... %% - if THIEF, PC will probably compete with them to get plunder %% - if POLIC, they'll be guarding a ship belonging to someone else %% - if nonexistent, PC will get a new enemy %% - otherwise, the ship will probably belong to them, except in the case of %% an enemy faction upgrade. %% %% The target is activated as soon as this subplot is. %% %% When this subplot concludes, it sets one of the following triggers: %% .%id%_%plotid%_GoWin %% .%id%_%plotid%_GoLoss %% %% This is a MAIN COURSE subplot, and as such should alter the story context. %% %% %% Param1: The outdoors location of the target. %% Content name desc requires <*:CS_Plunder F:++ E:--> changes % Element1 is the outdoors scene for the ship % Element2 is the encounter for the ship % Element3 is the enemy faction % Element4 is the initial encounter with the enemy % Element5 is the new enemy NPC Element2 Place2 <1> Element3 Element4 Place4 <1> Element5 Place5 <4 (Enemies) sd enemy> % P%id%01 = Initialization Counter % At startup, activate the escort and level the enemy. update % Pass the hint-buck to the ship being plundered. HINT_%id% <:%id1%> % The narrative hooks for the BASE_PlunderShip encounter. % "This is where the magic happens." .%id1%_%plotid1%_GoAttackShip <> .%id1%_%plotid1%_GoBoardShip <> .%id1%_%plotid1%_GoPlunderOK .%id1%_%plotid1%_GoPlunderBad .%id1%_%plotid1%_GoPlunderFail % SubPlot1 is the plundering core. % SubPlot2 is the good loot % SubPlot3 is whatever the PC gets for blowing up the ship SubPlot1 <*:CS_BASE_PlunderShip 1 2 3> SubPlot2 <*:CS_FilthyLucre_OK 3> SubPlot3 <*:CS_FilthyLucre_Bad 3> sub MetaScene 4 2 %% An initial combat scene with the ship's escort. If combat is %% won, then the ship gets activated and the escort gets deactivated. % L1 = Encounter Over Counter % L2 = Initialization Counter % L3 = Attempt Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end Persona 5 special greeting *result%id%01 <*THEME_EXPO&Enemy na> Msg%id%01 Msg%id%01_1 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 end inv STC CORE-ACTIVATABLE name STC CORE-ACTIVATABLE name <%name2%> end Content name desc requires <*:CS_Plunder (F:POLIC|F:POLIT) E:++> % Element1 is the outdoors scene for the ship % Element2 is the encounter for the ship % Element3 is a corporate faction for the ship; doesn't matter what % Element4 is the battle to get away % Element5 is the enemy NPC % Element6 is a secret about the NPC Element2 Place2 <1> Element3 Element4 Place4 <1> Element5 Place5 <4 (EnemyNPC) sd enemy> Element6 % P%id%01 = Initialization Counter % P%id%02 = Big Plunder (0) or Little Plunder (1) % P%id%03 = Have gained decimation % At startup activate the plunder, scale the NPC, and set the secret. update % Pass the hint-buck to the ship being plundered. HINT_%id% <:%id1%> % The narrative hooks for the BASE_PlunderShip encounter. % "This is where the magic happens." .%id1%_%plotid1%_GoAttackShip <> .%id1%_%plotid1%_GoBoardShip <> .%id1%_%plotid1%_GoPlunderOK .%id1%_%plotid1%_GoPlunderBad .%id1%_%plotid1%_GoPlunderFail % The narrative hooks for the confrontation. .%id4%_%plotid4%_GoWin .%id%_GoSmallWin .%id4%_%plotid4%_GoLoss % Other narrative hooks. .%id5%_%plotid5%_GoLearnSecret .%id6%_%plotid6%_GoDecimation Msg%id%01 % SubPlot1 is the plundering core. % SubPlot2 is the good loot % SubPlot3 is whatever the PC gets for blowing up the ship % SubPlot4 is the Confrontation bit % SubPlot5 is the chance to learn about E5's raid... % SubPlot6 is the GainAdvantage against E5 once you do know SubPlot1 <*:CS_BASE_PlunderShip 1 2 3> SubPlot2 <*:CS_FilthyLucre_OK 3> SubPlot3 <*:CS_FilthyLucre_Bad 3> SubPlot4 <*:CS_MIX_Confrontation 5 1> SubPlot5 <*:CS_LearnSecretAboutNPC 6 5> SubPlot6 <*:CS_GainAdvantageVsNPC&Decimation 5> sub MetaScene 4 2 %% After capturing the ship, you have to defend your take from %% the police. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu3 Msg1 Msg2 <%name5% tried to arrest you.> Msg3 sub team 1 SetEnemy 2 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 SetAlly 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 name SetEnemy 1 SetAlly 2 ParaX 45 ParaY 45 end end inv STC CORE-STATIONARY name Encounter name % The people who want to steal the PC's booty. update Use < > Attack < > Secret Msg <%name5% has been setting a trap for you.> end Content name desc %% Prototype: Mecha fight after ship; lose mission if lost requires <*:CS_Plunder F:CRIME E:++> % Element1 is the outdoors scene for the ship % Element2 is the encounter for the ship % Element3 is a corporate faction for the ship; doesn't matter what % Element4 is the battle to keep the booty % Element5 is the enemy NPC % Element6 is a secret about the NPC Element2 Place2 <1> Element3 Element4 Place4 <1> Element5 Place5 <4 (EnemyNPC) sd enemy> Element6 % P%id%01 = Initialization Counter % P%id%02 = Big Plunder (0) or Little Plunder (1) % P%id%03 = Have gained decimation % At startup activate the plunder, scale the NPC, and set the secret. update % Pass the hint-buck to the ship being plundered. HINT_%id% <:%id1%> % The narrative hooks for the BASE_PlunderShip encounter. % "This is where the magic happens." .%id1%_%plotid1%_GoAttackShip <> .%id1%_%plotid1%_GoBoardShip <> .%id1%_%plotid1%_GoPlunderOK .%id1%_%plotid1%_GoPlunderBad .%id1%_%plotid1%_GoPlunderFail % The narrative hooks for the confrontation. .%id4%_%plotid4%_GoWin .%id%_GoSmallWin .%id4%_%plotid4%_GoLoss % Other narrative hooks. .%id5%_%plotid5%_GoLearnSecret .%id6%_%plotid6%_GoDecimation Msg%id%01 % SubPlot1 is the plundering core. % SubPlot2 is the good loot % SubPlot3 is whatever the PC gets for blowing up the ship % SubPlot4 is the Confrontation bit % SubPlot5 is the chance to learn about E5's raid... % SubPlot6 is the GainAdvantage against E5 once you do know SubPlot1 <*:CS_BASE_PlunderShip 1 2 3> SubPlot2 <*:CS_FilthyLucre_OK 3> SubPlot3 <*:CS_FilthyLucre_Bad 3> SubPlot4 <*:CS_MIX_Confrontation 5 1> SubPlot5 <*:CS_LearnSecretAboutNPC 6 5> SubPlot6 <*:CS_GainAdvantageVsNPC&Decimation 5> sub MetaScene 4 2 %% After capturing the ship, you have to defend your take from %% these other crooks. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 nu3 Msg1 Msg2 <%name5% attempted to steal your booty.> Msg3 <%name5%'s gang steals all of your rightfully-pilfered loot.> sub team 1 SetEnemy 2 3 ParaX 5 ParaY 5 team 2 name SetEnemy 1 SetAlly 3 Deploy GoSmallDeploy ParaX 45 ParaY 45 team 3 name SetEnemy 1 SetAlly 2 ParaX 45 ParaY 45 end end inv STC CORE-STATIONARY name Encounter name % The people who want to steal the PC's booty. update Use < > Attack < > Secret Msg <%name5% has been planning a raid against %name2%.> end Content name desc %% Prototype: Mecha fight before ship; may re-attempt fight if lost requires <*:CS_Plunder F:-- (L:FCOMS|L:RISHI|L:CRIHN|L:MAQUI|L:BOHEM|L:REDMA) -P:PRIVA> changes % Element1 is the outdoors scene for the ship % Element2 is the encounter for the ship % Element3 is the corporate faction % Element4 is the initial encounter with corporate guards Element2 Place2 <1> Element3 Element4 Place4 <1> % P%id%01 = Initialization Counter % At startup, activate the escort. update % Pass the hint-buck to the ship being plundered. HINT_%id% <:%id1%> % The narrative hooks for the BASE_PlunderShip encounter. % "This is where the magic happens." .%id1%_%plotid1%_GoAttackShip <> .%id1%_%plotid1%_GoBoardShip <> .%id1%_%plotid1%_GoPlunderOK .%id1%_%plotid1%_GoPlunderBad .%id1%_%plotid1%_GoPlunderFail % SubPlot1 is the plundering core. % SubPlot2 is the good loot % SubPlot3 is whatever the PC gets for blowing up the ship SubPlot1 <*:CS_BASE_PlunderShip 1 2 3> SubPlot2 <*:CS_FilthyLucre_OK 3> SubPlot3 <*:CS_FilthyLucre_Bad 3> sub MetaScene 4 2 %% An initial combat scene with the ship's escort. If combat is %% won, then the ship gets activated and the escort gets deactivated. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-ACTIVATABLE name STC CORE-ACTIVATABLE name <%name2%> end Content name desc requires <*:CS_Plunder F:-- P:PRIVA> changes % Element1 is the outdoors scene for the ship % Element2 is the encounter for the ship % Element3 is the corporate faction % Element4 is the initial encounter with corporate guards Element2 Place2 <1> Element3 Element4 Place4 <1> % P%id%01 = Initialization Counter % At startup, activate the escort. update % Pass the hint-buck to the ship being plundered. HINT_%id% <:%id1%> % The narrative hooks for the BASE_PlunderShip encounter. % "This is where the magic happens." .%id1%_%plotid1%_GoAttackShip <> .%id1%_%plotid1%_GoBoardShip <> .%id1%_%plotid1%_GoPlunderOK .%id1%_%plotid1%_GoPlunderBad .%id1%_%plotid1%_GoPlunderFail % SubPlot1 is the plundering core. % SubPlot2 is the good loot % SubPlot3 is whatever the PC gets for blowing up the ship SubPlot1 <*:CS_BASE_PlunderShip 1 2 3> SubPlot2 <*:CS_FilthyLucre_OK 3> SubPlot3 <*:CS_FilthyLucre_Bad 3> sub MetaScene 4 2 %% An initial combat scene with the ship's escort. If combat is %% won, then the ship gets activated and the escort gets deactivated. % L1 = Encounter Over Counter % L2 = Initialization Counter MapWidth 50 MapHeight 50 Start nu1 nu2 end Msg1 <%name2% is being escorted by a team of mecha from %name3%. With any luck, they're carrying something worth stealing.> Msg2 Msg3 sub team 1 SetEnemy 2 ParaX 5 ParaY 5 team 2 name SetEnemy 1 Deploy ParaX 45 ParaY 45 end end inv STC CORE-ACTIVATABLE name STC CORE-ACTIVATABLE name <%name2%> end %% %% *CS_SeekPlunder %% %% The PC has to find someone and convince them to hand over some info leading %% to a plunder event. For instance, an enemy base may be raided to steal their %% info, or a thief might give the PC a corporate route plan in exchange for %% some kind of service. %% %% This subplot needs a HINT_%id%, which will explain to the PC what needs to %% be done to gain the info (or, at least, how to meet the person who will %% explain). %% Content name desc requires <*CS_SeekPlunder> % The PC can get the tip by performing a service for the NPC. % E1 is a local public scene % E2 is the NPC to pass the info % E3 is the datachip with the info % E4 is the environs scene for the robbery Element1 Element2 NeverFail2 Place2 Element3 Place3 <2> Element4 %% HINT HINT_%id% <%name2% knows how to score some easy money, but won't be giving the info away for free.> % P%id%01 = Initialization Counter update %% FAIL CONDITIONS: %% - E2 dies before PLUNDER activated end % Stealing the datachip will give the PC the info. Get%3% % Completing the PLUNDER ends this episode. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%02 Msg%id%03 Msg%id%04 % SubPlot1 is the Plunder itself. % SubPlot2 is the mission the PC must complete. SubPlot1 <*:CS_Plunder 4> SubPlot2 <*:CS_MinorMission 2> sub Persona 2 &Price greeting .%id%_GoCheckWin .%id%_GoCheckLoss *.%id%_GoRemind <*GenericMissionReminder> .%id%_GoGiveTip .%id%_GoChat *.%id%_GoGreet <*IHaveInformation&AboutMoney .%id%_GoOffer na> .%id%_GoOffer result%id%01 *result%id%02 <*YourLossSucker .%id%_GoLoseEpisode> .%id%_GoLoseEpisode *result%id%03 <*HurryBackWithMoney> result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 <%name2% told you about a good target. \HINT %id1%> Msg%id%04 <%name2% told you about a good target.> Msg%id%05 Msg%id%06 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 end inv Treasure name end Content name desc requires <*CS_SeekPlunder ~!Ne> % The PC will get a call from someone about a fast cash opportunity. % Then they can buy the info, steal it, or maybe even talk it out. % E1 is a local public scene % E2 is the NPC to pass the info % E3 is the datachip with the info % E4 is the environs scene for the robbery Element1 Element2 NeverFail2 Place2 Element3 Place3 <2> Element4 %% HINT HINT_%id% <%name2% will be selling some valuable info to the higest bidder.> % P%id%01 = Initialization Counter update %% FAIL CONDITIONS: %% - E2 dies before PLUNDER activated end % Stealing the datachip will give the PC the info. Get%3% % Completing the PLUNDER ends this episode. .%id1%_%plotid1%_GoWin .%id1%_%plotid1%_GoLoss Msg%id%02 Msg%id%03 Msg%id%04 % SubPlot1 is the Plunder itself. Nice and simple. SubPlot1 <*:CS_Plunder 4> sub Persona 2 &Price greeting .%id%_GoChat *.%id%_GoGreet <*IHaveInformation&AboutMoney .%id%_GoOffer na> .%id%_GoOffer result%id%01 *result%id%02 <*YourLossSucker .%id%_GoLoseEpisode> .%id%_GoLoseEpisode *result%id%03 <*HurryBackWithMoney> Msg%id%01 Msg%id%02 Msg%id%03 <%name2% told you about a good target. \HINT %id1%> Msg%id%04 <%name2% told you about a good target.> Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 end inv Treasure name end %% %% *:CS_FilthyLucre_OK Content %% *:CS_FilthyLucre_Bad Content %% %% The PC has just robbed someone. Give them their filthy lucre! Just set the %% plotstatus to give the loot and cancel this subplot. %% %% Note that *CORE_EMO_ should get better loot than other plunder missions. %% %% Param1: The faction wot is being robbed. %% Content name requires <*:CS_FilthyLucre_OK *CORE_EMO_> update Msg%id%01 Content name requires <*:CS_FilthyLucre_OK -*CORE_EMO_> update Msg%id%01 Content name requires <*:CS_FilthyLucre_Bad> update Msg%id%01 GH2/series/MEGA_PromotionRewards.txt0000644000175000017500000000403111326004535016233 0ustar kaolkaol%% %% *FACTION_PROMOTION_REWARD %% %% These components contain faction reward stuff. %% %% Reward components have to check the PC's RENOWN. Renown must be greater %% than RewardRank * 10 or the PC will get a cash prize instead of whatever %% super reward was being planned. Certain factions may add an additional %% test; for instance, a police faction may only give the premium reward %% if the PC's Lawfulness is also greater than RewardRank * 10. %% %% Reward components also have to check to make sure the level being %% rewarded is in the range 1..8; for faction level 9 and above, a cash %% prize is all that the PC can get. %% %% PARAM 1 = The NPC to give the reward. %% This NPC must belong to the faction rewarding the PC. Content name desc requires <*FACTION_PROMOTION_REWARD> % E1 is the character giving the reward sub Persona 1 % V%id%01 = Mecha rating calculator greeting *.%id%_GoMinorPrize <*PROMOTION_Minor_Reward> % Generate a mecha of the requested renown level with a random theme and % rewardlevel * 2 modification points. .%id%_GoBigPrize .%id%_fac Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 end GH2/series/QUEST_DungeonQuest.txt0000644000175000017500000002677611326004537015554 0ustar kaolkaol%% %% *:Q_DungeonQuest Content %% %% A dungeon is a dangerous place filled with monsters. We give them dungeon %% quests in order to prevent them from being boring. %% %% Set label to *:Q_DDungeonQuest for debuging. %% %% %% PARAM1: The dungeon involved %% Content name requires <*:Q_DungeonQuest> % Someone died in this dungeon; apparently, they want their ashes % returned there. Yes, I know I did this plot in GH1... I'm trying % to get the Cayley dungeon ready for release! % E1 is the dungeon involved % E2 is a local public scene % E3 is the priest who requests this % E4 is the urn to be placed % E5 is the character to die Element2 Element3 Place3 <2 (Citizens) pass ally> Element4 Place4 <3> Element5 Place5 <2 (Citizens) pass ally> % P%id%01 = Have accepted task % P%id%02 = Initialization Counter %% FAIL CONDITIONS: %% - Before accepting quest, priest dies or item destroyed %% - After accepting quest, urn destroyed end .%id%_GoCheckUrn .%id%_GoCU2 update % Victory is achieved if the PC has accepted the mission, the urn is in E1, % and E1 isn't the current scene. start Msg%id%01 Msg%id%02 % SubPlot1 is the victory condition % SubPlot2 is the loss condition SubPlot1 <*Q_WinTask 3> SubPlot2 <*Q_LoseTask 3> sub Persona 3 rumor%id% <%name3% needs a cavalier to make a very unusual delivery.> greeting *GoRemind <*GenericMissionReminder> *GoGreet <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> *GoComeHere <*NotByPhone> *GoMakeOffer <*INeedYourHelp&Spiritual GoListen GoRefuse> *GoRefuse <*RejectMission GoCancelQuest> GoCancelQuest GoListen result1 result2 result3 result4 result5 Msg1 Msg2 Msg3 <%name3% hired you to bring %name5%'s ashes to \SCENE %1% .> Msg4 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 end inv NPC Priest chardesc heroic spiritual Treasure name mass 1 Fudge 65000 NPC Explorer chardesc Male end Content name requires <*:Q_DungeonQuest> % E1 is the dungeon involved % E2 is a local public scene % E3 is an adventurer who is looking for an artifact % E4 is the artifact being sought Element2 Element3 Place3 <2 (Citizens) pass ally> Element4 % P%id%01 = Initialization Counter % P%id%02 = Have accepted quest %% FAIL CONDITIONS: %% - E4 gets destroyed %% - E3 dies update end .%id%_GoFailTask .%id%_GoCheckDeath % If the PC gets the item, call the Win Fetch subplot. Get%4% .%id%_GoFetchNoMission % SubPlot1 is the win fetch quest. % SubPlot2 is the task lost quest. % SubPlot3 is the didn't-accept-quest-but-fetched-anyways bit. SubPlot1 <*Q_Win_FetchItem 3 4> SubPlot2 <*Q_LoseTask 3> SubPlot3 <*Q_FetchWantedItem 3 4> sub Persona 3 rumor%id% <%name3% is looking for something in %name1%.> greeting *.%id%_GoRemind <*GenericMissionReminder> *.%id%_GoGreet <*AreYouHereAboutJob .%id%_GoBriefing> .%id%_GoBriefing result%id%01 *result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%03 <\ITEM_HISTORY %4% \ITEM_USAGE %4% If you find it for me you'll be greatly rewarded.> Msg%id%04 Msg%id%05 <%name3% in %name2% hired you to recover the %name4% from %name1%.> Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 end inv NPC Explorer end Content name requires <*:Q_DungeonQuest> % E1 is the dungeon involved % E2 is a local public scene % E3 is a professor who is looking for an artifact % E4 is the artifact being sought Element2 Element3 Place3 <2 (Citizens) pass ally> Element4 % P%id%01 = Initialization Counter % P%id%02 = Have accepted quest %% FAIL CONDITIONS: %% - E4 gets destroyed %% - E3 dies update end .%id%_GoFailTask .%id%_GoCheckDeath % If the PC gets the item, call the Win Fetch subplot. Get%4% .%id%_GoFetchNoMission % SubPlot1 is the win fetch quest. % SubPlot2 is the task lost quest. % SubPlot3 is the didn't-accept-quest-but-fetched-anyways bit. SubPlot1 <*Q_Win_FetchItem 3 4> SubPlot2 <*Q_LoseTask 3> SubPlot3 <*Q_FetchWantedItem 3 4> sub Persona 3 rumor%id% <%name3% has been trying to locate the %name4%.> greeting *.%id%_GoRemind <*GenericMissionReminder> *.%id%_GoGreet <*AreYouHereAboutJob .%id%_GoBriefing> .%id%_GoBriefing result%id%01 *result%id%02 <*RejectMission .%id%_GoR2Exit> .%id%_GoR2Exit result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%02 Msg%id%03 <\ITEM_DESC %4% \ITEM_HISTORY %4% If you recover it, I can continue my research.> Msg%id%04 Msg%id%05 <%name3% in %name2% hired you to recover the %name4% from %name1%.> Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 Prompt%id%05 Prompt%id%06 CPrompt%id%06 end inv NPC Professor end %% %% *:Q_DungeonSecret %% %% Loads a hopefully unique treasure for this dungeon. %% %% Set label to *:Q_DDungeonQuest for debuging. %% %% %% PARAM1: The dungeon involved %% Content name requires <*:Q_DungeonSecret> % E1 is the dungeon level % E2 is the entrance to the treasure room % E3 is the treasure room itself Element2 Place2 <1> Element3 Place3 <1> sub MetaScene 2 sub room minimap <......###..#1#...........> desig end MetaScene 3 name <# Mecha Bay> type mapwidth 17 mapheight 12 BoxMap IndustrialTiles Ceiling special start Msg1 NeededCells 4 content1 sub team 1 team 2 name SetEnemy 1 room minimap <.......##...1#...##......> inv Elevator name MiniMapComponent 1 Destination -1 end room minimap <......###..##1..###......> inv Elevator MiniMapComponent 1 name % V1 = Have obtained mecha use .mektype GoBeenBefore Msg1 Msg2 Msg3 Msg4 end end end inv Elevator desig end %% %% *:Debug Frame %% %% Loads a dungeon quest into the Cavalier's Club for easy debugging. %% Content requires <*:Q_Debug> % E1 is the CavClub % E2 is the new dungeon Element1 Element2 Place2 <1> update % SubQuest1 is the mission. SubPlot1 <*:Q_DDungeonQuest 2> sub STC QS_Dungeon_Sewer SetID 2 entrance <*GoDown> end GH2/series/QUEST_MechaArena_CQuest.txt0000644000175000017500000001457011326004535016366 0ustar kaolkaol%% %% *Q_CHALLENGER_SUBQUEST %% Contains a subquest activated after defeating a mecha arena challenger. %% This can be an extravagant new adventure, or a pithy closing sequence. %% %% The ID for this subquest must be set by the parent quest. %% %% PARAM1: The Challenger %% Content name % The challenger can train the PC in mecha combat skills requires <*Q_CHALLENGER_SUBQUEST 1:MILIT -!Ne -!Lo> start % E1 is the challenger sub Persona 1 % V%id%01 = Have accepted offer greeting .%id%_GoSchool *.%id%_GoFail <*IConcedeDefeat&Battle> .%id%_GoChat *.%id%_GoBye <*GoodBye> .%id%_Skills <1 2 3> result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 <%name1% offered to train you in mecha combat.> Msg%id%05 Msg%id%06 Msg%id%07 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%04 end Content name % The challenger will likely become friends with the PC. requires <*Q_CHALLENGER_SUBQUEST> special start % E1 is the challenger sub Persona 1 greeting *.%id%_GoFail <*IConcedeDefeat&Battle> Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 end Content name % The challenger will likely become either a mentor or ally to the PC, % depending on their relative ages. requires <*Q_CHALLENGER_SUBQUEST (1:ADVEN|1:MILIT) -!Ne -!Lo> start % E1 is the challenger sub Persona 1 % V%id%01 = Have become trainer greeting .%id%_GoSchool .%id%_GoMakeAlly *.%id%_GoFail <*IConcedeDefeat&Battle> result%id%01 .%id%_Skills <1 2 3 4 5 6 11 15 16> *.%id%_GoBye <*GoodBye> Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 Msg%id%02_1 <> CMsg%id%02_1 Msg%id%02_2 <> CMsg%id%02_2 Msg%id%02_3 <> CMsg%id%02_3 Msg%id%02_4 <> CMsg%id%02_4 Msg%id%02_5 <> CMsg%id%02_5 Msg%id%02_6 <> CMsg%id%02_6 Msg%id%03 <%name1% in \SCENE RootSceneID offered to become your mentor.> Msg%id%04 Msg%id%04_1 <> CMsg%id%04_1 Msg%id%04_2 <> CMsg%id%04_2 Msg%id%04_3 <> CMsg%id%04_3 Msg%id%04_4 <> CMsg%id%04_4 Msg%id%04_5 <> CMsg%id%04_5 Msg%id%04_6 <> CMsg%id%04_6 Msg%id%05 Msg%id%05_1 <> CMsg%id%05_1 Msg%id%05_2 <> CMsg%id%05_2 Msg%id%05_3 <> CMsg%id%05_3 Msg%id%05_4 <> CMsg%id%05_4 Msg%id%05_5 <> CMsg%id%05_5 Msg%id%05_6 <> CMsg%id%05_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 end GH2/series/PLOT_MOOD_Disaster.txt0000644000175000017500000002124311401151245015355 0ustar kaolkaol%% %% Disaster Plots %% %% Something bad has happened, like an earthquake or a typhoon. Various %% rescue and rebuilding efforts may be underway. %% %% Mood Spec: %% - No Elements %% Plot name desc requires <*Disaster> % E1 is the town itself % E2 is a police or military character aligned with the town % E3 is the urban scene where the looters are Element1 Element2 Element3 % V1 = Time Limit % V2 = Memo Counter % There's a 24-hour time limit. start GoCheckTime end % Winning the battle against the looters will automatically end the plot. update GoCheckLoss % SubPlot1 is the mecha encounter task SubPlot1 <*:TASK_DefeatLooters 3> sub Persona 2 rumor0 <%name2% has been trying to control the looters in %name3%.> greeting GoRemind *GoCheckEnemy <*ENEMY_CHECK GoCheckOffer ChatNPCFac GoEnd> GoEnd GoCheckOffer GoNoOffer result1 result2 Msg1 Msg1_1 Msg2 Msg2_1 CMsg2_1 Msg2_2 CMsg2_2 Msg2_3 CMsg2_3 Msg2_4 CMsg2_4 Msg2_5 CMsg2_5 Msg2_6 CMsg2_6 Msg3 Msg3_1 Msg3_2 CMsg3_2 Msg4 Msg4_1 Msg4_2 Msg5 Msg6 Msg6_1 Prompt1 Prompt1_1 Prompt2 Prompt2_1 end Plot name desc requires <*Disaster> % E1 is the town itself % E2 is a businessperson or politician Element1 Element2 % V1 = Time Limit % There's a 24-hour time limit. start GoCheckTime end sub Persona 2 rumor <%name2% has been collecting donations to help %name1%.> &HighPledge &MediumPledge &LowPledge <500> greeting GoGive result1 result2 result3 result4 result5 Msg1 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg2 Msg2_1 Prompt1 CPrompt1 Prompt2 CPrompt2 Prompt3 CPrompt3 Prompt4 Prompt4_1 Prompt5 <[Continue]> end Plot name desc requires <*Disaster> % E1 is a medical professional- a nurse, doctor, etc Element1 % V1 = Time Limit % There's a 24-hour time limit. start GoCheckTime end sub Persona 1 rumor <%name1% has been running a blood drive for victims of the disaster.> greeting GoNotHere %% Being a blood donor abuses vitality, just a little- this is the price for the point of heroism. %% Note that it can't actually reduce your vitality skill, it'll just delay your next vitality %% boost. %% Donating blood will contribute to faction XP for the government faction. result1 .desc result2 result3 Msg1 Msg1_1 CMsg1_1 Msg1_2 <\SCENE RootSceneID has run out of synthetic blood. I'm sure that a brave young adventurer such as yourself has no problem with needles. How about being a blood donor?> CMsg1_2 Msg1_3 <\SCENE RootSceneID is currently hosting a blood drive. How would you like to be a donor? You'll get a free lunch in exchange for your time.> Msg2_1 Msg2_2 Msg2_3 Msg3 <\PC donates blood.> Msg4 <\PC gets a free lunch from %name1%.> Msg5_1 Msg5_2 Msg5_3 Msg6 Prompt1 Prompt1_1 Prompt2 Prompt2_1 Prompt3 Prompt3_1 end GH2/series/QUEST_FindMoney.txt0000644000175000017500000006425711326004537015017 0ustar kaolkaol%% %% *:Q_FindMoney Contents %% .Investor = The NPC needs the money to invest in a new product %% .Charity = The NPC needs the money to save an orphanage or whatever %% %% The PC is sent by someone to scrounge funds for some project or another. %% %% Set the requires attribute to *:Q_DFindMoney for testing %% %% Param1: The character who needs the money %% Content name desc requires <*:Q_FindMoney> % E1 is the character who needs the money % E2 is the character who can give the money % E3 is a scene for E2 Element2 Place2 <3 (Citizens) Pass> Element3 %% FAIL CONDITIONS: %% - E2 dies end % Quest1 is the mission offered by the shopkeeper. SubPlot1 <*:Q_MinorMission 2> sub Persona 2 rumor%id% <%name2% is known as a great philanthropist.> greeting GoCheckLoss GoCheckQuest *GoGreet <*NiceToMeetYou GoShop> *GoShop <*SHOP_MECHA GoBye> *GoBye <*GoodBye> result1 result2 result3 Msg1 Msg3 Msg4 Msg5 Prompt1 <%name1% needs your help.> Prompt2 Prompt3 <[Continue]> MetaScene 2 sub room minimap <....1...............&---&> special desig end end inv NPC Mechanic chardesc heroic end Content name desc requires <*:Q_FindMoney> % E1 is the character who needs the money % E2 is the character who can give the money % E3 is a scene for E2 Element2 Place2 <3 (Citizens) Pass> Element3 %% FAIL CONDITIONS: %% - E2 dies end % Quest1 is the mission offered by the shopkeeper. SubPlot1 <*:Q_MinorMission 2> sub Persona 2 rumor%id% <%name2% has been known to donate money to a variety of causes.> greeting GoCheckLoss GoCheckQuest *GoGreet <*NiceToMeetYou GoShop> *GoShop <*SHOP_ARMOR GoBye> *GoBye <*GoodBye> result1 result2 result3 Msg1 Msg3 Msg4 Msg5 Prompt1 Prompt2 Prompt3 <[Continue]> MetaScene 2 sub room minimap <....1..........2...3&---&> special desig inv STC MANNEKIN-M PDir 1 MiniMapComponent 2 STC MANNEKIN-F PDir 3 MiniMapComponent 3 end end end inv NPC Shopkeeper Chardesc Shy end Content name desc requires <*:Q_FindMoney .Charity> % E1 is the character who needs the money % E2 is the organizer of the tournament % E3 is the tournament encounter % E4 is a scene for E2 % E5 is an environs scene for E3 Element2 Place2 <4 (Citizens) Pass> Element3 Place3 <5> Element4 Element5 sub Persona 2 rumor%id% <%name2% has organized a charity mecha tournament.> % NPCVar %2% 1 = Have won the fight % V1 = Match recharge timer % V2 = Registration counter greeting GoCheckStatus GoCheckRecharge GoCheckFirst GoRegister GoSetCombat *GoGreet <*NiceToMeetYou GoChat> *GoChat <*MISC_CHATTER> result1 result2 result3 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 msg9 Msg10 Prompt1 Prompt2 Prompt3 % Death check Metascene 2 start end inv NPC Arena Pilot chardesc Heroic Sociable STC QUEST-MAPMARKER-STATIONARY name %% V11 = Primed for combat update use GoStartCombat .nu1 .nu2 .msg1 Msg1 end Content name desc requires <*:Q_FindMoney .Investor ~"safe" ~"financial" ~"lawful"> % E1 is the character who needs the money % E2 is a banker who the PC can argue with % E3 is the scene for the bank Element2 Place2 <3 (Citizens) Pass> Element3 %% FAIL CONDITIONS: %% - E2 dies end sub Persona 2 rumor%id% <%name2% offers business loans at reasonable rates.> % V1 = Stupid question counter Greeting *GoGreet <*NiceToMeetYou GoChat> GoChat Result1 Result2 Result3 GoR3Fail result4 GoR4Fail result5 % Vouching for a criminal character will result in a reputation hit result6 result7 result8 result9 result10 result11 *result12 <*GoodBye> Msg1 Msg2 Msg3 Msg4 Msg5 Msg5_1 CMsg5_1 Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Prompt1 CPrompt1 Prompt2 <%name1% needs a business loan.> CPrompt2 Prompt3 Prompt4 CPrompt4 Prompt5 Prompt6 Prompt7 Prompt7_1 Prompt7_2 Prompt8 Prompt9 CPrompt9 Prompt10 CPrompt10 Prompt11 Prompt12 MetaScene 2 sub room % The bank itself. Can't add a bank without adding a safe to rob, can you? name <%name2% Financial Services> minimap <2.......1...........#&+&#> special desig inv STC Safe % V1 = Have robbed safe % V2 = Have triggered alarm MiniMapComponent 2 PDir 2 Use GoAlreadyRobbed CLUE_CODEBREAKING GoRiskyOpen GoSafeOpen GoDiscovered GoCantOpen GoOpen Msg1_1 CMsg1_1 Msg1_2 CMsg1_3 GoAccept Msg2 Msg3 Msg4 Msg5 end end end inv NPC Corporate Executive job end Content name desc requires <*:Q_FindMoney SPINNER> % Element 1 will be the NPC who needs some money % Element 2 will be the tycoon who can maybe provide the money % E3 is the yacht scene % E4 is the outdoors scene where it will be placed. element2 Place2 <3 (Citizens) pass> Element3 Place3 <4> Element4 % P%id%01 = Initialization Counter %% FAIL CONDITIONS: %% - E2 dies end update SubPlot1 <*:Q_RevealEncounter 3 4> sub Persona 2 special % V1 = Have applied reaction boost. greeting GoBadParty *GoChat <*MISC_CHATTER> result1 result2 result3 Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 <%name2% isn't enjoying \PPR %2% yacht party in \SCENE RootSceneID very much.> Prompt1 <%name2%, I need to ask you a favor.> Prompt2 Prompt3 CPrompt3 MetaScene 3 name <%name2%'s Yacht> rumor%id% <%name2% is hosting a fancy yacht party for all the spinner's richest inhabitants.> entrance <*QUEST-INACTIVE> type mapwidth 21 mapheight 21 ClubMap special NeededCells 5 Content1 Content2 sub team 1 team 2 name SetAlly 1 Passive team 3 name SetAlly 2 room name desig Content1 Content3 room minimap <#######1###...##...###+##> inv Elevator name MiniMapComponent 1 Destination -1 end end end inv NPC Corporate Executive job chardesc Renowned Renowned Old Sociable Cheerful end Content name % The PC needs a few million dollars to help a friend. This rich shopkeeper will % give him the money if the PC will clear the rats out of his basement. Fairly % standard RPG plot, except the PC will be able to call the NPC on his clear insanity. requires <*:Q_FindMoney> % E1 is the character who needs the money % E2 is the shopkeeper who might be able to provide it % E3 is the basement full of rats % E4 is the scene for the office element2 Place2 <4 (Citizens) pass ally> Element3 Place3 <4> Element4 %% FAIL CONDITIONS: %% - E2 dies end sub Persona 2 rumor%id% <%name2% is very rich, but doesn't seem to be all that bright.> % V1 = Have accepted quest greeting GoQuestIntro GoCancel *GoFirstTime <*NiceToMeetYou GoOpenShop> *GoOpenShop <*SHOP_ARMOR GoGoodbye> *GoGoodbye <*GOODBYE> result1 result2 result3 GoR3NotHere result4 result5 result6 result7 result8 result9 result10 result11 result12 result13 result14 result15 Msg1 Msg1_1 Msg2 Msg3 Msg4 Msg5 <%name2% in %name4% agreed to fund %name1% if you clear the vermin out of \PPR %2% storage room.> Msg6 Msg7 Msg8 Msg9 Msg10 Msg11 Msg12 Msg13 Msg14 Msg14_1 Msg14_2 Msg15 Prompt1 Prompt2 Prompt3 Prompt4 Prompt5 Prompt5_1 Prompt5_2 Prompt5_3 Prompt6 Prompt7 Prompt8 Prompt9 Prompt10 Prompt11 Prompt12 Prompt13 CPrompt13 Prompt14 Prompt14_1 CPrompt14 Prompt15 MetaScene 2 sub room minimap <2#...+#.......1.....&---&> special desig sub Door update GoCheckQuest end inv TrapDoor MiniMapComponent 2 desig end end STC QS_ExterminationBasement name <%name2%'s Basement> SetID 3 end inv NPC Shopkeeper Knowledge 8 Ego 14 end Content name desc requires <*:Q_FindMoney ~.Investor> % E1 is the character who needs the money % E2 is the character who can give the money % E3 is the city scene; no money involved % E4 is the child who squandered the money % E5 is a local public scene Element2 Place2 <5 (Citizens) pass ally> Element3 <.> Element4 Element5 %% FAIL CONDITIONS: %% - E2 dies end % SubQuest1 is the intervention for the target SubPlot1 <*:Q_PersonalIntervention 4 2> sub Persona 2 rumor%id% <%name2% has been known to donate money to businesses and charities.> % V1 = Have accepted quest greeting GoCheckStatus GoFirstTime GoNoQuest result1 result2 result3 result4 % If the prodigal sondaughter is already dead at the start of this % subquest, the investment will be decided by a single conversation roll. GoR4Trivial GoR4Fail Result5 result6 Msg1 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 <%name4% left home some years ago and fell in with a bad crowd. Since then I've had very little contact with \OPR %4% . If you could find \OPR %4% and convince \OPR %4% to come back, I would agree to finance %name1%.> Msg11 <%name2% will agree to finance %name1% if you convince %name4% to return home.> Msg12 Msg13 <%name2% will agree to finance %name1% if you convince %name4%, who may or may not be at \SCENE NPCScene %4% , to return home.> Prompt1 Prompt2 CPrompt2 Prompt3 <%name4% has died.> CPrompt3 Prompt4 Prompt5 Prompt6 end inv NPC Corporate Executive job Age 30 end %% %% Debugging Frame %% %% Loads a mission into the Cavalier's Club for easy debugging. %% Content requires <*:Q_Debug> desc % E1 is the person who will be giving the mission. % E2 is the CavClub Element1 Place1 <2 (Citizens) ally pass> Element2 % SubQuest1 is the mission. SubPlot1 <*:Q_DFindMoney 1> sub Persona 1 rumor0 <%name1% needs a cavalier for a mission of some kind.> greeting GoCheckLoss *GoEndInLoss <*MissionWasFailure na na na> GoCheckFirst *GoOfferQuest <*DoYouWantAJob GoStartQuest> GoStartQuest GoChat Msg1 Msg2 MetaScene 1 sub room desig minimap <#&&&#&...&&.1.&&...&&&-&&> end end GH2/series/PLOT_FAC_Privateers.txt0000644000175000017500000000667111374513723015602 0ustar kaolkaol%% %% Plots specifically dealing with the Privateer's Guild of L5. %% Plot name % The PC is off to capture an enemy vessel. % This job will result in the PC making an enemy. requires <*GENERAL -!Ne -!Lo (FCOMS|MAQUI)> PayRate 250 % E1 is the town itself % E2 is a character who will offer the mission % E3 is a scene where the encounter will take place % E4 is the enemy faction Element1 Element2 Element3 Element4 % SubPlot1 is the combat encounter SubPlot1 <*MECHAMISSION_Capture 2 3 4> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Insert email here 5min Msg1 <%name2%@ \SCENE NPCSCene %2% :// Hey \RANK , there's a ship in \EXACT_SCENE %3% that needs to get blown up real good.> Msg1_1 <%name2%@ \SCENE NPCSCene %2% :// \RANK \PC , your services are requested by the Guild. Give me a call.> Msg1_2 <%name2%@ \SCENE NPCSCene %2% :// There's been trouble with %name4%. It's time to teach them a lesson.> Msg1_3 <%name2%@ \SCENE NPCSCene %2% :// I've got a mission for you. Come to \EXACT_SCENE NPCSCene %2% for the briefing.> sub Persona 2 rumor0 <%name2% needs a pilot to catch a ship belonging to %name4%.> greeting *GoRemind <*MechaMissionReminder %3%> GoCheckOffer *GoCheckEnemy <*ENEMY_CHECK GoCheckEmail ChatNPCFac GoEnd> GoCheckEmail *GoGotEmail <*DidYouGetEmail GoMissionBriefing> GoCheckMember *GoIsMember <*IHaveAJobForYou GoMissionBriefing> *GoCheckAuto <*AutoMissionTest&Mecha GoMissionBriefing GoRejectMission GoCheckSkill ChatNPCFac %4%> *GoCheckSkill <*GenericMissionTest&Mecha GoMissionBriefing GoEnd GoRejectMission ChatNPCFac %4% %threat%> *GoRejectMission <*RejectMission GoEnd> GoEnd GoMissionBriefing *result1 <*GoodLuckOnMission GoR1Final ChatNPCFac %4%> GoR1Final result2 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 Msg2 <%name2% in \SCENE NPCSCene %2% hired you to capture a vessel belonging to %name4% in \EXACT_SCENE %3% .> Prompt1 Prompt1_1 Prompt1_2 Prompt2 CPrompt2 Prompt2_1 Prompt2_2 end GH2/series/MEGA_CORE_LoveInterest.txt0000644000175000017500000000457011326004537016162 0ustar kaolkaol%% %% Love Interest Content %% %% All of these component types have something to do with the love interest. %% %% %% *CS_LoversGift Content %% %% The PC's love interest is going to provide some kind of help. %% This isn't a proper subplot, but just a utility extension for %% an existing subplot. To activate, the love interest's persona %% should goto the script line ".%id%_GoLoversGift", where %id% %% is the ID of this subplot. %% %% Once the script is activated, this subplot should terminate %% with "WinSubPlot %plotid%". %% %% Important: Make sure E1 is in play! %% %% PARAM1: The love interest %% Content name desc requires <*CS_LoversGift -1:LOVER> % E1 is the love interest sub Persona 1 .%id%_GoLoversGift *.%id%_GoFail <*ComeHereForIt> Msg%id%01 <%name1% leans in close and kisses you.> Msg%id%02 Msg%id%02_1 <> CMsg%id%02_1 Msg%id%02_3 <> CMsg%id%02_3 Msg%id%02_2 <> CMsg%id%02_2 Msg%id%02_4 <> CMsg%id%02_4 Msg%id%02_5 <> CMsg%id%02_5 Msg%id%02_6 <> CMsg%id%02_6 Msg%id%03 <%name1% kissed you.> end Content name desc requires <*CS_LoversGift 1:LOVER> % E1 is the item-giver sub Persona 1 % V%id%01 = Threat Counter .%id%_GoLoversGift *.%id%_GoFail <*ComeHereForIt> Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 .%id%_Factions end GH2/series/PFRAG_CORE_Defalt.txt0000644000175000017500000006317111374513723015073 0ustar kaolkaol%% %% CORE STORY PERSONA FRAGMENTS %% %% These fragments are used by the core story. Therefore, they can use all the story %% context descriptors in their REQUIRES blocks, and can access the XRan palette freely. %% %% In addition, some of these fragments are tied to specific components, and may not %% be readily reusable in other components. % TYPE: *CS_PEA_DiscussTerms % The PC has come to negotiate a peace agreement. To do so they need to % get past a little bit of negotiation. There will usually be a way for % the PC to grease the wheels a little bit if the skill rolls don't work % out. This conversation snippet will lead into the NPC asking to perform % some task as a show of good faith. % PARAM1: Difficulty Rating % PARAM2: Success Exit Label % PARAM3: Give Up Exit Label Persona requires <*CS_PEA_DiscussTerms> % V%id%01 = Have gotten through the diplomacy START .%id%_GoFailRoll result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 result%id%07 result%id%08 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 Prompt%id%06 Prompt%id%07 CPrompt%id%07 Prompt%id%08 CPrompt%id%08 % TYPE: *CS_BASEPS_CaptainSurrenders % The captain of the ship which the PC is plundering has just surrendered. % Give a comment, and maybe alter the captain's attitude. % PARAM1: Exit Label Persona requires <*CS_BASEPS_CaptainSurrenders> START Msg%id%01 % TYPE: *CS_BASEPS_CaptainNegotiates % The PC has just struck up a coversation with the captain of the ship % which they are currently plundering. Maybe a surrender can be forced % without actually doing anything. % PARAM1: Captain surrenders exit label % PARAM2: Captain attacks exit label % PARAM3: Nothing much happens exit label Persona requires <*CS_BASEPS_CaptainNegotiates> START .%id%_GoAttack result%id%01 result%id%02 .%id%_GoR2Fail Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Msg%id%05 Prompt%id%01 Prompt%id%02 % TYPE: *CS_WarZone_Greeting % The PC has just entered a warzone. This NPC will brief the PC on what % happened. The PC can either sound concerned or shrug it off. % PARAM1: Offer help label % PARAM2: Noncommital label Persona requires <*CS_WarZone_Greeting> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 % TYPE: *CS_ForcedInduction_Greeting % The PC has just been shangheid into joining a faction. % PARAM1: Exit label (no message printed here) % PARAM2: Faction that's been joined Persona requires <*CS_ForcedInduction_Greeting> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 % TYPE: *COMP_Pme_MFH_Greeting % The PC has just run into his friend and a bunch of hostile mecha. % Friend runs off, leaving PC to fight the mecha. % PARAM1: Exit label (no message printed here) Persona requires <*COMP_Pme_MFH_Greeting> START result%id%01 Msg%id%01 Msg%id%01_1 <\PC , what in the world are you doing here!? How much do you know?> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 prompt%id%01 prompt%id%01_1 prompt%id%01_2 % TYPE: *COMP_Pun_MAE_Greeting % The PC is meeting his new arch-enemy for the first time. Yay! % PARAM1: Exit label (no message printed here) Persona requires <*COMP_Pun_MAE_Greeting E:Friend> start result%id%01 Msg%id%01 <\PC ! What are you doing here? You shouldn't be in this place... you shouldn't even be in \SCENE &CompScene . I don't want to fight you but I will if I have to.> Prompt%id%01 Prompt%id%01_1 Persona requires <*COMP_Pun_MAE_Greeting F:++> start result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*COMP_Pun_MAE_Greeting> start result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%02 Prompt%id%01 Prompt%id%02 Prompt%id%03 % TYPE: *CORE_Pme_MEIB_Greeting % The PC is meeting someone he's met before, but this time in battle. % PARAM1: Exit label (no message printed here) Persona requires <*CORE_Pme_MEIB_Greeting E:Friend> start result%id%01 Msg%id%01 Prompt%id%01 Prompt%id%01_1 Persona requires <*CORE_Pme_MEIB_Greeting -E:Friend> start result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *CORE_BasicJob % The PC will be given a default +Tin +Ffi mission. The exact text involved % is going to depend upon the propp state and who the mission-giver is. % NOTE: This component is responsible for setting the StoryNote % Generally, a note will be stored for accepting the mission, but not for rejecting % the mission. % PARAM1: Accept Mission Exit Label % PARAM2: Reject Mission Exit Label (Note: Not all missions may be rejected!) % (Also note: If this component features another ending, neither exit may % be called) % PARAM3: the location where the encounter will take place Persona requires <*CORE_BasicJob +Pew F:++> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Msg%id%02 <\CHATNPC told you that \FACTION &EnemyFac are testing weapons nearby. You must locate and destroy their research mission in \SCENE %3% .> Msg%id%03 <\CHATNPC told you that \FACTION &EnemyFac were testing weapons nearby.> Prompt%id%01 Prompt%id%01_1 <> Prompt%id%01_2 <> Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 <> Prompt%id%02_2 <> Persona requires <*CORE_BasicJob F:-- (+Pun|+Pme)> % V%id%01 = Have done introduction already START result%id%01 result%id%02 result%id%03 Result%id%04 result%id%05 Msg%id%01 Msg%id%02 <\CHATNPC sent you to intercept some mysterious attackers in \SCENE %3% .> Msg%id%03 <\CHATNPC sent you to intercept the mysterious attackers.> Msg%id%04 Msg%id%05 Msg%id%06 Prompt%id%01 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%05 CPrompt%id%05 Persona requires <*CORE_BasicJob R:A.jr_ ArchAlly> START Result%id%01 result%id%02 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_20 CMsg%id%01_20 Msg%id%02_10 <\CHATNPC sent you and \PERSONA &PartnerNPC to fight some mecha in \SCENE %3% .> CMsg%id%02_10 Msg%id%02_20 <\CHATNPC sent you to fight some mecha in \SCENE %3% .> CMsg%id%02_20 Prompt%id%01 Prompt%id%02 CPrompt%id%02 Persona requires <*CORE_BasicJob F:++ E:++ "MILIT"> START Result%id%01 result%id%02 Msg%id%01 Msg%id%02 <\CHATNPC sent you to fight \PERSONA &EnemyNPC in \SCENE %3% .> Prompt%id%01 Prompt%id%02 CPrompt%id%02 Persona requires <*CORE_BasicJob (CORPO|TRADE) +Pun ~F:-- Common> START Result%id%01 msg%id%01 msg%id%02 <\CHATNPC sent you to fight mecha in \SCENE %3% , and maybe learn who they work for.> Prompt%id%01 Persona requires <*CORE_BasicJob ("CORPO"|"TRADE"|"LABOR") ~"ArchAlly" (+P--|+Pme|+Pun) Common> START Result%id%01 result%id%02 msg%id%01 msg%id%01_1 Cmsg%id%01_1 msg%id%01_2 Cmsg%id%01_2 msg%id%01_3 Cmsg%id%01_3 msg%id%01_4 Cmsg%id%01_4 msg%id%01_5 Cmsg%id%01_5 msg%id%01_6 Cmsg%id%01_6 msg%id%02 <\CHATNPC asked you to find the mecha in \SCENE %3% which have been attacking company investments.> msg%id%03 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Persona requires <*CORE_BasicJob ("POLIT"|"MILIT") -"ARchENEMY" -L:ENEMY +Pun Common> START result%id%01 result%id%02 result%id%03 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <\NARRATIVE 7 must be defended! You've heard about the unauthorized mecha spotted in town, haven't you?> CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <\NARRATIVE 7 is under attack. A team of commando mecha has been striking at targets within the city and then disappearing. You've heard of them, I suppose.> CMsg%id%01_6 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 <\CHATNPC hired you to defend \SCENE %3% against unknown mecha.> Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Prompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*CORE_BasicJob> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 <\CHATNPC hired you to investigate mecha in \SCENE %3% .> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CORE_BasicJob +P-- ~"ArchALLY" P:++ ~P:MILIT Common> START result%id%01 result%id%02 result%id%03 result%id%04 .%id%_mek Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%02_10 CMsg%id%02_10 Msg%id%02_20 CMsg%id%02_20 Msg%id%03 Msg%id%04 <\CHATNPC sent you to investigate unidentified mecha in \SCENE %3% .> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 Prompt%id%04 CPrompt%id%04 GH2/series/RANCON_Intro.txt0000644000175000017500000000164011326004537014264 0ustar kaolkaol%% %% START-OF-GAME RANDOM CONTENT %% %% The cavalier club or wherever the PC starts should have several things %% to help the PC out. %% *INTRO_SHOP Content %% This will be a shop selling things a starting PC might need. Content requires <*INTRO_SHOP> content element1 team1 teamdata1 sub Persona 1 special *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_INTRO GoBye> *GoBye <*GOODBYE> end inv NPC Shopkeeper end %% *INTRO_HEALTHCLUB Content %% The guaranteed health club for the VIP Room. Content requires <*INTRO_HEALTHCLUB> minimap <#...#............1..&---&> element1 team1 teamdata1 sub Persona 1 *greeting <*NiceToMeetYou GoShop> *GoShop <*SHOP_HEALTHCLUB GoBye> *GoBye <*GOODBYE> end inv NPC Athlete end GH2/series/RANCON_PartyGames.txt0000644000175000017500000004413311326004535015247 0ustar kaolkaol%% %% *PartyGame Content %% %% PARAM: The difficulcy level of the party, measured as Renown %% %% For a party of business conference, these content fragments represent the PC's %% chance to make friends and influence people. The party games will typically be %% added to the foyer of the building and so should not have minimaps. Content %% Broken Stereo %% The music for the party is broken. % L%id%01 = Performance Counter % L%id%02 = Have earned party point Element1 APPLAUSE Msg%id%01 inv STC TV % V1 = Have repaired it use GoAlreadyRepaired Clue_REPAIR CLUE_SCIENCE GoWin GoFail Msg1 msg2 Msg3 Msg4 end Content %% Uncool Jerk %% There's an obnoxious person at the party. Go PC! Browbeat %% him into silence! requires <*PartyGame ~Static> Element1 team1 teamdata1 sub Persona 1 % V1 = Have earned party point counter Greeting *GoChat <*MISC_CHATTER> GoWinPoint result1 result2 result3 result4 GoR4Win GoR4Fail result5 GoR5Win GoR5Fail result6 result7 *result8 <*InsultContest GoR8Win GoR8Fail %param%> GoR8Win *GoR8Fail <*BrushOff> Msg1 Msg2 Msg3 Msg4 Msg5 Msg6 Msg7 Msg8 Msg9 Msg10 Prompt1 Prompt2 Prompt3 CPrompt3 Prompt4 Prompt5 Prompt6 Prompt7 Prompt8 end inv NPC Citizen CharDesc Sociable Passionate Melancholy Wangtta end Content %% Urban Sophisticate %% The PC will meet a sophisticated person of some type. requires <*PartyGame ~Static> Element1 team1 teamdata1 sub Persona 1 % V1 = Have earned party point counter % V4 = Dance Counter *Greeting <*NiceToMeetYou GoInteract> GoInteract *GoChat <*MISC_CHATTER> GoWinPoint result1 GoR1CheckInt GoR1Win GoR1Fail result2 GoR2Win GoR2Fail result3 result4 *GoR3Win <*YouDanceWell> *GoR3Fail <*YouDanceTerribly> Msg1 Msg1_1 Msg1_2 Msg2 Msg2_1 Msg3 Msg3_1 Msg4 Msg4_1 Msg4_2 Msg5 Msg5_1 Msg5_2 Msg6 Msg6_1 Msg7 Msg7_1 Prompt1 Prompt1_1 Prompt1_2 Prompt2 Prompt2_1 Prompt2_2 Prompt3 Prompt3_1 CPrompt3 Prompt4 <[Dance]> end inv NPC Citizen CharDesc Renowned end Content %% Excited Plebian %% The PC has a chance to impress this person with epic tales of adventure. Or not. requires <*PartyGame ~Static> Element1 team1 teamdata1 sub Persona 1 % V1 = Have earned party point counter *Greeting <*NiceToMeetYou GoInteract> GoInteract *GoChat <*MISC_CHATTER> GoWinPoint result1 GoR1Win GoR1Fail result2 GoR2Fail result3 GoR3Win GoR3Fail result4 GoR4Win GoR4Fail result5 GoR5Win GoR5Fail Msg1 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg1_3 CMsg1_3 Msg1_4 CMsg1_4 Msg1_5 CMsg1_5 Msg2 Msg2_1 Msg2_2 Msg3 Msg3_1 Msg3_2 Msg4 Msg4_1 Msg5 Msg5_1 Msg5_2 Msg6 Msg6_1 Msg6_2 Msg7 Msg7_1 Msg8 Msg8_1 Msg8_2 Msg9 Msg9_1 Msg9_2 Msg10 Msg10_1 Msg10_2 Msg11 Msg11_1 Msg11_2 Prompt1 Prompt1_1 Prompt1_2 Prompt2 Prompt2_1 Prompt2_2 Prompt3 Prompt3_1 Prompt3_2 Prompt4 Prompt4_1 Prompt4_2 Prompt5 Prompt5_1 Prompt5_2 end inv NPC Citizen CharDesc Sociable Wangtta end Content %% Bored Mercenary requires <*PartyGame ~Static> Element1 team1 teamdata1 sub Persona 1 % V1 = Have earned party point counter *Greeting <*NiceToMeetYou GoInteract> GoInteract *GoChat <*MISC_CHATTER> GoWinPoint result1 GoR1Fail result2 GoR2Fail result3 GoR3Fail result4 GoR4Fail *result5 <*YouDanceWell> Msg1 Msg1_1 CMsg1_1 Msg1_2 CMsg1_2 Msg1_3 CMsg1_3 Msg1_4 <> CMsg1_4 Msg2 Msg2_1 Msg3 Msg3_1 <> CMsg3_1 Msg3_2 <> CMsg3_2 Msg3_3 <> CMsg3_3 Msg3_4 <> CMsg3_4 Msg4 Msg4_1 CMsg4_1 Msg4_2 <> CMsg4_2 Msg4_3 <> CMsg4_3 Msg4_4 <> CMsg4_4 Msg5 Msg5_1 CMsg5_1 Msg5_2 CMsg5_2 Msg5_3 CMsg5_3 Msg5_4 CMsg5_4 Msg5_5 CMsg5_5 Msg6 Msg6_1 Msg7 Msg7_1 Msg8 Msg8_1 Msg9 Msg9_1 Prompt1 CPrompt1 Prompt1_1 Prompt1_2 Prompt2 Prompt2_1 Prompt2_2 Prompt2_3 Prompt3 Prompt3_1 Prompt3_2 Prompt3_3 Prompt4 Prompt4_1 Prompt4_2 Prompt4_3 Prompt5 <[DANCE]> end inv NPC Mercenary CharDesc Shy end GH2/series/PFRAG_LookingForInformation.txt0000644000175000017500000002260511365256063017341 0ustar kaolkaol%% This unit contains persona fragments in which the PC is looking for some kind %% of information. The NPC involved may or may not be forthcoming with the information. % TYPE: *QuestionsAboutFaction % &IsEnemy The faction is an enemy of the PC. % The PC will ask the NPC about a faction. % PARAM1: Exit Label % PARAM2: Faction being asked about Persona requires <*QuestionsAboutFaction> START Result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 % TYPE: *IHaveInformation % &AboutNPC The information is about a NPC. % &AboutItem The information is about an item. % &AboutScene The information is about a scene. % &AboutFaction The information is about a faction. % &AboutMoney % The NPC has information that the PC might find valuable. % Note that depending on the NPC, the information may be withheld until % the PC pays money or something else. % PARAM1: Tell Info Exit Label (no message printed here) % PARAM2: ID number of whatever the info's about; must use one of the above subtypes. % If no subtype is specified, you can use "na" for this. Persona requires <*IHaveInformation &AboutScene> START Result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*IHaveInformation &AboutItem> START Result%id%01 Msg%id%01 Prompt%id%01 Persona requires <*IHaveInformation &AboutNPC> %% V%id%01 = Have already given spiel START Result%id%01 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Persona requires <*IHaveInformation "friend"> %% V%id%01 = Have already given spiel START Result%id%01 Msg%id%01 <\PC , I'm glad you're here. I have some important news.> Msg%id%01_1 Msg%id%01_2 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 Persona requires <*IHaveInformation> %% V%id%01 = Have already given spiel START Result%id%01 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 <\PC , I have some information you'll want to know.> CMsg%id%01_12 Msg%id%01_13 CMsg%id%01_13 Msg%id%01_14 CMsg%id%01_14 Msg%id%01_15 CMsg%id%01_15 Msg%id%01_16 CMsg%id%01_16 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 % TYPE: *LookingForScene % The PC is looking for a scene. In this fragment, he'll be able to interview % the NPC and hopefully learn something. If the NPC refuses to tell, this PFrag % will terminate here. % PARAM1: Identity of Scene % PARAM2: Success Exit Label (no message printed here) Persona requires <*LookingForScene> START Result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 % TYPE: *LookingForItem % The PC is looking for an item. In this fragment, he'll be able to interview % the NPC and hopefully learn something. If the NPC refuses to tell, this PFrag % will terminate here. % PARAM1: Identity of Item % PARAM2: Success Exit Label (no message printed here) Persona requires <*LookingForItem> START Result%id%01 Msg%id%01 Prompt%id%01 % TYPE: *LookingForNPC % &NoFailure It's not possible to fail the test % The PC is looking for a NPC. In this fragment, he'll be able to interview % the NPC and hopefully learn something. % PARAM1: Identity of NPC % PARAM2: Success Exit Label % PARAM3: Failure Exit Label Persona requires <*LookingForNPC> START Result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 CPrompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 CPrompt%id%01_3 Prompt%id%01_4 CPrompt%id%01_4 Prompt%id%01_5 CPrompt%id%01_5 Prompt%id%01_6 CPrompt%id%01_6 GH2/series/PLOT_ThiefMissions.txt0000644000175000017500000000611011365256063015556 0ustar kaolkaol%% %% Thief missions aren't usually missions in the standard sense- instead, a %% fellow thief will clue the PC in about something which can be stolen. %% Plot name desc requires <*GENERAL -Lawful> PayRate 500 % E1 is a local criminal character % E2 is the datachip containing the info % E3 is a SPACE scene for the cruiser % E4 is the cruiser itself Element1 Element2 Place2 <1> Element3 Element4 Place4 <3> % P1 = Time Limit % P2 = Email Indicator start GoDelete update % Obtaining the datachip will reveal the encounter to the PC. Get%2% Msg%id%01 Msg%id%02 % SubPlot1 is the plunder mission itself SubPlot1 <*TM_PlunderShip 3 4> sub Persona 1 rumor0 <%name1% is very interested in shipping.> &AskingPrice % V%id%01 = Have heard briefing(1)/Have failed briefing (-1) greeting .%id%_GoCancelPlot .%id%_GoRevert *.%id%_GoCheckAuto <*AutoThiefTest .%id%_GoBriefing .%id%_GoReject .%id%_GoCheckSkill> *.%id%_GoCheckSkill <*SkillThiefTest .%id%_GoBriefing .%id%_GoSayGoodbye .%id%_GoFailure %threat%> .%id%_GoSayGoodbye *.%id%_GoGoodbye <*GoodBye> *.%id%_GoReject <*YourLossSucker .%id%_GoFailure> .%id%_GoFailure .%id%_GoBriefing result%id%01 *result%id%02 <*HurryBackWithMoney> result%id%03 Msg%id%01 Msg%id%02 Msg%id%03 Prompt%id%01 CPrompt%id%01 Prompt%id%02 Prompt%id%03 end inv Treasure name desc STC CORE-ACTIVATABLE name end GH2/series/adventurestub.txt0000644000175000017500000000033011326004535015015 0ustar kaolkaol% Adventure Stub Adventure UPDATE 5min start QUARTER inv % Stick the plot item container here. Gear -16 0 0 name end GH2/series/PFRAG_MissionTest.txt0000644000175000017500000015477511343146535015357 0ustar kaolkaol%%% MISSION TEST FRAGMENTS %%% %%% The PC will be put through some kind of test to see if he'll be offered %%% a mission or not. These tests frequently involve either Renown or React. % Refile this test later... Persona requires <*REFILE> start result%id%01 .%id%_GoR1NoCon .%id%_GoR1Fail .%id%_GoR1Pass result%id%02 result%id%03 result%id%04 Msg%id%01 Msg%id%02 Msg%id%03 Msg%id%04 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 % % TYPE: *AutoThiefTest % % The NPC has a criminal proposition for the PC. % % PARAM1: Accept Exit Label ( no message ) % PARAM2: Reject Exit Label ( no message ) % PARAM3: Failure Exit Label ( no message ) % Persona requires <*AutoThiefTest "PCFAC"> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 Msg%id%01_2 <> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*AutoThiefTest -"PCFAC"> START result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 Msg%id%01_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 <> Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 <> % % TYPE: *SkillThiefTest % % The NPC has a criminal proposition, which the PC may be able to learn % about. % % PARAM1: Accept Exit Label ( no message ) % PARAM2: Reject Exit Label ( no message ) % PARAM3: Failure Exit Label % PARAM4: Difficulty rating, expressed as renown % Persona requires <*SkillThiefTest ~"Melancholy" ~"Villainous"> START result%id%01 .%id%_GoR1Fail result%id%02 .%id%_GoR2Fail result%id%03 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%02_1 Msg%id%02_2 <> Msg%id%03 Msg%id%03_1 Msg%id%03_2 <> Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Persona requires <*SkillThiefTest ~"Shy"> START result%id%01 .%id%_GoFail result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 Msg%id%02 Msg%id%02_1 Msg%id%03 Msg%id%03_1 Prompt%id%01 Prompt%id%02 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Prompt%id%05_1 % % TYPE *ReferredForJob % % The PC has been referred for this job by an NPC. % % PARAM1: The NPC in question % PARAM2: Exit Label % Persona requires <*ReferredForJob> START result%id%01 Msg%id%01 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_20 CMsg%id%01_20 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 % % TYPE: *RejectMission % The PC has been offered a mission, but has declined. % PARAM1: Exit Label Persona requires <*RejectMission =MIL_DEFAULT_DEFENSE> start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_7 CMsg%id%01_7 Msg%id%01_8 CMsg%id%01_8 Persona requires <*RejectMission> start Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 % TYPE: *PerformanceTest % The NPC needs to hire an entertainer for a performance of some type. % PARAM1: Accept Exit Label ( no message ) % PARAM2: Reject Exit Label ( no message ) % PARAM3: Failure Exit Label ( no message ) Persona requires <*PerformanceTest> START %% Method 1 - Conversation result%id%01 %% Method 2 - Flirtation result%id%02 %% Method 3 - Reputation result%id%03 %% Reject the mission result%id%04 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%03 CPrompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 % TYPE: *AutoMissionTest % &Mecha It's a mecha mission % &Chara It's a character mission % &NonCom It's a non-combat mission % The NPC has a mission; the PC may be offered this mission automatically. % PARAM1: Accept Exit Label ( no message ) % PARAM2: Reject Exit Label ( no message- link to REJECTMISSION above. ) % PARAM3: Failure Exit Label ( no message ) % PARAM4: Allied Faction [optional] % PARAM5: Enemy Faction [optional] Persona requires <*AutoMissionTest ("FRIEND"|"FAMILY"|"LANCEMATE"|"LOVER") Common> start result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 <\PC , you're just the person I need! I've got to find a pilot for an upcoming mission, and you'd be perfect for it!> CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*AutoMissionTest =MIL_DEFAULT_DEFENSE> % Because of =MIL_DEFAULT_DEFENSE, we know that %4% and %5% exist % V%id%01 = Can only try the auto-test once counter start result%id%01 result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*AutoMissionTest ~Sociable> % V%id%01 = Can only try the auto-test once counter start result%id%01 result%id%02 Msg%id%01 <\PC , I was hoping to see you. I need someone for a mission and could use your help.> Msg%id%01_10 CMsg%id%01_10 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_30 CMsg%id%01_30 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*AutoMissionTest> % V%id%01 = Can only try the auto-test once counter start result%id%01 result%id%02 Msg%id%01 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_30 CMsg%id%01_30 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 % TYPE: *GenericMissionTest % &Mecha It's a mecha mission % &Chara It's a character mission % &Extermination The PC has to exterminate some monsters % The NPC has a mission available, will imply this to the PC, and the PC will then % have a chance to maybe get the mission. % PARAM1: Success Exit Label (no message) % PARAM2: Failure Exit Label % PARAM3: Reject Mission Exit Label (no message) % PARAM4: Allied Faction [optional] % PARAM5: Enemy Faction [optional] % PARAM6: Difficulcy number, expressed as Renown Persona requires <*GenericMissionTest &Mecha "POLIC"> start .%id%_GoFail result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%02_1 Msg%id%02_2 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%03_6 CMsg%id%03_6 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%01_3 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%02_3 Prompt%id%03 CPrompt%id%03 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Prompt%id%05_1 Persona requires <*GenericMissionTest &Mecha> start .%id%_GoBeenBefore result%id%01 .%id%_GoR1Lose result%id%02 result%id%03 .%id%_GoR3Lose result%id%04 result%id%05 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 CMsg%id%01_12 Msg%id%01_13 CMsg%id%01_13 Msg%id%01_14 CMsg%id%01_14 Msg%id%01_15 CMsg%id%01_15 Msg%id%01_16 CMsg%id%01_16 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_21 CMsg%id%01_21 Msg%id%01_22 CMsg%id%01_22 Msg%id%01_23 CMsg%id%01_23 Msg%id%01_24 CMsg%id%01_24 Msg%id%01_25 CMsg%id%01_25 Msg%id%01_26 CMsg%id%01_26 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_10 CMsg%id%02_10 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Msg%id%03 Msg%id%03_1 Msg%id%03_2 Msg%id%04 Msg%id%04_1 Msg%id%04_2 Prompt%id%01 Prompt%id%01_1 Prompt%id%02 Prompt%id%02_1 Prompt%id%03 CPrompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 Prompt%id%05 Prompt%id%05_1 Persona requires <*GenericMissionTest ~Shy> start .%id%_GoBeenBefore result%id%01 .%id%_GoR1Fail result%id%02 .%id%_GoR2Fail .%id%_GoOfferMission result%id%03 result%id%04 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%03_6 CMsg%id%03_6 Msg%id%04 Msg%id%04_2 Msg%id%04_11 CMsg%id%04_11 Msg%id%04_12 CMsg%id%04_12 Msg%id%04_13 CMsg%id%04_13 Msg%id%04_14 CMsg%id%04_14 Msg%id%04_15 CMsg%id%04_15 Msg%id%04_21 CMsg%id%04_21 Msg%id%04_22 CMsg%id%04_22 Msg%id%04_23 CMsg%id%04_23 Msg%id%04_24 CMsg%id%04_24 Msg%id%04_25 CMsg%id%04_25 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%04 Prompt%id%04_1 % TYPE: *CivilDefenseTest % The NPC has a mission available, will imply this to the PC, and the PC will then % have a chance to maybe get the mission. This is a town defense mission. The PC % will almost always be able to get the mission guaranteed by taking a pay hit. % PARAM1: Success Exit Label (no message) % PARAM2: Failure Exit Label % PARAM3: Reject Exit Label (no message) % PARAM4: LowSuccess Exit Label (no message) % PARAM5: Allied Faction [optional] % PARAM6: Enemy Faction [optional] % PARAM7: Difficulcy number, expressed as Renown Persona requires <*CivilDefenseTest> Start .%id%_GoFail .%id%_GoSuccess result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 result%id%06 Msg%id%01 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 CMsg%id%01_12 Msg%id%01_13 CMsg%id%01_13 Msg%id%01_14 CMsg%id%01_14 Msg%id%01_15 CMsg%id%01_15 Msg%id%01_16 CMsg%id%01_16 Msg%id%03 Msg%id%03_1 CMsg%id%03_1 Msg%id%03_2 CMsg%id%03_2 Msg%id%03_3 CMsg%id%03_3 Msg%id%03_4 CMsg%id%03_4 Msg%id%03_5 CMsg%id%03_5 Msg%id%03_6 CMsg%id%03_6 Msg%id%03_7 CMsg%id%03_7 Msg%id%04 Msg%id%04_1 CMsg%id%04_1 Msg%id%04_2 CMsg%id%04_2 Msg%id%04_3 CMsg%id%04_3 Msg%id%04_4 CMsg%id%04_4 Msg%id%04_5 CMsg%id%04_5 Msg%id%04_6 CMsg%id%04_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 CPrompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 CPrompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Prompt%id%05 Prompt%id%05_1 Prompt%id%05_2 Prompt%id%06 Prompt%id%06_1 Persona requires <*CivilDefenseTest ~Melancholy> Start result%id%01 result%id%02 result%id%03 result%id%04 .%id%_GoFail Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_21 CMsg%id%01_21 Msg%id%01_22 CMsg%id%01_22 Msg%id%01_23 CMsg%id%01_23 Msg%id%01_24 CMsg%id%01_24 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Persona requires <*CivilDefenseTest ~Sociable> start result%id%01 result%id%02 .%id%_Go2Fail result%id%03 Msg%id%01 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_21 CMsg%id%01_21 Msg%id%01_22 CMsg%id%01_22 Msg%id%01_23 CMsg%id%01_23 Msg%id%01_24 CMsg%id%01_24 Msg%id%01_25 CMsg%id%01_25 Msg%id%01_26 CMsg%id%01_26 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 % TYPE: *JoinMyPatrolTest % There's a patrol, the PC will be invited to join if he passes some test. % PARAM1: Success exit label % Success if PC is chosen for mission, and selects "Join Patrol" menu option. % PARAM2: Failure exit label % Only if test failed- if mission rejected, use %5%. % PARAM3: Difficulcy rating % PARAM4: Cash reward for partaking in mission % PARAM5: Rejection exit label. Message printed here. % PARAM6: Who the NPC is working for [optional] % PARAM7: Who the NPC is fighting [optional] Persona requires <*JoinMyPatrolTest =MIL_DEFAULT_DEFENSE> %% Because this is =MIL_DEFAULT_DEFENSE we know %6% and %7% exist %% We also know that cash is being offered. start Result%id%01 Result%id%02 Msg%id%01 Msg%id%01_1 CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 CMsg%id%01_3 Msg%id%01_4 CMsg%id%01_4 Msg%id%01_5 CMsg%id%01_5 Msg%id%01_6 CMsg%id%01_6 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%02_3 CMsg%id%02_3 Msg%id%02_4 CMsg%id%02_4 Msg%id%02_5 CMsg%id%02_5 Msg%id%02_6 CMsg%id%02_6 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*JoinMyPatrolTest> start result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%01_10 CMsg%id%01_10 Msg%id%01_11 CMsg%id%01_11 Msg%id%01_12 CMsg%id%01_12 Msg%id%01_13 CMsg%id%01_13 Msg%id%01_14 <> CMsg%id%01_14 Msg%id%01_15 <> CMsg%id%01_15 Msg%id%01_16 <> CMsg%id%01_16 Msg%id%01_20 CMsg%id%01_20 Msg%id%01_21 <> CMsg%id%01_21 Msg%id%01_21 <> CMsg%id%01_21 Msg%id%01_21 <> CMsg%id%01_21 Msg%id%01_21 CMsg%id%01_21 Msg%id%01_21 CMsg%id%01_21 Msg%id%01_21 CMsg%id%01_21 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%03_10 CMsg%id%03_10 Msg%id%03_20 CMsg%id%03_20 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 Prompt%id%01_4 Prompt%id%01_5 Prompt%id%01_6 Prompt%id%01_7 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%02_3 Prompt%id%02_4 Prompt%id%02_5 Prompt%id%02_6 Prompt%id%02_7 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%03_3 Prompt%id%03_4 Prompt%id%03_5 CPrompt%id%03_5 Prompt%id%03_6 Prompt%id%03_7 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Prompt%id%05 Prompt%id%05_1 Prompt%id%05_2 Persona requires <*JoinMyPatrolTest> start result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Msg%id%02 Msg%id%02_1 CMsg%id%02_1 Msg%id%02_2 CMsg%id%02_2 Msg%id%03_10 CMsg%id%03_10 Msg%id%03_20 CMsg%id%03_20 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 CPrompt%id%01_2 Prompt%id%01_3 Prompt%id%01_4 Prompt%id%01_5 Prompt%id%01_6 Prompt%id%01_7 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Prompt%id%02_3 Prompt%id%02_4 Prompt%id%02_5 Prompt%id%02_6 Prompt%id%02_7 Prompt%id%03 Prompt%id%03_1 Prompt%id%03_2 Prompt%id%03_3 Prompt%id%03_4 Prompt%id%03_5 CPrompt%id%03_5 Prompt%id%03_6 Prompt%id%03_7 Prompt%id%04 Prompt%id%04_1 Prompt%id%04_2 Prompt%id%05 Prompt%id%05_1 Prompt%id%05_2 GH2/series/PFRAG_CORE_Conclusion.txt0000644000175000017500000004167711401151245016003 0ustar kaolkaol%% %% CORE STORY CONCLUSION %% %% These fragments are used by the game ending. %% %% %% *CS_END_FinalBattle_Greeting %% %% The final battle has started, and the Enemy has just appeared. This is the greeting %% for the PC. %% %% PARAM1: Exit Label, presumably to THEME_EXPO %% Persona requires <*CS_END_FinalBattle_Greeting (@:A.hat|@:A.mut|@:A.pch)> START Result%id%01 Msg%id%01 Msg%id%01_1 <> Msg%id%01_2 <> Prompt%id%01_1 Prompt%id%01_2 <> Persona requires <*CS_END_FinalBattle_Greeting @:A.obs> START Result%id%01 Msg%id%01 Msg%id%01_1 <> Msg%id%01_2 <> Prompt%id%01_1 Prompt%id%01_2 Persona requires <*CS_END_FinalBattle_Greeting @:M.nih> START Result%id%01 Msg%id%01 Msg%id%01_1 <> Msg%id%01_2 <> Prompt%id%01_1 Prompt%id%01_2 Persona requires <*CS_END_FinalBattle_Greeting "Villainous"> START Result%id%01 Result%id%02 Msg%id%01 Msg%id%01_1 <> Msg%id%01_2 <> Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_END_FinalBattle_Greeting> START Result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01_1 Prompt%id%01_2 %% %% *CS_END_Intro_Greeting %% %% The PC has just stumbled into the Enemy's plans, and the Enemy will greet the PC. %% This pfrag should be based on the E:A and E:M context. %% %% The PFrag will exit with the PC making a defiant challenge, to be answered below. %% %% PARAM1: Exit Label %% Persona requires <*CS_END_Intro_Greeting> START Result%id%01 Msg%id%01 Msg%id%01_1 <> CMsg%id%01_1 Msg%id%01_2 <> CMsg%id%01_2 Msg%id%01_3 <> CMsg%id%01_3 Msg%id%01_4 <> CMsg%id%01_4 Msg%id%01_5 <> CMsg%id%01_5 Msg%id%01_6 <> CMsg%id%01_6 Prompt%id%01_1 Prompt%id%01_2 %% %% *CS_END_Intro_Explanation %% %% The Enemy NPC will explain what's going on, and may mention that the PC is too %% late anyhow. This pfrag should be based on the +P state and the enemy faction. %% %% This PFrag should set a storynote (smemo+history). %% Persona requires <*CS_END_Intro_Explanation +Pla I:++> START Msg%id%01 Msg%id%02 <\CHATNPC revealed that \SPR ChatNPCID has obtained the \ITEM &TargetItem ; \SPR ChatNPCID assigned several of \PPR ChatNPCID best fighters to hunting you down.> Msg%id%03 <\CHATNPC found the \ITEM &TargetItem , and revealed that it held great knowledge.> Persona requires <*CS_END_Intro_Explanation F:REDMA (+P--|+Pme|+Pun|+Psh|+Pew)> START Msg%id%01 Msg%id%02 <\CHATNPC revealed that the Red Mask Raiders are making a play for dominance.> Msg%id%03 Persona requires <*CS_END_Intro_Explanation> START Msg%id%01 Msg%id%02 <\CHATNPC revealed that \PPR ChatNPCID forces have already launched; \SPR ChatNPCID assigned several of \PPR ChatNPCID best fighters to hunting you down.> Msg%id%03 %% %% *CS_END_StatusReport %% %% This friendly NPC will explain to the PC what's been going on in the city. This %% pfrag will be based on the enemy faction, the enemy NPC, and the +P state. %% %% PARAM1: [Continue] exit label %% Persona requires <*CS_END_StatusReport F:REDMA E:++> START result%id%01 Msg%id%01 Prompt%id%01 <[Continue]> Persona requires <*CS_END_StatusReport F:REDMA +Pew> START result%id%01 Msg%id%01 Prompt%id%01 <[Continue]> Persona requires <*CS_END_StatusReport F:REDMA> START result%id%01 Msg%id%01 Prompt%id%01 <[Continue]> Persona requires <*CS_END_StatusReport> START result%id%01 Msg%id%01_1 <\PERSONA &EnemyNPC is launching some kind of big operation. We can't be sure what, but \SPR &EnemyNPC 's got a veritable army in town enforcing \PPR &EnemyNPC will.> CMsg%id%01_1 Msg%id%01_2 CMsg%id%01_2 Msg%id%01_3 <\SCENE &EpisodeScene has been engulfed in chaos! Unaligned cavaliers have been battling in the street and nobody seems to know who's responsible or how to stop it.> CMsg%id%01_3 Prompt%id%01 <[Continue]> %% ************************************ %% *** PEACE AND LOVE ENDING *** %% ************************************ %% %% Persona fragments for the final conversation in the Peace and Love ending. %% %% %% *CS_END_FPAL_Greeting %% %% The PC is attempting to gain the Peace and Love ending. In this first %% question, you get to choose whether to negotiate peace or simply fight. %% Yeah, I know you've been working up to this the whole game, but it's still %% a legitimate question. %% %% PARAM1: Negotaite Exit Label %% PARAM2: Fight Exit Label %% Persona requires <*CS_END_FPAL_Greeting> START result%id%01 result%id%02 Msg%id%01 Prompt%id%01 Prompt%id%01_1 Prompt%id%01_2 Prompt%id%02 Prompt%id%02_1 Prompt%id%02_2 Persona requires <*CS_END_FPAL_Greeting (F:CRIHN|F:BOHEM) (P:L5LAW|P:RISHI|P:FCOMS|P:MAQUI|P:AEGSF|P:SILKN|P:ROCKE|P:PRIVA)> START result%id%01 result%id%02 Msg%id%01 <\SCENE RootSceneID has fallen easily enough... At last, Crihna Rock will be avenged!> Prompt%id%01 Prompt%id%02 %% %% *CS_END_FPAL_Quest1 %% %% The PC is attempting to gain the Peace and Love ending. In this second %% question, the PC's reason for wanting peace will be established. Having %% friends, allies, family or lovers on the opposite side is needed for the %% top score. %% %% PARAM1: Best Exit Label %% PARAM2: Okay Exit Label %% PARAM3: Bad Exit Label %% Persona requires <*CS_END_FPAL_Quest1> START result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 CPrompt%id%04 Persona requires <*CS_END_FPAL_Quest1 (F:CRIHN|F:BOHEM|F:REDMA) (P:L5LAW|P:RISHI|P:FCOMS|P:MAQUI|P:AEGSF|P:SILKN|P:ROCKE|P:PRIVA)> START result%id%01 result%id%02 result%id%03 result%id%04 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Prompt%id%04 CPrompt%id%04 %% %% *CS_END_FPAL_Quest2 %% %% The PC is attempting to gain the Peace and Love ending. In this third %% question, the PC must explain what the enemy faction has to gain from %% pursuing peace. Having a high Heroism score will enable the PC to make %% the best offer. %% %% PARAM1: Best Exit Label %% PARAM2: Okay Exit Label %% PARAM3: Bad Exit Label %% Persona requires <*CS_END_FPAL_Quest2> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 Persona requires <*CS_END_FPAL_Quest2 (F:CRIHN|F:BOHEM) (P:L5LAW|P:RISHI|P:FCOMS|P:AEGSF|P:ROCKE)> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 CPrompt%id%03 %% %% *CS_END_FPAL_Quest3 %% %% The PC is attempting to gain the Peace and Love ending. In this fourth %% question, the PC must establish the authority by which they're negotiating. %% This generally requires a master ranking in the allied faction, or a really %% good conversation roll. %% %% PARAM1: Best Exit Label %% PARAM2: Okay Exit Label %% PARAM3: Bad Exit Label %% Persona requires <*CS_END_FPAL_Quest3> START result%id%01 result%id%02 result%id%03 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 %% %% *CS_END_FPAL_Quest4 %% %% The PC is attempting to gain the Peace and Love ending. In this final %% question, the PC must remove all doubt... usually with a skill roll. %% The difficulty rating is based on the success so far and will not be %% any higher than 125. %% %% PARAM1: Difficulty Rating %% PARAM2: Success Exit Label %% PARAM3: Failure Exit Label %% Persona requires <*CS_END_FPAL_Quest4> START result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 CPrompt%id%05 Persona requires <*CS_END_FPAL_Quest4 (C:MEDIC|C:ACADE)> START result%id%01 result%id%02 result%id%03 result%id%04 result%id%05 Msg%id%01 Prompt%id%01 CPrompt%id%01 Prompt%id%02 CPrompt%id%02 Prompt%id%03 Prompt%id%04 CPrompt%id%04 Prompt%id%05 CPrompt%id%05 %% %% *CS_END_FPAL_Accept %% %% Your offer of peace has been accepted. %% %% PARAM1: Exit Label %% Persona requires <*CS_END_FPAL_Accept> START Msg%id%01 %% %% *CS_END_FPAL_Reject %% %% Your offer of peace has been rejected. %% %% PARAM1: Exit Label %% Persona requires <*CS_END_FPAL_Reject> START result%id%01 Msg%id%01 Prompt%id%01 <[Continue]> GH2/series/QUEST_FreeMecha.txt0000644000175000017500000000703211326004537014732 0ustar kaolkaol%% %% *:Q_FreeMecha %% %% At the beginning of the game, there should be a chance for an enterprising %% young character to earn a free mecha. Lazy and old characters should also be %% able to get one. That's what these quests are for. %% %% If there's an NPC associated with this quest, he should be placed in the %% Cavalier's Club, so the PC can find him easily. %% %% The free mecha should be generated using renown 15. In the case of having %% to recover the mecha from a dungeon, a custom mecha with 2 modification %% points may be given. Content name desc requires <*:Q_FreeMecha ASTEROID> % E1 is the environs scene % E2 is the dungeon % E3 is the elevator in the dungeon that will provide the mecha Element1 Element2 Place2 <1> sub STC QS_Dungeon_UrbanHellHole name <#'s Demise> SetID 2 Entrance <*QUEST-ACTIVE> sub room minimap <......###..##1..###......> special ForGoalLevel inv Elevator rumor0 MiniMapComponent 1 name use .mektype GoBeenBefore Msg1 Msg2 Msg3 Msg4 end end end Content name desc requires <*:Q_FreeMecha SPINNER> % E1 is the environs scene % E2 is the dungeon Element1 Element2 Place2 <1> sub STC QS_Dungeon_Derelict SetID 2 Entrance <*QUEST-ACTIVE> sub room minimap <......###..##1..###......> special ForGoalLevel inv Elevator rumor0 MiniMapComponent 1 name use .mektype GoBeenBefore Msg1 Msg2 Msg3 Msg4 end end end Content name desc requires <*:Q_FreeMecha (SPINNER|ASTEROID)> % E1 is the environs scene % E2 is the dungeon Element1 Element2 Place2 <1> sub STC QS_Dungeon_AsteroidMine SetID 2 Entrance <*QUEST-ACTIVE> sub room minimap <......###..##2..###......> special ForGoalLevel inv Elevator rumor0 MiniMapComponent 2 name use .mektype GoBeenBefore Msg1 Msg2 Msg3 Msg4 end end end GH2/series/UNICON_PartyGames.txt0000644000175000017500000002145411326004537015265 0ustar kaolkaol%% See RANCON_PartyGames.txt for documentation. Content %% Bishounen-A-Go-Go %% The PC must either dance with E1 or convince E2 to dance instead. Or vice versa. %% L%id%01 = Indicator whether E1 or E2 were approached first requires <*PartyGame Static L5Pat ~Yacht> Element1 Team1 TeamData1 Element2 Team2 TeamData2 sub Persona 1 % V1 = Flirtation/Conversation Counter *greeting <*NiceToMeetYou GoCheckOver> GoCheckOver *GoChat <*MISC_CHATTER> GoCheckDance GoCheckOther GoFirstTime Result1 Result2 Result3 result4 GoR4Fail result5 Result6 result7 result8 result9 result10 SDL_SPRITE SDL_COLORS <150 112 89 170 130 100 210 40 50> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrLootGizzard 50 lizard> CloseCombat 5 Dodge 8 Vitality 6 Stealth 7 sub head armor 4 sub Melee 10 name recharge 3 type end torso armor 7 leg armor 7 leg armor 7 leg armor 7 leg armor 7 tail armor 7 end Arch Albino Alligator statline 13 20 10 7 2 9 2 2 MonsterTV 72 DomTarget 22 roguechar SDL_SPRITE SDL_COLORS <199 188 162 152 61 97 208 35 51> type habitat <> *CLUE_SURVIVAL <*SURVIVAL_GetMeatOrLootGizzard 72 alligator> CloseCombat 5 Dodge 7 Vitality 6 SpotWeakness 5 Stealth 7 Athletics 7 Vitality 8 Toughness 8 sub head armor 7 sub Melee 15 name recharge 3 end torso armor 3 leg armor 7 leg armor 7 leg armor 7 leg armor 7 tail armor 7 end Arch Emperor Fire Penguin statline 14 10 16 15 6 7 2 10 type habitat <> roguechar