#include "Player.h" #include #include "Globals.h" void potionEffect(int type) { if(type == 1) { UnderStrengthEffect = true; } if(type == 2) { UnderSpeedEffect = true; } if (type == 3) { regening = true; } if (type == 4) { UnderSwimBreathEffect = true; } } void initPlayers() { for(int i=0; ientity.type = ENTITY_PLAYER; pd->entity.level = 1; pd->entity.xr = 4; pd->entity.yr = 3; pd->entity.canSwim = true; pd->entity.p.ax = 0; pd->entity.p.ay = 0; pd->entity.p.health = 10; pd->entity.p.stamina = 10; pd->entity.p.walkDist = 0; pd->entity.p.attackTimer = 0; pd->entity.p.dir = 0; pd->entity.p.isDead = false; pd->entity.p.hasWon = false; pd->entity.p.data = pd; } void playerInitInventory(PlayerData *pd) { //reset inventory pd->inventory.lastSlot = 0; pd->activeItem = &noItem; addItemToInventory(newItem(ITEM_WORKBENCH,0), &(pd->inventory)); addItemToInventory(newItem(ITEM_POWGLOVE,0), &(pd->inventory)); if(TESTGODMODE) { addItemToInventory(newItem(ITEM_GOLD_APPLE,1), &(pd->inventory)); addItemToInventory(newItem(ITEM_STRENGTH_POTION,1), &(pd->inventory)); addItemToInventory(newItem(ITEM_REGEN_POTION,1), &(pd->inventory)); addItemToInventory(newItem(ITEM_SWIM_BREATH_POTION,1), &(pd->inventory)); addItemToInventory(newItem(ITEM_SPEED_POTION,1), &(pd->inventory)); addItemToInventory(newItem(ITEM_POTION_MAKER,0), &(pd->inventory)); addItemToInventory(newItem(TOOL_SHOVEL,1), &(pd->inventory)); addItemToInventory(newItem(TOOL_HOE,4), &(pd->inventory)); addItemToInventory(newItem(TOOL_SWORD,4), &(pd->inventory)); addItemToInventory(newItem(TOOL_PICKAXE,4), &(pd->inventory)); addItemToInventory(newItem(TOOL_AXE,4), &(pd->inventory)); addItemToInventory(newItem(ITEM_ANVIL,0), &(pd->inventory)); addItemToInventory(newItem(ITEM_CHEST,0), &(pd->inventory)); addItemToInventory(newItem(ITEM_OVEN,0), &(pd->inventory)); addItemToInventory(newItem(ITEM_FURNACE,0), &(pd->inventory)); addItemToInventory(newItem(ITEM_LANTERN,0), &(pd->inventory)); addItemToInventory(newItem(TOOL_MAGIC_COMPASS,1), &(pd->inventory)); int i; for (i = 7;i < 28;++i) addItemToInventory(newItem(i,50), &(pd->inventory)); } } void playerInitSprite(PlayerData *pd) { pd->sprite.choosen = false; pd->sprite.legs = 0; pd->sprite.body = 0; pd->sprite.arms = 0; pd->sprite.head = 0; pd->sprite.eyes = 0; } void playerInitMenus(PlayerData *pd) { pd->ingameMenu = MENU_NONE; pd->ingameMenuSelection = 0; pd->ingameMenuInvSel = 0; pd->ingameMenuInvSelOther = 0; pd->ingameMenuAreYouSure = false; pd->ingameMenuAreYouSureSave = false; pd->ingameMenuTimer = 0; resetNPCMenuData(&(pd->npcMenuData)); pd->mapShouldRender = false; pd->mapScrollX = 0; pd->mapScrollY = 0; pd->mapZoomLevel = 2; sprintf(pd->mapText,"x%d", pd->mapZoomLevel); pd->touchLastX = -1; pd->touchLastY = -1; pd->touchIsDraggingMap = false; pd->touchIsChangingSize = false; } void initPlayer(PlayerData *pd) { pd->isSpawned = false; playerInitMiniMapData(pd->minimapData); playerInitEntity(pd); playerInitInventory(pd); playerInitSprite(pd); initQuests(&(pd->questManager)); resetQuests(&(pd->questManager)); playerInitMenus(pd); pd->score = 0; } void freePlayer(PlayerData *pd) { freeQuests(&(pd->questManager)); } PlayerData* getNearestPlayer(s8 level, s16 x, s16 y) { int nearest = -1; unsigned int nearestDist = UINT_MAX; for(int i=0; iactiveItem->id){ //shooting arrows case TOOL_BOW: item = getItemFromInventory(ITEM_ARROW_WOOD, &(pd->inventory)); if(item!=NULL) { aitemID = ITEM_ARROW_WOOD; aitem = item; } item = getItemFromInventory(ITEM_ARROW_STONE, &(pd->inventory)); if(item!=NULL) { aitemID = ITEM_ARROW_STONE; aitem = item; } item = getItemFromInventory(ITEM_ARROW_IRON, &(pd->inventory)); if(item!=NULL) { aitemID = ITEM_ARROW_IRON; aitem = item; } item = getItemFromInventory(ITEM_ARROW_GOLD, &(pd->inventory)); if(item!=NULL) { aitemID = ITEM_ARROW_GOLD; aitem = item; } item = getItemFromInventory(ITEM_ARROW_GEM, &(pd->inventory)); if(item!=NULL) { aitemID = ITEM_ARROW_GEM; aitem = item; } if(aitemID!=0) { --aitem->countLevel; if (isItemEmpty(aitem)) { removeItemFromInventory(aitem->slotNum, &(pd->inventory)); } switch(pd->entity.p.dir) { case 0: addEntityToList(newArrowEntity(&(pd->entity), aitemID, 0, 2, pd->entity.level), &eManager); break; case 1: addEntityToList(newArrowEntity(&(pd->entity), aitemID, 0, -2, pd->entity.level), &eManager); break; case 2: addEntityToList(newArrowEntity(&(pd->entity), aitemID, -2, 0, pd->entity.level), &eManager); break; case 3: addEntityToList(newArrowEntity(&(pd->entity), aitemID, 2, 0, pd->entity.level), &eManager); break; } return true; } break; // Health items case ITEM_APPLE: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 2)){ playerHeal(pd, 1); --(pd->activeItem->countLevel); } break; case ITEM_STRENGTH_POTION: if(pd->entity.p.health < 20 && pd->entity.p.strengthTimer == 0){ potionEffect(1); --(pd->activeItem->countLevel); } return 0; case ITEM_SPEED_POTION: if(pd->entity.p.health < 20 && pd->entity.p.speedTimer == 0){ potionEffect(2); --(pd->activeItem->countLevel); } return 0; case ITEM_REGEN_POTION: if(pd->entity.p.health < 20 && pd->entity.p.regenTimer == 0){ potionEffect(3); --(pd->activeItem->countLevel); } return 0; case ITEM_SWIM_BREATH_POTION: if(pd->entity.p.health < 20 && pd->entity.p.swimBreathTimer == 0){ potionEffect(4); --(pd->activeItem->countLevel); } return 0; case ITEM_GOLD_APPLE: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 1)){ playerHeal(pd, 8); playerUseEnergy(pd, -10); --(pd->activeItem->countLevel); } return 0; case ITEM_FLESH: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ playerHeal(pd, 1); --(pd->activeItem->countLevel); } break; case ITEM_BREAD: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ playerHeal(pd, 2); --(pd->activeItem->countLevel); } break; case ITEM_PORK_RAW: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ playerHeal(pd, 1); --(pd->activeItem->countLevel); } break; case ITEM_PORK_COOKED: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ playerHeal(pd, 3); --(pd->activeItem->countLevel); } break; case ITEM_BEEF_RAW: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 4+(rand()%4))){ playerHeal(pd, 1); --(pd->activeItem->countLevel); } break; case ITEM_BEEF_COOKED: if(pd->entity.p.health < 10 && playerUseEnergy(pd, 3)){ playerHeal(pd, 4); --(pd->activeItem->countLevel); } break; //special item case ITEM_WIZARD_SUMMON: if(pd->entity.level==0) { --(pd->activeItem->countLevel); airWizardHealthDisplay = 2000; addEntityToList(newAirWizardEntity(630, 820, 0), &eManager); } break; } if (isItemEmpty(pd->activeItem)) { removeItemFromInventory(pd->activeItem->slotNum, &(pd->inventory)); pd->activeItem = &noItem; } return false; } bool playerInteract(PlayerData *pd, int x0, int y0, int x1, int y1) { Entity * es[eManager.lastSlot[pd->entity.level]]; int eSize = getEntities(es, pd->entity.level, x0, y0, x1, y1); int i; for (i = 0; i < eSize; ++i) { Entity * ent = es[i]; if (ent != &(pd->entity)){ if (ItemVsEntity(pd, pd->activeItem, ent, pd->entity.p.dir)) return true; } } return false; } void playerAttack(PlayerData *pd) { bool done = false; pd->entity.p.attackTimer = 5; int yo = -2; int range = 12; //directly using an item if(playerUseItem(pd)) return; //interacting with entities switch(pd->entity.p.dir){ case 0: if(playerInteract(pd, pd->entity.x - 8, pd->entity.y + 4 + yo, pd->entity.x + 8, pd->entity.y + range + yo)) return; break; case 1: if(playerInteract(pd, pd->entity.x - 8, pd->entity.y - range + yo, pd->entity.x + 8, pd->entity.y - 4 + yo)) return; break; case 2: if(playerInteract(pd, pd->entity.x - range, pd->entity.y - 8 + yo, pd->entity.x - 4, pd->entity.y + 8 + yo)) return; break; case 3: if(playerInteract(pd, pd->entity.x + 4, pd->entity.y - 8 + yo, pd->entity.x + range, pd->entity.y + 8 + yo)) return; break; } int xt = pd->entity.x >> 4; int yt = (pd->entity.y + yo) >> 4; int r = 12; if (pd->entity.p.dir == 0) yt = (pd->entity.y + r + yo) >> 4; if (pd->entity.p.dir == 1) yt = (pd->entity.y - r + yo) >> 4; if (pd->entity.p.dir == 2) xt = (pd->entity.x - r) >> 4; if (pd->entity.p.dir == 3) xt = (pd->entity.x + r) >> 4; //interacting with tiles if (xt >= 0 && yt >= 0 && xt < 128 && yt < 128) { s8 itract = itemTileInteract(getTile(pd->entity.level, xt, yt), pd, pd->activeItem, pd->entity.level, xt, yt, pd->entity.x, pd->entity.y, pd->entity.p.dir); if(itract > 0){ if(itract==2) pd->entity.p.isCarrying = false; done = true; } if (pd->activeItem != &noItem && isItemEmpty(pd->activeItem)) { removeItemFromInventory(pd->activeItem->slotNum, &(pd->inventory)); pd->activeItem = &noItem; } } if(done) return; //breaking tiles if (pd->activeItem == &noItem || pd->activeItem->id == TOOL_SWORD || pd->activeItem->id == TOOL_AXE) { if (xt >= 0 && yt >= 0 && xt < 128 && 128) { playerHurtTile(pd, getTile(pd->entity.level, xt, yt), pd->entity.level, xt, yt, (rand()%3) + 1, pd->entity.p.dir); } } } bool playerUseArea(PlayerData *pd, int x0, int y0, int x1, int y1) { Entity * entities[eManager.lastSlot[pd->entity.level]]; int i; int ae = getEntities(entities, pd->entity.level, x0, y0, x1, y1); for(i = 0; i < ae; ++i){ if(useEntity(pd, entities[i])) return true; } return false; } bool playerUse(PlayerData *pd) { int yo = -2; if (pd->entity.p.dir == 0 && playerUseArea(pd, pd->entity.x - 8, pd->entity.y + 4 + yo, pd->entity.x + 8, pd->entity.y + 12 + yo)) return true; if (pd->entity.p.dir == 1 && playerUseArea(pd, pd->entity.x - 8, pd->entity.y - 12 + yo, pd->entity.x + 8, pd->entity.y - 4 + yo)) return true; if (pd->entity.p.dir == 3 && playerUseArea(pd, pd->entity.x + 4, pd->entity.y - 8 + yo, pd->entity.x + 12, pd->entity.y + 8 + yo)) return true; if (pd->entity.p.dir == 2 && playerUseArea(pd, pd->entity.x - 12, pd->entity.y - 8 + yo, pd->entity.x - 4, pd->entity.y + 8 + yo)) return true; return false; } void tickPlayer(PlayerData *pd, bool inmenu) { if (pd->entity.p.isDead) return; //invincibility time if (pd->entity.hurtTime > 0) pd->entity.hurtTime--; //stamina recharging bool swimming = isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4); if (pd->entity.p.stamina <= 0 && pd->entity.p.staminaRechargeDelay == 0 && pd->entity.p.staminaRecharge == 0) { pd->entity.p.staminaRechargeDelay = 40; } if (pd->entity.p.staminaRechargeDelay > 0) { --pd->entity.p.staminaRechargeDelay; } if (pd->entity.p.staminaRechargeDelay == 0) { ++pd->entity.p.staminaRecharge; if (swimming) pd->entity.p.staminaRecharge = 0; while (pd->entity.p.staminaRecharge > 10) { pd->entity.p.staminaRecharge -= 10; if (pd->entity.p.stamina < 10) ++pd->entity.p.stamina; } } if(!inmenu) { if(!pd->sprite.choosen) { pd->ingameMenu = MENU_CHARACTER_CUSTOMIZE; pd->ingameMenuSelection = 0; return; } //movement pd->entity.p.ax = 0; pd->entity.p.ay = 0; if (pd->inputs.k_left.down){ pd->entity.p.ax -= 1; pd->entity.p.dir = 2; ++pd->entity.p.walkDist; } if (pd->inputs.k_right.down){ pd->entity.p.ax += 1; pd->entity.p.dir = 3; ++pd->entity.p.walkDist; } if (pd->inputs.k_up.down){ pd->entity.p.ay -= 1; pd->entity.p.dir = 1; ++pd->entity.p.walkDist; } if (pd->inputs.k_down.down){ pd->entity.p.ay += 1; pd->entity.p.dir = 0; ++pd->entity.p.walkDist; } if (pd->entity.p.staminaRechargeDelay % 2 == 0) moveMob(&(pd->entity), pd->entity.p.ax, pd->entity.p.ay); //"pausing", TODO: since multiplayer this will no longer pause if (pd->inputs.k_pause.clicked){ pd->ingameMenuSelection = 0; pd->ingameMenu = MENU_PAUSED; } //attacking if(pd->inputs.k_attack.clicked){ if (pd->entity.p.stamina != 0) { if(!TESTGODMODE) pd->entity.p.stamina--; pd->entity.p.staminaRecharge = 0; playerAttack(pd); } } if (pd->inputs.k_menu.clicked){ pd->ingameMenuInvSel = 0; if(!playerUse(pd)) pd->ingameMenu = MENU_INVENTORY; } } //swimming stamina and drowning if (swimming && pd->entity.p.swimTimer % 60 == 0) { if (pd->entity.p.stamina > 0) { if(!TESTGODMODE) --pd->entity.p.stamina; } else { hurtEntity(&(pd->entity), 1, -1, 0xFFAF00FF, NULL); } } if(isWater(pd->entity.level, pd->entity.x>>4, pd->entity.y>>4)) ++pd->entity.p.swimTimer; if(pd->entity.p.attackTimer > 0) --pd->entity.p.attackTimer; //TODO - maybe move to own function //Update Minimap int xp; int yp; for(xp = (pd->entity.x>>4)-5; xp<(pd->entity.x>>4)+5; ++xp) { for(yp = (pd->entity.y>>4)-5; yp<(pd->entity.y>>4)+5; ++yp) { if(xp>=0 && xp<128 && yp>=0 && yp<128) { if(!getMinimapVisible(pd, pd->entity.level, xp, yp)) { setMinimapVisible(pd, pd->entity.level, xp, yp, true); } } } } } void playerSetActiveItem(PlayerData *pd, Item *item) { pd->activeItem = item; if(pd->activeItem->id > 27 && pd->activeItem->id < 51) pd->entity.p.isCarrying = true; else pd->entity.p.isCarrying = false; } bool playerUseEnergy(PlayerData *pd, int amount) { if(TESTGODMODE) return true; if(amount > pd->entity.p.stamina) return false; pd->entity.p.stamina -= amount; return true; } void playerHeal(PlayerData *pd, int amount) { pd->entity.p.health += amount; if(pd->entity.p.health > 10) pd->entity.p.health = 10; char healText[11]; sprintf(healText, "%d", amount); addEntityToList(newTextParticleEntity(healText,0xFF00FF00, pd->entity.x, pd->entity.y, pd->entity.level), &eManager); } void playerSpawn(PlayerData *pd) { while(true){ int rx = rand()%128; int ry = rand()%128; if(getTile(pd->entity.level, rx, ry) == TILE_GRASS){ pd->entity.x = (rx << 4) + 8; pd->entity.y = (ry << 4) + 8; pd->isSpawned = true; break; } } }