I've been reminded of the existence of this thread so here's my current stock of helper functions (and a dockscreen and short example item to demonstrate the use of some of them.)
These functions swap weapons, armor, and shields. As currently written, they must be called where gSource is defines (eg in an invoke, ship event, or device event)
Code: Select all
; Define the function for switching weapon.
(setq switchweapon (lambda (theweapon)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhweapon theitem)
(setq theitem gItem)
(setq status (ItmIsEnhanced theitem))
(setq enhweapon (itmSetEnhanced theweapon status))
(objAddItem gSource enhweapon)
; even worse kludge to handle launchers
(if(itmMatches theitem "l")
(block (nil)
(objEnumItems gSource "Il" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))
(shpInstallDevice gSource enhweapon)
; kludge for new item weirdness
(shpEnhanceItem gSource theitem status)
; Remove previous weapon
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)
))
; Define the function for switching shields.
(setq switchshield (lambda (theshield)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhshield theitem shieldHP)
(setq theitem gItem)
(setq shieldHP (subtract (shpGetShieldMaxHitPoints gSource) (shpGetShieldDamage gSource)))
(setq status (ItmIsEnhanced theitem))
(setq enhshield (itmSetEnhanced theshield status))
(objAddItem gSource enhshield)
; shields require the same kludge as launchers
(if(itmMatches theitem "s")
(block (nil)
(objEnumItems gSource "Is" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))
; Install shield and set HP
(shpInstallDevice gSource enhshield)
(shpRechargeShield gSource shieldHP)
; Remove previous shield
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)
))
; Define the function for switching armor
(setq switcharmor (lambda (thearmor)
(block (oldinventory newinventory count)
(setq count 0)
; list the inventory
(setq oldinventory (objGetItems gsource "*U"))
(setq armorcount (shpGetArmorCount gsource))
(for armorposition 0 (subtract armorcount 1)
(block (armoriterator)
(setq armoriterator (shpGetArmor gsource armorposition))
(if (eq (itmGetUnid gitem) (itmGetUnid armoriterator))
(block (status enharmor armordamage armorhp)
(setq count (add count 1))
; get HP
(setq armorHP (subtract (shpGetArmorMaxHitPoints gSource armorposition) (objGetArmorDamage gSource armorposition)))
; store enhancements
(setq status (itmIsEnhanced armoriterator))
; enhance the new armor
(setq enharmor (itmSetEnhanced thearmor status))
(objAddItem gSource enharmor 1)
; install the armor
(shpInstallArmor gsource enharmor armorposition)
; damage the new armor to 0 HP
(shpDamageArmor gsource armorposition 15 (multiply (shpGetArmorMaxHitPoints gsource armorposition) 10))
; repair the armor
(objRepairArmor gsource armorposition armorHP)
))
))
; get the inventory again
(setq newinventory (objGetItems gsource "*U"))
; remove anything new. May remove items that match the old armor, but I don't see a way to avoid that. It shouldn't come up much anyways.
(enum newinventory outeriterator
(block (isitold)
(setq isitold nil)
(enum oldinventory inneriterator
(if (eq inneriterator outeriterator) (setq isitold TRUE))
)
(if (not isitold) (objRemoveItem gSource outeriterator count))
))
)
))
This pair of functions is for trig. AngleNormalize is going to be deprecated in 1.08 because of improvements to the builtin modulo function.
Code: Select all
; Define the function for switching weapon. (NB-- This function is an update to 0.99c of a function of Digdug's, renamed to avoid conflicts.)
(setq nwfswitchweapon (lambda (theweapon)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhweapon theitem)
(setq theitem gItem)
(setq status (ItmIsEnhanced theitem))
(setq enhweapon (itmSetEnhanced theweapon status))
(objAddItem gSource enhweapon)
; even worse kludge to handle launchers
(if(itmMatches theitem "l")
(block (nil)
(objEnumItems gSource "Il" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))
(shpInstallDevice gSource enhweapon)
; kludge for new item weirdness
(shpEnhanceItem gSource theitem status)
; Remove previous weapon
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)
))
; Define the function for switching shields.
(setq nwfswitchshield (lambda (theshield)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhshield theitem shieldHP)
(setq theitem gItem)
(setq shieldHP (subtract (shpGetShieldMaxHitPoints gSource) (shpGetShieldDamage gSource)))
(setq status (ItmIsEnhanced theitem))
(setq enhshield (itmSetEnhanced theshield status))
(objAddItem gSource enhshield)
; shields require the same kludge as launchers
(if(itmMatches theitem "s")
(block (nil)
(objEnumItems gSource "Is" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))
; Install shield and set HP
(shpInstallDevice gSource enhshield)
(shpRechargeShield gSource shieldHP)
; Remove previous shield
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)
))
; Define the function for switching armor
(setq nwfswitcharmor (lambda (thearmor)
(block (oldinventory newinventory count)
(setq count 0)
; list the inventory
(setq oldinventory (objGetItems gsource "*U"))
(setq armorcount (shpGetArmorCount gsource))
(for armorposition 0 (subtract armorcount 1)
(block (armoriterator)
(setq armoriterator (shpGetArmor gsource armorposition))
(if (eq (itmGetUnid gitem) (itmGetUnid armoriterator))
(block (status enharmor armordamage armorhp)
(setq count (add count 1))
; get HP
(setq armorHP (subtract (shpGetArmorMaxHitPoints gSource armorposition) (objGetArmorDamage gSource armorposition)))
; store enhancements
(setq status (itmIsEnhanced armoriterator))
; enhance the new armor
(setq enharmor (itmSetEnhanced thearmor status))
(objAddItem gSource enharmor 1)
; install the armor
(shpInstallArmor gsource enharmor armorposition)
; damage the new armor to 0 HP
(shpDamageArmor gsource armorposition 15 (multiply (shpGetArmorMaxHitPoints gsource armorposition) 10))
; repair the armor
(objRepairArmor gsource armorposition armorHP)
))
))
; get the inventory again
(setq newinventory (objGetItems gsource "*U"))
; remove anything new. May remove items that match the old armor, but I don't see a way to avoid that. It shouldn't come up much anyways.
(enum newinventory outeriterator
(block (isitold)
(setq isitold nil)
(enum oldinventory inneriterator
(if (eq inneriterator outeriterator) (setq isitold TRUE))
)
(if (not isitold) (objRemoveItem gSource outeriterator count))
))
)
))
This function is a kludge for real number division. The average return is the real number quotient of two integers but each individual return is an integer, either rounded up or down at random. It is useful where a calculation will be made many times such as in a short timer and regular integer division produces unacceptable bias.
Code: Select all
(setq probabilisticDivide (lambda (dividend divisor)
(add
(divide dividend divisor)
(if (leq (modulo dividend divisor) (random 1 divisor))
0
1
)
)
))
These functions are the heart of the virtual ammo system for making non-launcher weapons that support multiple ammo types or weapons that are intended to get more than one shot per item of ammo.
Code: Select all
(setq VirtualAmmoUpdate (lambda nil
(block (ammolist realshots shots virtualUNID)
(setq ammolist (itmGetData gItem "ammolist"))
(setq virtualUnid (typGetDataField gItem "ammoType"))
(setq realshots 0)
; count ammo
(objEnumItems gSource "m" ammo
(if (find ammolist (itmGetType ammo))
(setq realshots (add realshots (itmGetCount ammo)))
)
)
; count shots
(objEnumItems gSource "*V" theItem
(if (eq (itmGetType theItem) virtualUNID)
(setq shots (itmGetCount theItem))
)
)
(switch
(gr realshots shots)
(objAddItem gSource (itmCreate virtualUNID (subtract realshots shots)))
(ls realshots shots)
(objRemoveItem gSource (itmCreate virtualUNID (subtract shots realshots)))
)
)
))
(setq fireUnlauncher (lambda nil
(block (ammolist launcher shot speed munition)
(setq ammolist (itmGetData gItem "ammolist"))
(setq launcher (typGetStaticData (itmGetUnid gItem) 'VirtualLauncher))
; determine ammo type
(enum ammolist ammo
(if (not munition)
(if (objHasItem gSource (itmCreate ammo 1))
(setq munition ammo)
)
)
)
(if munition
(block nil
(setq speed (typGetDataField munition "speed"))
(if (objRemoveItem gSource (itmCreate munition 1))
(block nil
(setq shot (sysCreateWeaponFire munition gSource aFirePos aFireAngle speed aTargetObj nil aWeaponBonus))
(objIncVel shot (objGetVel gSource))
)
)
)
;; else don't fire!
)
true
)
))
(setq initializeUnlauncher (lambda nil
(block (launcher ammonumber ammolist)
; construct ammo list
(setq launcher (typGetStaticData (itmGetType gItem) 'VirtualLauncher))
(setq ammonumber (typGetDataField launcher 'variantCount))
(setq ammolist (list))
(for i 0 ammonumber
(setq ammolist (append (typGetDataField launcher (cat "ammoType:" i)) ammolist))
)
; store ammo list
(objSetItemData gSource gItem "ammolist" ammolist)
)
))
These support the virtual ammo system ammo priority selection dockscreen.
Code: Select all
(setq reorderAction (lambda (direction)
(block (ammolist ammo index)
(setq ammolist (itmGetData weItem "ammolist"))
(setq ammo (item (scrGetListEntry gScreen) 3))
;; save for keeping out position in the list
(setq weItem (objSetItemData weSource weItem "current-ammo" ammo))
(setq index (find ammolist ammo))
(lnkRemove ammolist index)
(setq index (add index direction))
(setq ammolist (append (subset ammolist 0 index) (list ammo) (subset ammolist index)))
;; update ammolist
(setq weItem (objSetItemData weSource weItem "ammolist" ammolist))
)
))
;when the screen is refreshed between switching ammo this is evaluated for each item
;it will keep the cursor on the ammo type that it was on before changing the list order
(setq dsInitialAmmo (lambda nil
(block (entry ammo)
(setq entry (scrGetListEntry gScreen))
(setq ammo (itmGetData weItem "current-ammo"))
(or (not ammo) (eq (item entry 3) ammo))
)
))
And this is that dockscreen.
Code: Select all
<DockScreen UNID="&WE_dsAmmoPriority;"
name= "ammo list"
type= "customPicker"
backgroundID= "&rsItemListScreen;"
>
<List initialItem="=(dsInitialAmmo)">
(block (ammolist (custom (list)))
(setq ammolist (itmGetData weItem "ammolist"))
(enum ammolist ammo
(block (itm)
(setq itm (itmCreate ammo 1))
(lnkAppend custom (list
(itmGetName itm 4)
(itmGetImageDesc itm)
"item description (perhaps an amount here as well?)"
ammo
))
)
)
custom
)
</List>
<Panes>
<Default>
<OnPaneInit>
(block (ammolist index length)
(setq ammolist (itmGetData weItem "ammolist"))
(setq length (count ammolist))
(setq index (find ammolist (item (scrGetListEntry gScreen) 3)))
(if (eq length 1) (block nil
(scrEnableAction gScreen 0 nil)
(scrEnableAction gScreen 1 nil)
))
(if (eq index 0) (scrEnableAction gScreen 0 nil))
(if (eq (add index 1) length) (scrEnableAction gScreen 1 nil))
(scrSetDesc gScreen "Choose the priority of your ammo")
)
</OnPaneInit>
<Actions>
<Action name="Higher" key="H">
(block nil
(reorderAction -1)
;refresh the screen so any changes are immediately visible
(scrShowScreen gScreen &WE_dsAmmoPriority;)
)
</Action>
<!-- same as move up, but add instead of subtract -->
<Action name="Lower" key="L">
(block nil
(reorderAction 1)
;refresh the screen so any changes are immediately visible
(scrShowScreen gScreen &WE_dsAmmoPriority;)
)
</Action>
<Action name="Done" key="d" cancel="1">
(block (uid ammolist)
;; clear our globals vars
(objSetItemData weSource weItem "current-ammo" nil)
(setq weSource nil)
(setq weItem nil)
(scrExitScreen gScreen)
)
</Action>
</Actions>
</Default>
</Panes>
</DockScreen>
And this is an example of the weapon looks like. The virtual launcher referenced in staticdata would normally be a virtual item. The ammotype is, of course, a virtual item with no need of any attributes except a name, a level and 'virtual="true"'.
Code: Select all
<ItemType UNID="&itNAMIMissileLauncher2;"
name= "NAMI missile unlauncher"
level= "3"
value= "1000"
mass= "1000"
frequency= "common"
attributes= "MajorItem; NAMI"
description= "This launcher is compatible with a full range of popular missiles including the KM100 Longbows and the XM900 Lucifers."
>
<Image imageID="&rsItemsNAMI2;" imageX="0" imageY="0" imageWidth="96" imageHeight="96"/>
<Weapon
fireRate= "30"
powerUse= "10"
>
<Missiles>
<Missile ammoID="&itNAMIVirtualAmmo;"
type= "missile"
damage= "kinetic:0"
missileSpeed= "0"
lifetime= "1"
>
</Missile>
</Missiles>
</Weapon>
<StaticData>
<VirtualLauncher>
&itNAMIMissileLauncher;
</VirtualLauncher>
</StaticData>
<Invoke installedOnly="true">
(block nil
; save the source and the item
(setq weSource gSource)
(setq weItem gItem)
(scrShowScreen gScreen "&dsAmmoPriority;")
)
</Invoke>
<Events>
<OnInstall>
(WE_initializeUnlauncher)
<OnInstall>
<OnFireWeapon>
(WE_fireUnlauncher)
</OnFireWeapon>
<OnUpdate>
(WE_VirtualAmmoUpdate)
</OnUpdate>
<Events>
</ItemType>