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