[Abandoned] Oracus & Domina I: The March of the Heretic

A place to discuss mods in development and concepts for new mods.
Post Reply
User avatar
0xABCDEF
Militia Lieutenant
Militia Lieutenant
Posts: 124
Joined: Thu May 19, 2016 12:58 am
Location: Was destroyed by a Phobos-class dreadnought in the Eridani system

[Note: This was originally planned to be an adventure about the villainous side of the Stars of the Pilgrim, featuring a storyline in which the player sides with the Order of Penitence. However, the idea has been scrapped completely due to issues with game balance and story progression]
Last edited by 0xABCDEF on Sun Oct 22, 2017 1:35 am, edited 30 times in total.
JohnBWatson
Fleet Officer
Fleet Officer
Posts: 1452
Joined: Tue Aug 19, 2014 10:17 pm

Interesting. It's an ambitious mod, but the idea is promising. I especially like the concept of a different system for using powers - the current one is a bit too generous to people who have patience and an 'a' key.
george moromisato
Developer
Developer
Posts: 2997
Joined: Thu Jul 24, 2003 9:53 pm
Contact:

This is a great idea! I hugely support it. Let me offer a few guidelines so you can avoid conflict with Part II and other things:

1. Remember that there are many unreliable narrators throughout the game. We mostly know about the Penitents from the Sisters (and Benedict) but not from the Penitents themselves. I'm not saying the Penitents are good, only that their true motives and goals are as yet unrevealed. I'll try to guide you as appropriate, but it's OK to be vague or even contradictory about specific points.

2. The Chimera storyline hints that Oracus can "connect" to certain people just as Domina can, but that he uses that connection to transmit alien (and often dangerous) technology. That's why I love the concept of the "charms" that you've come up with. The Penitents could have come up with that technology via connection with Oracus.

3. The Sanctuary system is indeed the location of the Sisters' first and largest cathedral. It is not currently included in the game, but it will be eventually. I think it's a good place to start, although it means your adventure will start at a higher level than Stars of the Pilgrim. That's perfectly fine.

4. The Order of Saint Josephine is actually a sub-sect of the Sisters. They are mostly in the Outer Realm, the most dangerous places, including Heretic. The cathedral in Sanctuary is probably called the "Cathedral of St. Euginia" (but don't quote me--I'm not sure yet). More about the Sisters here: http://transcendence.kronosaur.com/guid ... xm?id=1715

5. I like the call-back to the attack on Fiona's freighter. The Benedict story arc will continue; as I flesh that story out, I'll post some updates.

Keep going on this--it's a great idea.
User avatar
0xABCDEF
Militia Lieutenant
Militia Lieutenant
Posts: 124
Joined: Thu May 19, 2016 12:58 am
Location: Was destroyed by a Phobos-class dreadnought in the Eridani system

I finished part of the introduction and have the first mission and the charge-power system available for testing. The first intro screen is incomplete and Penitent Sanctum and player ships still require large images.
Last edited by 0xABCDEF on Sun Oct 22, 2017 1:30 am, edited 1 time in total.
User avatar
0xABCDEF
Militia Lieutenant
Militia Lieutenant
Posts: 124
Joined: Thu May 19, 2016 12:58 am
Location: Was destroyed by a Phobos-class dreadnought in the Eridani system

Here is an advanced wingman base class I will be using in the adventure

Code: Select all

<?xml version="1.0" encoding="utf-8"?>

