register_item "new_pulsecannon" {
name = "pulse cannon",
id = "extra_pulsecannon",
desc = "The Microsol Pulse Cannon provides cost-effective firepower thanks to the self-recharging battery.",
ascii = "}" ,
sprite = SPRITE_CHAINGUN,
level = 1,
weight = 1000, --bit of a guess, see item generation page on wiki
flags = {IF_RECHARGE},
firstmsg = "",
color_id = "generic",
--type specific
type = ITEMTYPE_RANGED, --required field
damage = "2d4", --required field
damagetype = DAMAGE_BULLET, --required field
group = "weapon-pistol", --defaults to "weapon-other"
fire = 10, --defaults to 10
acc = 0, --defaults to 0
radius = 0, --defaults to 0 (_RANGED and _NRANGED only)
shots = 1, --defaults to 0
ammo_id = "ammo", --required field (_RANGED only)
ammomax = 12, --required field (_RANGED only)
reload = 12, --defaults to 10 (_RANGED only)
shotcost = 1, --defaults to 0 (_RANGED only). I think 1 makes it a rapid-fire weapon and 0 a single-shot
altfire = ALT_AIMED, --defaults to ALT_NONE (_MELEE and _RANGED only)
altfirename = "aimed shot", --default depends on altfire (_MELEE and _RANGED only)
altreload = RELOAD_FULL, --defaults to RELOAD_NONE (_RANGED only)
altreloadname = "full", --default depends on altreload (_RANGED only)
--soundID = "pistol", --defaults to "id" (_RANGED and _NRANGED only)
missile = "gun",
psprite = SPRITE_PLAYER_CHAINGUN --the sprite used to represent the player when equipped
}
2. I was also wondering about creating my own mod items. I guess you use ui.msg_choice to let the player choose an item slot to mod, but I can't find out from the wiki how to reference the player's equipped items. Is there any example code for this?
OnUseCheck = function(self,being)
local item, result = being:pick_mod_item('P',being.techbonus)
if not result then return false end
if item ~= nil then self:add_property("chosen_item", item) end
return true
end,
OnUse = function(self,being)
if not self:has_property("chosen_item") then return true end
local item = self.chosen_item
if item.itype == ITEMTYPE_MELEE then
item.damage_sides = item.damage_sides + 1
elseif item.itype == ITEMTYPE_RANGED then
if item.damage_sides >= item.damage_dice then
item.damage_sides = item.damage_sides + 1
else
item.damage_dice = item.damage_dice + 1
end
elseif item.itype == ITEMTYPE_ARMOR then
item.armor = item.armor + 2
elseif item.itype == ITEMTYPE_BOOTS then
item.armor = item.armor * 2
end
item:add_mod('P')
return true
end,
The whole dance with having to use OnUseCheck and the custom property is inelegant, but it allows the mod to not take any time when the player cancels it. If you don't like that, here's the simpler version from before the change. OnUse = function(self,being)
local item, result = being:pick_mod_item('P',being.techbonus)
if not item then return result end
if item.itype == ITEMTYPE_MELEE then
item.damage_sides = item.damage_sides + 1
elseif item.itype == ITEMTYPE_RANGED then
if item.damage_sides >= item.damage_dice then
item.damage_sides = item.damage_sides + 1
else
item.damage_dice = item.damage_dice + 1
end
elseif item.itype == ITEMTYPE_ARMOR then
item.armor = item.armor + 2
elseif item.itype == ITEMTYPE_BOOTS then
item.armor = item.armor * 2
end
item:add_mod('P')
return true
end,
Using :pick_mod_item will prompt the player, check for assemblies, and make sure you are allowed to mod the item with the given mod.3. Is there any easy way to prevent the level generator from generating the built-in items, or will I have to wait for total conversion mod support?
items.pistol.weight = 0
If you want to get rid of all of them, you can just loop through before registering any of your own itemsfor _, it in ipairs(items) do
it.weight = 0
end
Modifying prototypes after they are declared is a bit messy, but it's the only option for built-in things.Here's my item declaration:Wow, what a useless error message. It is failing because "gun" is not defined as a missile. It has been renamed to "mgun". (I think all the missiles gut "m" added at the beginning and all the shotgun-type missiles got "s" added to the beginning.)
It is failing because "gun" is not defined as a missile. It has been renamed to "mgun". (I think all the missiles gut "m" added at the beginning and all the shotgun-type missiles got "s" added to the beginning.)I have the same error as this dude (and did the same mistake) however even after changing "gun" to "mgun" I still have the error.
Also, the id field should no longer be included in the table; the initial argument to register_item (e.g. "new_pulsecannon") will be used as the id.
register_item "SPTXsniperrifle"{
name = "Long range rifle",
type = ITEMTYPE_RANGED,
sprite = SPRITE_CHAINGUN,
level = 2,
weight = 160,
psprite = SPRITE_PLAYER_SHOTGUN,
ammo_id = "ammo",
group = "weapon-shotgun",
damage = "10d2",
damagetype = DAMAGE_BULLET,
missile = "mgun",
fire = 15,
reload = 10,
acc = 2,
ammomax = 5,
flags = {IF_PUMPACTION,IF_SHOTGUN},
desc = "A rifle designed to shoot targets at very long distances.",
altfire = ALT_AIMED,
}
I have the same error as this dude (and did the same mistake) however even after changing "gun" to "mgun" I still have the error.
Since you have IF_SHOTGUN, the game is looking in the shotgun list instead of the normal list. If you want it to be a shotgun, change the missile to a proper shotgun type (like "snormal"), otherwise remove the IF_SHOTGUN flag.Well that fixed it. Thanks. A lot.
...what's the property name of "fire" on a weapon? The wiki says "usetime", but I've tried both "usetime" and just "fire" and I get a lua error saying I'm reading an undeclared variable."fire" is the name of the prototype property and "usetime" is the object property. If the Lua error is for an undeclared variable, the problem could be the context you are using it in, as that doesn't sound like an error you'd get when declaring an item. I'd have to see what you're doing.
Also, how are OnAltFire hooks for scripted alt fire modes meant to be used. Should I temporarily alter the stats of the weapon and then change them back in the OnFired hook, or is that completely wrong?
I know the lua files for the main game aren't being released yet, but have you considered including an example file with each release containing the definitions for one or two items of each type, and perhaps the ai for one or two enemies? ...This would probably be good, but I'd have to go through Kornel to get permission about what we can post.
how do I access the player's inventory? I want to go through it to check if a particular kind of ammo is present - I'm trying to make a weapon that draws ammo directly from the inventory.
for it in player.inv:items() do
print(it.name)
end
The order they are stored in is undefined (i.e. should not be relied upon). The rest of the inventory API would be .inv:empty() (this is a predicate), .inv:clear(), .inv:add(it, [params])
OnFire = function(self) --to draw ammo from inventory
for it in player.inv:items() do
if it.id == "hl_nuclear_ammo" then
it.ammo = it.ammo - 1
return true
end
return false
end
end,
But the hook never seems to return true, although I'm sure I have an ammo item with that id in my inventory. If I make the function always return true, the weapon fires, so I think the problem must be here.I tried looking through the inventory like this:The "return false" needs to be outside the loop for this to work as you intended. You may also need to manually :destroy() the item when its ammo reaches 0, but I'm not 100% sure.
1. I have a missile defined with the MF_RAY flag set, but the game seems to ignore that and still draws a bullet travelling across the screen. It works in console mode however - has the flag become deprecated since graphics were introduced?I wouldn't say it is deprecated, just not supported in graphics mode yet.
2. Is there a list of sprites somewhere? I don't know the names of any of the missile sprites, so I've had to put in random sprites that I do know for now. Although I suppose there is something to be said for a shotgun that shoots shotguns...
3. I've been trying to change the missile property of a weapons using the OnFire hook, but I always get an error when I try to assign it - what should the assignment look like?The value needs to be the number id of the missile. Number ids are subject to change between versions, so you should translate from the string id e.g.
it.missile = missiles.mgun.nid
-- or
it.missile = missiles[string_id].nid
4. This is just out of interest, for now: I notice that calling Player:power_backpack() reshuffles the player's ammo stacks. Is there any way to do this without also turning the backpack on? It would be quite a handy feature.Honestly, I suspect there are still bugs in power_backpack ;) But no, I'm not aware of any other code that resorts the inventory like this; it is tied together with the backpack flag.
I need to see how a sprite of a size bigger than one tile is handled. When I try to use them, they are cropped from their upper left corner to one tile.
Doesn't seem to work by simply doing that.
OnAltFire is called when you use altfire = ALT_SCRIPT. I don't consider this to be very mature yet, as most of DoomRL alternate fire modes are not implemented in Lua. However, what you suggest sounds like the best way to do it to me. DoomRL's rocket launcher actually uses OnFire to switch the stats back, but I think OnFired would generally work better.
register_item "hl_handgun" {
name = "Glock-17",
desc = "A guard's pistol. Has a large clip and a semi-automatic mode for when you need more firepower.",
ascii = "}" ,
sprite = SPRITE_PISTOL,
level = 0,
weight = 1000, --bit of a guess, see item generation page on wiki
flags = {IF_PISTOL},
firstmsg = "",
color_id = "generic",
--type specific
type = ITEMTYPE_RANGED, --required field
damage = "2d4", --required field
damagetype = DAMAGE_BULLET, --required field
group = "weapon-pistol", --defaults to "weapon-other"
fire = 10, --defaults to 10
acc = 2, --defaults to 0
radius = 0, --defaults to 0 (_RANGED and _NRANGED only)
shots = 0, --defaults to 0. I think 1 makes it a rapid-fire weapon and 0 a single-shot
ammo_id = "ammo", --bullets are "ammo" shells are "" rockets are "" cells are
ammomax = 17, --required field (_RANGED only)
reload = 12, --defaults to 10 (_RANGED only)
shotcost = 1, --defaults to 0 (_RANGED only)
altfire = ALT_SCRIPT, --defaults to ALT_NONE (_MELEE and _RANGED only)
altfirename = "semi-auto", --default depends on altfire (_MELEE and _RANGED only)
altreload = RELOAD_DUAL , --defaults to RELOAD_NONE (_RANGED only)
altreloadname = "dualreload", --default depends on altreload (_RANGED only)
--soundID = "pistol", --defaults to "id" (_RANGED and _NRANGED only)
missile = "mgun",
psprite = SPRITE_PLAYER_PISTOL, --the sprite used to represent the player when equipped
OnCreate = function(self)
self:add_property("normal_shots", self.shots) --these are set again in OnFire to respect changes made by mod packs
self:add_property("normal_acc", self.acc)
end,
OnFire = function(self, being)
self:set_property("normal_shots", self.shots)
self:set_property("normal_acc", self.acc)
return true
end,
OnAltFire = function(self, being)
self:set_property("normal_shots", self.shots)
self:set_property("normal_acc", self.acc)
self.shots = self.shots + 3
self.acc = self.acc - 3
return true
end,
OnFired = function(self, being)
self.shots = self:get_property("normal_shots") --these are set again in OnFire to respect changes made by mod packs
self.acc = self:get_property("normal_acc")
end,
}
We have custom item properties? I thought that was beings only...
If you are worried about modding, I think you could fix this by using a custom item property to keep track of which mode the weapon is in. In OnFire and OnFired, you can subtract 3 shots and add 3 accuracy, but only if the weapon is in "alt-fire-mode", and in OnAltFire you can add 3 shots and subtract 3 accuracy, but only if the weapon is in "normal-mode". It still isn't perfect because the effect of a mod sometimes depends on the item's current stats which may be in either mode, so you could also try resetting the stats to normal mode in OnEquipTick.
I also have another question: From experimentation it seems that a being's scount can become negative without causing errors. What's the lowest value that it can be safely set to? Or are negative values not intended to be used at all?Scount is a 32-bit signed integer, so it can be as small as -2147483648. I found one bug relating to the player having a negative scount however. It may not occur very much in practice because it only happens on the player's turn (when the player's scount will usually be positive).
EDIT2: Could I have an up to date list of the names of the ally prototype fields? When I use the values from the wiki, it doesn't recognise some of them.
Scount is a 32-bit signed integer, so it can be as small as -2147483648. I found one bug relating to the player having a negative scount however. It may not occur very much in practice because it only happens on the player's turn (when the player's scount will usually be positive).
Ally? I'm not sure what you're referring to.
core.register_blueprint "being"
{
-- fieldname = { required, type, [default] }
name = { true, core.TSTRING },
name_plural = { false, core.TSTRING },
id = { false, core.TSTRING }, -- doesn't need to be in the prototype table; id is passed to the declaration function
sound_id = { false, core.TIDIN("beings") },
ascii = { true, core.TSTRING },
color = { true, core.TNUMBER },
sprite = { true, core.TNUMBER },
coscolor = { false, core.TTABLE },
glow = { false, core.TTABLE },
overlay = { false, core.TTABLE },
hp = { false, core.TNUMBER , 10 },
armor = { false, core.TNUMBER , 0 },
attackchance= { false, core.TNUMBER , 75 },
todam = { false, core.TNUMBER , 0 },
tohit = { false, core.TNUMBER , 0 },
tohitmelee = { false, core.TNUMBER , 0 },
speed = { false, core.TNUMBER , 100 },
vision = { false, core.TNUMBER , 0 },
min_lev = { true, core.TNUMBER },
max_lev = { false, core.TNUMBER , 10000 },
corpse = { false, core.TANY, 0 },
danger = { true, core.TNUMBER },
weight = { true, core.TNUMBER },
xp = { false, core.TNUMBER },
bulk = { false, core.TNUMBER , 100 },
flags = { false, core.TFLAGS, {} },
ai_type = { true, core.TSTRING },
is_group = false, -- hard-coded prototype value; can't be specified
res_bullet = { false, core.TNUMBER, 0 },
res_melee = { false, core.TNUMBER, 0 },
res_shrapnel= { false, core.TNUMBER, 0 },
res_acid = { false, core.TNUMBER, 0 },
res_fire = { false, core.TNUMBER, 0 },
res_plasma = { false, core.TNUMBER, 0 },
desc = { true, core.TSTRING },
kill_desc = { false, core.TSTRING },
kill_desc_melee = { false, core.TSTRING },
weapon = { false, core.TANY },
OnCreate = { false, core.TFUNC },
OnAction = { false, core.TFUNC },
OnAttacked = { false, core.TFUNC },
OnDie = { false, core.TFUNC },
OnDieCheck = { false, core.TFUNC },
OnPickupItem = { false, core.TFUNC },
}
I think it may be related to scripted levels as they do not use the generator to place enemies. Try to make a replacement of each being you need replaced using the OnEnter hook.
local i = 0
self:set_inv_item(i,being.new("hl_handgun")) i = i+1
self:set_inv_item(i,being.new("ammo")) i = i+1
being.inv:add("item")
Not sure if it's an actual procedure, but seeing how it isn't documented and that both player and being have inventory procedures while player inherits from being, it might just work.Thanks. Could you tell me what the names of the built in ai types are?
Although I've set the weight of all the normal doomRL enemies to zero, the game is still generating special rooms full of lost souls, and it also seems to be generating imps on some levels. Is there any easy way to stop this?
OnAction()
{
if Being:attack(player) then "your_action"
}
?
beings["soldier"].HookYouWantToChange = function
beings["soldier"].OnPickupItem = core.create_seq_function(beings["soldier"].OnPickupItem,
function(self,i)
do_stuff
end)
OnCreate(self)
OnAction(self)
OnAttacked(self)
OnDie(self, overkill)
OnDieCheck(self, overkill)
OnPickupItem(self,i)
register_item "hl_grenade" { --delayed explosion requires more hooks
name = "Hand Grenade",
desc = "Good thing you can throw accurately.",
ascii = "*" ,
color = BROWN,
sprite = SPRITE_ROCKET,
glow = {1,0,0,0.5},
level = 6,
weight = 200,
flags = {IF_NONMODABLE, IF_NOAMMO},
firstmsg = "These could come in handy.",
color_id = "generic",
--type specific
type = ITEMTYPE_RANGED,
damage = "5d5",
damagetype = DAMAGE_FIRE,
group = "weapon-rocket",
fire = 10,
acc = 0,
radius = 4,
shots = 0, --setting to 1 leads to x1 being appended to name, which is seriously confusing here
ammo_id = "shell", --not actually used
ammomax = 0,
reload = 10,
shotcost = 0,
altfire = ALT_NONE,
--altfirename = "double shot",
altreload = RELOAD_FULL,
--altreloadname = "dualreload",
--sound_id = "shotgun",
missile = "hl_grenade_missile",
psprite = SPRITE_PLAYER, --the sprite used to represent the player when equipped
OnCreate = function(self)
self:add_property("quantity", 2)
self:add_property("base_name", self.name) --used by test.SetNameFromQuantity
test.SetNameFromQuantity(self)
end,
OnEquipCheck = function(self, equipper)
if self:get_property("quantity") == 1 then return true end
self:set_property("quantity", self:get_property("quantity") - 1)
test.SetNameFromQuantity(self)
local spawned = item.new(items[self.id].nid) --spawn a new grenade item with 1 quantity
spawned:set_property("quantity", 1) --set quantity
test.SetNameFromQuantity(spawned)
if player:wear(spawned) == true then ui.msg("success") else ui.msg("fail") end --equip the new item
return false --don't equip this item
end,
}
EDIT: What sound IDs exist that are appropriate for explosions?I'll bounce on that asking for a full list of sound IDs.
If I inadvertently end up providing info that's both useful and not on the wiki please put it up. I would myself but the time... it is not there.
It's the IF_SHOTGUN flag indeed, the rocket launcher has it for that matter.
ui.msg(item.desc)
What are the property names of glow and sprite?sprite - on ground
sprite - on ground
psprite - on soldier, equipped
color - in inventory and ascii
coscolor - color for sprites
glow - glow
According to the wiki, one sniper weapon pack causes a weapon to have no range penalties to accuracy, and a second one causes it not to suffer any penalty against hitting a target you can't see. Are there some new item flags that set this behaviour? The wiki states that IF_AUTOHIT is used by sniper packs, but I assume that's for older versions where a sniper pack just caused a weapon to always hit.