Tentative changelist for 2.1.19

Ask for help about creating mods and scripts for Grimrock 2 or share your tips, scripts, tools and assets with other modders here. Warning: forum contains spoilers!
User avatar
Dr.Disaster
Posts: 2876
Joined: Wed Aug 15, 2012 11:48 am

Re: Tentative changelist for 2.1.19

Post by Dr.Disaster »

Thx Issac for reminding me of this issue.
The effect of the Rage potion can be stacked without limit: drinking X Rage potions adds 20 times X Strength to a character.
MrChoke
Posts: 324
Joined: Sat Oct 25, 2014 7:20 pm

Re: Tentative changelist for 2.1.19

Post by MrChoke »

MrChoke wrote:
petri wrote:
MrChoke wrote:Ugh. I just confirmed a bug. Its in the Brain component, the "seesParty" property. It is erroneously getting set to true even though the party should not be seen. It doesn't appear to fail if there is a solid wall blocking. However, it does fail for walls with door components, even if multiple of these block the party. When it fails, it will go to true for one movement of the monster and then go back to false. I cannot pinpoint why it is doing it only that it clearly shouldn't be.
I think you doors are 'sparse'
Hi. I didn't know about sparse. I did try it though and the problem is still happening. To ensure that it was not any mistake I made with custom objects, I replaced all of them with "castle_door_wood". This is not a sparse door. The "seesParty" property will still go to true for one move and then back to false. It seems to do it only at perfect or close to perfect diagonals to the party, never at horizontal or vertical positions.

I also see if I put a sold wall tile in the perfect diagonal direction, it stops happening.
Would it be possible to get conformation from AH that this setting of "seesParty" when it shouldn't be set is indeed a bug? Or any other suggestions to stop it from happening. It is a very serious bug for my plans of doing a custom AI. I have a whole new path finding algorithm working and my intention is to use this when the party is not seen. But if "seesParty" is going to true when the party is behind walls, this design will fail. I am basically dead in the water with my AI plans without a fix or workaround to this problem. Thanks.
User avatar
petri
Posts: 1917
Joined: Thu Mar 01, 2012 4:58 pm
Location: Finland

Re: Tentative changelist for 2.1.19

Post by petri »

MrChoke wrote:Would it be possible to get conformation from AH that this setting of "seesParty" when it shouldn't be set is indeed a bug?
It works for me. Maybe you are trying to access the brain outside the onThink hook? The senses of monsters are updated just before their brain is updated.
MrChoke
Posts: 324
Joined: Sat Oct 25, 2014 7:20 pm

Re: Tentative changelist for 2.1.19

Post by MrChoke »

petri wrote:
MrChoke wrote:Would it be possible to get conformation from AH that this setting of "seesParty" when it shouldn't be set is indeed a bug?
It works for me. Maybe you are trying to access the brain outside the onThink hook? The senses of monsters are updated just before their brain is updated.
Thanks for looking into this. To make sure we are both seeing the exact same thing, below is a very simple test dungeon I made specifically to show you the issue.

Dungeon.lua:

Code: Select all

-- This file has been generated by Dungeon Editor 2.1.18

--- level 1 ---

newMap{
	name = "Unnamed",
	width = 32,
	height = 32,
	levelCoord = {0,0,0},
	ambientTrack = "dungeon",
	tiles = {
		"beach_ground",
		"beach_ground_grass",
		"beach_wall",
	}
}

loadLayer("tiles", {
	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,1,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,
})