<TranscendenceModule>

	<!-- Wingman Base Class
		USAGE NOTES
		
		Do not rely on the implementation of this class across versions. If you wish
		to inherit from this class in an Extension, you should copy the class into
		your own Extension and then inherit from that.
		EXTRA DATA
		
		The following data is stored on the wingman object. These track internal state
		used by the base class. You should not change these values nor make assumptions
		about their meaning.
		
		behavior:			Ship's current behavior
								Nil					= nothing
								'attackingAtWill			= attacking any target in range
								'attackingTarget			= attacking a target
								'escorting				= escorting the player
								'goingHome			= wingman wants to go home
								'lookingAround			= patrolling the player at 90 ls and marking visible enemies.
								'patrollingTarget			= patrolling a target at 10 ls
								'repairingArmor			= docking with station to repair
								'waiting				= waiting for the player
		homeSystem			System nodeID where we were created
		nextArmorRepairMsg	Tick at which we should tell player about armor damage
		welcomeMsg			True if we already said hello to player
		GLOBAL DATA
		status:				Ship's status
								Nil						= never encountered
								'joined					= Has joined the player
								'declined				= Player has declined escort
								'destroyed				= Dead
								'destroyedByPlayer		= Killed by player
								'returnedHome			= Left the player to return home
								
		ORDERS
		
		orderJoinPlayer:	Fire this event to order the wingman to join the player. The
							object will receive an OnWingmanJoinedPlayer event.
								
		OPTIONS
		
		These variables may be set in StaticData to alter the behavior of wingmen.
		
		maxSystemLevel:		If set, the wingman will leave the player if they exceed
							this system level.
							
		EVENTS
		
		The wingman type may override these events.
		
		OnWingmanJoinedPlayer: Fired when the wingman joins the player.

		OnOrderAttackSelf: Fired when the player orders the wingman to attack self. Returns Nil by default
		OnOrderAttackFriend: Fired when the player orders the wingman to attack a friend. Returns Nil by default.
								
	-->

	<ShipClass UNID="&baAdvWingmanBase;"
			class=				"(advanced wingman base class)"
			virtual=			"true"
			
			attributes=			"baseClass"
			>

		<Communications>
			<Message name="Attack target" key="A">
				<OnShow>
					(and (objGetTarget gSender)
						(not (eq (objGetTarget gSender) (objGetTarget gSource)))
						(not (eq (objGetData gSource "behavior") 'goingHome))
						)
				</OnShow>
				
				<Invoke>
					(switch
						(eq (objGetTarget gSender) gSource)
							(if (objFireEvent gSource 'OrderAttackSelf gSender)
								(block Nil
									(shpCancelOrders gSource)
									(shpOrder gSource 'attack (objGetTarget gSender))
									(objSetData gSource "behavior" 'attackingTarget)
									)
								(objSendMessage gSender gSource (objTranslate gSource 'CannotAttackMyself "\"Unable to comply\""))
								)

						(and (not (objIsEnemy gSender (objGetTarget gSender)))
								(not (objIsAngryAt (objGetTarget gSender) gSender))
								)
							(if (objFireEvent gSource 'OrderAttackFriend (objGetTarget gSender))
								(block Nil
									(shpCancelOrders gSource)
									(shpOrder gSource 'attack (objGetTarget gSender))
									(objSetData gSource "behavior" 'attackingTarget)
									)
								(objSendMessage gSender gSource (objTranslate gSource 'CannotAttackFriend "\"Unable to comply\""))
								)

						(block Nil
							(shpCancelOrders gSource)
							(shpOrder gSource 'attack (objGetTarget gSender))
							
							(objSetData gSource "behavior" 'attackingTarget)
							(objSendMessage gSender gSource (objTranslate gSource 'AttackTargetAck "\"Acknowledged\""))
							)
						)
				</Invoke>
			</Message>
			
			<Message name="Break & attack" key="B">
				<OnShow>
					(eq (objGetData gSource "behavior") 'escorting)
				</OnShow>
				
				<Invoke>
					(block (theTarget)
						(switch
							(or (not (setq theTarget (sysFindObject gPlayerShip "sTYAN")))
									(gr (objGetDistance gPlayerShip theTarget) 100)
									)
								(objSendMessage gSender gSource (objTranslate gSource 'NoTargetInRange "\"No target in range\""))
							
							(block Nil
								(shpCancelOrders gSource)
								(shpOrder gSource 'attack theTarget)
								
								(objSetData gSource "behavior" 'attackingAtWill)
								(objSendMessage gSender gSource (objTranslate gSource 'AttackTargetAck "\"Acknowledged\""))
								)
							)
						)
				</Invoke>
			</Message>

			<Message name="Form up" key="F">
				<OnShow>
					(and 
						(or (eq (objGetData gSource "behavior") 'waiting)
							(objCommunicate gSource gSender 'QueryAttackStatus)
							)
						(not (eq (objGetData gSource "behavior") 'goingHome))
						)
				</OnShow>

				<Invoke>
					(block (behavior)
						(setq behavior (objGetData gSource "behavior"))
						(switch
							(eq behavior 'escorting)
								(block Nil
									(objCommunicate gSource gSender 'FormUp)
									(objSendMessage gSender gSource (objTranslate gSource 'FormUpAck "\"Acknowledged\""))
									)

							(block Nil
								(shpCancelOrders gSource)
								(shpOrder gSource 'follow gPlayerShip)
								
								(objSetData gSource "behavior" 'escorting)

								(switch
									(or
										(eq behavior 'attackingTarget)
										(eq behavior 'attackingAtWill)
										)
																					(objSendMessage gSender gSource (objTranslate gSource 'CancelAttackAck "\"Acknowledged\""))

																				(objSendMessage gSender gSource (objTranslate gSource 'FormUpAck "\"Acknowledged\""))
									)
								)
							)
						)
				</Invoke>
			</Message>
			
			<Message name="Look around" key="L">
				<OnShow>
					(eq (objGetData gSource "behavior") 'escorting)
				</OnShow>

				<Invoke>
						(block Nil
							(shpCancelOrders gSource)
							(shpOrder gSource 'patrol gSender 90)
							
							(objSetData gSource "behavior" 'lookingAround)
							(objSendMessage gSender gSource (objTranslate gSource 'LookAroundAck "\"Acknowledged\""))
							)
				</Invoke>
			</Message>

			<Message name="Patrol target" key="P">
				<OnShow>
					(and (objGetTarget gSender)
						(eq (objGetData gSource "behavior") 'escorting)
						)
				</OnShow>

				<Invoke>
					(switch
						(eq (objGetTarget gSender) gSource)
							(objSendMessage gSender gSource (objTranslate gSource 'CannotPatrolMyself "\"Unable to comply\""))
						
						(objIsEnemy gSource (objGetTarget gSender))
								)
							(objSendMessage gSender gSource (objTranslate gSource 'CannotPatrolEnemies "\"Unable to comply\""))

						(block Nil
							(shpCancelOrders gSource)
							(shpOrder gSource 'patrol (objGetTarget gSender) 10)
							
							(objSetData gSource "behavior" 'patrollingTarget)
							(objSendMessage gSender gSource (objTranslate gSource 'PatrolTargetAck "\"Acknowledged\""))
							)
						)
				</Invoke>
			</Message>

			<Message name="Status" key="S">
				<OnShow>
					True
				</OnShow>
				
				<Invoke>
					(block (behavior armorDamage shieldLevel)
						(setq behavior (objGetData gSource "behavior"))
						
						(objSendMessage gSender gSource
							(switch
								(or (eq behavior 'attackingTarget) (eq behavior 'attackingAtWill))
									(objTranslate gSource 'StatusAttackingTarget "\"Engaging target\"")

								(eq behavior 'goingHome)
									(objTranslate gSource 'StatusGoingHome "\"Returning home\"")
								
								(eq behavior 'repairingArmor)
									(objTranslate gSource 'StatusRepairingArmor "\"Repairing armor\"")
									
								(eq behavior 'waiting)
									(objTranslate gSource 'StatusWaiting "\"Waiting\"")
									
								(and (eq (setq armorDamage (objGetVisibleDamage gSource)) 0)
										(or (eq (setq shieldLevel (objGetShieldLevel gSource)) 100) (eq shieldLevel -1))
										)
									(objTranslate gSource 'Status100Percent "\"No damage\"")

								(eq armorDamage 0)
									(cat "Shields down to " shieldLevel "%")
								
								(cat "Armor is " armorDamage "% damaged")
								)
							)
							
						(objSetShowAsDestination gSource Nil 'autoclear)
						)
				</Invoke>
			</Message>

			<Message name="Wait" key="W">
				<OnShow>
					(eq (objGetData gSource "behavior") 'escorting)
				</OnShow>

				<Invoke>
					(block Nil
						(shpCancelOrders gSource)
						(shpOrder gSource 'hold)
						
						(objSetData gSource "behavior" 'waiting)
						(objSendMessage gSender gSource (objTranslate gSource 'WaitAck "\"Acknowledged\""))
						)
				</Invoke>
			</Message>
		</Communications>
		
		<Language>
			<Text id="EscortReportingIn">
				(if (not (objGetData gSource "welcomeMsg"))
					(block Nil
						(objSetData gSource "welcomeMsg" True)
						(objTranslate gSource 'WingmanJoined "\"Ready\"")
						)
					""
					)
			</Text>
			
			<Text id="NiceShooting">			"\"Nice shooting!\""</Text>
			<Text id="WatchYourTargets">		"\"Watch your targets!\""</Text>
		</Language>
		
		<Events>
			<OnBehavior>
				(block (behavior theTarget nextTime maxSystemLevel armorDamage)
					(setq behavior (objGetData gSource "behavior"))
					
					(switch
						; If we're injured and we're near a base that repairs armor then
						; dock with the base and get repaired

						(and (eq behavior 'escorting)
								(geq (setq armorDamage (objGetVisibleDamage gSource)) 10)
								(not (objIsUnderAttack gSource))
								)
							(switch
								; Some wingmen (like Jenna) doesn't want to proceed beyond a certain point
								(and (setq maxSystemLevel (objGetStaticData gSource "MaxSystemLevel"))
										(gr (sysGetLevel) maxSystemLevel)
										)
									(block Nil
										(shpCancelOrders gSource)
										(shpOrder gSource 'hold 30)
										(shpOrder gSource 'gate (intGetGateToSystem gSource (objGetData gSource "homeSystem")))

										(objSetData gSource "behavior" 'goingHome)
										(objSendMessage gPlayerShip gSource (objTranslate gSource 'ImGoingHome "\"Going home\""))
										)
								
								; If there is a station that can repair armor, dock with it
								(setq theTarget (srvFindRepairArmor gSource 60 (itmGetLevel (shpGetArmor gSource 0))))
									(block Nil
										(shpCancelOrders gSource)
										(shpOrder gSource 'dock theTarget)
										(shpOrder gSource 'wait (random 3 8))
										
										(objSetData gSource "behavior" 'repairingArmor)
										(objSendMessage gPlayerShip gSource (objTranslate gSource 'ImRepairingArmor "\"Docking to repair armor\""))
										)
								
								(eq behavior 'lookingAround)
									(if (setq theTarget (sysFindObject gSource "TsA E P"))
										(block
											(
												(playerPower (objGetCombatPower gPlayerShip))
												(threat (match theTarget ship (geq (objGetCombatPower ship) playerPower)))
												)
											(enum theTarget ship
												(block Nil
													(objSetShowAsDestination ship 'autoClear)
													(objSetKnown ship)
													)
												)
											(if threat
												(objSendMessage gPlayerShip gSource (cat (objTranslate gSource 'ThreatDetectedStart "\"Threat detected: ") (objGetName threat 0x0040) (objTranslate gSource 'ThreatDetectedEnd ".\""))
												(objSendMessage gPlayerShip gSource (cat (objTranslate gSource 'ThreatDetectedStart "\"Detected ") (count theTarget) (objTranslate gSource 'ThreatDetectedEnd " enemy ships.\""))
												)
											)
										)
									)

								; Otherwise, tell the player that we need repairs
								(and (geq armorDamage 50)
										(or (not (setq nextTime (objGetData gSource "nextArmorRepairMsg")))
											(geq (unvGetTick) nextTime)
											)
										)
									(block Nil
										(objSendMessage gPlayerShip gSource (objTranslate gSource 'MyArmorNeedsRepair "\"Armor repair needed\""))
										(objSetData gSource "nextArmorRepairMsg" (add (unvGetTick) 3600))
										)
								)
						)
					)
			</OnBehavior>

			<OnCreate>
				(block Nil
					(objSetData gSource "homeSystem" (sysGetNode))
					(sysAddObjRecurringTimerEvent 60 gSource "OnBehavior")
					)
			</OnCreate>

			<OnDestroy>
				(switch
					(eq (objGetGlobalData gSource "status") 'returnedHome)
						Nil

					(and gPlayerShip (eq aOrderGiver gPlayerShip))
						(objSetGlobalData gSource "status" 'destroyedByPlayer)

					(block Nil
						(objSetGlobalData gSource "status" 'destroyed)
						(plyMessage gPlayer (objTranslate gSource 'WingmanKilled "Wingman killed"))
						)
					)
			</OnDestroy>
			
			<OnEnteredGate>
				(if (eq (objGetData gSource "behavior") 'goingHome)
					(objSetGlobalData gSource "status" 'returnedHome)
					)
			</OnEnteredGate>
			
			<OnEnteredSystem>
				(block (maxSystemLevel)
					(setq maxSystemLevel (objGetStaticData gSource "MaxSystemLevel"))
					(switch
						(not maxSystemLevel)
							Nil
							
						(eq (objGetData gSource "homeSystem") (sysGetNode))
							Nil

						(geq (sysGetLevel) maxSystemLevel)
							(objSendMessage gPlayerShip gSource (objTranslate gSource 'ImTooFarFromHome "\"I'm too far from home\""))

						(eq (sysGetLevel) (subtract maxSystemLevel 1))
							(objSendMessage gPlayerShip gSource (objTranslate gSource 'ImFarFromHome "\"I'm far from home\""))
						)
					)
			</OnEnteredSystem>

			<OnOrdersCompleted>
				(block (behavior theTarget)
					(setq behavior (objGetData gSource "behavior"))
					(switch
						(eq behavior 'repairingArmor)
							(block Nil
								(intArmorRepairAll gSource 25 'alwaysRepair)
								(objSendMessage gPlayerShip gSource (objTranslate gSource 'ArmorRepaired "\"Armor repairs completed\""))
								)
						)
					
					; Set orders and state
					
					(switch
						(and (eq behavior 'attackingAtWill)
								(setq theTarget (sysFindObject gPlayerShip "sTYAN"))
								(leq (objGetDistance gPlayerShip theTarget) 100)
								)
							(shpOrder gSource 'attack theTarget)
								
						gPlayerShip
							(block Nil
								(shpOrder gSource 'follow gPlayerShip)
								(objSetData gSource "behavior" 'escorting)
								)

						(block Nil
							(shpOrder gSource 'hold)
							(objSetData gSource "behavior" 'waiting)
							)
						)
					)
			</OnOrdersCompleted>
			
			<OnPlayerLeftSystem>
				(block (behavior)
					(setq behavior (objGetData gSource "behavior"))
					(switch
						(or (not behavior)
								(eq behavior 'goingHome)
								)
							Nil

						; If we're waiting, stay in this system and wait for the player
						(or (eq behavior 'waiting)
								(eq behavior 'repairingArmor)
								)
							'waitForPlayer

						; Otherwise, follow the player through the gate
						'followPlayer
						)
					)
			</OnPlayerLeftSystem>
			
			<OnTranslateMessage>
				(switch
					(eq aMessage 'EscortReportingIn)
						(if (not (objGetData gSource "welcomeMsg"))
							(block Nil
								(objSetData gSource "welcomeMsg" True)
								(objTranslate gSource 'WingmanJoined "\"Ready\"")
								)
							""
							)
							
					(eq aMessage 'WatchYourTargets)
						(objTranslate gSource 'WatchYourTargets "\"Watch your targets!\"")
						
					(eq aMessage 'NiceShooting)
						(objTranslate gSource 'NiceShooting "\"Nice shooting!\"")
						
					Nil
					)
			</OnTranslateMessage>

			<OrderJoinPlayer>
				(block Nil
					(objSetProperty gSource 'playerWingman True)
					(objSetGlobalData gSource "status" 'joined)
					(objFireEvent gSource "OnWingmanJoinedPlayer")
					)
			</OrderJoinPlayer>
			
			<OrderLeavePlayer>
				(block Nil
					(objSetProperty gSource 'playerWingman Nil)
					
					(shpCancelOrders gSource)
					(shpOrder gSource 'hold 30)
					(shpOrder gSource 'gate)
					(objSetData gSource "behavior" 'goingHome)
					
					(objFireEvent gSource "OnWingmanLeftPlayer")
					)
			</OrderLeavePlayer>
		</Events>
	</ShipClass>

	<Globals>
		(block Nil
			;(item1 item2 item2 item3 item3 item3) --> ((item1 1) (item2 2) (item3 3))
			;Group each entry with the number of times it occurs
			(setq group (lambda (theList)
				(block (result theIndex)
					(enum theList theItem
						(if (setq theIndex (find result theItem 0)) ;The index of the (item count) entry
							(set@ result theIndex (inc@ (@ seen theIndex) 1 1))
							(lnkAppend result (list theItem 1))
							)
						)
					result
					)
				))
			(setq sortByLambda (lambda (theList theLambda theOrder)
				;Remove the lambda result from each entry and return the list
				(map
					;Sort by lambda result
					(sort
						;Append each entry with the result of its lambda
						(map
							theList
							theEntry
							(list
								theEntry									(theLambda theEntry)
								)
							)
						theOrder
						1
						)
					theEntry
					(@ theEntry 0)
					)
				))
			;DOES NOT WORK WITH POINTERS
			(setq inc@ (lambda (theList theIndex theAmount)
				(set@ theList theIndex (add (@ theList theIndex) theAmount))
				))
			)
	</Globals>

</TranscendenceModule>
User avatar
0xABCDEF
Militia Lieutenant
Militia Lieutenant
Posts: 124
Joined: Thu May 19, 2016 12:58 am
Location: Was destroyed by a Phobos-class dreadnought in the Eridani system

Work on this adventure has become slow and stagnant, though I plan to incorporate the Commonwealth Police into it.

Edit: Mod is broken again
Last edited by 0xABCDEF on Fri Sep 08, 2017 5:16 am, edited 2 times in total.
JohnBWatson
Fleet Officer
Fleet Officer
Posts: 1452
Joined: Tue Aug 19, 2014 10:17 pm

Doesn't show up in the list of adventures, but command screen does appear in the SoTP menu. Does the download work for you if you clear your extensions folder, or is the problem on my end?
User avatar
0xABCDEF
Militia Lieutenant
Militia Lieutenant
Posts: 124
Joined: Thu May 19, 2016 12:58 am
Location: Was destroyed by a Phobos-class dreadnought in the Eridani system

JohnBWatson wrote:
Wed Mar 22, 2017 6:26 am
Doesn't show up in the list of adventures, but command screen does appear in the SoTP menu. Does the download work for you if you clear your extensions folder, or is the problem on my end?
Turns out that I forgot to include a name-randomization library that I planned to use for pilgrim characters.

http://xelerus.de/index.php?s=mod&id=1580
JohnBWatson
Fleet Officer
Fleet Officer
Posts: 1452
Joined: Tue Aug 19, 2014 10:17 pm

Found a bug - the starting station's harass attack can kill you in the middle of the opening dialogue. Making the opening battle a bit smaller and shorter could reduce the risk of this, as could ordering threats to gate out once the freighter arrives.

You may want to update the Penitent ship graphics.

Edit:

There are also some issues going on with docking before you get your ship, traffic at the Cathedral after it's killed, and the like. I haven't yet gotten the mod to work past the cathedral raid. The cathedral should also avoid stations it's hostile to, and keep its little metropolis.

Pilgrim behavior also feels a bit off. There should definitely be far fewer of them, and they should probably be more responsive to what's going on. At the very least, they should stop what they're doing and attack things that attack them. Trying to gate once desperate escape or sustain gets activated would also be an improvement.

Looking at the powers from the source code, they seem interesting. Is there a reason CORRUPT is used the way it is?
User avatar
0xABCDEF
Militia Lieutenant
Militia Lieutenant
Posts: 124
Joined: Thu May 19, 2016 12:58 am
Location: Was destroyed by a Phobos-class dreadnought in the Eridani system

I will make sure that the fighting completes before the first dockscreen.

I am not really sure how to handle ship images and armor segment stuff besides the facings. I also need a way to render the hero images for the playership and monastery. I might make the NPCs use the updated graphics and keep the player with the old graphics (for some reason).

The Sanctuary system uses an outdated version of the XML (before the Benedict updates). Also, I was initially unsure about how to handle pilgrim behavior.

CORRUPT was initially a gimmick-based power. I'm going to get rid of the crate gimmick and replace it with calculations based on the matchup between the player and the target.
Post Reply