Example mod: Drunken missile launcher

This is a moderated forum that collects tutorials, guides, and references for creating Transcendence extensions and scripts.
Post Reply
PM
Fleet Admiral
Fleet Admiral
Posts: 2570
Joined: Wed Sep 01, 2010 12:54 am

This example mod adds a missile launcher that fires drunken missiles and installs it on the playership at the start of a new game. The mod is fully functional and should work after a simple copy-and-paste to a blank xml file.

Code: Select all

<?xml version="1.0" ?>
<!DOCTYPE TranscendenceExtension
[
	<!ENTITY unid912DrunkMissile	"0xd912fffa">
	<!ENTITY it912DrunkMissile	"0xd912fffb">
]>

; Made for Transcendence v1.3 rc1 or later.
<TranscendenceExtension UNID="&unid912DrunkMissile;"
	apiVersion="22"
	name="Drunken Missile Launcher Tutorial"
	credits="PM"
>

; This weapon was a copy of the DM1500 disposable missile rack.
; Now heavily modified for our purposes.

; The weapon will be given for free at the start of a game.
; Value will be zero so that the player cannot sell for quick cash.
; We do not want this weapon to appear anywhere else.
; Frequency is notrandom, and attribute includes CannotOrder.

; Note:  No launcher in the standard game has unlimited ammo, but this launcher does.

	<ItemType UNID="&it912DrunkMissile;"
			name=				"Drunken missile launcher"
			level=				"4"
			value=				"0"
			mass=				"3000"
			frequency=			"notrandom"
			attributes=			"CannotOrder; MajorItem"

			description=		"This weapon carries a bottomless magazine full of drunken homing missiles."
			>

		<Image imageID="&rsItems1;" imageX="0" imageY="480" imageWidth="96" imageHeight="96"/>

	; We need 'maneuverability' to make the game show "tracking" on item stats.
	; But we don't want the builtin tracking.  Solution:  Set it to very high value
	; to minimize interference from it.

		<Weapon
				type=				"missile"
				launcher=			"true"

				damage=				"blast:4d6; momentum4; WMD5"
				fireRate=			"30"
				hitPoints=			"10"
				lifetime=			"120"
				powerUse=			"20"

				missileSpeed=		"40"
				maneuverability=	"5000"
				sound=				"&snMissileLauncher;"

				effect=				"&efMissileNAMI;"
				>

			<HitEffect
					sound="&snArmorHit1;"
					>
				<Image imageID="&rsExplosionsAG48;"
						imageX="0"
						imageY="0"
						imageWidth="48"
						imageHeight="48"
						imageFrameCount="16"
						imageTicksPerFrame="2"/>
			</HitEffect>
		</Weapon>

		<Events>
		; Called when we start a new game, after ship select.
			<OnGlobalUniverseCreated>
				(block (newItem)
				; Remove all installed launchers.
					(objEnumItems gPlayerShip "lI" thisItem
						(block (junkItem)
						; Uninstall launcher first.
							(setq junkItem (shpRemoveDevice gPlayerShip thisItem))
						; Dispose the launcher if we can.
							(if junkItem (objRemoveItem gPlayerShip junkItem) )
						)
					)

				; Create our gift.
					(setq newItem (itmCreate &it912DrunkMissile; 1))
				; Add the new launcher to ship's cargo hold.
					(objAddItem gPlayerShip newItem)
				; Install the launcher.
					(shpInstallDevice gPlayerShip newItem)
				)
			</OnGlobalUniverseCreated>

		; Called when we fire our launcher.
			<OnFireWeapon>
				; gSource is our attacker.
				(block (theShot)
				; Spawn the missile.
					(setq theShot (sysCreateWeaponFire aWeaponType gSource aFirePos aFireAngle 30 aTargetObj Nil aWeaponBonus))

				; Missile does not inherit attacker's velocity unless we say so.
					(objIncVel theShot (objGetVel gSource))

				; We need to assign a recurring event to our missile to make it do things
				; during its flight.  In this case, we want the missile to fly erratically
				; and track its target while doing so.  5 calls event six times per second.
					(objSetEventHandler theShot &it912DrunkMissile;)
					(sysAddObjRecurringTimerEvent 5 theShot "ThinkDrunk")

				; Remember our target!
					(objSetObjRefData theShot "target" aTargetObj)

				; Abort because We already fired the shot.
					0
				)
			</OnFireWeapon>

		; Custom event called periodically.
			<ThinkDrunk>
				(block (theTarget oldYaw newYaw maxChangeYaw oldSpeed idealSpeed newSpeed maxChangeSpeed)
				; Remember our facing.
					(setq oldYaw (sysVectorAngle (objGetVel gSource)) )
				; Does our target exist?
					(if (setq theTarget (objGetObjRefData gSource "target"))
					; YES:  We want to go to our target.
						(block (idealYaw diffYaw maxRotate)
						; Find the facing that points to our target!
							(setq idealYaw (sysVectorAngle (sysVectorSubtract (objGetPos theTarget) (objGetPos gSource))) )

						; We need to know the difference current and ideal facings.
							(setq diffYaw (modulo 'degrees (subtract idealYaw oldYaw) 360))

						; Set the most degrees the missile can rotate toward its target in this check.
							(setq maxRotate 45)

						; If the difference is small enough, we simply take the ideal facing.
						; Else, we turn toward the ideal facing by so many degrees.
							(setq newYaw (switch
								(or (leq diffYaw maxRotate) (geq diffYaw (subtract 360 maxRotate)) )
									idealYaw
								(ls diffYaw 180)
									(add oldYaw maxRotate)
								(subtract oldYaw maxRotate)
							))
						)

					; NO:  Continue on current path because missile has no target.
						(setq newYaw oldYaw)
					)
				; Set the most degrees the missile can randomly veer off-center per check.
					(setq maxChangeYaw 45)
				; Randomize our new facing.  This is how we get drunken, erratic flight.
					(setq newYaw (add (random (subtract 360 maxChangeYaw) (add 360 maxChangeYaw)) newYaw) )
					(setq newYaw (modulo 'degrees newYaw 360))


				; Missile speed may not be what was advertised if attacker velocity
				; messed with it too much.  Thus, we gradually correct our speed.
				; First, get our current speed.
					(setq oldSpeed (sysVectorSpeed (objGetVel gSource)) )

				; Get the speed we want.  Default is 40, but we will randomize it a bit
				; to make the missile a bit less predictable.
					(setq idealSpeed (random 32 48))

				; Change toward ideal speed by up to the maximum defined.
					(setq maxChangeSpeed 10)
					(setq newSpeed (switch
						(gr oldSpeed (add idealSpeed maxChangeSpeed))
							(subtract oldSpeed maxChangeSpeed)
						(ls oldSpeed (subtract idealSpeed maxChangeSpeed))
							(add oldSpeed maxChangeSpeed)
						idealSpeed
					))

				; We did all of the number crunching above.  Now apply the results!
				; Update missile velocity.
					(objSetVel gSource (sysVectorPolarVelocity newYaw newSpeed))
				; And missile facing too.  Now, we're done - for now!
					(objSetProperty gSource 'rotation newYaw)
				)
			</ThinkDrunk>
		</Events>
	</ItemType>

</TranscendenceExtension>
Download and Play in 1.9 beta 1...
Drake Technologies (Alpha): More hardware for combat in parts 1 and 2!
Star Castle Arcade: Play a classic arcade game adventure, with or without more features (like powerups)!
Playership Drones: Buy or restore exotic ships to command!

Other playable mods from 1.8 and 1.7, waiting to be updated...
Godmode v3 (WIP): Dev/cheat tool compatible with D&O parts 1 or 2.
Post Reply