spawn("starting_location",13,16,2,0,"starting_location_1")
spawn("beach_grass_pressure_plate",13,2,0,0,"beach_grass_pressure_plate_2")
beach_grass_pressure_plate_2.floortrigger:setTriggeredByParty(true)
beach_grass_pressure_plate_2.floortrigger:setTriggeredByMonster(false)
beach_grass_pressure_plate_2.floortrigger:setTriggeredByItem(false)
beach_grass_pressure_plate_2.floortrigger:setTriggeredByDigging(false)
beach_grass_pressure_plate_2.floortrigger:setDisableSelf(true)
beach_grass_pressure_plate_2.floortrigger:addConnector("onActivate", "brain_scr", "plate1Activated")
spawn("trickster",21,3,3,0,"trickster_1")
trickster_1.brainScript:setSource("")
spawn("script_entity",0,0,3,0,"brain_scr")
brain_scr.script:loadFile("mod_assets/scripts/brainTest.lua")
spawn("forest_heightmap",1,0,0,0,"forest_heightmap_1")
spawn("forest_day_sky",1,0,1,0,"forest_day_sky_1")
spawn("forest_ruins_wall_01",15,16,3,0,"forest_ruins_wall_01_1")
spawn("beach_grass_pressure_plate",13,17,3,0,"beach_grass_pressure_plate_1")
beach_grass_pressure_plate_1.floortrigger:setTriggeredByParty(true)
beach_grass_pressure_plate_1.floortrigger:setTriggeredByMonster(true)
beach_grass_pressure_plate_1.floortrigger:setTriggeredByItem(true)
beach_grass_pressure_plate_1.floortrigger:setTriggeredByDigging(false)
beach_grass_pressure_plate_1.floortrigger:setDisableSelf(false)
beach_grass_pressure_plate_1.floortrigger:addConnector("onActivate", "brain_scr", "plate2Activated")
spawn("forest_ruins_wall_01",14,17,1,0,"forest_ruins_wall_01_2")
spawn("forest_ruins_wall_01",14,18,1,0,"forest_ruins_wall_01_3")
spawn("forest_ruins_wall_01",15,19,3,0,"forest_ruins_wall_01_4")
spawn("forest_ruins_wall_01",14,20,1,0,"forest_ruins_wall_01_5")
spawn("forest_ruins_wall_01",15,21,3,0,"forest_ruins_wall_01_6")
spawn("forest_ruins_wall_01",15,22,3,0,"forest_ruins_wall_01_7")
spawn("forest_ruins_wall_01",14,24,1,0,"forest_ruins_wall_01_8")
spawn("forest_ruins_wall_01",14,25,1,0,"forest_ruins_wall_01_9")
spawn("forest_ruins_wall_01",14,26,1,0,"forest_ruins_wall_01_10")
spawn("forest_ruins_wall_01",15,27,3,0,"forest_ruins_wall_01_11")
spawn("forest_ruins_wall_01",15,28,3,0,"forest_ruins_wall_01_12")
spawn("forest_ruins_wall_01",15,29,3,0,"forest_ruins_wall_01_13")
spawn("forest_ruins_wall_01",15,30,3,0,"forest_ruins_wall_01_15")
spawn("trickster",20,18,3,0,"trickster_2")
trickster_2.brainScript:setSource("")
spawn("castle_door_wood",15,16,1,0,"castle_door_wood_1")
spawn("castle_door_wood",15,17,1,0,"castle_door_wood_2")
spawn("castle_door_wood",15,18,1,0,"castle_door_wood_3")
spawn("castle_door_wood",15,19,1,0,"castle_door_wood_4")
spawn("castle_door_wood",15,20,1,0,"castle_door_wood_6")
spawn("castle_door_wood",15,21,1,0,"castle_door_wood_8")
spawn("castle_door_wood",15,22,1,0,"castle_door_wood_9")
I also use an external LUA file for my brain code. brainTest.lua:

Code: Select all

monsterID1 = "trickster_1"
monsterID2 = "trickster_2"

lastSeesParty = false   -- last value set by AI

brainTestObj = {
    bBrainTestComplete = true,
    targetSqr = {}   
}

function plate1Activated()
    local mob = findEntity(brain_scr.script.monsterID1)
    if mob and mob.level == party.level then
        brain_scr.script.initBrainTest(mob, 11, 5)
    end
end

function plate2Activated()
    local mob = findEntity(brain_scr.script.monsterID2)
    if mob and mob.level == party.level then
        brain_scr.script.initBrainTest(mob, 11, 20)
    end
end

function initBrainTest(mob, x, y)
    brainTestObj.targetSqr = { ["x"] = x, ["y"] = y }
    brainTestObj.bBrainTestComplete = false
    print("Starting brainTest")
    mob.brain:addConnector("onThink", "brain_scr", "thinkTestFunc")
end

function thinkTestFunc(monster)
    local br = monster.go.brain
        
    if br.seesParty ~= lastSeesParty then
        --print("new seesParty", br.seesParty, "party rel-loc: s-ahead:"..tostring(br.partyStraightAhead)..", s-behind:"..tostring(br.partyStraightBehind)..", left:"..tostring(br.partyLeft)..", right:"..tostring(br.partyRight)..", behind:"..tostring(br.partyBehind)..", ahead: "..tostring(br.partyAhead)..", adj:"..tostring(br.partryAdjacent)..", diag: "..tostring(br.partyDiagonal))
        --print("onLevel: "..tostring(br.partyOnLevel)..", lastSeen: "..br.partyLastSeen..", dist:("..br.partyDistX..","..br.partyDistY.."), locXY:("..br.partyX..","..br.partyY..")")
        print("Current seesParty", br.seesParty, "X,Y,facing=", br.go.x, br.go.y, br.go.facing)
        lastSeesParty = br.seesParty
        
        -- Kill brain when he first sees party
        br:disable()
    end
       
    if not brainTestObj.bBrainTestComplete then        
        if monster.go.x == brainTestObj.targetSqr.x and monster.go.y == brainTestObj.targetSqr.y then
            print("Monster found target!!!  BrainTest passed.")        
            brainTestObj.bBrainTestComplete = true
        else 
            br:seek(brainTestObj.targetSqr.x, brainTestObj.targetSqr.y)
        end
    else
        -- do normal brain stuff
    end
