ID:2236251
 
(See the best response by Kaiochao.)
Code:
mob
Login()
HealthSystem = new(1, 1)
Attack = new(1)

var
HealthSystem/Health
CalculatedStat/Attack

proc
DeathCheck()
if(Health.IsDead())
world << "[src] dies!"
del(src)

// Monster procs & variables.
monster
var
Name
obj/spawnPoint/spawner
LootSystem/loots

New()
loots = new(1, list())
..()

Del()
spawner.AllowNewSpawn()

var/LootItem/rolledLoot = loots.RollForLoot()
if(rolledLoot == null)
..()
return

var/obj/chest/Chest = new (loc)
Chest.loot = rolledLoot

..()

proc
Copy()
var/mob/monster/result = new()
result.icon = icon
result.icon_state = icon_state
result.Health = Health
result.Attack = Attack
result.Name = Name
result.spawner = spawner
result.loots = loots
return result

// Bug - Basic monster.
monster/bug
Name = "Bug"
icon = 'bug.dmi'

New()
Health = new(15, 15)
Attack = new(1)
Attack.ChangeRandomBonus(1, 2)

var/LootItem/lootTable[] = list()

var/LootItem/goldDrop = new("Gold")
goldDrop.ChangeRandomQuantity(3, 5)
lootTable.Add(goldDrop)

// 50% loot chance of 3-5 Gold.
loots = new(9, lootTable)
..()

obj
spawnPoint/monsterSpawner
minSpawnOffset = 10
maxSpawnOffset = 100

var
// Map Maker's Note
// -------------------------------------
// When you place a monsterSpawner on a
// map, you must right-click/Edit...,
// and in the possibleMinerals field enter
// {mob/monster/[monsterName],...} to
// set this field up.
mob/monster/possibleMonsters[]

Spawn()
var/index = rand(1, possibleMonsters.len)
var/mob/monster/newMonster = possibleMonsters[index]
//var/mob/monster/newMonster = newMonsterPrototype.New(loc)
world.log << "Spawning [newMonster.Name] into world..."
world.log << "HP: [newMonster.Health.HP]/[newMonster.Health.MaxHP]"
newMonster.loc = loc
newMonster.spawner = src
..()


Problem description:
I'm working on creating a generic monster spawner. The idea is to supply a list of monster types (e.g. mob/monster/bug, mob/monster/goblin, mob/monster/mutant_taco), and have the spawner periodically spawn off unique instances of these monster mobs, assuming the previous one has been killed off.

One problem - I can spin off a monster once using a concrete type...

// This works, but limits me to just ever creating a single type of mob.
var/mob/monster/bugMonster/newMonster = new()


...But, it doesn't allow for me to specify which mobs the spawner can generate; I'm locked into only one hardcoded type of mob.

Another thing I tried, is to simply use the type passed into the possibleMonsters list declared on the spawner.

var/index = rand(1, possibleMonsters.len)
var/mob/monster/newMonster = possibleMonsters[index]
newMonster.loc = loc


The code I've got above does a slightly better job of spawning cosmetically distinct mobs, but doesn't copy their more complex stats, like Health Systems or Calculated Stats.

var/index = rand(1, possibleMonsters.len)
var/mob/monster/newMonsterPrototype = possibleMonsters[index]
// Copy is a method that returns a deep copy of the monster, supposedly.
var/mob/monster/newMonster = newMonsterPrototype.Copy()
newMonster.loc = loc


I've found this is functionally no different than what I had before.

Question - DreamMaker is a new language for me. I'm trying to apply patterns I'm familiar with, but it's certain that A) I don't know a great many things about DM, and B) my spawner setup as it currently is doesn't work in a way that DM supports. So, in what way can I write a more generic spawner?
Best response
In DM, type paths are literal values. You've managed to avoid using them so far by taking advantage of the fact that "new" in a var declaration assumes the type of the var being declared.
// Special shorthand syntax for "new" in a var declaration:
var/some_type/variable = new // empty args
var/some_type/variable = new(args)

