Jump to content

havenphillip

Members
  • Posts

    550
  • Joined

  • Last visited

Everything posted by havenphillip

  1. Yeah it's not even getting that far. I keep getting the same error.
  2. Oh yeah. That's weird. Because it's written correctly in the code. This is all the code I added. It's all in the crawler script: function Script:Start() ... self.onAttackedId = SubscribeEvent("onAttacked", self, self.onAttacked) --subscribed to event end function Script:CleanUp() -- (Rick) Unsubscribe(self.onAttackedId) --unsubscribed to event end function Script:Hurt(damage,distributorOfPain) if self.health>0 then player = self.target RaiseEvent("onAttacked", { hurtMonsterPosition = self.entity:GetPosition(true), player = distributorOfPain }) --- raise event ... -- if this is the function linked to the onAttacked event function Script:onAttacked(data) --raise event function -- I don't recall if this is the correct syntax for distance checks but it gets the idea across. you'll have to research distance checks. if data.hurtMonsterPosition:GetDistance(self.entity:GetPosition(),false) < 5 then -- if this monster is in a given range of the monster that was attacked then assign this monster the player target as well and it'll come running towards it! self.target = data.player end end In the original kills thing you you showed me you wrote this: function Script:Start() self.onDeadId = SubscribeEvent("onDead", self, self.enemyDied) ...I was trying to figure out where you got "self.onDeadId" I just tried to mimic that in the crawler script. I also tried setting "player = self.target" How would it know what "player" means in the Hurt()? I'm getting there slowly.
  3. I'm getting "attempt to index a nil value" on this line in the event system script: local scriptFunc = events[eventName].scriptFunction I had added these to the crawler script: function Script:Start() self.onAttackedId = SubscribeEvent("onAttacked", self, self.onAttacked) ... function Script:CleanUp() -- (Rick) Unsubscribe(self.onAttackedId) end Just tried to mimic what you did before.
  4. Ah ok. Hold on let me see if I can figure that out.
  5. That one doesn't seem to be doing anything. I have it set up in the crawler script like: function Script:Hurt(damage,DistributorofPain) RaiseEvent("onAttacked", { hurtMonsterPosition = self.entity:GetPosition(true), player = distributorOfPain }) -- the script doesn't reference "player" anywhere else. Is it that? -- if this is the function linked to the onAttacked event function Script:onAttacked(data) -- I don't recall if this is the correct syntax for distance checks but it gets the idea across. you'll have to research distance checks. if data.hurtMonsterPosition:GetDistance(self.entity:GetPosition(),true) < 500 then -- if this monster is in a given range of the monster that was attacked then assign this monster the player target as well and it'll come running towards it! self.target = data.player end end
  6. This is cool, man. It's totally working. In the crawler script I set it like this: RaiseEvent("onDead", { enemyName = self.name.." was killed by player" }) and in the player script under enemyDied(data) like you said: self.killMessage = data.enemyName What's another event that this would be good for? I want to try and see if I can follow the pattern and piece one together myself. I'm going to also see if I can get the names to list downward with a table iteration. That ought to keep me going for awhile.
  7. Ok cool! Because I tend to have a lot of questions. I totally want to learn this now that I'm seeing what it can do. I think I get this: " One event string name can have many different function callbacks linked to it... before we added coroutines to this all, those callback functions were just be called right there... Now inside raise event instead of looping through and calling the function, we loop through and get the function for the raised event string name and create a coroutine variable from it and store it in a separate table." So basically the event system grabs the information by the string name and puts it into a table so it can loop everything, runs it through that extra loop within itself, adding whatever you want to it, before passing it back into its original loop? So basically this just pulls out a piece of information and stores it, sends it around an outside loop, then puts it back? Like a track that you switch so the train (string name) always makes an extra stop? Here's the parts that I have. I'm not getting anything currently. I'm not sure what you mean by "render2D function"? I'm doing the drawtext in the player script under the PostRender function. In the player script: function Script:enemyDied(data) self.kills = self.kills + 1 WaitForSeconds(2.5); self.killMessage = "" ---do I write the context:DrawText(...) here? WaitForSeconds(1.0); while self.killMessageAlpha >= 0 do self.killMessageAlpha = self.killMessageAlpha - .01 WaitForSeconds(0.25) self.killMessage = "" self.killMessageAlpha = 1 end end function Script:CleanUp() Unsubscribe(self.onDeadId) Unsubscribe(self.killMessage) end --This function will be called when an entity is loaded in a map. Use this for intitial setup stuff. function Script:Start() self.onDeadId = SubscribeEvent("onDead", self, self.enemyDied) self.killMessage = "" self.killMessageAlpha = 1 ... ...and later in the PostRender just: --draw kills context:SetBlendMode(1) context:SetColor(1,1,1,1) context:DrawText("Kills: "..self.kills,30,30,200,200) --- Do I need to say "self.killMessage = " here first? Edit: My bad, this is just the kills. But is it good write the other text here? ... I changed the name of my "Coroutine" script to "Event System" and did like you said in the Main.lua just added "BackLoop()" between the Time and World update. That seems to be working. And my crawler script still looks the same. Do I need to add something there? or I guess this happens in the same "onDead" event. function Script:Hurt(damage,distributorOfPain) ... if self.health<=0 then self.entity:SetMass(0) self.entity:SetCollisionType(0) self.entity:SetPhysicsMode(Entity.RigidBodyPhysics) self:SetMode("dying") RaiseEvent("onDead", {}) end end end
  8. Dude this is like chaos to me. Probably because it's coming all at once. I'm having a hard time seeing the process from beginning to end in a coherent line. I want to grasp it because I can see that it gives you a lot of control over events and sequences of events and I assume it's a more "industry-standard" way to do things. I looked up "callback" and it kind of makes sense that its passing a function as a variable from one "system" to another, which then executes (or "calls back") that function within the limits that you put on it in the parent function. So when you "subscribe" to an event the coroutine basically just grabs it or gets involved in the process, tells it to do some things, and then "unsubscribes" from it. It's vaguely familiar how you're passing information around in parentheses and defining them somewhere else because I've seen some of that in the shaders. Maybe if you have the time (and/or patience) you could walk me through how I could use this to use context:DrawText() to put "enemy killed" on the screen instead of in the System:Print(). Would that be easy? That was something I was going to try to figure out after I got the kill-counter working and I was trying to think of how I could set it when the enemy is killed and then wait a few seconds and then delete it, and it seems like this may be the way to do that. Eventually I want to set it up to say something like "entity.name.." was killed by player" and then set that in a table that iterates each name so I might have several names on the screen at one time (basically like a list of recent kills). I can maybe figure out that last part on my own later. But like what's step one? I made another script called "Coroutine" (should I call it that?) for that part that I put in the Main.lua, and then in the main put " import("Scripts/Coroutine.lua") but I left this part because I wasn't sure how to import it into that specific place. -- loop over backwards so we can safely remove event function coroutines that are finished for i = #eventCoroutines, 1, -1 do if coroutine.status(eventCoroutines.co) == "dead" then table.remove(eventCoroutines, i) else -- go back into the event function passing the script as the first param so it ends up being 'self' inside the function and args as the second parameter coroutine.resume(eventCoroutines.co, eventCoroutines.script, eventCoroutines.args) end end This is the idea:
  9. I forgot I had pasted in those other two functions. I combined the two "enemyDied(data)" and it works now: function Script:enemyDied(data) self.kills = self.kills + 1 WaitForSeconds(2.5); System:Print("Enemy died") WaitForSeconds(1.0); System:Print("Wow this is cool!") end
  10. So that's like how in the video you put a function inside a function.
  11. Ah ok! That's working. Still not getting anything in the System:Print() though.
  12. Here's the relevant part of the player: function Script:enemyDied(data) WaitForSeconds(2.5); System:Print("Enemy died") WaitForSeconds(1.0); System:Print("Wow this is cool!") end --This function will be called when an entity is loaded in a map. Use this for intitial setup stuff. function Script:Start() self.onDeadId = SubscribeEvent("onDead", self, self.enemyDied) ... And the relevant part of the crawler: function Script:Hurt(damage,distributorOfPain) if self.health>0 then if self.target==nil then self.target=distributorOfPain self:SetMode("attack") end self.health = self.health - (damage + math.random(-4,4)) if self.health<=0 then self.entity:SetMass(0) self.entity:SetCollisionType(0) self.entity:SetPhysicsMode(Entity.RigidBodyPhysics) self:SetMode("dying") self:RaiseEvent("onDead", {}) end end end
  13. Ok. Here's my main: import("Scripts/Menu.lua") events = {} subId = 0 eventCoroutines = {} function SubscribeEvent(eventName, script, func) -- check to see if this event name exists already or not and if not create a new table for the event -- we do this because we can have many subscribers to one event if events[eventName] == nil then events[eventName] = {} end -- increase our eventId by 1 subId = subId + 1 -- add this script function to our list of subscribers for this event -- one event can have many subscribers that need to know about it for various reasons events[eventName][subId] = { scriptObject = script, scriptFunction = func } -- return this subId id so the subscriber can unsubscribe if they need to return subId end function Unsubscribe(eventName, subId) if events[EventName] == null then return end -- remove this subscription for this event events[EventName][subId] = nil end function RaiseEvent(eventName, data) -- if someone tried to raise an event that doesn't have an entry in our events table do nothing if events[eventName] == null then return end -- loop through all the subscriptions for this event (there may be many game entities who want to know about this event) for i = 1, #events[eventName] do -- get the script and function local scriptFunc = events[eventName].scriptFunction local script = events[eventName].scriptObject -- insert the functions into the eventCoroutines table. this will be iterated over in the main game loop below and resumed into table.insert(eventCoroutines, { co = coroutine.create(scriptFunc), args = data, script = script }) end end function WaitForSeconds(interval) local tm = Time:GetCurrent() while Time:GetCurrent() <= tm + (interval * 1000) do coroutine.yield() end end --Initialize Steamworks (optional) --Steamworks:Initialize() --Initialize analytics (optional). Create an account at www.gameamalytics.com to get your game keys --[[if DEBUG==false then Analytics:SetKeys("GAME_KEY_xxxxxxxxx", "SECRET_KEY_xxxxxxxxx") Analytics:Enable() end]] --Set the application title title="TEMP" --Create a window local windowstyle = 0 local winwidth local winheight local gfxmode = System:GetGraphicsMode(System:CountGraphicsModes()-1) if System:GetProperty("devmode")=="1" then gfxmode.x = math.min(1280,gfxmode.x) gfxmode.y = Math:Round(gfxmode.x * 9 / 16) windowstyle = Window.Titlebar else gfxmode.x = System:GetProperty("screenwidth",gfxmode.x) gfxmode.y = System:GetProperty("screenheight",gfxmode.y) windowstyle = Window.Fullscreen end window = Window:Create(title,0,0,gfxmode.x,gfxmode.y,windowstyle) if window == nil then gfxmode = System:GetGraphicsMode(System:CountGraphicsModes()-1) window = Window:Create(title,0,0,gfxmode.x,gfxmode.y,windowstyle) end --Create the graphics context context=Context:Create(window,0) if context==nil then return end --Create a world world=World:Create() local gamemenu = BuildMenu(context) --Load a map local mapfile = System:GetProperty("map","Maps/start.map") if mapfile~="" then if Map:Load(mapfile)==false then return end prevmapname = FileSystem:StripAll(changemapname) --Send analytics event Analytics:SendProgressEvent("Start",prevmapname) gamemenu.newbutton:Hide() gamemenu.resumebutton:Show() window:HideMouse() else gamemenu:Show() end while window:Closed()==false do --Show game menu when escape key is hit if gamemenu:Hidden() then if window:KeyHit(Key.Escape) then Time:Pause() gamemenu:Show() end end --Update events while EventQueue:Peek() do local event = EventQueue:Wait() event = gamemenu:ProcessEvent(event) end --Handle map change if changemapname~=nil then --Pause the clock Time:Pause() --Pause garbage collection System:GCSuspend() --Clear all entities world:Clear() --Send analytics event Analytics:SendProgressEvent("Complete",prevmapname) --Load the next map if Map:Load("Maps/"..changemapname..".map")==false then return end prevmapname = changemapname --Send analytics event Analytics:SendProgressEvent("Start",prevmapname) --Resume garbage collection System:GCResume() --Resume the clock Time:Resume() changemapname = nil end if gamemenu:Hidden() then --Update the app timing Time:Update() -- loop over backwards so we can safely remove event function coroutines that are finished for i = #eventCoroutines, 1, -1 do if coroutine.status(eventCoroutines.co) == "dead" then table.remove(eventCoroutines, i) else -- go back into the event function passing the script as the first param so it ends up being 'self' inside the function and args as the second parameter coroutine.resume(eventCoroutines.co, eventCoroutines.script, eventCoroutines.args) end end --Update the world world:Update() end --Render the world world:Render() --Render statistics context:SetBlendMode(Blend.Alpha) if DEBUG then context:SetColor(1,0,0,1) context:DrawText("Debug Mode",2,2) context:SetColor(1,1,1,1) context:DrawStats(2,22) context:SetBlendMode(Blend.Solid) else --Toggle statistics on and off if (window:KeyHit(Key.F11)) then showstats = not showstats end if showstats then context:SetColor(1,1,1,1) context:DrawText("FPS: "..Math:Round(Time:UPS()),2,2) end end --Refresh the screen if VSyncMode==nil then VSyncMode=true end context:Sync(VSyncMode) end
  14. Ok. I did that. I'm not getting anything, though. Put the script in Main. Didn't duplicate the Time or World updates. Added the enemyDied function in the player script. Should this line : "self.onDeadId = SubscribeEvent("onDead",self,self.enemyDied)" ..be like this? "self.onDeadId = SubscribeEvent("onDead", self, self:enemyDied())" I tried it like that and it starts me off with one kill. And then doesn't count any of the kills. You got it working so what am I missing here? Also how do I move it to a different script? Just put it on a different script and then at the top of Main.lua add "import"..scriptname...""? This would be awesome for making a little "kill list" on the screen. I got my little random name generator working on the Crawlers, so I could make a list like "Gorgon was killed by player" etc.
  15. Ok so let me see if I'm following you here. I put that first code in my Main.lua at the top under "import("Scripts/Menu.lua")". I hid "self.kills = self.kills + 1" in the FPSGun script. I put " RaiseEvent("onDead", {}) " in the crawler script under the Hurt() below "if self.health <= 0 then" I put "self.onDeadId = SubscribeEvent("onDead", self, self.enemyDied)" in the Start() of the player. I put ... "function Script:enemyDied(data) self.kills = self.kills + 1 end" and... "function Script:CleanUp() Unsubscribe(self.onDeadId) end" ... in the player script. Everything just like you wrote it. I'm getting no errors but it's also not counting kills. Is it supposed to work as-is? I re-watched your vids on the SMC hoping some light would go on but my brain just won't retain it.
  16. That's rad, dude. That's definitely the level I want to get to. I want to be able to think in terms of whole systems like that but I just don't know what steps to take to get there. I'm still a total noob so pretty much everything is uphill now. It's getting easier but it all tends to get cluttered in my head and I can't remember where I put things, etc. What do you recommend I study next to get to the next level? I have Aggror's FlowGUI. I was thinking of getting into that to see if I can understand his thinking behind it. I like that it's a whole system but maybe its a little too big for me at this point. What do you mean by "onDead"? Like a single function that takes care of everything? Or a single script? I can't even grasp how I would uncouple this lol
  17. Ah that works perfectly. It was as simple as putting... "if enemy.script.health <=0 then self.player.kills = self.player.kills + 1 end" ...in the FPSGun script and "Script.kills = 0" in the player script. And then drawing the text also in the player script.
  18. Count the kills in the Hurt function of the AI? Or count it in the player script? I'm currently attempting to count them from a pivot to the player.
  19. Yeah here's the whole function. You're right. Later it calls: if enemy.script.health>0 then enemy.script:Hurt(self.bulletdamage,self.player) end ...so like if enemy.script.health>0 then enemy.script:Hurt(self.bulletdamage,self.player) if enemy.script.health <=0 then self.player.script.kills = self.player.script.kills + 1 end end function Script:UpdateWorld() local bullet,n,dist local pickinfo=PickInfo() local firstbullet=true local travel for n,bullet in ipairs(self.bullets) do --Check how far the bullet has travelled dist = (bullet.position-bullet.origin):Length() if dist>self.bulletrange then table.remove(self.bullets,n) bullet.sprite:Release() bullet=nil end if bullet~=nil then travel = bullet.velocity/60.0*Time:GetSpeed() if self.entity.world:Pick(bullet.position,bullet.position+travel,pickinfo,0,true,Collision.Projectile) then --Find first parent with the Hurt() function local enemy = self:FindScriptedParent(pickinfo.entity,"Hurt") --local sph = Model:Sphere() --sph:SetPosition(pickinfo.position) --Bullet mark decal local mtl local scale = 0.1 if enemy~=nil then mtl = Material:Load("Materials/Decals/wound.mat") scale = 0.1 else if pickinfo.surface~=nil then local pickedmaterial = pickinfo.surface:GetMaterial() if pickedmaterial~=nil then rendermode = pickedmaterial:GetDecalMode() end end mtl = Material:Load("Materials/Decals/bulletmark.mat") end local decal = Decal:Create(mtl) decal:AlignToVector(pickinfo.normal,2) decal:Turn(0,0,Math:Random(0,360)) decal:SetScript("Scripts/Objects/Effects/BulletMark.lua") if mtl~=nil then mtl:Release() end decal:SetPosition(pickinfo.position) decal:SetParent(pickinfo.entity) --Apply global scaling local mat = decal:GetMatrix() mat[0] = mat[0]:Normalize() * scale mat[1] = mat[1]:Normalize() * scale mat[2] = mat[2]:Normalize() * scale decal:SetMatrix(mat) table.remove(self.bullets,n) bullet.sprite:Release() if enemy~=nil then if enemy.script.health>0 then enemy.script:Hurt(self.bulletdamage,self.player) end --Blood emitter --[[e = self.emitter[2]:Instance() e = tolua.cast(e,"Emitter") e:Show() e:SetLoopMode(false,true) e:SetPosition(pickinfo.position+pickinfo.normal*0.1) e:SetVelocity(0,0,0)]]-- else --Add a temporary particle emitter for bullet effects local e e = self.emitter[0]:Instance() e = tolua.cast(e,"Emitter") e:Show() e:SetLoopMode(false,true) e:SetPosition(pickinfo.position) local v=3 e:SetVelocity(pickinfo.normal.x*v,pickinfo.normal.y*v,pickinfo.normal.z*v,0) --Smoke emitter e = self.emitter[1]:Instance() e = tolua.cast(e,"Emitter") e:Show() e:SetLoopMode(false,true) e:SetPosition(pickinfo.position+pickinfo.normal*0.1) local v=0.2 e:SetVelocity(pickinfo.normal.x*v,pickinfo.normal.y*v,pickinfo.normal.z*v,0) --Play bullet impact noise e:EmitSound(self.sound.ricochet[math.random(#self.sound.ricochet)],30) if pickinfo.entity~=nil then --Add impulse to the hit object if pickinfo.entity:GetMass()>0 then --local force = pickinfo.normal*-1*self.bulletforce local dir = bullet.velocity:Normalize() local force = dir * self.bulletforce * math.max(0,-pickinfo.normal:Dot(dir)) --force = force * math.max(0,-pickinfo.normal:Dot(d))--multiply by dot product of velocity and collided normal, to weaken glancing blows pickinfo.entity:AddPointForce(force,pickinfo.position) end --Extract a partial surface from the hit surface and make a bullet mark --To be added later --if pickinfo.surface~=nil then -- local aabb = AABB(pickinfo.position-radius,pickinfo.position+radius) -- local surf = pickinfo.surface:Extract(aabb) --end end end else bullet.position = bullet.position+travel bullet.sprite:SetPosition(bullet.position - bullet.velocity:Normalize()*1) if bullet.sprite:Hidden() then dist = (bullet.position-bullet.origin):Length() if dist>bullet.sprite:GetSize().y then bullet.sprite:Show() end end end end firstbullet = false end end
  20. Ah ok. Looks like it's in the FPSGun script. function Script:UpdateWorld() local bullet,n,dist local pickinfo=PickInfo() local firstbullet=true local travel for n,bullet in ipairs(self.bullets) do --Check how far the bullet has travelled dist = (bullet.position-bullet.origin):Length() if dist>self.bulletrange then table.remove(self.bullets,n) bullet.sprite:Release() bullet=nil end if bullet~=nil then travel = bullet.velocity/60.0*Time:GetSpeed() if self.entity.world:Pick(bullet.position,bullet.position+travel,pickinfo,0,true,Collision.Projectile) then --Find first parent with the Hurt() function local enemy = self:FindScriptedParent(pickinfo.entity,"Hurt") <--- this guy right here. --local sph = Model:Sphere() --sph:SetPosition(pickinfo.position) Could I just put the kill counter in the crawler script under the "Hurt" function then? Since it's telling it to read that here?
  21. Just the FPS script. This, I think: --Raycast Pick that is being send from the camera in to the world self.canUse = false local fire = false local currentime = Time:GetCurrent() if self.carryingEntity==nil then if self.weapons[self.currentweaponindex]~=nil then if self.weapons[self.currentweaponindex].automatic then if window:MouseDown(1) then fire=true else self.suspendfire=false <-- so like in here somewhere? like a pickInfo.entity.script.health something? end else if window:MouseHit(1) then fire=true end end end end
  22. I'm able to shoot it and kill it. I just can't count it. Are you saying I need to make another pick on mouse down only in order to count it? Like if health <=0 then kill = kill +1, but within the other pick?
  23. Yeah the health is really beside the point. I got the pick working. It's counting the kill that I'm having a problem with. It only counts the kill currently if I keep picking the crawler after health is <= 0. So if I look away too soon I don't get the kill point, which is lame. You can see in the top left corner I'm trying to count cumulative crawlers killed.
  24. I don't think I want that? I want it to display the enemy health when I'm looking at it. Ideally what I would like to do is pick the entity and somehow save that information so I don't have to keep picking it in order to count the kill. I got it to count 1 kill if I change the collision type on ...mode = "dying" but it will sometimes miss because I moved the camera. function Script:UpdateWorld() local pickInfo = PickInfo() self.proj = window:GetMousePosition() if self.camera:Pick(self.proj.x, self.proj.y, pickInfo, self.pickradius, true,2) then if pickInfo.entity:GetKeyValue("type") == "enemy" then if pickInfo.entity.script.mode ~= "dying" and pickInfo.entity.script.mode ~= "dead" then self.camPick = true else pickInfo.entity:SetCollisionType(Collision.None,false) <-- changed these two lines here pickInfo.entity:SetMass(0) for i = 1,1,1 do self.kills = self.kills + i System:Print(i) end end end else self.camPick = false end end This is what I'm doing with it:
×
×
  • Create New...