end

The level has two sections. The 1st section is used to show that seesParty works fine when walls are in between the mob and the party. My code will disable the brain as soon as the party is seen. For this test, this is at cords: 11, 6

In the 2nd section, I have both forest_ruins_wall and castle_door_wood Door objects instead of walls. Both are not sparse. You will see the mob see the party thru these doors at or near 16, 20.

Please let me know if you can re-create the issue.

UPDATE:
I decide to see if "partyLastSeen" was having issues as well. I confirm it was in a similar but actually more concerning way. Most of the time when the mob sees the party in error, it somehow is reports seeing him for 0 seconds! I am talking 0 to the nanosecond. And that makes no sense considering another turn had to have gone by. Look at my code where I keep track of when he is first seen and compare it to when he stops seeing the party. Below is a new version of my brainTest.lua. I think this is more revealing that something is not right...

Code: Select all

monsterID1 = "trickster_1"
monsterID2 = "trickster_2"

lastSeesParty = false   -- last value set by AI
lastSeenTime = 0
startSeenTime = nil

brainTestObj = {
    bBrainTestComplete = true,
    targetSqr = {}   
}

function plate1Activated()
    local mob = findEntity(brain_scr.script.monsterID1)
    if mob and mob.level == party.level then
        brain_scr.script.initBrainTest(mob, 11, 5)
    end
end

function plate2Activated()
    local mob = findEntity(brain_scr.script.monsterID2)
    if mob and mob.level == party.level then
        --brain_scr.script.initBrainTest(mob, 11, 20)
        brain_scr.script.initBrainTest(mob, 11, 28)
    end
end

function initBrainTest(mob, x, y)
    brainTestObj.targetSqr = { ["x"] = x, ["y"] = y }
    brainTestObj.bBrainTestComplete = false
    print("Starting brainTest")
    mob.brain:addConnector("onThink", "brain_scr", "thinkTestFunc")
    lastSeenTime = mob.brain.partyLastSeen
    --print("Time vals", Time.currentTime(), Time.deltaTime, Time.systemTime())
end

function thinkTestFunc(monster)   
    local br = monster.go.brain    
        
    if br.partyLastSeen ~= lastSeenTime then
        if not startSeenTime then
            startSeenTime = br.partyLastSeen
            --print("Mob did not see party for: "..tostring(br.partyLastSeen - lastSeenTime).." secs.  Mob SEES party at: "..br.partyLastSeen)
        end
    else
        if startSeenTime then
            print("Mob saw party for: "..tostring(br.partyLastSeen - startSeenTime).." secs.  Mob STOPED seeing party at: "..br.partyLastSeen..".  Start seen time: "..startSeenTime)
            startSeenTime = nil
        end
    end    
    lastSeenTime = br.partyLastSeen
        
    --[[       
    if br.seesParty ~= lastSeesParty then    
        --print("new seesParty", br.seesParty, "party rel-loc: s-ahead:"..tostring(br.partyStraightAhead)..", s-behind:"..tostring(br.partyStraightBehind)..", left:"..tostring(br.partyLeft)..", right:"..tostring(br.partyRight)..", behind:"..tostring(br.partyBehind)..", ahead: "..tostring(br.partyAhead)..", adj:"..tostring(br.partryAdjacent)..", diag: "..tostring(br.partyDiagonal))
        --print("onLevel: "..tostring(br.partyOnLevel)..", lastSeen: "..br.partyLastSeen..", dist:("..br.partyDistX..","..br.partyDistY.."), locXY:("..br.partyX..","..br.partyY..")")
        print("Current seesParty", br.seesParty, "seesPartyCount", seesPartyCount, "X,Y,facing=", br.go.x, br.go.y, br.go.facing)
        lastSeesParty = br.seesParty      
        seesPartyCount = 0 
        
        -- Kill brain when he first sees party
        --br:disable()
    end    
    seesPartyCount = seesPartyCount + 1
    ]]--       
           
    if not brainTestObj.bBrainTestComplete then                  
        if monster.go.x == brainTestObj.targetSqr.x and monster.go.y == brainTestObj.targetSqr.y then
            print("Monster found target!!!  BrainTest passed.")        
            brainTestObj.bBrainTestComplete = true
        else                   
            br:seek(brainTestObj.targetSqr.x, brainTestObj.targetSqr.y)
        end
    else
        -- do normal brain stuff
    end    
    
