#define EVENT_ATTACHMENT_POINT_NONE 0
#define EVENT_ATTACHMENT_POINT_MELEE 1
#define EVENT_ATTACHMENT_POINT_ATTACKED 2
/proc/loadCustomCritterFromFile(var/target_name)
var/target
if (!isfile(target_name))
target = file(target_name)
else
target = target_name
if (!target)
return null
var/fname = "adventure/CRIT_LOAD_CUSTOMFILE"
if (fexists(fname))
fdel(fname)
var/savefile/F = new /savefile(fname)
F.dir.len = 0
F.eof = -1
boutput(F, null)
F.ImportText("/", file2text(target))
if (!F)
boutput(usr, "Import failed.")
else
var/datum/sandbox/S = new()
var/obj/critter/custom/template = new()
template.deserialize(F, "critter", S)
template.is_template = 1
if (fexists(fname))
fdel(fname)
return template
/obj/critter/custom
name = "custom critter"
desc = "custom critter"
icon_state = "floateye"
var/suspend_ai = 0
var/melee = 1
var/attack_power = 15
var/attack_type = "brute"
var/stun_prob = 20
var/anger_text = "%src% charges at %target%!"
var/chase_text = "%src% slams into %target%!"
var/stun_text = "%src% knocks down %target%!"
var/stun_fail_text = "%src% fails to knock down %target%!"
var/attack_text = "%src% bashes %target%!"
var/gib_corpses = 0
var/sound/anger_sound
var/sound/chase_sound
var/sound/stun_sound
var/sound/stun_fail_sound
var/sound/attack_sound
var/sound/death_sound
var/sound/ambient_sound
brutevuln = 1
firevuln = 1
var/explosivevuln = 1
var/datum/critterDeath/on_death = null
var/list/abil = list()
var/datum/critterLootTable/loot_table = null
var/dead_change_icon
var/icon/dead_icon
var/dead_icon_state
New()
..()
loot_table = new()
var/list/events = list()
ex_act(severity)
if (src.sleeping)
sleeping = 0
on_wake()
switch(severity)
if(1.0)
src.health -= 200 * explosivevuln
if (src.health <= 0)
src.CritterDeath()
return
if(2.0)
src.health -= 75 * explosivevuln
if (src.health <= 0)
src.CritterDeath()
return
else
src.health -= 25 * explosivevuln
if (src.health <= 0)
src.CritterDeath()
return
process()
if (suspend_ai)
return
..()
if (is_template || !alive)
return
if (prob(25))
play_optional_sound(ambient_sound)
for (var/datum/critterAbility/A in abil)
A.tick()
if (on_death)
on_death.tick()
attackby(obj/item/W as obj, mob/living/user as mob)
..()
if (W.force || istype(W, /obj/item/artifact/melee_weapon))
for (var/datum/critterEvent/E in events)
if (E.attachment_point == EVENT_ATTACHMENT_POINT_ATTACKED)
E.trigger()
seek_target()
src.anchored = initial(src.anchored)
if (src.target)
src.task = "chasing"
return
for (var/mob/living/C in hearers(src.seekrange,src))
if ((C.name == src.oldtarget_name) && (world.time < src.last_found + 100)) continue
if (iscarbon(C) && !src.atkcarbon) continue
if (istype(C, /mob/living/silicon/) && !src.atksilicon) continue
if (C.health < 0) continue
if (C in src.friends) continue
if (ishuman(C))
if (C:bioHolder && C:bioHolder.HasEffect("revenant"))
continue
if (C.name == src.attacker) src.attack = 1
if (iscarbon(C) && src.atkcarbon) src.attack = 1
if (istype(C, /mob/living/silicon/) && src.atksilicon) src.attack = 1
if (src.attack)
src.target = C
src.oldtarget_name = C.name
tokenized_message(anger_text, target)
play_optional_sound(anger_sound)
src.task = "chasing"
on_grump()
break
else
continue
ChaseAttack(mob/M)
if (!melee)
return
tokenized_message(chase_text, target)
play_optional_sound(chase_sound)
if (stun_prob)
spawn(10)
if (get_dist(src, target) <= 1)
if (prob(stun_prob))
M.stunned += 3
tokenized_message(stun_text, target)
play_optional_sound(stun_sound)
else
tokenized_message(stun_fail_text, target)
play_optional_sound(stun_fail_sound)
proc/dodamage(var/mob/M, var/atype, var/damage)
switch (atype)
if ("brute")
random_brute_damage(src.target, damage)
if ("burn")
random_burn_damage(src.target, damage)
if ("toxin")
M.take_toxin_damage(damage)
M.updatehealth()
if ("suffocation")
M.take_oxygen_deprivation(damage)
M.updatehealth()
if ("radiation")
M.radiation += damage
if (damage != 0)
M.take_toxin_damage(damage / 2)
M.updatehealth()
CritterAttack(mob/N)
if (!melee)
return
src.attacking = 1
tokenized_message(attack_text, target)
play_optional_sound(attack_sound)
var/damage = max(rand(attack_power), rand(attack_power))
var/mob/M = target
dodamage(M, attack_type, damage)
for (var/datum/critterEvent/E in events)
if (E.attachment_point == EVENT_ATTACHMENT_POINT_MELEE)
E.trigger()
spawn(25)
src.attacking = 0
CritterDeath()
if (!src.alive)
return
//src.icon_state += "-dead"
src.alive = 0
src.anchored = 0
src.density = 0
loot_table.drop()
if (dead_change_icon)
icon = dead_icon
icon_state = dead_icon_state
tokenized_message(death_text, null)
play_optional_sound(death_sound)
if (on_death)
on_death.doOnDeath()
walk_to(src,0)
clone()
var/obj/critter/custom/C = ..()
C.melee = melee
C.attack_power = attack_power
C.attack_type = attack_type
C.stun_prob = stun_prob
C.anger_text = anger_text
C.chase_text = chase_text
C.stun_text = stun_text
C.stun_fail_text = stun_fail_text
C.attack_text = attack_text
C.gib_corpses = gib_corpses
C.death_text = death_text
C.explosivevuln = explosivevuln
C.dead_icon = dead_icon
C.dead_icon_state = dead_icon_state
C.dead_change_icon = dead_change_icon
C.anger_sound = anger_sound
C.chase_sound = chase_sound
C.stun_sound = stun_sound
C.stun_fail_sound = stun_fail_sound
C.attack_sound = attack_sound
C.death_sound = death_sound
C.ambient_sound = ambient_sound
C.loot_table = loot_table.clone()
C.loot_table.C = C
if (on_death)
C.on_death = on_death.clone()
C.on_death.C = C
C.abil.len = abil.len
for (var/i = 1, i <= abil.len, i++)
var/datum/critterAbility/A = abil[i]
if (istype(A))
var/datum/critterAbility/B = A.clone()
C.abil[i] = B
B.attach(C)
B.C = C
return C
serialize(var/savefile/F, var/path, var/datum/sandbox/sandbox)
..()
F["[path].melee"] << melee
F["[path].attack_power"] << attack_power
F["[path].attack_type"] << attack_type
F["[path].stun_prob"] << stun_prob
F["[path].anger_text"] << anger_text
F["[path].chase_text"] << chase_text
F["[path].stun_text"] << stun_text
F["[path].stun_fail_text"] << stun_fail_text
F["[path].attack_text"] << attack_text
F["[path].gib_corpses"] << gib_corpses
F["[path].death_text"] << death_text
F["[path].anger_sound"] << anger_sound
F["[path].chase_sound"] << chase_sound
F["[path].stun_sound"] << stun_sound
F["[path].stun_fail_sound"] << stun_fail_sound
F["[path].attack_sound"] << attack_sound
F["[path].death_sound"] << death_sound
F["[path].ambient_sound"] << ambient_sound
F["[path].explosivevuln"] << explosivevuln
F["[path].dead_change_icon"] << dead_change_icon
icon_serializer(F, "[path].dead_icon", sandbox, dead_icon, dead_icon_state)
loot_table.serialize(F, "[path].loot_table", sandbox)
F["[path].abil.LEN"] << abil.len
if (on_death)
F["[path].on_death.type"] << on_death.type
on_death.serialize(F, "[path].on_death", sandbox)
for (var/i = 1, i <= abil.len, i++)
var/datum/critterAbility/A = abil[i]
if (istype(A))
F["[path].abil.[i].type"] << A.type
A.serialize(F, "[path].abil.[i]", sandbox)
deserialize(var/savefile/F, var/path, var/datum/sandbox/sandbox)
..()
F["[path].melee"] >> melee
F["[path].attack_power"] >> attack_power
F["[path].attack_type"] >> attack_type
F["[path].stun_prob"] >> stun_prob
F["[path].anger_text"] >> anger_text
F["[path].chase_text"] >> chase_text
F["[path].stun_text"] >> stun_text
F["[path].stun_fail_text"] >> stun_fail_text
F["[path].attack_text"] >> attack_text
F["[path].gib_corpses"] >> gib_corpses
F["[path].death_text"] >> death_text
F["[path].explosivevuln"] >> explosivevuln
F["[path].anger_sound"] >> anger_sound
F["[path].chase_sound"] >> chase_sound
F["[path].stun_sound"] >> stun_sound
F["[path].stun_fail_sound"] >> stun_fail_sound
F["[path].attack_sound"] >> attack_sound
F["[path].death_sound"] >> death_sound
F["[path].ambient_sound"] >> ambient_sound
F["[path].dead_change_icon"] >> dead_change_icon
var/datum/iconDeserializerData/IDS = icon_deserializer(F, "[path].dead_icon", sandbox, dead_icon, dead_icon_state)
dead_icon = IDS.icon
dead_icon_state = IDS.icon_state
loot_table = new()
loot_table.deserialize(F, "[path].loot_table", sandbox)
loot_table.C = src
var/odt
F["[path].on_death.type"] >> odt
if (odt)
on_death = new odt()
on_death.deserialize(F, "[path].on_death", sandbox)
on_death.C = src
var/abs
F["[path].abil.LEN"] >> abs
abil.len = abs
for (var/i = 1, i <= abil.len, i++)
var/T
F["[path].abil.[i].type"] >> T
if (T)
var/datum/critterAbility/A = new T()
if (istype(A))
A.deserialize(F, "[path].abil.[i]", sandbox)
A.attach(src)
abil[i] = A
else
logTheThing("debug", null, null, "Marquesas/CritterCreator: Cannot deserialize type [T].")
proc/play_optional_sound(var/sound/sound)
if (sound)
playsound(src, sound, 50, 1)
proc/addUntiedEvent(var/datum/critterEvent/E)
events += E
E.attachment_point = EVENT_ATTACHMENT_POINT_NONE
proc/addAttackEvent(var/datum/critterEvent/E)
events += E
E.attachment_point = EVENT_ATTACHMENT_POINT_MELEE
/datum/critterCreatorHolder
var/list/critterCreators = list()
var/list/activeCritterTypes = list()
proc/blank(var/mob/M)
if (!M.client)
boutput(M, "Hello.")
return 0
// look I think it's okay if you maybe let non-admins access this sometimes
/*if (!M.client.holder)
boutput(M, "What are you doing here?")
return 0
if (M.client.holder.level < LEVEL_PA)
boutput(M, "You must be at least PA to use this.")
return 0*/
var/key = M.ckey
if (!(key in critterCreators))
critterCreators += key
critterCreators[key] = new /datum/critterCreator
proc/getCreator(var/mob/M)
var/key = M.ckey
if (!(key in critterCreators))
if (!blank(M))
return null
return critterCreators[key]
var/global/datum/critterCreatorHolder/critter_creator_controller = new()
/datum/critterLoot
var/datum/critterLootTable/lootTable
var/dropped = null
var/amount = 1
var/chance = 100
proc/configuration(var/datum/critterCreator/configurer)
. = configurer.clickable_link("lootamount", amount, "0", "\ref[src]")
. += " "
. += configurer.clickable_link("lootdropped", configurer.stripPath(dropped), "(null)", "\ref[src]")
. += " ("
. += configurer.clickable_link("lootchance", "[chance]%", "0%", "\ref[src]")
. += ") "
. += configurer.clickable_link("lootremove", "(remove)", "(remove)", "\ref[src]")
proc/change_configuration(var/datum/critterCreator/configurer, var/which)
switch (which)
if ("amount")
amount = configurer.getNum("dropped amount", amount)
if ("dropped")
var/filter = configurer.getText("enter part of the pathname of the dropped object", "")
var/typename = get_one_match(filter, /obj/item)
if (typename)
dropped = typename
if ("chance")
chance = configurer.getNum("drop chance", chance)
if ("remove")
lootTable.loot -= src
qdel(src)
proc/clone()
var/datum/critterLoot/L = new type()
L.dropped = dropped
L.amount = amount
L.chance = chance
return L
proc/serialize(var/savefile/F, var/path, var/datum/sandbox/sandbox)
F["[path].dropped"] << dropped
F["[path].amount"] << amount
F["[path].chance"] << chance
proc/deserialize(var/savefile/F, var/path, var/datum/sandbox/sandbox)
F["[path].dropped"] >> dropped
F["[path].amount"] >> amount
F["[path].chance"] >> chance
proc/drop(var/max = -1)
if (!dropped)
return 0
if (amount < 1)
return 0
if (prob(chance))
for (var/i = 1, i <= amount && (i <= max || max == -1), i++)
new dropped(get_turf(lootTable.C))
return amount
return 0
/datum/critterLootTable
var/maxDropped = 2
var/list/loot = list()
var/obj/critter/custom/C
proc/configuration(var/datum/critterCreator/configurer)
. = "Loot table dropping up to "
. += configurer.clickable_link("loottable", "[maxDropped] [maxDropped == 1 ? "item" : "items"]", "0 items", "maxDropped")
. += ":
| Critter creation kit | |
| "
output += attribute_clicker("Name", "name", template.name)
output += attribute_clicker("Description", "desc", template.desc)
output += attribute_clicker("Health", "health", template.health)
output += " Default AI behaviour " output += attribute_clicker("Aggressive", "aggressive", template.aggressive ? "yes" : "no") output += attribute_clicker("Defensive", "defensive", template.defensive ? "yes" : "no") output += attribute_clicker("Attacks carbon", "atkcarbon", template.atkcarbon ? "yes" : "no") output += attribute_clicker("Attacks silicon", "atksilicon", template.atksilicon ? "yes" : "no") output += attribute_clicker("Mobile", "mobile", template.mobile ? "yes" : "no") output += attribute_clicker("Wanderer", "wanderer", template.wanderer ? "yes" : "no") output += " Default attack " output += attribute_clicker("Use melee", "melee", template.melee ? "yes" : "no") output += attribute_clicker("Attack power", "power", template.attack_power) output += attribute_clicker("Attack type", "atype", template.attack_type) output += attribute_clicker("Stun chance", "stunp", "[template.stun_prob]%") output += " Vulnerabilities " output += attribute_clicker("Brute", "brutevuln", "[template.brutevuln * 100]%") output += attribute_clicker("Burn", "firevuln", "[template.firevuln * 100]%") output += attribute_clicker("Explosive", "explosivevuln", "[template.explosivevuln * 100]%") output += " Flavor " output += sound_link("Ambient", template.ambient_sound, "sounds", "ambient_sound") output += attribute_clicker("Charge text", "anger", template.anger_text) output += sound_link("Charge", template.anger_sound, "sounds", "anger_sound") output += attribute_clicker("Chase text", "chase", template.chase_text) output += sound_link("Chase", template.chase_sound, "sounds", "chase_sound") output += attribute_clicker("Stun text", "stun", template.stun_text) output += sound_link("Stun", template.stun_sound, "sounds", "stun_sound") output += attribute_clicker("Stun fail text", "stunf", template.stun_fail_text) output += sound_link("Stun fail", template.stun_fail_sound, "sounds", "stun_fail_sound") output += attribute_clicker("Attack text", "attack", template.attack_text) output += sound_link("Attack", template.attack_sound, "sounds", "attack_sound") output += attribute_clicker("Death text", "death", template.death_text) output += sound_link("Death", template.death_sound, "sounds", "death_sound") output += " Appearance " output += "Color (click box to change): " output += "" output += " " output += "Icon: " var/icon/browsed_icon browsed_icon = icon(template.icon, template.icon_state, 2) if (template.color) browsed_icon.Blend(template.color, ICON_MULTIPLY) M << browse_rsc(browsed_icon, "preview_S.png") browsed_icon = icon(template.icon, template.icon_state, 1) if (template.color) browsed_icon.Blend(template.color, ICON_MULTIPLY) M << browse_rsc(browsed_icon, "preview_N.png") browsed_icon = icon(template.icon, template.icon_state, 4) if (template.color) browsed_icon.Blend(template.color, ICON_MULTIPLY) M << browse_rsc(browsed_icon, "preview_E.png") browsed_icon = icon(template.icon, template.icon_state, 8) if (template.color) browsed_icon.Blend(template.color, ICON_MULTIPLY) M << browse_rsc(browsed_icon, "preview_W.png") output += " " output += "Note: The North, East and West preview images do not show up correctly for unidirectional sprites. " output += "Set to preset " output += "Set icon file " output += "To add directions, upload your icon in a .dmi format, with a single icon state with 4 directions. " output += attribute_clicker("Icon state", "icon_state", template.icon_state) output += "Dead icon: " output += "[template.dead_change_icon? "Changes" : "Does not change"] icon on death. " if (template.dead_change_icon) browsed_icon = icon(template.dead_icon, template.dead_icon_state) if (template.color) browsed_icon.Blend(template.color, ICON_MULTIPLY) usr << browse_rsc(browsed_icon, "preview_Death.png") output += " " output += clickable_link("dead_icon", "Set icon file", "Set icon file") output += " Icon state: " output += clickable_link("dead_icon_state", template.dead_icon_state, "(null)") output += " | "
output += "Abilities: " for (var/i = 1, i <= template.abil.len, i++) output += switcher(i, abilid, "ability", "[i]") output += " " output += "Add new " if (abilid) var/datum/critterAbility/A = template.abil[abilid] if (A) output += A.configuration(src) else output += clickable_link("abilchange", A, "Select ability type") output += " " output += "Other AI behaviour " output += "On death, [clickable_link("ondeath", template.on_death, "do nothing")]. " if (template.on_death) output += template.on_death.configuration(src) output += " Loot table " output += template.loot_table.configuration(src) output += " |
| Actions: [clickable_link("spawn", "Spawn")] | [clickable_link("reset", "Reset")] " output += "This round: [clickable_link("roundsave", "Save")] | [clickable_link("roundload", "Load")] " output += "Into file: [clickable_link("filesave", "Save")] | [clickable_link("fileload", "Load")] | |