var A4_HASH_SIZE:number = 1000; var A4_DIRECTION_NONE:number = -1; var A4_DIRECTION_LEFT:number = 0; var A4_DIRECTION_UP:number = 1; var A4_DIRECTION_RIGHT:number = 2; var A4_DIRECTION_DOWN:number = 3; var A4_NDIRECTIONS:number = 4; var A4_ANIMATION_IDLE:number = 0; var A4_ANIMATION_IDLE_LEFT:number = 1; var A4_ANIMATION_IDLE_UP:number = 2; var A4_ANIMATION_IDLE_RIGHT:number = 3; var A4_ANIMATION_IDLE_DOWN:number = 4; var A4_ANIMATION_MOVING:number = 5; var A4_ANIMATION_MOVING_LEFT:number = 6; var A4_ANIMATION_MOVING_UP:number = 7; var A4_ANIMATION_MOVING_RIGHT:number = 8; var A4_ANIMATION_MOVING_DOWN:number = 9; var A4_ANIMATION_INTERACTING:number = 10; var A4_ANIMATION_INTERACTING_LEFT:number = 11; var A4_ANIMATION_INTERACTING_UP:number = 12; var A4_ANIMATION_INTERACTING_RIGHT:number = 13; var A4_ANIMATION_INTERACTING_DOWN:number = 14; var A4_ANIMATION_TALKING:number = 15; var A4_ANIMATION_TALKING_LEFT:number = 16; var A4_ANIMATION_TALKING_UP:number = 17; var A4_ANIMATION_TALKING_RIGHT:number = 18; var A4_ANIMATION_TALKING_DOWN:number = 19; var A4_ANIMATION_DEATH:number = 32; var A4_ANIMATION_DEATH_LEFT:number = 21; var A4_ANIMATION_DEATH_UP:number = 22; var A4_ANIMATION_DEATH_RIGHT:number = 23; var A4_ANIMATION_DEATH_DOWN:number = 24; var A4_ANIMATION_CLOSED:number = 25; var A4_ANIMATION_IDLE_FULL:number = 26; var A4_ANIMATION_CLOSED_FULL:number = 27; var A4_N_ANIMATIONS:number = 28; // aliases: var A4_ANIMATION_OPEN:number = 0; var A4_ANIMATION_IDLE_EMPTY:number = 0; var A4_ANIMATION_OPEN_EMPTY:number = 0; var A4_ANIMATION_OPEN_FULL:number = 36; var A4_ANIMATION_CLOSED_EMPTY:number = 35; //var A4_LAYER_BG:number = 0; //var A4_LAYER_FG:number = 1; //var A4_LAYER_CHARACTERS:number = 2; var A4_N_LAYERS:number = 4; var A4_TILE_WALKABLE:number = 0; var A4_TILE_WALL:number = 1; var A4_TILE_TREE:number = 2; var A4_TILE_CHOPPABLE_TREE:number = 3; var A4_TILE_WATER:number = 4; //var A4_INVENTORY_SIZE:number = 8; var A4_INVENTORY_SIZE:number = 256; // some very large number var A4_N_MESSAGES_IN_HUD:number = 5; var A4_MAX_MESSAGE_LENGTH:number = 42; var CYCLES_IN_PERCEPTION_BUFFER:number = 50; var TEXT_INITIAL_DELAY:number = 60; var TEXT_SPEED:number = 8; var animationNames:string[] = [ "idle", "idle-left", "idle-up", "idle-right", "idle-down", "moving", "moving-left", "moving-up", "moving-right", "moving-down", "interacting", "interacting-left", "interacting-up", "interacting-right", "interacting-down", "talking", "talking-left", "talking-up", "talking-right", "talking-down", "death", "death-left", "death-up", "death-right", "death-down", "closed", "full", "closed-full" ]; var direction_x_inc:number[] = [-1,0,1,0]; var direction_y_inc:number[] = [0,-1,0,1]; class WarpRequest { constructor(o:A4Object, map:A4Map, x:number, y:number) {//, layer:number) { this.o = o; this.map = map; this.x = x; this.y = y; // this.layer = layer; } o:A4Object; map:A4Map; x:number; y:number; // layer:number; } class A4Game { constructor(xml:Element, game_path:string, ontology_path:string, GLTM:GLTManager, SFXM:SFXManager, a4of:A4ObjectFactory, a_sfx_volume:number) { // LOGICMOO ADDED (window)['theA4Game'] = this; this.objectFactory = a4of; this.loadContentFromXML(xml, game_path, ontology_path, GLTM, SFXM); this.sfx_volume = a_sfx_volume; } loadContentFromXML(xml:Element, game_path:string, ontology_path:string, GLTM:GLTManager, SFXM:SFXManager) { this.ontology = new Ontology(); Sort.clear(); this.screen_height_last = 1; this.screen_width_last = 1; let xmlhttp:XMLHttpRequest = new XMLHttpRequest(); xmlhttp.overrideMimeType("text/xml"); xmlhttp.open("GET", ontology_path, false); xmlhttp.send(); this.ontology.loadSortsFromXML(xmlhttp.responseXML.documentElement); A4Object.s_nextID = 10000; this.game_path = game_path; this.GLTM = GLTM; this.SFXM = SFXM; this.xml = xml; this.gameName = xml.getAttribute("name"); this.gameTitle = xml.getAttribute("title"); this.gameSubtitle = xml.getAttribute("subtitle"); console.log("game name: " + this.gameName); console.log("game title: " + this.gameTitle); console.log("game subtitle: " + this.gameSubtitle); let title_xml:Element = getFirstElementChildByTag(xml, "titleImage"); if (title_xml!=null) { console.log("Title image:" + title_xml.firstChild.nodeValue); this.gameTitleImage = title_xml.firstChild.nodeValue; } let tmp:string = xml.getAttribute("allowSaveGames"); if (tmp!=null) this.allowSaveGames = (tmp == "true"); if (xml.getAttribute("cycle") != null) { this.cycle = Number(xml.getAttribute("cycle")); } let story_xml:Element = getFirstElementChildByTag(xml, "story"); if (story_xml!=null) { let story_lines:Element[] = getElementChildrenByTag(story_xml,"line"); this.storyText = []; for(let i:number = 0;i = onstart_xml.children; let script_xml_l:HTMLCollection = onstart_xml.children; for(let j:number = 0;j= A4Object.s_nextID) A4Object.s_nextID = Number(id)+1; } p.loadObjectAdditionalContent(player_xml, this, this.objectFactory, objectsToRevisit_xml, objectsToRevisit_object); p.warp(x, y, this.maps[mapIdx]); this.players.push(p); } } // load messages: { let console_xml:Element = getFirstElementChildByTag(this.xml, "console"); if (console_xml != null) { for(let message_xml of getElementChildrenByTag(console_xml, "message")) { if (message_xml.getAttribute("timeStamp") != null) { this.addMessageWithColorTime(message_xml.getAttribute("text"), message_xml.getAttribute("color"), Number(message_xml.getAttribute("timeStamp"))); } else { this.addMessageWithColor(message_xml.getAttribute("text"), message_xml.getAttribute("color")); } } } } for(let i:number = 0;i 0) { this.currentPlayerIndex = 0; this.currentPlayer = this.players[this.currentPlayerIndex]; } this.gameComplete = false; this.gameComplete_ending_ID = null; this.ensureUniqueObjectIDs(); console.log("A4Game created\n"); console.log("currentPlayer = " + this.currentPlayer); } saveGame(saveName:string) { let complete_xmlString:string = "\n"; let xmlString:string = this.saveToXML(); console.log("A4Game.saveGame: game xmlString length " + xmlString.length); complete_xmlString += xmlString; for(let i:number = 0;i\n"; } if (this.storyText != null) { xmlString += "\n"; for(let text of this.storyText) { xmlString += "" + text + "\n"; } xmlString += "\n"; } for(let i:number = 0;i\n"; for(let text of this.endingTexts[i]) { xmlString += "" + text + "\n"; } xmlString += "\n"; } // tiles: xmlString+="\n"; for(let gf of this.graphicFiles) { xmlString += "\n" // xmlString += "\n" // for(let i:number = 0;i\n" // for(let i:number = 0;i\n" // for(let i:number = 0;i\n"; } for(let tmp of this.objectDefinitionFiles) { xmlString+="\n"; } // maps for(let idx:number = 0;idx\n"; } // players: for(let pc of this.players) { for(let idx:number = 0;idx\n"; } } */ for(let v in this.storyState) { if (!onStarttagOpen) { xmlString += "\n"; onStarttagOpen = true; } xmlString+="\n"; } if (onStarttagOpen) xmlString += "\n"; // each execution queue goes to its own "onStart" block: for(let seq of this.scriptQueues) { xmlString += "\n"; for(let s of seq.scripts) xmlString += s.saveToXML() + "\n"; xmlString += "\n"; } // rules: for(let i:number = 0;i\n"; } xmlString += "\n"; return xmlString; } saveToXML() : string { let xmlString:string = ""; xmlString += " 0) this.cycles_without_redrawing--; return true; } updateInternal(k:KeyboardState, additional_maps_to_update:A4Map[]) : boolean { if (this.cycle==0) { if (this.eventScripts[A4_EVENT_START] != null) { for(let rule of this.eventScripts[A4_EVENT_START]) { rule.executeEffects(null, null, this, null); } } } this.zoom = (0.95*this.zoom + 0.05*this.targetZoom); // update all the objects in the game: // figure out which maps to update: let maps_to_update:A4Map[] = []; for(let m of this.currentPlayer.map.getNeighborMaps()) { if (maps_to_update.indexOf(m) == -1) maps_to_update.push(m); } for(let m of additional_maps_to_update) { if (maps_to_update.indexOf(m) == -1) maps_to_update.push(m); } for(let player of this.players) { if (maps_to_update.indexOf(player.map)==-1) maps_to_update.push(player.map); } for(let map of maps_to_update) { map.update(this); } this.processWarpRequests(); for(let o of this.deletionRequests) { o.event(A4_EVENT_END, null, o.map, this); this.objectRemoved(o); } this.deletionRequests = []; // rules: if (this.eventScripts[A4_EVENT_TIMER]!=null) { for(let r of this.eventScripts[A4_EVENT_TIMER]) r.execute(null,null,this,null); } if (this.eventScripts[A4_EVENT_STORYSTATE]!=null) { for(let r of this.eventScripts[A4_EVENT_STORYSTATE]) r.execute(null,null,this,null); } if (this.currentPlayer==null) return false; this.executeScriptQueues(); return true; } processWarpRequests() { for(let wr of this.warpRequests) { let m:A4Map = wr.map; let createRecord:boolean = wr.o.isCharacter() || wr.o.isVehicle(); let acceptWarp:boolean = true; if (createRecord && m!=null && !m.walkableConsideringVehicles(wr.x, wr.y, wr.o.getPixelWidth(), wr.o.getPixelHeight(), wr.o)) acceptWarp = false; if (acceptWarp) { if (m!=null && createRecord) { wr.o.map.addPerceptionBufferObjectWarpedRecord( new PerceptionBufferObjectWarpedRecord(wr.o.ID, wr.o.sort, m.name, wr.o.x, wr.o.y, wr.o.x+wr.o.getPixelWidth(), wr.o.y+wr.o.getPixelHeight())); } wr.o.warp(wr.x,wr.y,wr.map);//,wr.layer); } else { // can't warp, since there is a collision! if (wr.o == this.currentPlayer) this.addMessage("Something is blocking the way!"); } } this.warpRequests = []; } draw(screen_width:number, screen_height:number) { let tileSize:number = (screen_height/24); let split:number = Math.floor(tileSize*17); // do not draw anything unless we have already executed a cycle: if (this.cycle==0) return; if (this.cycles_without_redrawing>0) return; this.drawWorld(screen_width, split+tileSize); this.drawHUD(screen_width, screen_height, split); } drawWorld(screen_width:number, screen_height:number) { this.screen_width_last = screen_width; this.screen_height_last = screen_height; if (this.currentPlayer!=null) { let map:A4Map = this.currentPlayer.map; let mapx:number = this.getCameraX(this.currentPlayer, map.width*this.tileWidth, screen_width); let mapy:number = this.getCameraY(this.currentPlayer, map.height*this.tileHeight, screen_height); let tx:number = Math.floor(this.currentPlayer.x/this.tileWidth); let ty:number = Math.floor(this.currentPlayer.y/this.tileHeight); map.drawRegion(mapx, mapy, this.zoom, screen_width, screen_height, map.visibilityRegion(tx,ty), this); map.drawTextBubblesRegion(mapx, mapy, this.zoom, screen_width, screen_height, map.visibilityRegion(tx,ty), this); } else { let map:A4Map = this.maps[0]; map.drawRegion(0, 0, this.zoom, screen_width, screen_height, map.visibilityRegion(0,0), this); map.drawTextBubbles(0, 0, this.zoom, screen_width, screen_height, this); } } drawHUD(screen_width:number, screen_height:number, split:number) { ctx.fillStyle = "black"; ctx.fillRect(0,split+PIXEL_SIZE,screen_width,(screen_height-split)); // this is game dependent, so, this function is empty: // ... } mouseClick(mouse_x: number, mouse_y: number, button: number) { if (mouse_y < PIXEL_SIZE*8*17) { // click in the game screen: this should skip text bubbles, etc. this.skipSpeechBubble(); if (this.currentPlayer!=null) { if(!(this.mouse_draw_skip > 0)) { this.mouse_draw_skip = 100; this.mouse_x_last = mouse_x; this.mouse_y_last = mouse_y; let map:A4Map = this.currentPlayer.map; let screen_width:number = this.screen_width_last; let screen_height:number = this.screen_height_last; let mapx:number = this.getCameraX(this.currentPlayer, map.width*this.tileWidth, screen_width); let mapy:number = this.getCameraY(this.currentPlayer, map.height*this.tileHeight, screen_height); let tx:number = Math.floor(mouse_x/this.tileWidth); let ty:number = Math.floor(mouse_y/this.tileHeight); let vr = map.visibilityRegion(tx,ty); // map.drawRegion(mapx, mapy, this.zoom, screen_width, screen_height, vr, this); console.log(`map.drawRegion(${mapx}, ${mapy}, ${this.zoom}, ${screen_width}, ${screen_height}, ${vr}, this);`); } console.log(`mouse_x/y= ${mouse_x - this.currentPlayer.x}/${mouse_y - this.currentPlayer.y} = ${button} `); } } if (mouse_x >= PIXEL_SIZE*8*27 && mouse_x < PIXEL_SIZE*8*28 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { this.playerInput_ToogleInventory(); } if (this.HUD_state == SHRDLU_HUD_STATE_INVENTORY) { // inventory window: if (mouse_x >= PIXEL_SIZE*8*29 && mouse_x < PIXEL_SIZE*8*30 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { this.HUD_inventory_start-=2; if (this.HUD_inventory_start<0) this.HUD_inventory_start = 0; if (this.currentPlayer.selectedItem>=0) { while(this.currentPlayer.selectedItem >= this.HUD_inventory_start+SHRDLU_INVENTORY_DISPLAY_SIZE) { this.currentPlayer.previousItem(); } } } if (mouse_x >= PIXEL_SIZE*8*30 && mouse_x < PIXEL_SIZE*8*31 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { if (this.currentPlayer.inventory.length > this.HUD_inventory_start+SHRDLU_INVENTORY_DISPLAY_SIZE) { this.HUD_inventory_start+=2; if (this.currentPlayer.selectedItem>=0) { while(this.currentPlayer.selectedItem < this.HUD_inventory_start) { this.currentPlayer.nextItem(); } } } } if (mouse_x < PIXEL_SIZE*8*12 && mouse_y >= PIXEL_SIZE*8*18) { let y:number = Math.floor((mouse_y-PIXEL_SIZE*8*18)/(PIXEL_SIZE*8)); let selected:number = this.HUD_inventory_start + y*2; if (selected < this.currentPlayer.inventory.length && this.currentPlayer.inventory[selected] != null) { this.currentPlayer.selectedItem = selected; } } if (mouse_x >= PIXEL_SIZE*8*14 && mouse_x < PIXEL_SIZE*8*26 && mouse_y >= PIXEL_SIZE*8*18) { let y:number = Math.floor((mouse_y-PIXEL_SIZE*8*18)/(PIXEL_SIZE*8)); let selected:number = this.HUD_inventory_start + 1 + y*2; if (selected < this.currentPlayer.inventory.length && this.currentPlayer.inventory[selected] != null) { this.currentPlayer.selectedItem = selected; } } // Use if (mouse_x >= PIXEL_SIZE*8*27 && mouse_y >= PIXEL_SIZE*8*19 && mouse_y < PIXEL_SIZE*8*20) { this.playerInput_UseItem(); } // Drop if (mouse_x >= PIXEL_SIZE*8*27 && mouse_y >= PIXEL_SIZE*8*21 && mouse_y < PIXEL_SIZE*8*22) { this.playerInput_DropItem(); } } else if (this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) { if (mouse_x < PIXEL_SIZE*8*12 && mouse_y >= PIXEL_SIZE*8*18) { let y:number = Math.floor((mouse_y-PIXEL_SIZE*8*18)/(PIXEL_SIZE*8)); let selected:number = this.HUD_inventory_start + y; if (selected < this.currentPlayer.inventory.length && this.currentPlayer.inventory[selected] != null) { this.currentPlayer.selectedItem = selected; this.HUD_remote_inventory_selected = -1; } } // select on the other inventory: if (mouse_x >= PIXEL_SIZE*8*20 && mouse_y >= PIXEL_SIZE*8*19) { let y:number = Math.floor((mouse_y-PIXEL_SIZE*8*19)/(PIXEL_SIZE*8)); let selected:number = this.HUD_remote_inventory_start + y; if (selected < this.HUD_remote_inventory.content.length && this.HUD_remote_inventory.content[selected] != null) { this.HUD_remote_inventory_selected = selected; this.currentPlayer.selectedItem = -1; } } // arrows (inventory): if (mouse_x >= PIXEL_SIZE*8*10 && mouse_x < PIXEL_SIZE*8*11 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { this.HUD_inventory_start--; if (this.HUD_inventory_start<0) this.HUD_inventory_start = 0; if (this.currentPlayer.selectedItem>=0) { while(this.currentPlayer.selectedItem >= this.HUD_inventory_start+SHRDLU_INVENTORY_DISPLAY_SIZE/2) { this.currentPlayer.previousItem(); } } } if (mouse_x >= PIXEL_SIZE*8*11 && mouse_x < PIXEL_SIZE*8*12 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { if (this.currentPlayer.inventory.length > this.HUD_inventory_start+SHRDLU_INVENTORY_DISPLAY_SIZE/2) { this.HUD_inventory_start++; if (this.currentPlayer.selectedItem>=0) { while(this.currentPlayer.selectedItem < this.HUD_inventory_start) { this.currentPlayer.nextItem(); } } } } // arrows (remote): if (mouse_x >= PIXEL_SIZE*8*29 && mouse_x < PIXEL_SIZE*8*30 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { this.HUD_remote_inventory_start--; if (this.HUD_remote_inventory_start<0) this.HUD_remote_inventory_start = 0; if (this.HUD_remote_inventory_selected>=0) { while(this.HUD_remote_inventory_selected >= this.HUD_remote_inventory_start+(SHRDLU_INVENTORY_DISPLAY_SIZE/2)-1) { this.HUD_remote_inventory_selected--; } } } if (mouse_x >= PIXEL_SIZE*8*30 && mouse_x < PIXEL_SIZE*8*31 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { if (this.HUD_remote_inventory.content.length > this.HUD_remote_inventory_start+(SHRDLU_INVENTORY_DISPLAY_SIZE/2)-1) { this.HUD_remote_inventory_start++; if (this.HUD_remote_inventory_selected>=0) { while(this.HUD_remote_inventory_selected < this.HUD_remote_inventory_start) { this.HUD_remote_inventory_selected++; } } } } // buttons: // take if (mouse_x >= PIXEL_SIZE*8*14 && mouse_x < PIXEL_SIZE*8*19 && mouse_y >= PIXEL_SIZE*8*19 && mouse_y < PIXEL_SIZE*8*20) { if (this.HUD_remote_inventory_selected>=0 && this.HUD_remote_inventory.content[this.HUD_remote_inventory_selected].takeable) { let item:A4Object = this.HUD_remote_inventory.content[this.HUD_remote_inventory_selected]; let idx:number = this.HUD_remote_inventory.content.indexOf(item); this.HUD_remote_inventory.content.splice(idx,1); this.HUD_remote_inventory.objectRemoved(item); this.currentPlayer.inventory.push(item); this.HUD_remote_inventory_selected = -1; this.playSound("data/sfx/itemPickup.wav"); } } // put if (mouse_x >= PIXEL_SIZE*8*14 && mouse_x < PIXEL_SIZE*8*19 && mouse_y >= PIXEL_SIZE*8*21 && mouse_y < PIXEL_SIZE*8*22) { if (this.currentPlayer.selectedItem>=0) { let item:A4Object = this.currentPlayer.inventory[this.currentPlayer.selectedItem]; let idx:number = this.currentPlayer.inventory.indexOf(item); this.currentPlayer.inventory.splice(idx,1); this.HUD_remote_inventory.addContent(item); this.currentPlayer.selectedItem = -1; this.playSound("data/sfx/itemPickup.wav"); } } } else { // message window: if (mouse_x >= PIXEL_SIZE*8*29 && mouse_x < PIXEL_SIZE*8*30 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { this.messageConsoleUp(); } if (mouse_x >= PIXEL_SIZE*8*30 && mouse_x < PIXEL_SIZE*8*31 && mouse_y >= PIXEL_SIZE*8*17 && mouse_y < PIXEL_SIZE*8*18) { this.messageConsoleDown(); } } } setGameComplete(gc:boolean, ID:string) { this.gameComplete = gc; this.gameComplete_ending_ID = ID; } executeScriptQueues() { let toDelete:A4ScriptExecutionQueue[] = []; for(let seb of this.scriptQueues) { while(true) { let s:A4Script = seb.scripts[0]; let retval:number = s.execute(seb.object, seb.map, (seb.game == null ? this:seb.game), seb.otherCharacter); if (retval==SCRIPT_FINISHED) { seb.scripts.splice(0,1); if (seb.scripts.length == 0) { toDelete.push(seb); break; } } else if (retval==SCRIPT_NOT_FINISHED) { break; } else if (retval==SCRIPT_FAILED) { toDelete.push(seb); break; } } } for(let seb of toDelete) { let idx:number = this.scriptQueues.indexOf(seb); this.scriptQueues.splice(idx, 1); } } addScriptQueue(seq: A4ScriptExecutionQueue) { this.scriptQueues.push(seq); } // if an object is removed from a map, this needs to be called, to notify // the game that this happened. objectRemoved(o:A4Object) { let idx:number = this.players.indexOf(o); if (idx>=0) this.players.splice(idx,1); if (this.currentPlayer == o) { this.currentPlayerIndex = 0 if (this.players.length>0) { this.currentPlayer = this.players[this.currentPlayerIndex]; } else { this.currentPlayer = null; } } for(let map of this.maps) map.objectRemoved(o); if (this.HUD_remote_inventory == o) { this.HUD_remote_inventory = null; if (this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) this.HUD_state = SHRDLU_HUD_STATE_INVENTORY; } } contains(o:A4Object) : boolean { for(let o2 of this.deletionRequests) if (o2==o) return false; for(let map of this.maps) if (map.contains(o)) return true; return false; } /* - Prevents the characters from accidentally going to a map that they should not go to - Should be overwriten by each specific game with any custom rules necessary */ checkPermissionToWarp(character:A4Character, target:A4Map) : boolean { return true; } // Teleports an object to a requested map and position. This queues up the request, // but it is not executed until at the end of a game cycle, to prevent this from // happening while we are still looping through lists of objects (concurrent modification) requestWarp(o:A4Object, map:A4Map, x:number, y:number)//, layer:number) { this.warpRequests.push(new WarpRequest(o,map,x,y)); } requestedWarp(o:A4Object) { for(let request of this.warpRequests) { if (request.o == o) return true; } return false; } // waits until the end of a cycle, and then deletes o requestDeletion(o:A4Object) { this.deletionRequests.push(o); } setStoryStateVariable(variable:string, value:string) { this.storyState[variable] = value; this.lastTimeStoryStateChanged = this.cycle; } getStoryStateVariable(variable:string) : string { return this.storyState[variable]; } playSound(sound:string) { this.SFXM.play(sound); } getGraphicFile(file:string) : A4GraphicFile { for(let gf of this.graphicFiles) { if (file == gf.name) return gf; } return null; } getMap(name:string) : A4Map { for(let m of this.maps) { if (name == m.name) return m; } return null; } getMapIndex(name:string) : number { for(let m of this.maps) { if (name == m.name) return this.maps.indexOf(m); } return -1; } getCameraX(focus:A4Object, map_width:number, screen_width:number) : number { let target_x:number = 0; if (map_width= 3 && text.substring(0,7) == "[ERROR:" && this.messages[this.messages.length-3][0].substring(0,7) == "[ERROR:") return; // split longer messages into different lines: let buffer:string = ""; let last_space:number = 0; for(let i:number=0;i=A4_MAX_MESSAGE_LENGTH) { if (last_space==0) { // a single word doesn't fit, just split it! this.messages.push([buffer,color,""+timeStamp]); buffer = ""; } else { let backspaces:number = i - last_space; let tmp:string = buffer.substring(0, buffer.length-backspaces); this.messages.push([tmp,color,""+timeStamp]); buffer = " " + buffer.substring((buffer.length-backspaces)); } } } if (buffer != "") this.messages.push([buffer,color,""+timeStamp]); } // message added only if the "originator" is in the same map as the "this.current_player" addMessageWithOriginator(originator:A4Object, msg:string) { if (this.currentPlayer.map != originator.map) return; this.addMessage(msg); } addMessageWithOriginatorAndColor(originator:A4Object, msg:string, color:string) { if (this.currentPlayer.map != originator.map) return; this.addMessageWithColor(msg, color); } // getting input form the player: playerInput_ToogleInventory() { if (this.HUD_state == SHRDLU_HUD_STATE_INVENTORY || this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) { this.HUD_state = SHRDLU_HUD_STATE_MESSAGES; this.HUD_remote_inventory = null; } else { this.HUD_state = SHRDLU_HUD_STATE_INVENTORY; } } playerInput_UseItem() { if (this.HUD_state == SHRDLU_HUD_STATE_INVENTORY) { if (!this.currentPlayer.isInVehicle() && this.currentPlayer.selectedItem>=0) { this.playerInput_issueCommand(A4CHARACTER_COMMAND_USE,app.game.currentPlayer.selectedItem,A4_DIRECTION_NONE); } } else if (this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) { if (!this.currentPlayer.isInVehicle() && this.HUD_remote_inventory_selected>=0 && this.HUD_remote_inventory.content[this.HUD_remote_inventory_selected].takeable) { let item:A4Object = this.HUD_remote_inventory.content[this.HUD_remote_inventory_selected]; let idx:number = this.HUD_remote_inventory.content.indexOf(item); this.HUD_remote_inventory.content.splice(idx,1); this.HUD_remote_inventory.objectRemoved(item); this.currentPlayer.inventory.push(item); this.HUD_remote_inventory_selected = -1; this.playSound("data/sfx/itemPickup.wav"); } } } playerInput_DropItem() { if (this.HUD_state == SHRDLU_HUD_STATE_INVENTORY) { if (!this.currentPlayer.isInVehicle() && this.currentPlayer.selectedItem>=0) { let item:A4Object = this.currentPlayer.inventory[this.currentPlayer.selectedItem]; if (item!=null && (item).droppable) { this.playerInput_issueCommand(A4CHARACTER_COMMAND_DROP,app.game.currentPlayer.selectedItem,null); this.currentPlayer.selectedItem = -1; } } } else if (this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) { if (!this.currentPlayer.isInVehicle() && this.currentPlayer.selectedItem>=0) { let item:A4Object = this.currentPlayer.inventory[this.currentPlayer.selectedItem]; if (item!=null && (item).droppable) { let item:A4Object = this.currentPlayer.inventory[this.currentPlayer.selectedItem]; let idx:number = this.currentPlayer.inventory.indexOf(item); this.currentPlayer.inventory.splice(idx,1); this.HUD_remote_inventory.addContent(item); this.currentPlayer.selectedItem = -1; this.playSound("data/sfx/itemPickup.wav"); } } } } playerInput_NextItem() { this.currentPlayer.nextItem(); } messageConsoleUp() { if (this.HUD_state == SHRDLU_HUD_STATE_INVENTORY) { this.currentPlayer.previousItem(); } else if (this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) { if (this.currentPlayer.selectedItem == 0) { if (this.HUD_remote_inventory.content.length>0) { this.currentPlayer.selectedItem =-1; this.HUD_remote_inventory_selected = this.HUD_remote_inventory.content.length-1; } else { this.currentPlayer.previousItem(); } } else if (this.currentPlayer.selectedItem > 0) { this.currentPlayer.previousItem(); } else if (this.HUD_remote_inventory_selected == 0) { this.HUD_remote_inventory_selected = -1; this.currentPlayer.previousItem(); } else if (this.HUD_remote_inventory_selected > 0) { this.HUD_remote_inventory_selected--; } else { if (this.currentPlayer.inventory.length>0) { this.currentPlayer.previousItem(); } else { if (this.HUD_remote_inventory.content.length>0) { this.HUD_remote_inventory_selected = this.HUD_remote_inventory.content.length-1; } } } } else { if (this.console_first_message>0) this.console_first_message--; if (this.console_first_message==-1 && this.messages.length>A4_N_MESSAGES_IN_HUD) this.console_first_message = this.messages.length-(A4_N_MESSAGES_IN_HUD+1); } } messageConsoleDown() { if (this.HUD_state == SHRDLU_HUD_STATE_INVENTORY) { this.currentPlayer.nextItem(); } else if (this.HUD_state == SHRDLU_HUD_STATE_SPLIT_INVENTORY) { if (this.currentPlayer.selectedItem>=0 && this.currentPlayer.selectedItem == this.currentPlayer.inventory.length-1) { if (this.HUD_remote_inventory.content.length>0) { this.currentPlayer.selectedItem =-1; this.HUD_remote_inventory_selected = 0; } else { this.currentPlayer.nextItem(); } } else if (this.currentPlayer.selectedItem>=0 && this.currentPlayer.selectedItem < this.currentPlayer.inventory.length-1) { this.currentPlayer.nextItem(); } else if (this.HUD_remote_inventory_selected>=0 && this.HUD_remote_inventory_selected == this.HUD_remote_inventory.content.length-1) { this.HUD_remote_inventory_selected = -1; this.currentPlayer.nextItem(); } else if (this.HUD_remote_inventory_selected >= 0 && this.HUD_remote_inventory_selected < this.HUD_remote_inventory.content.length-1) { this.HUD_remote_inventory_selected++; } else { if (this.currentPlayer.inventory.length>0) { this.currentPlayer.nextItem(); } else { if (this.HUD_remote_inventory.content.length>0) { this.HUD_remote_inventory_selected = 0; } } } } else { if (this.console_first_message!=-1) { if (this.console_first_messageo).AI; if (ai.isUnfriendly(this.currentPlayer.ID)) { // attack! this.currentPlayer.issueCommandWithArguments(A4CHARACTER_COMMAND_ATTACK, arg, direction, o, this); return A4CHARACTER_COMMAND_ATTACK; } // if (ai.conversationGraph!=null) { // // talk: // // don't issue anything, the calling code will trigger the talk dialog // return A4CHARACTER_COMMAND_TALK; // } this.currentPlayer.issueCommandWithArguments(cmd, arg, direction, null, this); return cmd; } } } } } */ this.currentPlayer.issueCommandWithArguments(cmd, arg, direction, null, this); return cmd; } playerInput_issueCommandWithOther(cmd:number, arg:number, target:A4Object) { this.currentPlayer.issueCommandWithArguments(cmd, arg, 0, target, this); } // This function returns a list with the hierarchy of objects necessary to find the desired object // For example, if an object is directly in a map, the list with be length 1, but if // the object is in the inventory of a character, then we will get a list with the character and then the object findObjectByName(name:string) : A4Object[] { for(let m of this.maps) { let o2:A4Object[] = m.findObjectByName(name); if (o2!=null) return o2; } return null; } findObjectByIDJustObject(ID:string) : A4Object { let tmp:A4Object[] = this.findObjectByID(ID); if (tmp == null) return null; return tmp[tmp.length-1]; } // This function returns a list with the hierarchy of objects necessary to find the desired object // For example, if an object is directly in a map, the list with be length 1, but if // the object is in the inventory of a character, then we will get a list with the character and then the object findObjectByID(ID:string) : A4Object[] { for(let m of this.maps) { let o2:A4Object[] = m.findObjectByID(ID); if (o2!=null) return o2; } return null; } skipSpeechBubble() : boolean { if (this.currentPlayer.map.textBubbles.length > 0) { this.currentPlayer.map.textBubbles[0][1] = 0; return true; } if (this.currentPlayer.talkingBubble != null) { this.currentPlayer.stateCycle = this.currentPlayer.talkingBubbleDuration; return true; } return false; } /* Checks to see if there is any repeated IDs, and returns false if there are any repeated, pringing info about them */ ensureUniqueObjectIDs() { let IDs:string[] = []; for(let map of this.maps) { for(let object of map.objects) { let ID:string = object.ID; if (IDs.indexOf(ID) >= 0) { console.error("Repeated ID: " + ID + " in map " + map.name); } IDs.push(ID); if (object instanceof A4Character) { for(let o2 of (object).inventory) { let ID2:string = o2.ID; if (IDs.indexOf(ID2) >= 0) { console.error("Repeated ID: " + ID2 + " in map " + map.name); } IDs.push(ID2); } } if (object instanceof A4Container) { for(let o2 of (object).content) { let ID2:string = o2.ID; if (IDs.indexOf(ID2) >= 0) { console.error("Repeated ID: " + ID2 + " in map " + map.name); } IDs.push(ID2); } } } } //console.log("IDs: " + IDs); } // To be overwriten by each individual game checkCustomVehicleCollisionEvents(vehicle:A4Vehicle) { } xml:Element = null; // the XML definition of the game sfx_volume:number; drawTextBubbles:boolean = true; gameName:string = null; gameTitle:string = null; gameSubtitle:string = null; gameTitleImage:string = null; storyText:string[] = null; endingIDs:string[] = []; endingTexts:string[][] = []; allowSaveGames:boolean = false; characterDefinitionFiles:string[] = []; objectDefinitionFiles:string[] = []; cycle:number = 0; cycles_without_redrawing:number = 0; in_game_seconds:number = 0; gameComplete:boolean = false; gameComplete_ending_ID:string = null; game_path:string = null; GLTM:GLTManager = null; SFXM:SFXManager = null; graphicFiles:A4GraphicFile[] = []; objectFactory:A4ObjectFactory = null; tileWidth:number = 16; tileHeight:number = 16; mapTiles:{[ID:number] : A4MapTile } = {}; // AI: ontology:Ontology = new Ontology(); // HUD: HUD_state:number = SHRDLU_HUD_STATE_MESSAGES; messages:string[][] = []; // [text, color, timestamp] trade_requested:A4Character = null; console_first_message:number = -1; HUD_hseparator:GLTile = null; HUD_vseparator:GLTile = null; HUD_tseparator:GLTile = null; HUD_uparrow1:GLTile = null; HUD_uparrow2:GLTile = null; HUD_downarrow1:GLTile = null; HUD_downarrow2:GLTile = null; HUD_button1:GLTile = null; HUD_button2:GLTile = null; HUD_oxygen:GLTile = null; HUD_oxygen_bar:GLTile = null; HUD_inventory_start:number = 0; HUD_remote_inventory_start:number = 0; HUD_remote_inventory:A4Container = null; HUD_remote_inventory_selected:number = -1; HUD_text_input_buffer:string = ""; HUD_text_input_cursor:number = 0; inputBufferHistory:string[] = []; // messages typed by the player, so that she can browse it quickly ussing up/down inputBufferHistory_position:number = -1; lastInputBufferBeforeBrowsingHistory:string = null; maps:A4Map[] = []; players:A4PlayerCharacter[] = []; currentPlayer:A4PlayerCharacter = null; currentPlayerIndex:number = 0; warpRequests:WarpRequest[] = []; deletionRequests:A4Object[] = []; // camera: zoom:number = 1; targetZoom:number = 1; defaultZoom:number = 1; // scripts: eventScripts:A4EventRule[][] = new Array(A4_NEVENTS); // script excution queues (these contain scripts that are pending execution, will be executed in the next "cycle"): scriptQueues: A4ScriptExecutionQueue[] = []; // story state: storyState: { [id: string] : string; } = {}; lastTimeStoryStateChanged:number = 0; // error logging (the engine just logs them into these arrays, it's up to the specific game to do // something with these): errorMessagesForLog:string[][] = []; inGameActionsForLog:string[][] = []; debugTextBubbleLog:[number,string,A4TextBubble][] = null; // this is already defined in A4Game screen_width_last:number = -1; screen_height_last:number = -1; mouse_x_last:number = -1; mouse_y_last:number = -1; mouse_draw_skip:number = -1; }