end
Thanks
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: Tentative changelist for 2.1.19

Post by minmay »

minmay wrote:Found what I believe is a bug. Light firearms (firearms with the "light_weapon" trait) behave strangely when combined with dual-wielding: if you attack with the light firearm, it puts both hands on cooldown, but if you attack with a light melee weapon in the other hand, it doesn't put the firearm on cooldown.
Update, this also happens with throwing, missiles, and wand spells. However, RunePanelComponent works fine with dual-wielding.
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
minmay
Posts: 2790
Joined: Mon Sep 23, 2013 2:24 am

Re: Tentative changelist for 2.1.19

Post by minmay »

LightComponent:getFadeOut() always gives the following error when I try to call it:

Code: Select all

attempt to call a nil value
stack traceback:
	[C]: in function 'error'
	[string "ScriptInterface.lua"]: in function 'getFadeOut'
Grimrock 1 dungeon
Grimrock 2 resources
I no longer answer scripting questions in private messages. Please ask in a forum topic or this Discord server.
User avatar
JKos
Posts: 464
Joined: Wed Sep 12, 2012 10:03 pm
Location: Finland
Contact:

Re: Tentative changelist for 2.1.19

Post by JKos »

It would be great if you could implement these 2 methods:
champion:getPortrait() -- returns the file path to the champion portrait.
champion:setLevel(number)

Or some other way to downdgrade (or even reset) the level of the champion. champion:gainExp(negativeValue) doesn't downgrade the level.

These are needed for example for dialogs and recruiting new champions.
- LoG Framework 2http://sites.google.com/site/jkoslog2 Define hooks in runtime by entity.name or entity.id + multiple hooks support.
- cloneObject viewtopic.php?f=22&t=8450
User avatar
Eleven Warrior
Posts: 752
Joined: Thu Apr 18, 2013 2:32 pm
Location: Australia

Re: Tentative changelist for 2.1.19

Post by Eleven Warrior »

JKos wrote:It would be great if you could implement these 2 methods:
champion:getPortrait() -- returns the file path to the champion portrait.
champion:setLevel(number)

Or some other way to downdgrade (or even reset) the level of the champion. champion:gainExp(negativeValue) doesn't downgrade the level.

These are needed for example for dialogs and recruiting new champions.
I most certainly 2nd this motion, I do miss the:
champion:getPortrait() -- returns the file path to the champion portrait.
champion:setLevel(number)

With this implemented we will be able to have better starting champs for the player(s), down grade there level if they do something bad, have a player start with 4 x rattlings champs or other races etc...
Please AH can we get these codes into LOG 2? :) In LOG 1 you could let the player start with 1 or more champs and change their races, classes etc... Man I hope I got this right though lol.
User avatar
petri
Posts: 1917
Joined: Thu Mar 01, 2012 4:58 pm
Location: Finland

Re: Tentative changelist for 2.1.19

Post by petri »

Champion:getPortrait() is imposible to add in general case. Remember the portraits can be imported, so portrait data is stored in the save games. Otherwise save games couldn't be transferred between computers.
User avatar
Isaac
Posts: 3191
Joined: Fri Mar 02, 2012 10:02 pm

Re: Tentative changelist for 2.1.19

Post by Isaac »

petri wrote:Champion:getPortrait() is imposible to add in general case. Remember the portraits can be imported, so portrait data is stored in the save games. Otherwise save games couldn't be transferred between computers.
Meaning transfered between PC/MAC/Linux/IOS? That is pretty cool; Fallout was like that. The PC & Mac games loaded via floppy on either OS from either OS; pretty sharp feature considering the endian differences between x86 and PPC. 8-)
____

Is it possible to define them as materials at load? Doesn't the game fall back to the default portraits if the custom ones are not present when the save is loaded?
The only reason that users want this feature is to be able to restore the original portraits after a change; (including after changing a PC to a different one, and wanting to change it back).

**Obviously none of us know the technical impediments involved; but it would be a cool feature if it could be managed.
Post Reply