more rooms
more rooms, level editor changes, more logic for ability checks and trigger doors, platform body collision fixes
This commit is contained in:
@@ -350,6 +350,9 @@
|
||||
if(rect.name) out.name = String(rect.name);
|
||||
if(rect.quest) out.quest = String(rect.quest);
|
||||
if(rect.quest_complete) out.quest_complete = String(rect.quest_complete);
|
||||
if(rect.quest_turned_in) out.quest_turned_in = String(rect.quest_turned_in);
|
||||
if(rect.lock_when_pass) out.lock_when_pass = true;
|
||||
if(rect.open_on_miniboss_dead) out.open_on_miniboss_dead = true;
|
||||
if(Array.isArray(rect.respawn)) out.respawn = deepClone(rect.respawn);
|
||||
return out;
|
||||
}
|
||||
@@ -505,13 +508,14 @@
|
||||
if(Array.isArray(src.ability_doors)){
|
||||
for(const d of src.ability_doors){
|
||||
if(!d) continue;
|
||||
const kind = (typeof d === "object" && d.kind === "shade") ? "shade" : "ranged";
|
||||
if(Array.isArray(d) && d.length >= 4){
|
||||
r.ability_doors.push({ x:+d[0]||0, y:+d[1]||0, w:+d[2]||0, h:+d[3]||0, layer: d.length>=5 ? (+d[4]||0) : 0 });
|
||||
r.ability_doors.push({ x:+d[0]||0, y:+d[1]||0, w:+d[2]||0, h:+d[3]||0, layer: d.length>=5 ? (+d[4]||0) : 0, kind });
|
||||
} else if(typeof d === "object"){
|
||||
if(Array.isArray(d.rect)){
|
||||
r.ability_doors.push({ x:+d.rect[0]||0, y:+d.rect[1]||0, w:+d.rect[2]||0, h:+d.rect[3]||0, layer: +(d.layer||0) });
|
||||
r.ability_doors.push({ x:+d.rect[0]||0, y:+d.rect[1]||0, w:+d.rect[2]||0, h:+d.rect[3]||0, layer: +(d.layer||0), kind });
|
||||
} else {
|
||||
r.ability_doors.push({ x:+d.x||0, y:+d.y||0, w:+d.w||0, h:+d.h||0, layer: +(d.layer||0) });
|
||||
r.ability_doors.push({ x:+d.x||0, y:+d.y||0, w:+d.w||0, h:+d.h||0, layer: +(d.layer||0), kind });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1645,11 +1649,19 @@ Cancel = Choose whether to remove without saving`);
|
||||
const d = it.d;
|
||||
const [sx,sy] = worldToScreen(d.x, d.y, canvas);
|
||||
const [sx2,sy2] = worldToScreen(d.x+d.w, d.y+d.h, canvas);
|
||||
ctx.fillStyle = "rgba(16,185,129,0.18)";
|
||||
ctx.fillRect(sx,sy, sx2-sx, sy2-sy);
|
||||
ctx.strokeStyle = "rgba(16,185,129,0.75)";
|
||||
ctx.lineWidth = 2*dpr;
|
||||
ctx.strokeRect(sx,sy, sx2-sx, sy2-sy);
|
||||
if(d.kind === "shade"){
|
||||
ctx.fillStyle = "rgba(129,110,190,0.20)";
|
||||
ctx.fillRect(sx,sy, sx2-sx, sy2-sy);
|
||||
ctx.strokeStyle = "rgba(129,110,190,0.80)";
|
||||
ctx.lineWidth = 2*dpr;
|
||||
ctx.strokeRect(sx,sy, sx2-sx, sy2-sy);
|
||||
} else {
|
||||
ctx.fillStyle = "rgba(16,185,129,0.18)";
|
||||
ctx.fillRect(sx,sy, sx2-sx, sy2-sy);
|
||||
ctx.strokeStyle = "rgba(16,185,129,0.75)";
|
||||
ctx.lineWidth = 2*dpr;
|
||||
ctx.strokeRect(sx,sy, sx2-sx, sy2-sy);
|
||||
}
|
||||
} else if(it.kind === "exit"){
|
||||
const ex = it.ex;
|
||||
const [sx,sy] = worldToScreen(ex.x, ex.y, canvas);
|
||||
@@ -1948,7 +1960,10 @@ Cancel = Choose whether to remove without saving`);
|
||||
} else if(sid){
|
||||
pos = (dstWrap.data.entry_spawns && dstWrap.data.entry_spawns[sid]) ? dstWrap.data.entry_spawns[sid] : null;
|
||||
}
|
||||
if(!pos && Array.isArray(ex.spawn) && ex.spawn.length >= 2){
|
||||
// Only fall back to the raw spawn coordinate when the exit has NO spawn_id.
|
||||
// A stale spawn_id (e.g. from a target room that doesn't define it) would
|
||||
// point the line at an arbitrary spot, so skip those broken links instead.
|
||||
if(!pos && !sid && Array.isArray(ex.spawn) && ex.spawn.length >= 2){
|
||||
pos = ex.spawn;
|
||||
}
|
||||
if(!pos) continue;
|
||||
@@ -2143,6 +2158,16 @@ Cancel = Choose whether to remove without saving`);
|
||||
<div style="margin-top:8px;">
|
||||
<div class="mini">unlock when quest complete (id)</div>
|
||||
<input id="ptqcomplete" type="text" value="${p.quest_complete||""}" placeholder="e.g. quest_first_kills"/>
|
||||
</div>
|
||||
<div style="margin-top:8px;">
|
||||
<div class="mini">unlock when quest turned in (id)</div>
|
||||
<input id="ptqturnedin" type="text" value="${p.quest_turned_in||""}" placeholder="e.g. quest_first_kills"/>
|
||||
</div>
|
||||
<div style="margin-top:8px;">
|
||||
<label><input id="ptlockpass" type="checkbox" ${p.lock_when_pass?"checked":""}/> Appear once the player walks through (locks them in)</label>
|
||||
</div>
|
||||
<div style="margin-top:8px;">
|
||||
<label><input id="ptminibossdead" type="checkbox" ${p.open_on_miniboss_dead?"checked":""}/> Present until the room's miniboss is slain</label>
|
||||
</div>` : ""}
|
||||
${(p.group||"ground") === "hazard" ? `
|
||||
<div style="margin-top:8px;">
|
||||
@@ -2204,6 +2229,13 @@ Cancel = Choose whether to remove without saving`);
|
||||
<div><div class="mini">h</div><input id="adh" type="number" value="${d.h}"/></div>
|
||||
<div><div class="mini">layer (lower = back)</div><input id="adlayer" type="number" value="${d.layer||0}"/></div>
|
||||
</div>
|
||||
<div style="margin-top:8px;">
|
||||
<div class="mini">type</div>
|
||||
<select id="adkind">
|
||||
<option value="ranged" ${(d.kind||"ranged")==="ranged"?"selected":""}>Ranged door (broken by the Ranged bolt)</option>
|
||||
<option value="shade" ${d.kind==="shade"?"selected":""}>Shade door (passable in shade form, solid in light)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="row" style="margin-top:8px;">
|
||||
<button id="delSel" class="danger">Delete</button>
|
||||
</div>
|
||||
@@ -2376,6 +2408,16 @@ Cancel = Choose whether to remove without saving`);
|
||||
const v = ptqcomplete.value.trim();
|
||||
if(v){ p.quest_complete = v; } else { delete p.quest_complete; }
|
||||
}));
|
||||
const ptqturnedin=q("ptqturnedin"); if(ptqturnedin) ptqturnedin.addEventListener("change", ()=>pushAnd(()=>{
|
||||
const v = ptqturnedin.value.trim();
|
||||
if(v){ p.quest_turned_in = v; } else { delete p.quest_turned_in; }
|
||||
}));
|
||||
const ptlockpass=q("ptlockpass"); if(ptlockpass) ptlockpass.addEventListener("change", ()=>pushAnd(()=>{
|
||||
if(ptlockpass.checked){ p.lock_when_pass = true; } else { delete p.lock_when_pass; }
|
||||
}));
|
||||
const ptminibossdead=q("ptminibossdead"); if(ptminibossdead) ptminibossdead.addEventListener("change", ()=>pushAnd(()=>{
|
||||
if(ptminibossdead.checked){ p.open_on_miniboss_dead = true; } else { delete p.open_on_miniboss_dead; }
|
||||
}));
|
||||
const ptspeed=q("ptspeed"); if(ptspeed) ptspeed.addEventListener("change", ()=>pushAnd(()=>{ p.speed = Math.max(1, num(ptspeed.value)); }));
|
||||
const hzrespawn=q("hzrespawn");
|
||||
if(hzrespawn){
|
||||
@@ -2462,6 +2504,7 @@ Cancel = Choose whether to remove without saving`);
|
||||
}));
|
||||
});
|
||||
const adlayer=q("adlayer"); if(adlayer) adlayer.addEventListener("change", ()=>pushAnd(()=>{ d.layer = Math.round(num(adlayer.value)); }));
|
||||
const adkind=q("adkind"); if(adkind) adkind.addEventListener("change", ()=>pushAnd(()=>{ d.kind = adkind.value === "shade" ? "shade" : "ranged"; }));
|
||||
const del=q("delSel"); if(del) del.addEventListener("click", ()=>pushAnd(()=>{
|
||||
room.ability_doors.splice(sel.index,1);
|
||||
state.selection=null;
|
||||
@@ -3279,6 +3322,9 @@ Cancel = Choose whether to remove without saving`);
|
||||
if(p.name) obj.name = p.name;
|
||||
if(p.quest) obj.quest = p.quest;
|
||||
if(p.quest_complete) obj.quest_complete = p.quest_complete;
|
||||
if(p.quest_turned_in) obj.quest_turned_in = p.quest_turned_in;
|
||||
if(p.lock_when_pass) obj.lock_when_pass = true;
|
||||
if(p.open_on_miniboss_dead) obj.open_on_miniboss_dead = true;
|
||||
if(Array.isArray(p.respawn)) obj.respawn = p.respawn;
|
||||
platforms[group].push(obj);
|
||||
} else {
|
||||
@@ -3315,10 +3361,11 @@ Cancel = Choose whether to remove without saving`);
|
||||
return out;
|
||||
});
|
||||
|
||||
// ability doors back to {rect:[x,y,w,h]} (+ layer when set)
|
||||
// ability doors back to {rect:[x,y,w,h]} (+ layer/kind when set)
|
||||
const ability_doors = (d.ability_doors || []).map(ad => {
|
||||
const out = { rect: [ad.x, ad.y, ad.w, ad.h] };
|
||||
if(ad.layer) out.layer = ad.layer;
|
||||
if(ad.kind === "shade") out.kind = "shade";
|
||||
return out;
|
||||
});
|
||||
|
||||
@@ -3850,6 +3897,14 @@ els.canvasA.addEventListener("pointerdown", onPointerDown);
|
||||
e.preventDefault();
|
||||
deleteSelection(); return;
|
||||
}
|
||||
// Tool switching shortcuts (match the toolbar button titles).
|
||||
const toolByKey = { v:"select", p:"platform", e:"enemy_spawn", n:"npc_spawn", o:"object_spawn" };
|
||||
const toolKey = toolByKey[e.key.toLowerCase()];
|
||||
if(toolKey){
|
||||
e.preventDefault();
|
||||
setTool(toolKey);
|
||||
return;
|
||||
}
|
||||
if(e.key.toLowerCase()==='f'){
|
||||
e.preventDefault();
|
||||
fitViewToRoom(getActiveRoom(),'A');
|
||||
|
||||
Reference in New Issue
Block a user