// Actual syntax for "new":
new some_type // empty args
new some_type(args)

// However, you can actually extract the type into a variable:
var monster_type = /mob/monster/whatever
var monster_type = pick(possibleMonsters)
// where possibleMonsters = list(/mob/monster/whatever, ...)

// and then use that variable in place of a type literal:
var mob/monster/new_monster = new monster_type
Kaiochao - This did get me moving in a more positive direction...but only using hard-coded values:

        Spawn()
var list/testList = list(/mob/monster/bug)
var monsterType = pick(testList)
var mob/monster/newMonster = new monsterType(loc)
newMonster.spawner = src
..()


When I attempt to just let the variables be defined in the map editor, as in the Spawn() implementation below:

        Spawn()
var monsterType = pick(possibleMonsters)
var mob/monster/newMonster = new monsterType(loc)
newMonster.spawner = src
world.log << "Spawning [newMonster.Name] into world..."
world.log << "HP: [newMonster.Health.HP]/[newMonster.Health.MaxHP]"
..()


with the source list in the editor being "{/mob/monster/bug}", I get the following error:

"runtime error: Cannot create objects of type /mob/monster/bug.
proc name: Spawn (/obj/spawnPoint/monsterSpawner/Spawn)
usr: null
src: the monsterSpawner (/obj/spawnPoint/monsterSpawner)
src.loc: the grass (9,5,1) (/turf/terrain/grass)
call stack:
the monsterSpawner (/obj/spawnPoint/monsterSpawner): Spawn()
the monsterSpawner (/obj/spawnPoint/monsterSpawner): PeriodicSpawns()
the monsterSpawner (/obj/spawnPoint/monsterSpawner): New(the grass (9,5,1) (/turf/terrain/grass))"

I don't know what to make of this, since I'm calling the new proc that accepts a location as an argument, which I assume calls the parameterless new proc that my mob/monsters are overloading. Of course, that's two assumptions in one sentence, and even one assumption can be false and causing my code to not work.

I went further and added a world.log output to make sure that /mob/monster/bug is being selected...

Spawn()
var monsterType = pick(possibleMonsters)
world.log << "Spawning [monsterType]"
// Rest of the code beyond this point is the same as above...


When I ran it, the debug message read:

"Spawning the bug"

I'm clearly missing something. The pure-code setup you showed me works. What would it take to make the map editor's ability to set values on individual instances work?

EDIT: I also should add, I don't seem to have this problem with single paths...

obj
spawnPoint/treasureSpawner
var
itemSpawnType // Editor: /obj/chests/bluechest

Spawn()
var treasureType = itemSpawnType
var obj/treasure/newItem = new treasureType(loc)
newItem.spawner = src
..()


Since the Map Editor won't let me enter anything but { path } for a list of paths, that leads me to believe that the map editor is working OK, and the code you provided me is working OK, but there's a breakdown in how DM is interpreting the list of paths being retrieved from the editor. Have I discovered a DM bug?
In response to Asvarduil
It sounds like the map editor is treating your list as a list of instances by type, automatically instantiating each type (with empty constructor arguments), instead of leaving it as a list of types. I'm not too familiar with the map editor's instance editor, but it hasn't seen much use or updates; I wouldn't be surprised if it was a bug or just a poorly-documented feature.
In response to Kaiochao
Kaiochao wrote:
It sounds like the map editor is treating your list as a list of instances by type, automatically instantiating each type (with empty constructor arguments), instead of leaving it as a list of types. I'm not too familiar with the map editor's instance editor, but it hasn't seen much use or updates; I wouldn't be surprised if it was a bug or just a poorly-documented feature.


You can use lists of types just fine, but you can also use newlist() in the instance editor.

@Kaiochao - Interesting. I think I'll make a topic reporting this quirk of the map editor. Worst case scenario, is it's functioning as designed.

EDIT - Bug report is created, here.

@Nadrew - I'll do some research into newlist() in the editor.
Are you setting the variable to

list(/obj/one,/obj/two,/obj/three)


