util = require "util"
broadphase = require "broadphase"
main = require "main"
constrain = require "constrain"
color = require "color"
room = require "room"
import LobbyRoom from room
ability_reg = require "ability_reg"
mod = ...
mod.characters = {}
mod.classes = {}
require "char_tank"
require "char_mage"
require "char_theif"
require "char_fool"
require "char_jugg"
mod.class_order = {
"Tumbler",
"Fire Breather",
"Juggler",
"Troubador",
"Juggernaut"
}
mod.class_order_rev = {}
for k,v in ipairs(mod.class_order)
mod.class_order_rev[v] = k
print("After requireing characters, mod.classes was",mod.classes)
mod.enemies = {}
--require "e_rat"
require "e_bethany"
require "e_ruminating_randy"
require "e_mopey_marvin"
require "e_sullen_salley"
require "e_child"
print("After requireing rat, mod.enemies was",mod.enemies)
--print("test")
--[[Stores a single input, and the time it was inputed]]
class KeyInput
new:(key = "") =>
@time = 0
@key = key
@value = false
--print "test"
class AnimFrame
new:(anim,interupt,mode) =>
@anim = anim
@interupt = interupt
@mode = mode
class ActionInput
new:(action = "") =>
@time = 0
@action = action
mod.sprite_direction_co = () =>
return () ->
while true
@stop_anim(@sprites.right)
@stop_anim(@sprites.left)
@stop_anim(@sprites.stop_right)
@stop_anim(@sprites.stop_left)
@stop_anim(@sprites.falling_left)
@stop_anim(@sprites.falling_right)
for input in *@inputs
if @velocity.x > 0.01
@set_anim(@sprites.right,1,"loop")
break
elseif @velocity.x < -0.01
@set_anim(@sprites.left,1,"loop")
break
coroutine.yield!
mod.can_die_co = () =>
return () ->
while not @dead
coroutine.yield!
for k,v in pairs @sprites
if k ~= "idle"
@stop_anim(v)
if @sprites.die
@set_anim(@sprites.die,5)
coroutine.yield(true)
mod.make_animate = (c) ->
assertf(c.sprites ~= nil, "Tried to animate something that had no .sprites: %q", c.__class.__name)
assertf(c.sprites.idle ~= nil and #c.sprites.idle > 0, "Tried to animate something without a .idle animation: %q", c.__class.__name)
c.anim_stack = {AnimFrame(c.sprites.idle,0,"loop")}
c.anim = c.sprites.idle
c.keyframe = 0
c.animrate = c.animrate or 1
c.anim_interupt = 0
c.set_anim = (self,tbl,interupt,mode) ->
if type(tbl) ~= "table"
error("Tried to set anim to something that was not a table!",2)
if #tbl == 0
error("Tried to set anim to an empty table",2)
if interupt > @anim_interupt
table.insert(@anim_stack,AnimFrame(tbl,interupt,mode))
@anim = @anim_stack[#@anim_stack].anim
@anim_interupt = interupt
c.stop_anim = (self,tbl,err_if_unable=false) ->
anim_found = false
for k,v in pairs @anim_stack
if v.anim == tbl
--print("Found anim to remove")
anim_found = true
table.remove(@anim_stack,k)
break
if err_if_unable
assertf(anim_found, "Could not find animation to remove")
@anim = @anim_stack[#@anim_stack].anim
@anim_interupt = @anim_stack[#@anim_stack].interupt
c.node\action(coroutine.create(() ->
while not c.dead
c.keyframe = math.floor(am.current_time()*c.animrate) % #c.anim
spritename = c.anim[c.keyframe + 1]
assert(spritename, "Failed to find an appropriate image to draw.")
--print("Setting:",spritename)
c.node("sprite").source = spritename
coroutine.yield!
--we are dead
keyframe_0 = am.current_time()
while c.keyframe < #c.anim - 1
c.keyframe = math.floor((am.current_time! - keyframe_0)*c.animrate)
assert(c.anim[c.keyframe + 1], "Failed to find an appropriate image to draw.")
c.node("sprite").source = c.anim[c.keyframe + 1]
coroutine.yield!
c\remove()
coroutine.yield(true)
))
mod.inherited = {}
hp_bar_width = 20
--[[The character, extended to make both players and ai]]
class Character
@classes = {}
@players = {} -- players singleton, [peerid] = Character
new:(uname, data, charclass) =>
assert(charclass, "Charclass may not be nil")
@uname = uname or false
@data = data or {
status: "active",
location: -1,
position: charclass.default_position
}
@class = charclass
assert(@class.name, "Character classes must have a name")
@calc_class_values!
@node = am.group!
--print("Creating character!",uname,data,charclass)
@node\append(am.translate(0,0)\tag("char_translate")^ am.sprite(@class.sprite,color.white,"center","bottom")\tag("char_sprite"))
print("A character has been created!")
print(debug.traceback!)
--Draw healthbar
healthbar_trans = am.translate(0,60)
healthbar_left = am.sprite("data/bar_left.png", color.white,"left","center")\tag("hp_l")
healthbar_right = am.sprite("data/bar_right.png", color.white, "right","center")\tag("hp_r")
healthbar_bar = am.sprite("data/bar_mid.png", color.white,"left","center")\tag("hp_b")
healthbar_fill = am.sprite("data/bar_fill.png", color.white,"left","center")\tag("hp_f")
healthbar_scale = am.scale(hp_bar_width,1)
healthbar_fill_scale = am.scale(hp_bar_width + 1,1)\tag("hp_fill_scale")
healthbar_trans\append(am.translate(-hp_bar_width,0)^ healthbar_left)
healthbar_trans\append(am.translate(hp_bar_width,0)^ healthbar_right)
healthbar_trans\append(am.translate(-hp_bar_width/2,0)^ healthbar_scale^ healthbar_bar)
healthbar_trans\append(am.translate((-hp_bar_width/2) - 1,0)^ healthbar_fill_scale^ healthbar_fill)
@.node("char_sprite")\append(healthbar_trans)
--End draw healthbar
assert(@.__class.draw,"Characters must have a draw() method")
table.insert(mod.characters,@)
constrain(@,"set anim", (self,value) ->
assertf(type(value) == "table", "Tried to set anim on %q to something other than a table (%q)",@, type(value))
assert(#value > 0, "Tried to set animation for char to something with 0 frames!")
)
assert(@.__class != Character,"Character class must be subclassed")
--main.root("world_characters")\append(@node)
__tostring: () =>
return string.format(
"<%s, %s> at (%d)",
@.__class.__name,
(@class and @class.name or "no class"),
( @data and @data.position or -1)
)
@__inherited: (c) =>
assert(c, "Inheritance must exist")
assert(c.__name, "Inherited class must have a .__name")
@@.classes[c.__name] = c
mod.inherited[c.__name] = c
calc_class_values: () =>
for k,v in pairs(@class)
if k\match("^default")
field = k\match("^default_(.*)")
if type(v) == "function"
@data[field] = v!
else
@data[field] = v
set_field: (name, value) =>
assert(@data[name], "Field must exist to be set")
@data[name] = value
print("my data table is:",@data)
if name == "hp"
perc = (@data.hp / @data.maxhp) * 20
@.node("hp_fill_scale").x = perc
serialize: () =>
print("Serializing char:",@)
print("Name:", @@__name)
print("uname:",@uname)
print("data:",@data)
data_abilities = {}
for i,ability in pairs(@data.abilities)
data_abilities[i] = ability.__name
data_copy = table.shallow_copy(@data)
data_copy.abilities = data_abilities
print("class.name:",@class.name)
ret = am.to_json({name:@@__name, uname:@uname, data:data_copy, class:@class.name})
print("Ret is:",ret)
ret
deserialize: (data) ->
print("Deserializing character")
tbl = am.parse_json(data)
if mod.classes[tbl.class]
data_abilities = {}
for i, ability_name in pairs(tbl.data.abilities)
data_abilities[i] = ability_reg[ability_name]
tbl.data.abilities = data_abilities
return mod.inherited[tbl.name](tbl.uname, tbl.data, mod.classes[tbl.class])
elseif mod.enemies[tbl.class]
e = mod.Enemy(tbl.data, mod.enemies[tbl.class])
e.uname = tbl.uname
return e
draw:(screenpos) =>
print("draw")
remove: () =>
@node\remove_all!
die: () =>
@dead = true
print(@,"is dieing, node is",@.node,"and color is",color)
@.node("char_sprite")\append(am.line(vec2(-10,-10),vec2(10,10),5,color.bright))
@.node("char_sprite")\append(am.line(vec2(10,-10),vec2(-10,10),5,color.bright))
enter_room: (room) =>
@room = room
print("Character",@,"entered room",room)
@.node("char_translate").y = room.floor_y
if room.__class == LobbyRoom
print("Class was lobbyRoom")
rng_x = math.random(-(main.width/2), (main.width/2))
print("RNG x:",rng_x)
@.node("char_translate").x = rng_x
else
print("Class was not LobbyRoom")
x_pos = @room\player_location_of(@data.position)
print("Got x pos for",@,x_pos)
@.node("char_translate").x = x_pos + math.random(-15,15) --some random variance to stop stacking
set_class: (newclass) =>
assert(newclass, "Cannot set a class to nil")
@class = newclass
@calc_class_values!
@.node("char_sprite").source = newclass.sprite
set_position: (pos) =>
@data.position = pos
set_location: (loc) =>
@data.location = loc
mod.enemy_counter = 0
class Enemy extends Character
new: (data, eclass) =>
super(eclass.name .. ":" .. tostring(mod.enemy_counter), data, eclass)
mod.enemy_counter += 1
nwuname: (uname, data, eclass) ->
@(data, eclass)
mod.enemy_counter += 1
__tostring: () =>
return string.format("<%s> at (%d)",@uname, @data.position or 0)
enter_room: (room) => --overload character's to get enemy locations
print("Character",@,"entered room",room)
@room = room
@.node("char_translate").y = @room.floor_y
@.node("char_translate").x = @room\enemy_location_of(@data.position)
select_action: () =>
return @class.select_action(@)
mod["Character"] = Character
mod["KeyInput"] = KeyInput
mod["Enemy"] = Enemy
--Things that extend the character class
mod