According to your bug report you're setting it to

{/obj/one,/obj/two,/obj/three}


Which isn't valid syntax.
The map editor will not let me save the instance with 'list(/mob/monster/bug)'; the only syntax it accepts is
'{/mob/monster/bug}'.

I also tried round braces "(...)" and square braces "[...]".

I'll try your newlist(...) setup and see what that does.

EDIT: newlist(...) is not allowed in the map editor's instance editor either. The specific value I used was "newlist(/mob/monster/bug)". The specific error I got was, "variable possibleMonsters: newlist(/mob/monster/bug) is an invalid tag or path."
The only time using list() and newlist() in the instance editor will throw an error is if the values in the list aren't valid, such as type-paths that don't exist.

I just double-checked and it's definitely possible to set a variable to

"list(/obj/one,/obj/two)"

as long as /obj/one and /obj/two exist in the code, if not, it'll throw an invalid expression error.
Nadrew, /mob/monster/bug exists in my code. My code compiles. Would you like me to send you the project files, on the off-chance that I'm doing something really, really dumb somewhere?
The variable should look like



in the instance editor.
I did just learn that your initial usage of {} is actually valid, it's what happens when you properly use newlist().

So your initial suspicion that it was creating the instances was correct, because you were telling it to.
So, if {...} = newlist(...), why won't list(...) work properly?

Also, here's what's going on. This list only has one member, which is /mob/monster/bug. I'm wondering if list(...) doesn't work properly with only one member?



EDIT: Changing the value to 'list(/mob/monster/bug, /mob/monster/bug)' doesn't save either.

And, I wish you hadn't closed that as 'Not a bug', because something isn't right here.
It's because you have the variable defined improperly, possibleMonsters should be defined

var/list/possibleMonsters = list()


And not

var/mob/monster/possibleMonsters[]


It seems the instance editor does some basic error checking for things like that. At least that's the only other thing I can tell you.

The bug you reported is definitely not a bug, the usage of {} working like newlist() is intended behavior.
Nadrew, according to the documentation, the following two declarations are equivalent:

var
list/possibleMonsters
possibleMonsters[]


There's also a list constructor and defining a list with a default size using square brackets.

Is the documentation wrong?

Also, just in case the point has been lost along the way, the editor will not let me use "list(...)" as a value, it gives me the error message, 'list(...) is an invalid tag or path', even though I'm explicitly trying to define a list in a list-type value.

I don't think you're understanding what's going on here. But, it's OK - You can download my testbed project here, and see for yourself that, in the map editor, using BYOND 510, that you cannot set a list(...) in the map editor; the only thing allowed is {...}. I specifically need list(...) in order for my generic spawner to work, without having to code map-specific spawners.

If I can't do this, it's OK I guess, but not being able to use the Map Editor's ability to source data to DM objects is a problem, since it's one of the things your own documentation says you can do.
In response to Asvarduil
Definitely something strange going on here when editing the existing Bug Spawner.


If I create another new instance and give it list(/mob/monster/bug), it works. I placed it on the map and saw it spawn a bug.


I wanna say there's a bug involved here.
Worked fine for me right out of the gate.
So, Nadrew, what's different between your BYOND installation, and mine and Kaiochao's? I'm running the latest stable 510 installation. I can't speak for Kaiochao's.

That being said, I also think that the bug needs to be reopened. Something about your installation is non-standard, which means that your assertion that the bug isn't a bug, based on you being able to run it, is invalid. What it might mean, is that your non-standard installation might well have a fix for the issue in 'our' (again, assumption there) bog-standard version.
In response to Asvarduil
Oh, I'm using 511.1379, the latest public beta.
Again, the bug you filed has nothing to do with this issue. This is an entirely separate issue. My installation is also the latest public beta. I've had zero issues with this (in fact a few of my projects rely on exactly what you're doing).

The issue seems to stem from the initial edit of an instance, it has nothing to with with your usage of {}, which was working as intended. You'll need to file a separate report specific to this issue.